diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ff5412af6..75d475824 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -32,7 +32,7 @@ build_clazy_clang_tidy: - apt-get update - apt-get install --yes eatmydata - eatmydata apt-get build-dep --yes --no-install-recommends okular - - eatmydata apt-get install --yes --no-install-recommends ninja-build clazy clang clang-tidy libkf5crash-dev libkf5purpose-dev libegl-dev jq + - eatmydata apt-get install --yes --no-install-recommends ninja-build clazy clang clang-tidy libkf5crash-dev libkf5purpose-dev kirigami2-dev libegl-dev jq script: - srcdir=`pwd` && mkdir -p /tmp/okular_build && cd /tmp/okular_build && CC=clang CXX=clazy CXXFLAGS="-Werror -Wno-deprecated-declarations" cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -G Ninja $srcdir && cat compile_commands.json | jq '[.[] | select(.file | contains("'"$srcdir"'"))]' > compile_commands.aux.json && cat compile_commands.aux.json | jq '[.[] | select(.file | contains("/synctex/")| not)]' > compile_commands.json && cp "$srcdir/.clang-tidy" . diff --git a/CMakeLists.txt b/CMakeLists.txt index d561384a8..258722df0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -169,6 +169,11 @@ add_definitions(-DKF_DEPRECATED_WARNINGS_SINCE=0x054400) include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${PHONON_INCLUDES} core/synctex ${CMAKE_BINARY_DIR}/core) +if(BUILD_MOBILE AND KF5Kirigami2_VERSION VERSION_LESS "5.85") + message(FATAL_ERROR "Disable mobile build. This requires Kirigami 5.85 or greater.") + set(BUILD_MOBILE OFF) +endif() + if(BUILD_MOBILE) add_subdirectory( mobile ) endif() diff --git a/mobile/app/app.qrc b/mobile/app/app.qrc index 6cdc30de6..8482b4142 100644 --- a/mobile/app/app.qrc +++ b/mobile/app/app.qrc @@ -7,7 +7,8 @@ package/contents/ui/TableOfContents.qml package/contents/ui/Thumbnails.qml package/contents/ui/ThumbnailsBase.qml -package/contents/ui/TreeDelegate.qml package/contents/ui/PlaceholderMessage.qml +package/contents/ui/TreeItem.qml +package/contents/ui/TreeViewDecoration.qml diff --git a/mobile/app/package/contents/ui/Bookmarks.qml b/mobile/app/package/contents/ui/Bookmarks.qml index 4a7ac839a..fb272c61f 100644 --- a/mobile/app/package/contents/ui/Bookmarks.qml +++ b/mobile/app/package/contents/ui/Bookmarks.qml @@ -5,9 +5,22 @@ */ import QtQuick 2.1 +import org.kde.kirigami 2.8 as Kirigami ThumbnailsBase { + id: root + header: Kirigami.AbstractApplicationHeader { + topPadding: Kirigami.Units.largeSpacing + bottomPadding: Kirigami.Units.largeSpacing + rightPadding: Kirigami.Units.largeSpacing + leftPadding: Kirigami.Units.largeSpacing + Kirigami.Heading { + level: 2 + text: i18n("Bookmarks") + width: parent.width + } + } model: documentItem.bookmarkedPages onPageClicked: { pageArea.delegate.pageItem.goToBookmark(pageArea.delegate.pageItem.bookmarks[0]) diff --git a/mobile/app/package/contents/ui/OkularDrawer.qml b/mobile/app/package/contents/ui/OkularDrawer.qml index 461d55c80..4b3a2eb67 100644 --- a/mobile/app/package/contents/ui/OkularDrawer.qml +++ b/mobile/app/package/contents/ui/OkularDrawer.qml @@ -6,8 +6,9 @@ import QtQuick 2.1 import QtQuick.Controls 2.5 as QQC2 -import org.kde.kirigami 2.0 as Kirigami +import org.kde.kirigami 2.15 as Kirigami import org.kde.okular 2.0 as Okular +import QtQuick.Layouts 1.15 Kirigami.OverlayDrawer { @@ -17,20 +18,14 @@ Kirigami.OverlayDrawer { rightPadding: 0 edge: Qt.application.layoutDirection == Qt.RightToLeft ? Qt.LeftEdge : Qt.RightEdge - contentItem: Item { + contentItem: ColumnLayout { id: browserFrame - implicitWidth: Kirigami.Units.gridUnit * 45 - implicitHeight: implicitWidth - state: "Hidden" + spacing: 0 QQC2.StackView { id: pageStack - anchors { - left: parent.left - top: parent.top - right: parent.right - bottom: tabsToolbar.top - } + Layout.fillWidth: true + Layout.fillHeight: true clip: true } @@ -43,14 +38,8 @@ Kirigami.OverlayDrawer { QQC2.ToolBar { id: tabsToolbar - height: mainTabBar.height position: QQC2.ToolBar.Footer - anchors { - top: undefined - bottom: browserFrame.bottom - left: parent.left - right: parent.right - } + Layout.fillWidth: true Component.onCompleted: thumbnailsButton.checked = true; Item { width: parent.width diff --git a/mobile/app/package/contents/ui/TableOfContents.qml b/mobile/app/package/contents/ui/TableOfContents.qml index 71180dc9d..9a07ecf1a 100644 --- a/mobile/app/package/contents/ui/TableOfContents.qml +++ b/mobile/app/package/contents/ui/TableOfContents.qml @@ -7,29 +7,37 @@ import QtQuick 2.1 import QtQuick.Controls 2.2 as QQC2 import QtQuick.Layouts 1.2 -import org.kde.kirigami 2.0 as Kirigami +import org.kde.kirigami 2.10 as Kirigami +import org.kde.kitemmodels 1.0 -Kirigami.ScrollablePage { +ColumnLayout { id: root + Kirigami.AbstractApplicationHeader { + topPadding: Kirigami.Units.smallSpacing / 2; + bottomPadding: Kirigami.Units.smallSpacing / 2; + rightPadding: Kirigami.Units.smallSpacing + leftPadding: Kirigami.Units.smallSpacing - header: QQC2.ToolBar { - id: toolBarContent width: root.width - QQC2.TextField { + Kirigami.SearchField { id: searchField width: parent.width - placeholderText: i18n("Search...") } } - ColumnLayout { - spacing: 0 - Repeater { - model: VisualDataModel { - id: tocModel + QQC2.ScrollView { + Layout.fillWidth: true + Layout.fillHeight: true + ListView { + model: KDescendantsProxyModel { model: documentItem.tableOfContents - delegate: TreeDelegate { - Layout.fillWidth: true - sourceModel: tocModel + expandsByDefault: false + } + + delegate: TreeItem { + text: model.display + onClicked: { + documentItem.currentPage = page - 1; + contextDrawer.drawerOpen = false; } } } diff --git a/mobile/app/package/contents/ui/Thumbnails.qml b/mobile/app/package/contents/ui/Thumbnails.qml index eb5c2eb2d..6904478fe 100644 --- a/mobile/app/package/contents/ui/Thumbnails.qml +++ b/mobile/app/package/contents/ui/Thumbnails.qml @@ -5,20 +5,24 @@ */ import QtQuick 2.1 +import QtQuick.Layouts 1.2 import QtQuick.Controls 2.0 as QQC2 import org.kde.kirigami 2.8 as Kirigami ThumbnailsBase { id: root model: documentItem.matchingPages - padding: 0 - header: QQC2.ToolBar { - id: toolBarContent - padding: 0 - contentItem: Kirigami.SearchField { + header: Kirigami.AbstractApplicationHeader { + topPadding: Kirigami.Units.smallSpacing / 2; + bottomPadding: Kirigami.Units.smallSpacing / 2; + rightPadding: Kirigami.Units.smallSpacing + leftPadding: Kirigami.Units.smallSpacing + + width: root.width + Kirigami.SearchField { id: searchField - placeholderText: i18n("Search...") + width: parent.width enabled: documentItem ? documentItem.supportsSearching : false onTextChanged: { if (text.length > 2) { diff --git a/mobile/app/package/contents/ui/ThumbnailsBase.qml b/mobile/app/package/contents/ui/ThumbnailsBase.qml index 09e10b662..f1b4b8fed 100644 --- a/mobile/app/package/contents/ui/ThumbnailsBase.qml +++ b/mobile/app/package/contents/ui/ThumbnailsBase.qml @@ -5,57 +5,74 @@ */ import QtQuick 2.1 +import QtQuick.Layouts 1.2 import QtQuick.Controls 2.3 as QQC2 import QtGraphicalEffects 1.0 import org.kde.okular 2.0 as Okular -import org.kde.kirigami 2.5 as Kirigami +import org.kde.kirigami 2.15 as Kirigami -Kirigami.ScrollablePage { +ColumnLayout { id: root property alias model: resultsGrid.model property Item view: resultsGrid signal pageClicked(int pageNumber) - contentItem: Kirigami.CardsListView { - id: resultsGrid - clip: true + property alias header: control.contentItem - QQC2.Label { - anchors.centerIn: parent - visible: model.length == 0 - text: i18n("No results found.") - } + QQC2.Control { + id: control + Layout.fillWidth: true + leftPadding: 0 + topPadding: 0 + bottomPadding: 0 + rightPadding: 0 + } - delegate: Kirigami.AbstractCard { - implicitWidth: root.width - highlighted: delegateRecycler && delegateRecycler.GridView.isCurrentItem - showClickFeedback: true - readonly property real ratio: contentItem.implicitHeight/contentItem.implicitWidth - implicitHeight: width * ratio - contentItem: Okular.ThumbnailItem { - document: documentItem - pageNumber: modelData - Rectangle { - width: childrenRect.width - height: childrenRect.height - color: Kirigami.Theme.backgroundColor - radius: width - smooth: true - anchors { - top: parent.top - right: parent.right - } - QQC2.Label { - text: modelData + 1 + QQC2.ScrollView { + Layout.fillWidth: true + Layout.fillHeight: true + QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff + Kirigami.CardsListView { + id: resultsGrid + clip: true + + Kirigami.PlaceholderMessage { + anchors.centerIn: parent + visible: model.length == 0 + width: parent.width - Kirigami.largeSpacing * 4 + text: i18n("No results found.") + } + + delegate: Kirigami.AbstractCard { + implicitWidth: root.width + showClickFeedback: true + readonly property real ratio: contentItem.implicitHeight/contentItem.implicitWidth + implicitHeight: width * ratio + contentItem: Okular.ThumbnailItem { + document: documentItem + pageNumber: modelData + Rectangle { + width: childrenRect.width + height: childrenRect.height + color: Kirigami.Theme.backgroundColor + radius: width + smooth: true + anchors { + top: parent.top + right: parent.right + } + QQC2.Label { + text: modelData + 1 + } } } - } - onClicked: { - resultsGrid.currentIndex = index - documentItem.currentPage = modelData + onClicked: { + resultsGrid.currentIndex = index + documentItem.currentPage = modelData - contextDrawer.drawerOpen = false - root.pageClicked(modelData) + contextDrawer.drawerOpen = false + root.pageClicked(modelData) + } } } } diff --git a/mobile/app/package/contents/ui/TreeDelegate.qml b/mobile/app/package/contents/ui/TreeDelegate.qml deleted file mode 100644 index d06627f54..000000000 --- a/mobile/app/package/contents/ui/TreeDelegate.qml +++ /dev/null @@ -1,70 +0,0 @@ -/* - SPDX-FileCopyrightText: 2012 Marco Martin - - SPDX-License-Identifier: GPL-2.0-or-later -*/ - -import QtQuick 2.1 -import QtQuick.Controls 2.0 as QQC2 -import QtQuick.Layouts 1.2 -import org.kde.kirigami 2.0 as Kirigami - -Column { - id: treeDelegate - property variant sourceModel - property int rowIndex: index - width: parent.width - - property bool matches: display.toLowerCase().indexOf(searchField.text.toLowerCase()) !== -1 - - Kirigami.BasicListItem { - id: delegateArea - height: matches ? implicitHeight : 0 - opacity: matches ? 1 : 0 - Behavior on opacity { - NumberAnimation { duration: 250 } - } - Behavior on height { - NumberAnimation { duration: 250 } - } - - onClicked: { - documentItem.currentPage = page-1 - contextDrawer.drawerOpen = false - } - - label: display - highlighted: highlight - icon: highlight || highlightedParent ? (LayoutMirroring.enabled ? "arrow-left" : "arrow-right") : "" - - QQC2.Label { - text: pageLabel ? pageLabel : page - verticalAlignment: Text.AlignBottom - Layout.rightMargin: Kirigami.Units.largeSpacing - } - } - Column { - id: col - x: 20 - width: parent.width - 20 - property variant model: childrenModel - Repeater { - id: rep - model: VisualDataModel { - id: childrenModel - model: documentItem.tableOfContents - } - } - } - onParentChanged: { - if (treeDelegate.parent && treeDelegate.parent.model) { - sourceModel = treeDelegate.parent.model - } - - childrenModel.rootIndex = sourceModel.modelIndex(index) - - if (model.hasModelChildren) { - childrenModel.delegate = Qt.createComponent("TreeDelegate.qml") - } - } -} diff --git a/mobile/app/package/contents/ui/TreeItem.qml b/mobile/app/package/contents/ui/TreeItem.qml new file mode 100644 index 000000000..be946bcc9 --- /dev/null +++ b/mobile/app/package/contents/ui/TreeItem.qml @@ -0,0 +1,77 @@ +/* + * SPDX-FileCopyrightText: 2020 Marco Martin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +import QtQuick 2.12 +import QtQuick.Layouts 1.4 +import QtQuick.Controls 2.14 as QQC2 +import org.kde.kirigami 2.13 as Kirigami +import org.kde.kitemmodels 1.0 + +/** + * An item delegate for the TreeListView and TreeTableView components. + * + * It has the tree expander decoration but no content otherwise, + * which has to be set as contentItem + * + */ +QQC2.ItemDelegate { + id: listItem + + property alias decoration: decoration + + width: ListView.view.width + + data: [ + TreeViewDecoration { + id: decoration + anchors { + left: parent.left + top:parent.top + bottom: parent.bottom + leftMargin: listItem.padding + } + parent: listItem + parentDelegate: listItem + model: listItem.ListView.view ? listItem.ListView.view.model :null + } + ] + + Keys.onLeftPressed: if (kDescendantExpandable && kDescendantExpanded) { + decoration.model.collapseChildren(index); + } else if (!kDescendantExpandable && kDescendantLevel > 0) { + if (listItem.ListView.view) { + const sourceIndex = decoration.model.mapToSource(decoration.model.index(index, 0)); + const newIndex = decoration.model.mapFromSource(sourceIndex.parent); + listItem.listItem.ListView.view.currentIndex = newIndex.row; + } + } + + Keys.onRightPressed: if (kDescendantExpandable) { + if (kDescendantExpanded && listItem.ListView.view) { + ListView.view.incrementCurrentIndex(); + } else { + decoration.model.expandChildren(index); + } + } + + onDoubleClicked: if (kDescendantExpandable) { + decoration.model.toggleChildren(index); + } + + contentItem: Kirigami.Heading { + wrapMode: Text.Wrap + text: listItem.text + level: 5 + width: listItem.ListView.view.width - (decoration.width + listItem.padding * 3 + Kirigami.Units.smallSpacing) + } + + leftInset: Qt.application.layoutDirection !== Qt.RightToLeft ? decoration.width + listItem.padding * 2 : 0 + leftPadding: Qt.application.layoutDirection !== Qt.RightToLeft ? decoration.width + listItem.padding * 2 : 0 + + rightPadding: Qt.application.layoutDirection === Qt.RightToLeft ? decoration.width + listItem.padding * 2 : 0 + rightInset: Qt.application.layoutDirection === Qt.RightToLeft ? decoration.width + listItem.padding * 2 : 0 +} + diff --git a/mobile/app/package/contents/ui/TreeViewDecoration.qml b/mobile/app/package/contents/ui/TreeViewDecoration.qml new file mode 100644 index 000000000..413844d96 --- /dev/null +++ b/mobile/app/package/contents/ui/TreeViewDecoration.qml @@ -0,0 +1,113 @@ +/* + * SPDX-FileCopyrightText: 2020 Marco Martin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +import QtQuick 2.6 +import QtQuick.Layouts 1.4 +import QtQuick.Controls 2.2 as QQC2 +import QtQuick.Templates 2.2 as T +import org.kde.kitemmodels 1.0 +import org.kde.kirigami 2.14 as Kirigami + +/** + * The tree expander decorator for item views. + * + * It will have a "> v" expander button graphics, and will have indentation on the left + * depending on the level of the tree the item is in + */ +RowLayout { + id: decorationLayout + /** + * The delegate this decoration will live in. + * It needs to be assigned explicitly by the developer. + */ + property T.ItemDelegate parentDelegate + + /** + * The KDescendantsProxyModel the view is showing. + * It needs to be assigned explicitly by the developer. + */ + property KDescendantsProxyModel model + + property color decorationHighlightColor + + Layout.topMargin: -parentDelegate.topPadding + Layout.bottomMargin: -parentDelegate.bottomPadding + Repeater { + model: kDescendantLevel-1 + delegate: Item { + Layout.preferredWidth: controlRoot.width + Layout.fillHeight: true + + Rectangle { + anchors { + horizontalCenter: parent.horizontalCenter + top: parent.top + bottom: parent.bottom + } + visible: kDescendantHasSiblings[modelData] + color: Kirigami.Theme.textColor + opacity: 0.5 + width: 1 + } + } + } + T.Button { + id: controlRoot + Layout.preferredWidth: Kirigami.Units.gridUnit + Layout.fillHeight: true + enabled: kDescendantExpandable + hoverEnabled: enabled + onClicked: model.toggleChildren(index) + contentItem: Item { + id: styleitem + implicitWidth: Kirigami.Units.gridUnit + Rectangle { + anchors { + horizontalCenter: parent.horizontalCenter + top: parent.top + bottom: expander.visible ? expander.top : parent.verticalCenter + } + color: Kirigami.Theme.textColor + opacity: 0.5 + width: 1 + } + Kirigami.Icon { + id: expander + anchors.centerIn: parent + width: Kirigami.Units.iconSizes.small + height: width + source: kDescendantExpanded ? "go-down-symbolic" : (Qt.application.layoutDirection == Qt.RightToLeft ? "go-previous-symbolic" : "go-next-symbolic") + isMask: true + color: controlRoot.hovered ? decorationLayout.decorationHighlightColor ? decorationLayout.decorationHighlightColor : + Kirigami.Theme.highlightColor : Kirigami.Theme.textColor + Behavior on color { ColorAnimation { duration: Kirigami.Units.shortDuration; easing.type: Easing.InOutQuad } } + visible: kDescendantExpandable + } + Rectangle { + anchors { + horizontalCenter: parent.horizontalCenter + top: expander.visible ? expander.bottom : parent.verticalCenter + bottom: parent.bottom + } + visible: kDescendantHasSiblings[kDescendantHasSiblings.length - 1] + color: Kirigami.Theme.textColor + opacity: 0.5 + width: 1 + } + Rectangle { + anchors { + verticalCenter: parent.verticalCenter + left: expander.visible ? expander.right : parent.horizontalCenter + right: parent.right + } + color: Kirigami.Theme.textColor + opacity: 0.5 + height: 1 + } + } + background: Item {} + } +} diff --git a/mobile/app/package/contents/ui/main.qml b/mobile/app/package/contents/ui/main.qml index 298afe63e..47aaf93f5 100644 --- a/mobile/app/package/contents/ui/main.qml +++ b/mobile/app/package/contents/ui/main.qml @@ -51,9 +51,11 @@ Kirigami.ApplicationWindow { ] } contextDrawer: OkularDrawer { + width: columnWidth contentItem.implicitWidth: columnWidth modal: !fileBrowserRoot.wideScreen onModalChanged: drawerOpen = !modal + onEnabledChanged: drawerOpen = enabled && !modal enabled: documentItem.opened && pageStack.layers.depth < 2 handleVisible: enabled && pageStack.layers.depth < 2 }