diff --git a/src/app/qml/Book.qml b/src/app/qml/Book.qml index 5921128..93dd7f6 100644 --- a/src/app/qml/Book.qml +++ b/src/app/qml/Book.qml @@ -1,523 +1,525 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.2 import QtQuick.Layouts 1.1 import QtQuick.Controls 1.4 as QtControls import QtQuick.Window 2.2 -import org.kde.kirigami 1.0 as Kirigami +import org.kde.kirigami 2.1 as Kirigami import org.kde.peruse 0.1 as Peruse import "listcomponents" as ListComponents Kirigami.Page { id: root; objectName: "bookViewer"; clip: true; implicitWidth: applicationWindow().width; // Remove all the padding when we've hidden controls. Content is king! topPadding: applicationWindow().controlsVisible ? (applicationWindow() && applicationWindow().header ? applicationWindow().header.height : 0) : 0; leftPadding: applicationWindow().controlsVisible ? Kirigami.Units.gridUnit : 0; rightPadding: applicationWindow().controlsVisible ? Kirigami.Units.gridUnit : 0; bottomPadding: applicationWindow().controlsVisible ? Kirigami.Units.gridUnit : 0; background: Rectangle { anchors.fill: parent; opacity: applicationWindow().controlsVisible ? 0 : 1; - Behavior on opacity { NumberAnimation { duration: mainWindow.animationDuration; } } + Behavior on opacity { NumberAnimation { duration: applicationWindow().animationDuration; } } color: "black"; } // Perhaps we should store and restore this? property bool showControls: true; property Item pageStackItem: applicationWindow().pageStack.currentItem; onPageStackItemChanged: { - if(mainWindow.pageStack.currentItem == root) { + if(applicationWindow().pageStack.currentItem == root) { applicationWindow().controlsVisible = root.showControls; } else { root.showControls = applicationWindow().controlsVisible; applicationWindow().controlsVisible = true; } } property bool rtlMode: false; /** * zoomMode: Peruse.Config.ZoomMode */ property int zoomMode: Peruse.Config.ZoomFull; property string file; property int currentPage; property int totalPages; onCurrentPageChanged: { // set off a timer to slightly postpone saving the current page, so it doesn't happen during animations etc updateCurrent.start(); } function nextPage() { if(viewLoader.item.currentPage < viewLoader.item.pageCount - 1) { viewLoader.item.currentPage++; } else { bookInfo.showBookInfo(file); } } function previousPage() { if(viewLoader.item.currentPage > 0) { viewLoader.item.currentPage--; } else { bookInfo.showBookInfo(file); } } function closeBook() { - mainWindow.contextDrawer.close(); + applicationWindow().contextDrawer.close(); // also for storing current page (otherwise postponed a bit after page change, done here as well to ensure it really happens) applicationWindow().controlsVisible = true; - mainWindow.pageStack.pop(); + applicationWindow().pageStack.pop(); applicationWindow().globalDrawer.open(); } property Item contextualTopItems: ListView { id: thumbnailNavigator; anchors.fill: parent; clip: true; delegate: thumbnailComponent; } Component { id: thumbnailComponent; Item { width: parent.width; height: Kirigami.Units.gridUnit * 6; MouseArea { anchors.fill: parent; onClicked: viewLoader.item.currentPage = model.index; } Rectangle { anchors.fill: parent; color: Kirigami.Theme.highlightColor; opacity: root.currentPage === model.index ? 1 : 0; Behavior on opacity { NumberAnimation { duration: Kirigami.Units.shortDuration; } } } Image { anchors { top: parent.top; left: parent.left; right: parent.right; margins: Kirigami.Units.smallSpacing; } height: parent.height - pageTitle.height - Kirigami.Units.smallSpacing * 2; asynchronous: true; fillMode: Image.PreserveAspectFit; source: model.url; } Kirigami.Label { id: pageTitle; anchors { left: parent.left; right: parent.right; bottom: parent.bottom; } height: paintedHeight; text: model.title; elide: Text.ElideMiddle; horizontalAlignment: Text.AlignHCenter; } } } function toggleFullscreen() { - mainWindow.contextDrawer.close(); + applicationWindow().contextDrawer.close(); if(applicationWindow().visibility !== Window.FullScreen) { applicationWindow().visibility = Window.FullScreen; } else { applicationWindow().visibility = Window.AutomaticVisibility; } restoreViewLayoutStuff.start(); } Timer { id: restoreViewLayoutStuff; - interval: mainWindow.animationDuration * 2; + interval: applicationWindow().animationDuration * 2; running: false; repeat: false; onTriggered: { if(viewLoader.item.restoreCurrentPage !== undefined) { viewLoader.item.restoreCurrentPage(); } applicationWindow().pageStack.currentIndex = applicationWindow().pageStack.depth - 1; } } property list mobileActions: [ Kirigami.Action { text: applicationWindow().visibility !== Window.FullScreen ? i18nc("Enter full screen mode on a touch-based device", "Go full screen") : i18nc("Exit full sceen mode on a touch based device", "Exit full screen"); iconName: "view-fullscreen"; onTriggered: toggleFullscreen(); - enabled: mainWindow.pageStack.currentItem == root && mainWindow.deviceType === mainWindow.deviceTypePhone; + enabled: applicationWindow().pageStack.currentItem == root && applicationWindow().deviceType === applicationWindow().deviceTypePhone; }, Kirigami.Action { text: i18nc("Action used on touch devices to close the currently open book and return to whatever page was most recently shown", "Close book"); - shortcut: (bookInfo.opened ? "" : "Esc"); + shortcut: (bookInfo.sheetOpen ? "" : "Esc"); iconName: "dialog-close"; onTriggered: closeBook(); - enabled: mainWindow.pageStack.currentItem == root && mainWindow.deviceType === mainWindow.deviceTypePhone; + enabled: applicationWindow().pageStack.currentItem == root && applicationWindow().deviceType === applicationWindow().deviceTypePhone; } ] property list desktopActions: [ Kirigami.Action { text: i18nc("Top level entry leading to a submenu with options for the book display", "View options"); iconName: "configure"; QtObject { property string text: "Reading Direction" } Kirigami.Action { text: "Left to Right" iconName: "format-text-direction-ltr"; shortcut: rtlMode ? "r" : ""; - enabled: mainWindow.pageStack.currentItem == root && mainWindow.deviceType === mainWindow.deviceTypeDesktop && root.rtlMode === true; + enabled: applicationWindow().pageStack.currentItem == root && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop && root.rtlMode === true; onTriggered: { root.rtlMode = false; } } Kirigami.Action { text: "Right to Left" iconName: "format-text-direction-rtl"; shortcut: rtlMode ? "" : "r"; - enabled: mainWindow.pageStack.currentItem == root && mainWindow.deviceType === mainWindow.deviceTypeDesktop && root.rtlMode === false; + enabled: applicationWindow().pageStack.currentItem == root && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop && root.rtlMode === false; onTriggered: { root.rtlMode = true; } } QtObject {} // QtObject { // property string text: "Zoom" // } // Kirigami.Action { // text: "Fit full page" // iconName: "zoom-fit-best"; -// enabled: mainWindow.pageStack.currentItem == root && mainWindow.deviceType === mainWindow.deviceTypeDesktop && root.zoomMode !== Peruse.Config.ZoomFull; +// enabled: applicationWindow().pageStack.currentItem == root && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop && root.zoomMode !== Peruse.Config.ZoomFull; // onTriggered: { root.zoomMode = Peruse.Config.ZoomFull; } // } // Kirigami.Action { // text: "Fit width" // iconName: "zoom-fit-width"; -// enabled: mainWindow.pageStack.currentItem == root && mainWindow.deviceType === mainWindow.deviceTypeDesktop && root.zoomMode !== Peruse.Config.ZoomFitWidth; +// enabled: applicationWindow().pageStack.currentItem == root && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop && root.zoomMode !== Peruse.Config.ZoomFitWidth; // onTriggered: { root.zoomMode = Peruse.Config.ZoomFitWidth; } // } // Kirigami.Action { // text: "Fit height" // iconName: "zoom-fit-height"; -// enabled: mainWindow.pageStack.currentItem == root && mainWindow.deviceType === mainWindow.deviceTypeDesktop && root.zoomMode !== Peruse.Config.ZoomFitHeight; +// enabled: applicationWindow().pageStack.currentItem == root && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop && root.zoomMode !== Peruse.Config.ZoomFitHeight; // onTriggered: { root.zoomMode = Peruse.Config.ZoomFitHeight; } // } // QtObject {} }, Kirigami.Action { text: i18nc("Go to the previous page in the book", "Previous page"); - shortcut: bookInfo.opened ? "" : StandardKey.MoveToPreviousChar; + shortcut: bookInfo.sheetOpen ? "" : StandardKey.MoveToPreviousChar; iconName: "go-previous"; onTriggered: previousPage(); - enabled: mainWindow.pageStack.currentItem == root && mainWindow.deviceType === mainWindow.deviceTypeDesktop; + enabled: applicationWindow().pageStack.currentItem == root && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop; }, Kirigami.Action { text: i18nc("Go to the next page in the book", "Next page"); - shortcut: bookInfo.opened ? "" : StandardKey.MoveToNextChar; + shortcut: bookInfo.sheetOpen ? "" : StandardKey.MoveToNextChar; iconName: "go-next"; onTriggered: nextPage(); - enabled: mainWindow.pageStack.currentItem == root && mainWindow.deviceType === mainWindow.deviceTypeDesktop; + enabled: applicationWindow().pageStack.currentItem == root && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop; }, Kirigami.Action { text: applicationWindow().visibility !== Window.FullScreen ? i18nc("Enter full screen mode on a non-touch-based device", "Go full screen") : i18nc("Exit full sceen mode on a non-touch based device", "Exit full screen"); - shortcut: (applicationWindow().visibility === Window.FullScreen) ? (bookInfo.opened ? "" : "Esc") : "f"; + shortcut: (applicationWindow().visibility === Window.FullScreen) ? (bookInfo.sheetOpen ? "" : "Esc") : "f"; iconName: "view-fullscreen"; onTriggered: toggleFullscreen(); - enabled: mainWindow.pageStack.currentItem == root && mainWindow.deviceType === mainWindow.deviceTypeDesktop; + enabled: applicationWindow().pageStack.currentItem == root && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop; }, Kirigami.Action { text: i18nc("Action used on non-touch devices to close the currently open book and return to whatever page was most recently shown", "Close book"); - shortcut: (applicationWindow().visibility === Window.FullScreen) ? "" : (bookInfo.opened ? "" : "Esc"); + shortcut: (applicationWindow().visibility === Window.FullScreen) ? "" : (bookInfo.sheetOpen ? "" : "Esc"); iconName: "dialog-close"; onTriggered: closeBook(); - enabled: mainWindow.pageStack.currentItem == root && mainWindow.deviceType === mainWindow.deviceTypeDesktop; + enabled: applicationWindow().pageStack.currentItem == root && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop; }, // Invisible actions, for use in bookInfo Kirigami.Action { visible: false; - shortcut: bookInfo.opened ? StandardKey.MoveToPreviousChar : ""; + shortcut: bookInfo.sheetOpen ? StandardKey.MoveToPreviousChar : ""; onTriggered: bookInfo.previousBook(); - enabled: mainWindow.pageStack.currentItem == root && mainWindow.deviceType === mainWindow.deviceTypeDesktop; + enabled: applicationWindow().pageStack.currentItem == root && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop; }, Kirigami.Action { visible: false; - shortcut: bookInfo.opened ? StandardKey.MoveToNextChar : ""; + shortcut: bookInfo.sheetOpen ? StandardKey.MoveToNextChar : ""; onTriggered: bookInfo.nextBook(); - enabled: mainWindow.pageStack.currentItem == root && mainWindow.deviceType === mainWindow.deviceTypeDesktop; + enabled: applicationWindow().pageStack.currentItem == root && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop; }, Kirigami.Action { visible: false; - shortcut: bookInfo.opened ? "Return" : ""; + shortcut: bookInfo.sheetOpen ? "Return" : ""; onTriggered: bookInfo.openSelected(); - enabled: mainWindow.pageStack.currentItem == root && mainWindow.deviceType === mainWindow.deviceTypeDesktop; + enabled: applicationWindow().pageStack.currentItem == root && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop; } ] actions { - contextualActions: mainWindow.deviceType === mainWindow.deviceTypePhone ? mobileActions : desktopActions; - main: bookInfo.opened ? bookInfoAction : mainBookAction; + contextualActions: applicationWindow().deviceType === applicationWindow().deviceTypePhone ? mobileActions : desktopActions; + main: bookInfo.sheetOpen ? bookInfoAction : mainBookAction; } Kirigami.Action { id: mainBookAction; text: applicationWindow().visibility !== Window.FullScreen ? i18n("Enter full screen mode on any device type", "Go full screen") : i18nc("Exit full screen mode on any device type", "Exit full screen"); iconName: "view-fullscreen"; onTriggered: toggleFullscreen(); - enabled: mainWindow.pageStack.currentItem == root; + enabled: applicationWindow().pageStack.currentItem == root; } Kirigami.Action { id: bookInfoAction; text: i18n("Closes the book information drawer", "Close"); - shortcut: bookInfo.opened ? "Esc" : ""; + shortcut: bookInfo.sheetOpen ? "Esc" : ""; iconName: "dialog-cancel"; onTriggered: bookInfo.close(); - enabled: mainWindow.pageStack.currentItem == root; + enabled: applicationWindow().pageStack.currentItem == root; } Item { width: root.width - (root.leftPadding + root.rightPadding); height: root.height - (root.topPadding + root.bottomPadding); Timer { id: updateCurrent; - interval: mainWindow.animationDuration; + interval: applicationWindow().animationDuration; running: false; repeat: false; onTriggered: { if(viewLoader.item && viewLoader.item.pagesModel && viewLoader.item.pagesModel.currentPage !== undefined) { viewLoader.item.pagesModel.currentPage = root.currentPage; } } } - NumberAnimation { id: thumbnailMovementAnimation; target: thumbnailNavigator; property: "contentY"; duration: mainWindow.animationDuration; easing.type: Easing.InOutQuad; } + NumberAnimation { id: thumbnailMovementAnimation; target: thumbnailNavigator; property: "contentY"; duration: applicationWindow().animationDuration; easing.type: Easing.InOutQuad; } Loader { id: viewLoader; anchors.fill: parent; property bool loadingCompleted: false; onLoaded: { if(status === Loader.Error) { // huh! problem... } else { item.file = root.file; } } Timer { id: drawerTimer; - interval: mainWindow.animationDuration * 3; + interval: applicationWindow().animationDuration * 3; running: false; repeat: false; - onTriggered: applicationWindow().globalDrawer.close(); + onTriggered: { + applicationWindow().globalDrawer.close(); + } } Binding { target: viewLoader.item; property: "rtlMode"; value: root.rtlMode; } Binding { target: viewLoader.item; property: "zoomMode"; value: root.zoomMode; } Connections { target: viewLoader.item; onLoadingCompleted: { if(success) { thumbnailNavigator.model = viewLoader.item.pagesModel; if(viewLoader.item.thumbnailComponent) { thumbnailNavigator.delegate = viewLoader.item.thumbnailComponent; } else { thumbnailNavigator.delegate = thumbnailComponent; } peruseConfig.setFilesystemProperty(root.file, "totalPages", viewLoader.item.pageCount); if(root.totalPages !== viewLoader.item.pageCount) { root.totalPages = viewLoader.item.pageCount; } viewLoader.item.currentPage = root.currentPage; viewLoader.loadingCompleted = true; root.title = viewLoader.item.title; drawerTimer.start(); restoreViewLayoutStuff.start(); } } onCurrentPageChanged: { if(root.currentPage !== viewLoader.item.currentPage && viewLoader.loadingCompleted) { root.currentPage = viewLoader.item.currentPage; } thumbnailMovementAnimation.running = false; var currentPos = thumbnailNavigator.contentY; var newPos; thumbnailNavigator.positionViewAtIndex(viewLoader.item.currentPage, ListView.Center); newPos = thumbnailNavigator.contentY; thumbnailMovementAnimation.from = currentPos; thumbnailMovementAnimation.to = newPos; thumbnailMovementAnimation.running = true; } onGoNextPage: root.nextPage(); onGoPreviousPage: root.previousPage(); } } } Kirigami.OverlaySheet { id: bookInfo; function setNewCurrentIndex(newIndex) { seriesListAnimation.running = false; var currentPos = seriesListView.contentX; var newPos; seriesListView.positionViewAtIndex(newIndex, ListView.Center); newPos = seriesListView.contentX; seriesListAnimation.from = currentPos; seriesListAnimation.to = newPos; seriesListAnimation.running = true; seriesListView.currentIndex = newIndex; } function nextBook() { if(seriesListView.currentIndex < seriesListView.model.rowCount()) { setNewCurrentIndex(seriesListView.currentIndex + 1); } } function previousBook() { if(seriesListView.currentIndex > 0) { setNewCurrentIndex(seriesListView.currentIndex - 1); } } function openSelected() { applicationWindow().showBook(detailsTile.filename, detailsTile.currentPage); } function showBookInfo(filename) { - if(opened) { + if(sheetOpen) { return; } seriesListView.model = contentList.seriesModelForEntry(filename); setNewCurrentIndex(seriesListView.model.indexOfFile(filename)); open(); } - onOpenedChanged: { - if(opened === false) { + onSheetOpenChanged: { + if(sheetOpen === false) { applicationWindow().controlsVisible = controlsShown; } else { controlsShown = applicationWindow().controlsVisible; applicationWindow().controlsVisible = true; } } property bool controlsShown; property QtObject currentBook: fakeBook; property QtObject fakeBook: Peruse.PropertyContainer { property string author: ""; property string title: ""; property string filename: ""; property string publisher: ""; property string thumbnail: ""; property string currentPage: "0"; property string totalPages: "0"; } Column { clip: true; width: root.width - Kirigami.Units.largeSpacing * 2; height: childrenRect.height + Kirigami.Units.largeSpacing * 2; spacing: Kirigami.Units.largeSpacing; ListComponents.BookTile { id: detailsTile; height: neededHeight; width: parent.width; author: bookInfo.currentBook.readProperty("author"); publisher: bookInfo.currentBook.readProperty("publisher"); title: bookInfo.currentBook.readProperty("title"); filename: bookInfo.currentBook.readProperty("filename"); thumbnail: bookInfo.currentBook.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: bookInfo.currentBook.readProperty("currentPage"); totalPages: bookInfo.currentBook.readProperty("totalPages"); onBookSelected: { if(root.file !== filename) { applicationWindow().showBook(filename, currentPage); } } onBookDeleteRequested: { // Not strictly needed for the listview itself, but it's kind of // nice for making sure the details tile is right var oldIndex = seriesListView.currentIndex; seriesListView.currentIndex = -1; contentList.removeBook(detailsTile.filename, true); seriesListView.currentIndex = oldIndex; } } // tags and ratings, comment by self // store hook for known series with more content ListView { id: seriesListView; width: parent.width; height: Kirigami.Units.gridUnit * 12; orientation: ListView.Horizontal; - NumberAnimation { id: seriesListAnimation; target: seriesListView; property: "contentX"; duration: mainWindow.animationDuration; easing.type: Easing.InOutQuad; } + NumberAnimation { id: seriesListAnimation; target: seriesListView; property: "contentX"; duration: applicationWindow().animationDuration; easing.type: Easing.InOutQuad; } delegate: ListComponents.BookTileTall { height: model.filename != "" ? neededHeight : 1; width: seriesListView.width / 3; author: model.author; title: model.title; filename: model.filename; thumbnail: model.thumbnail; categoryEntriesCount: 0; currentPage: model.currentPage; totalPages: model.totalPages; onBookSelected: bookInfo.setNewCurrentIndex(model.index); selected: seriesListView.currentIndex === model.index; } onCurrentIndexChanged: { bookInfo.currentBook = model.get(currentIndex); } } } } onFileChanged: { // Let's set the page title to something useful var book = contentList.bookFromFile(file); root.title = book.readProperty("title"); // The idea is to have a number of specialised options as relevant to various // types of comic books, and then finally fall back to Okular as a catch-all // but generic viewer component. var attemptFallback = true; var mimetype = contentList.contentModel.getMimetype(file); console.debug("Mimetype is " + mimetype); if(mimetype == "application/x-cbz" || mimetype == "application/x-cbr" || mimetype == "application/vnd.comicbook+zip" || mimetype == "application/vnd.comicbook+rar") { viewLoader.source = "viewers/cbr.qml"; attemptFallback = false; } if(mimetype == "inode/directory") { viewLoader.source = "viewers/folderofimages.qml"; attemptFallback = false; } if(attemptFallback) { viewLoader.source = "viewers/okular.qml"; } } } diff --git a/src/app/qml/BookDetails.qml b/src/app/qml/BookDetails.qml index 741d716..d4dfd15 100644 --- a/src/app/qml/BookDetails.qml +++ b/src/app/qml/BookDetails.qml @@ -1,206 +1,206 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.2 import QtQuick.Layouts 1.1 import QtQuick.Controls 1.4 as QtControls -import org.kde.kirigami 1.0 as Kirigami +import org.kde.kirigami 2.1 as Kirigami import org.kde.peruse 0.1 as Peruse import "listcomponents" as ListComponents Item { id: root; property string file; height: childrenRect.height; onFileChanged: { var book = contentList.get(contentList.indexOfFile(file)); filename = book.readProperty("filename"); filetitle = book.readProperty("filetitle"); title = book.readProperty("title"); series = book.readProperty("series"); author = book.readProperty("author"); publisher = book.readProperty("publisher"); created = book.readProperty("created"); lastOpenedTime = book.readProperty("lastOpenedTime"); totalPages = book.readProperty("totalPages"); currentPage = book.readProperty("currentPage"); thumbnail = book.readProperty("thumbnail"); dataRepeater.model.clear(); dataRepeater.model.append({"label": i18nc("Label for the author field", "Author:"), value: root.author}); dataRepeater.model.append({"label": i18nc("Label for the publisher field", "Publisher:"), value: root.publisher}); dataRepeater.model.append({"label": i18nc("Label for the series field", "Series:"), value: root.series}); dataRepeater.model.append({"label": i18nc("Label for the filename field", "Filename:"), value: root.filename}); } property string filename; property string filetitle; // property string title; property string series; property string author; property string publisher; property date created; property date lastOpenedTime; property int totalPages; property int currentPage; property string thumbnail; Column { anchors.horizontalCenter: parent.horizontalCenter; spacing: Kirigami.Units.smallSpacing; width: parent.width; height: childrenRect.height; Item { id: bookCover; anchors { horizontalCenter: parent.horizontalCenter; margins: Kirigami.Units.largeSpacing; } width: Math.min(parent.width - Kirigami.Units.largeSpacing * 2, Kirigami.Units.iconSizes.enormous + Kirigami.Units.largeSpacing * 2); height: width; Rectangle { anchors.centerIn: coverImage; width: coverImage.paintedWidth + Kirigami.Units.smallSpacing * 2; height: coverImage.paintedHeight + Kirigami.Units.smallSpacing * 2; color: Kirigami.Theme.viewBackgroundColor; border { width: 2; color: Kirigami.Theme.viewTextColor; } radius: 2; } Image { id: coverImage; anchors { fill: parent; margins: Kirigami.Units.largeSpacing; } source: root.thumbnail; asynchronous: true; fillMode: Image.PreserveAspectFit; } } Repeater { id: dataRepeater; model: ListModel {} delegate: Item { id: base; width: root.width; height: valueLabel.height; Kirigami.Label { anchors { top: parent.top; left: parent.left; right: parent.horizontalCenter; bottom: parent.bottom; } verticalAlignment: Text.AlignTop; text: model.label; } Kirigami.Label { id: valueLabel; anchors { top: parent.top; left: parent.horizontalCenter; right: parent.right; } verticalAlignment: Text.AlignTop; height: paintedHeight; wrapMode: Text.WordWrap; text: model.value; } } } Item { id: deleteBase; width: root.width; height: deleteButton.height + Kirigami.Units.largeSpacing * 2; - Behavior on height { PropertyAnimation { duration: mainWindow.animationDuration; } } + Behavior on height { PropertyAnimation { duration: applicationWindow().animationDuration; } } states: [ State { name: "confirmDelete"; PropertyChanges { target: deleteButton; opacity: 0; } PropertyChanges { target: deleteConfirmBase; opacity: 1; } PropertyChanges { target: deleteBase; height: deleteConfirmBase.height; } } ] QtControls.Button { id: deleteButton; text: i18nc("Spawn inline dialog box to confirm permanent removal of this book", "Delete from device"); anchors { top: parent.top; topMargin: Kirigami.Units.largeSpacing; horizontalCenter: parent.horizontalCenter; } iconName: "edit-delete"; onClicked: deleteBase.state = "confirmDelete"; - Behavior on opacity { PropertyAnimation { duration: mainWindow.animationDuration; } } + Behavior on opacity { PropertyAnimation { duration: applicationWindow().animationDuration; } } } Item { id: deleteConfirmBase; opacity: 0; width: root.width; - Behavior on opacity { PropertyAnimation { duration: mainWindow.animationDuration; } } + Behavior on opacity { PropertyAnimation { duration: applicationWindow().animationDuration; } } height: yesDelete.height + confirmDeleteLabel.height + Kirigami.Units.largeSpacing * 2 + Kirigami.Units.smallSpacing; Kirigami.Label { id: confirmDeleteLabel; anchors { top: parent.top; topMargin: Kirigami.Units.largeSpacing; left: parent.left; right: parent.right; } height: paintedHeight; wrapMode: Text.WordWrap; horizontalAlignment: Text.AlignHCenter; text: i18nc("Dialog text for delete book dialog", "Are you sure you want to delete this from your device?"); } QtControls.Button { id: yesDelete; anchors { top: confirmDeleteLabel.bottom; topMargin: Kirigami.Units.smallSpacing; right: parent.horizontalCenter; rightMargin: (parent.width - width) / 4; } text: i18nc("Confirmation button for book delete dialog", "Yes, really delete"); iconName: "dialog-ok"; onClicked: { contentList.removeBook(root.file, true); - mainWindow.pageStack.pop(); + applicationWindow().pageStack.pop(); } } QtControls.Button { anchors { top: confirmDeleteLabel.bottom; topMargin: Kirigami.Units.smallSpacing; left: parent.horizontalCenter; leftMargin: (parent.width - width) / 4; } text: i18nc("Cancellation button or book delete dialog", "No, cancel delete"); iconName: "dialog-cancel"; onClicked: deleteBase.state = ""; } } } } } diff --git a/src/app/qml/Bookshelf.qml b/src/app/qml/Bookshelf.qml index 3a162b4..3669651 100644 --- a/src/app/qml/Bookshelf.qml +++ b/src/app/qml/Bookshelf.qml @@ -1,218 +1,218 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.2 import QtQuick.Layouts 1.1 import QtQuick.Controls 1.4 as QtControls -import org.kde.kirigami 1.0 as Kirigami +import org.kde.kirigami 2.1 as Kirigami import org.kde.peruse 0.1 as Peruse import "listcomponents" as ListComponents Kirigami.Page { id: root; title: headerText; property string categoryName: "bookshelf"; objectName: "bookshelf"; property alias model: shelfList.model; property string sectionRole: "title"; property int sectionCriteria: ViewSection.FirstCharacter; signal bookSelected(string filename, int currentPage); property string headerText; function openBook(index) { - mainWindow.contextDrawer.close(); + applicationWindow().contextDrawer.close(); if(shelfList.model.indexIsBook(index)) { var book = shelfList.model.get(index); root.bookSelected(book.readProperty("filename"), book.readProperty("currentPage")); } else { var catEntry = shelfList.model.getEntry(index); - mainWindow.pageStack.push(bookshelf, { focus: true, headerText: catEntry.readProperty("title"), model: catEntry.readProperty("entriesModel") }); + applicationWindow().pageStack.push(bookshelf, { focus: true, headerText: catEntry.readProperty("title"), model: catEntry.readProperty("entriesModel") }); } } function closeShelf() { - mainWindow.contextDrawer.close(); - mainWindow.pageStack.pop(); + applicationWindow().contextDrawer.close(); + applicationWindow().pageStack.pop(); } property list mobileActions; property list desktopActions: [ Kirigami.Action { text: i18nc("Navigate one page back", "Back"); - shortcut: bookDetails.opened ? "" : "Esc"; + shortcut: bookDetails.sheetOpen ? "" : "Esc"; iconName: "dialog-close"; onTriggered: closeShelf(); - enabled: mainWindow.pageStack.currentItem == root && mainWindow.deviceType === mainWindow.deviceTypeDesktop && mainWindow.pageStack.currentIndex > 0; + enabled: applicationWindow().pageStack.currentItem == root && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop && applicationWindow().pageStack.currentIndex > 0; }, Kirigami.Action { text: i18nc("Select the previous book in the list", "Select previous book"); shortcut: StandardKey.MoveToPreviousChar iconName: "go-previous"; onTriggered: shelfList.previousEntry(); - enabled: mainWindow.pageStack.currentItem == root && mainWindow.deviceType === mainWindow.deviceTypeDesktop; + enabled: applicationWindow().pageStack.currentItem == root && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop; }, Kirigami.Action { text: i18nc("Select the next book in the list", "Select next book"); shortcut: StandardKey.MoveToNextChar; iconName: "go-next"; onTriggered: shelfList.nextEntry(); - enabled: mainWindow.pageStack.currentItem == root && mainWindow.deviceType === mainWindow.deviceTypeDesktop; + enabled: applicationWindow().pageStack.currentItem == root && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop; }, Kirigami.Action { text: i18nc("Open the book which is currently selected in the list", "Open selected book"); shortcut: "Return"; iconName: "document-open"; onTriggered: openBook(shelfList.currentIndex); - enabled: mainWindow.pageStack.currentItem == root && mainWindow.deviceType === mainWindow.deviceTypeDesktop; + enabled: applicationWindow().pageStack.currentItem == root && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop; } ] actions { contextualActions: PLASMA_PLATFORM.substring(0, 5) === "phone" ? mobileActions : desktopActions; - main: bookDetails.opened ? bookDetailsAction : mainShelfAction; + main: bookDetails.sheetOpen ? bookDetailsAction : mainShelfAction; } Kirigami.Action { id: mainShelfAction; text: i18nc("search in the list of books (not inside the books)", "Search Books"); iconName: "system-search"; onTriggered: searchBox.activate(); - enabled: mainWindow.pageStack.currentItem == root; + enabled: applicationWindow().pageStack.currentItem == root; } Kirigami.Action { id: bookDetailsAction; text: i18n("Closes the book details drawer", "Close"); - shortcut: bookDetails.opened ? "Esc" : ""; + shortcut: bookDetails.sheetOpen ? "Esc" : ""; iconName: "dialog-cancel"; onTriggered: bookDetails.close(); - enabled: mainWindow.pageStack.currentItem == root; + enabled: applicationWindow().pageStack.currentItem == root; } Item { width: root.width - (root.leftPadding + root.rightPadding); height: root.height - root.topPadding; SearchBox { id: searchBox; anchors { top: parent.top; left: parent.left; right: parent.right; } maxHeight: parent.height; model: root.model; onBookSelected: root.bookSelected(filename, currentPage); } GridView { id: shelfList; clip: true; anchors { top: searchBox.bottom; left: parent.left; right: parent.right; } height: parent.height; footer: Item { width: parent.width; height: Kirigami.Units.iconSizes.large + Kirigami.Units.largeSpacing; } cellWidth: width / 2; cellHeight: root.height * 2 / 7; // header: ListComponents.ListPageHeader { text: root.headerText; } currentIndex: -1; function previousEntry() { if(currentIndex > 0) { currentIndex--; } } function nextEntry() { if(currentIndex < model.rowCount() - 1) { currentIndex++; } } delegate: Item { height: model.categoryEntriesCount === 0 ? bookTile.neededHeight : categoryTile.neededHeight; width: root.width / 2; ListComponents.CategoryTileTall { id: categoryTile; height: model.categoryEntriesCount > 0 ? neededHeight : 0; width: parent.width; count: model.categoryEntriesCount; title: model.title; entriesModel: model.categoryEntriesModel ? model.categoryEntriesModel : null; selected: shelfList.currentIndex === index; } ListComponents.BookTileTall { id: bookTile; height: model.categoryEntriesCount < 1 ? neededHeight : 0; width: parent.width; author: model.author ? model.author : i18nc("used for the author data in book lists if autor is empty", "(unknown)"); title: model.title; filename: model.filename; thumbnail: model.categoryEntriesCount < 1 ? model.thumbnail : ""; categoryEntriesCount: model.categoryEntriesCount; currentPage: model.currentPage; totalPages: model.totalPages; onBookSelected: root.bookSelected(filename, currentPage); selected: shelfList.currentIndex === index; onPressAndHold: bookDetails.showBookInfo(model.index); } } } } Kirigami.OverlaySheet { id: bookDetails; function showBookInfo(index) { currentBook = root.model.getEntry(index); open(); } property QtObject currentBook: fakeBook; property QtObject fakeBook: Peruse.PropertyContainer { property string author: ""; property string title: ""; property string filename: ""; property string publisher: ""; property string thumbnail: ""; property string currentPage: "0"; property string totalPages: "0"; } ListComponents.BookTile { id: detailsTile; height: neededHeight; width: shelfList.width - Kirigami.Units.largeSpacing * 2; author: bookDetails.currentBook.readProperty("author"); publisher: bookDetails.currentBook.readProperty("publisher"); title: bookDetails.currentBook.readProperty("title"); filename: bookDetails.currentBook.readProperty("filename"); thumbnail: bookDetails.currentBook.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: bookDetails.currentBook.readProperty("currentPage"); totalPages: bookDetails.currentBook.readProperty("totalPages"); onBookSelected: { bookDetails.close(); applicationWindow().showBook(filename, currentPage); } onBookDeleteRequested: { contentList.removeBook(detailsTile.filename, true); close(); } } } } diff --git a/src/app/qml/FileFinder.qml b/src/app/qml/FileFinder.qml index 824c41f..5b27061 100644 --- a/src/app/qml/FileFinder.qml +++ b/src/app/qml/FileFinder.qml @@ -1,112 +1,112 @@ /* * Copyright (C) 2016 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.2 import QtQuick.Layouts 1.1 import QtQuick.Controls 1.0 import Qt.labs.folderlistmodel 2.1 -import org.kde.kirigami 1.0 as Kirigami +import org.kde.kirigami 2.1 as Kirigami import "listcomponents" as ListComponents Item { id: root; signal accepted(); signal aborted(); function selectedItem() { var theItem = folderModel.folder; if(folderView.currentIndex > -1) { theItem = theItem + "/" + folderModel.get(folderView.currentIndex, "fileName"); } return theItem; } property alias folder: folderModel.folder; property alias showDirs: folderModel.showDirs; property alias showFiles: folderModel.showFiles; property alias nameFilters: folderModel.nameFilters; FolderListModel { id: folderModel } ListComponents.ListPageHeader { id: titleContainer; text: folderModel.folder; anchors { top: parent.top; left: parent.left; right: parent.right; } clip: true; height: Kirigami.Units.gridUnit * 2; } ToolButton { anchors { top: parent.top; right: parent.right; margins: Kirigami.Units.smallSpacing; } iconName: "dialog-ok-apply"; onClicked: root.accepted(); } ListView { id: folderView; anchors { top: titleContainer.bottom; topMargin: Kirigami.Units.largeSpacing; left: parent.left; right: parent.right; bottom: parent.bottom; } clip: true; model: folderModel; Component.onCompleted: folderView.currentIndex = -1; header: Kirigami.BasicListItem { enabled: true; supportsMouseEvents: enabled; clip: true; width: folderView.width; onClicked: folderModel.folder = folderModel.parentFolder; label: "(go up one level)"; icon: "go-up"; } delegate: Kirigami.BasicListItem { enabled: true; supportsMouseEvents: enabled; width: folderView.width; label: fileName; icon: fileIsDir ? "folder" : ""; onClicked: { if(fileIsDir) { folderView.currentIndex = -1; folderModel.folder = fileURL; } else { folderView.currentIndex = index; } } checked: folderView.currentIndex === index; } } } diff --git a/src/app/qml/MobileMain.qml b/src/app/qml/MobileMain.qml index a1163ac..7b48eb7 100644 --- a/src/app/qml/MobileMain.qml +++ b/src/app/qml/MobileMain.qml @@ -1,56 +1,56 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.1 -import org.kde.kirigami 1.0 as Kirigami +import org.kde.kirigami 2.1 as Kirigami import org.kde.peruse 0.1 as Peruse PeruseMain { id: root; width: 750; height: 1100; function openOther() { pageStack.push(openDlg, { folder: root.homeDir() } ); } Component { id: openDlg; Kirigami.Page { id: root; property string folder; title: i18nc("Title of a page which lets you open comics not in your collection by using a standard touch-friendly dig-down style filesystem browser", "Open comics not in your collection"); FileFinder { width: root.width - (root.leftPadding + root.rightPadding); height: root.height - (root.topPadding + root.bottomPadding); folder: root.folder; showFiles: true; property int splitPos: osIsWindows ? 8 : 7; onAccepted: { if(selectedItem().substring(0, 7) === "file://") { showBook(selectedItem().substring(splitPos), 0); } } onAborted: pageStack.pop(); } } } } diff --git a/src/app/qml/PeruseContextDrawer.qml b/src/app/qml/PeruseContextDrawer.qml index 585eaf4..efba184 100644 --- a/src/app/qml/PeruseContextDrawer.qml +++ b/src/app/qml/PeruseContextDrawer.qml @@ -1,175 +1,182 @@ /* * Copyright 2016 Dan Leinir Turthra Jensen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.1 import QtQuick.Layouts 1.2 import QtQuick.Controls 1.4 as QtControls -import org.kde.kirigami 1.0 +import org.kde.kirigami 2.1 // Modified version of the ContextDrawer component found in the Plasma Components // In addition to the original drawer, this will allow you to optionally insert an item // at the top of the menu, which can be any item, but originally designed for the thumbnail // navigation system for comic book pages found in Peruse. OverlayDrawer { id: root /** * title: string * A title for the action list that will be shown to the user when opens the drawer */ property string title: typeof i18n !== "undefined" ? i18n("Actions") : "Actions" // This can be any list of objects which can be a child of a column property Item topContent: pageStack.currentItem && pageStack.currentItem.contextualTopItems ? pageStack.currentItem.contextualTopItems : null; /** * actions: list * This can be any type of object that a ListView can accept as model. * It expects items compatible with either QAction or Kirigami Action */ property var actions: pageStack.currentItem ? pageStack.currentItem.contextualActions : null enabled: actions !== undefined && actions.length > 0; edge: Qt.RightEdge + drawerOpen: false - handleVisible: applicationWindow() ? applicationWindow().controlsVisible : true + //list items go to edges, have their own padding + leftPadding: 0 + rightPadding: 0 + bottomPadding: 0 + + handleVisible: applicationWindow == undefined || applicationWindow().wideScreen == true ? false : applicationWindow().controlsVisible Connections { target: pageStack onCurrentItemChanged: { - actions = pageStack.currentItem.contextualActions + if (pageStack.currentItem) + actions = pageStack.currentItem.contextualActions } } contentItem: QtControls.StackView { id: sidebarStack; implicitWidth: Units.gridUnit * 20 initialItem: sidebarPage; } Component { id: sidebarPage; Item { id: localRoot; implicitWidth: Units.gridUnit * 20 property Item topContent: root.topContent; property var actions: root.actions; property int level: 0 Item { id: topContainer; anchors { top: parent.top; left: parent.left; right: parent.right; bottom: menu.top; } children: localRoot.topContent; } Column { id: menu anchors { left: parent.left; right: parent.right; bottom: parent.bottom; } height: childrenRect.height; Item { height: localRoot.level === 0 ? heading.height : 0; visible: height > 0; width: menu.width Heading { id: heading anchors { left: parent.left right: parent.right margins: Units.largeSpacing } elide: Text.ElideRight level: 2 text: root.title } } Repeater { model: localRoot.actions; delegate: Item { width: menu.width; height: listItemHeader.visible ? listItemHeader.height : (listItem.visible ? listItem.height : (modelData.trigger === undefined ? Units.largeSpacing : 0)); Item { id: listItemHeader; anchors { top: parent.top; left: parent.left; } visible: modelData.trigger === undefined && heading.text !== ""; width: menu.width height: heading.height; Heading { id: heading anchors { left: parent.left right: parent.right margins: Units.largeSpacing } elide: Text.ElideRight level: 2 text: model && model.text ? model.text : (modelData.text ? modelData.text : "") } } BasicListItem { id: listItem; - width: parent.width; - anchors { top: parent.top; left: parent.left; } - property Item drawerRoot: root; - checked: modelData.checked ? modelData.checked : false; + checked: modelData.checked icon: modelData.iconName supportsMouseEvents: true - label: model && model.text ? model.text : (modelData.text ? modelData.text : "") - // this looks very silly - model.enabled /can/ be undefined, but enabled (and visible) only take bools, so... - enabled: model ? (model.enabled ? model.enabled : false) : (modelData.enabled ? modelData.enabled : true) - visible: (model ? (model.visible ? model.visible : false) : (modelData.visible ? modelData.visible : true)) && !listItemHeader.visible && text !== ""; + separatorVisible: false + label: model ? (model.tooltip ? model.tooltip : model.text) : (modelData.tooltip ? modelData.tooltip : modelData.text) + enabled: model ? model.enabled : modelData.enabled + visible: model ? model.visible : modelData.visible opacity: enabled ? 1.0 : 0.6 + onClicked: { + if (modelData.children!==undefined && modelData.children.length > 0) { + sidebarStack.push(sidebarPage, { actions: modelData.children, "level": level + 1, topContent: null }); + } else if (modelData && modelData.trigger !== undefined) { + modelData.trigger(); + // assume the model is a list of QAction or Action + } else if (menu.model.length > index) { + menu.model[index].trigger(); + } else { + console.warning("Don't know how to trigger the action") + } + } Icon { anchors { verticalCenter: parent.verticalCenter right: parent.right } height: Units.iconSizes.smallMedium selected: listItem.checked || listItem.pressed width: height source: "go-next" visible: modelData.children!==undefined && modelData.children.length > 0 } - onClicked: { - if (modelData.children!==undefined && modelData.children.length > 0) { - sidebarStack.push(sidebarPage, { actions: modelData.children, "level": level + 1, topContent: null }); - } else if (modelData && modelData.trigger !== undefined) { - modelData.trigger(); - } else { - console.warning("Don't know how to trigger the action") - } - } } } } BasicListItem { visible: level > 0 supportsMouseEvents: true icon: "go-previous" label: typeof i18n !== "undefined" ? i18n("Back") : "Back" onClicked: sidebarStack.pop() } } } } } diff --git a/src/app/qml/PeruseMain.qml b/src/app/qml/PeruseMain.qml index 17ad698..db3615f 100644 --- a/src/app/qml/PeruseMain.qml +++ b/src/app/qml/PeruseMain.qml @@ -1,292 +1,298 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.1 import QtQuick.Layouts 1.1 import QtQuick.Controls 1.4 as QtControls import QtQuick.Window 2.2 -import org.kde.kirigami 1.0 as Kirigami +import org.kde.kirigami 2.1 as Kirigami import org.kde.peruse 0.1 as Peruse import org.kde.contentlist 0.1 Kirigami.ApplicationWindow { id: mainWindow; title: "Comic Book Reader"; property int animationDuration: 200; property bool isLoading: true; pageStack.initialPage: welcomePage; visible: true; // If the controls are not visible, being able to drag the pagestack feels really weird, // so we just turn that ability off :) pageStack.interactive: controlsVisible; /// Which type of device we're running on. 0 is desktop, 1 is phone property int deviceType: PLASMA_PLATFORM.substring(0, 5) === "phone" ? 1 : 0; property int deviceTypeDesktop: 0; property int deviceTypePhone: 1; function showBook(filename, currentPage) { if(mainWindow.pageStack.lastItem.objectName === "bookViewer") { mainWindow.pageStack.pop(); } mainWindow.pageStack.push(bookViewer, { focus: true, file: filename, currentPage: currentPage }) peruseConfig.bookOpened(filename); } Peruse.BookListModel { id: contentList; contentModel: ContentList { onSearchCompleted: { mainWindow.isLoading = false; mainWindow.globalDrawer.actions = globalDrawerActions; } } + onCacheLoadedChanged: { + if(!cacheLoaded) { + return; + } + + var bookLocations = peruseConfig.bookLocations; + for(var i = 0; i < bookLocations.length; ++i) { + contentList.contentModel.addLocation(bookLocations[i]); + } + contentList.contentModel.setSearchString("cbz OR cbr OR cb7 OR cbt OR cba OR chm OR djvu OR epub OR pdf"); + contentList.contentModel.addMimetype("application/x-cbz"); + contentList.contentModel.addMimetype("application/x-cbr"); + contentList.contentModel.addMimetype("application/x-cb7"); + contentList.contentModel.addMimetype("application/x-cbt"); + contentList.contentModel.addMimetype("application/x-cba"); + contentList.contentModel.addMimetype("application/vnd.comicbook+zip"); + contentList.contentModel.addMimetype("application/vnd.comicbook+rar"); + contentList.contentModel.addMimetype("application/vnd.ms-htmlhelp"); + contentList.contentModel.addMimetype("image/vnd.djvu"); + contentList.contentModel.addMimetype("image/x-djvu"); + contentList.contentModel.addMimetype("application/epub+zip"); + contentList.contentModel.addMimetype("application/pdf"); + contentList.contentModel.setKnownFiles(contentList.knownBookFiles()); + contentList.contentModel.startSearch(); + } } Peruse.Config { id: peruseConfig; } function homeDir() { return peruseConfig.homeDir(); } + header: Kirigami.ApplicationHeader {} + contextDrawer: PeruseContextDrawer { id: contextDrawer; } globalDrawer: Kirigami.GlobalDrawer { title: i18nc("application title for the sidebar", "Peruse"); titleIcon: "peruse"; - opened: PLASMA_PLATFORM.substring(0, 5) === "phone" ? false : true; + drawerOpen: PLASMA_PLATFORM.substring(0, 5) === "phone" ? false : true; modal: PLASMA_PLATFORM.substring(0, 5) === "phone" ? true : false; actions: [] } property list globalDrawerActions: [ Kirigami.Action { text: "Welcome"; iconName: "start-over"; checked: mainWindow.currentCategory === "welcomePage"; checkable: true; onTriggered: { changeCategory(welcomePage); pageStack.currentItem.updateRecent(); } }, Kirigami.Action { }, Kirigami.Action { text: i18nc("Switch to the listing page showing the most recently discovered books", "Recently Added Books"); iconName: "appointment-new"; checked: mainWindow.currentCategory === "bookshelfAdded"; checkable: true; onTriggered: changeCategory(bookshelfAdded); }, Kirigami.Action { text: i18nc("Switch to the listing page showing items grouped by title", "Group by Title"); iconName: "view-media-title"; checked: mainWindow.currentCategory === "bookshelfTitle"; checkable: true; onTriggered: changeCategory(bookshelfTitle); }, Kirigami.Action { text: i18nc("Switch to the listing page showing items grouped by author", "Group by Author"); iconName: "actor"; checked: mainWindow.currentCategory === "bookshelfAuthor"; checkable: true; onTriggered: changeCategory(bookshelfAuthor); }, Kirigami.Action { text: i18nc("Switch to the listing page showing items grouped by series", "Group by Series"); iconName: "edit-group"; checked: currentCategory === "bookshelfSeries"; checkable: true; onTriggered: changeCategory(bookshelfSeries); }, Kirigami.Action { text: i18nc("Switch to the listing page showing items grouped by publisher", "Group by Publisher"); iconName: "view-media-publisher"; checked: mainWindow.currentCategory === "bookshelfPublisher"; onTriggered: changeCategory(bookshelfPublisher); }, Kirigami.Action { text: i18nc("Switch to the listing page showing items grouped by their filesystem folder", "Filter by Folder"); iconName: "tag-folder"; checked: mainWindow.currentCategory === "bookshelfFolder"; checkable: true; onTriggered: changeCategory(bookshelfFolder); }, Kirigami.Action { }, Kirigami.Action { text: i18nc("Open a book from somewhere on disk (uses the open dialog, or a drilldown on touch devices)", "Open other..."); iconName: "document-open"; onTriggered: openOther(); }, Kirigami.Action { text: i18nc("Switch to the book store page", "Get Hot New Books"); iconName: "get-hot-new-stuff"; onTriggered: changeCategory(storePage); }, Kirigami.Action { }, Kirigami.Action { text: i18nc("Open the settings page", "Settings"); iconName: "configure" checked: mainWindow.currentCategory === "settingsPage"; checkable: true; onTriggered: changeCategory(settingsPage); } ] Component { id: welcomePage; WelcomePage { onBookSelected: mainWindow.showBook(filename, currentPage); } } Component { id: bookViewer; Book { id: viewerRoot; onCurrentPageChanged: { contentList.setBookData(viewerRoot.file, "currentPage", viewerRoot.currentPage); } onTotalPagesChanged: { contentList.setBookData(viewerRoot.file, "totalPages", viewerRoot.totalPages); } } } Component { id: bookshelfTitle; Bookshelf { model: contentList.titleCategoryModel; headerText: i18nc("Title of the page with books grouped by the title start letters", "Group by Title"); onBookSelected: mainWindow.showBook(filename, currentPage); categoryName: "bookshelfTitle"; } } Component { id: bookshelfAdded; Bookshelf { model: contentList.newlyAddedCategoryModel; headerText: i18nc("Title of the page with all books ordered by which was added most recently", "Recently Added Books"); sectionRole: "created"; sectionCriteria: ViewSection.FullString; onBookSelected: mainWindow.showBook(filename, currentPage); categoryName: "bookshelfAdded"; } } Component { id: bookshelfSeries; Bookshelf { model: contentList.seriesCategoryModel; headerText: i18nc("Title of the page with books grouped by what series they are in", "Group by Series"); onBookSelected: mainWindow.showBook(filename, currentPage); categoryName: "bookshelfSeries"; } } Component { id: bookshelfAuthor; Bookshelf { model: contentList.authorCategoryModel; headerText: i18nc("Title of the page with books grouped by author", "Group by Author"); onBookSelected: mainWindow.showBook(filename, currentPage); categoryName: "bookshelfAuthor"; } } Component { id: bookshelfPublisher; Bookshelf { model: contentList; headerText: i18nc("Title of the page with books grouped by who published them", "Group by Publisher"); onBookSelected: mainWindow.showBook(filename, currentPage); categoryName: "bookshelfPublisher"; } } Component { id: bookshelfFolder; Bookshelf { model: contentList.folderCategoryModel; headerText: i18nc("Title of the page with books grouped by what folder they are in", "Filter by Folder"); onBookSelected: mainWindow.showBook(filename, currentPage); categoryName: "bookshelfFolder"; } } Component { id: bookshelf; Bookshelf { onBookSelected: mainWindow.showBook(filename, currentPage); } } Component { id: storePage; Store { } } Component { id: settingsPage; Settings { } } property string currentCategory: "welcomePage"; function changeCategory(categoryItem) { // Clear all the way to the welcome page if we change the category... mainWindow.pageStack.clear(); mainWindow.pageStack.push(categoryItem); currentCategory = mainWindow.pageStack.currentItem.categoryName; if (PLASMA_PLATFORM.substring(0, 5) === "phone") { globalDrawer.close(); } } - - Component.onCompleted: { - var bookLocations = peruseConfig.bookLocations; - for(var i = 0; i < bookLocations.length; ++i) { - contentList.contentModel.addLocation(bookLocations[i]); - } - contentList.contentModel.setSearchString("cbz OR cbr OR cb7 OR cbt OR cba OR chm OR djvu OR epub OR pdf"); - contentList.contentModel.addMimetype("application/x-cbz"); - contentList.contentModel.addMimetype("application/x-cbr"); - contentList.contentModel.addMimetype("application/x-cb7"); - contentList.contentModel.addMimetype("application/x-cbt"); - contentList.contentModel.addMimetype("application/x-cba"); - contentList.contentModel.addMimetype("application/vnd.comicbook+zip"); - contentList.contentModel.addMimetype("application/vnd.comicbook+rar"); - contentList.contentModel.addMimetype("application/vnd.ms-htmlhelp"); - contentList.contentModel.addMimetype("image/vnd.djvu"); - contentList.contentModel.addMimetype("image/x-djvu"); - contentList.contentModel.addMimetype("application/epub+zip"); - contentList.contentModel.addMimetype("application/pdf"); - contentList.contentModel.startSearch(); - } } diff --git a/src/app/qml/SearchBox.qml b/src/app/qml/SearchBox.qml index ee2e7f3..60668df 100644 --- a/src/app/qml/SearchBox.qml +++ b/src/app/qml/SearchBox.qml @@ -1,124 +1,124 @@ /* * Copyright (C) 2016 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.2 import QtQuick.Controls 1.4 as QtControls -import org.kde.kirigami 1.0 as Kirigami +import org.kde.kirigami 2.1 as Kirigami import org.kde.peruse 0.1 as Peruse import "listcomponents" as ListComponents Item { id: root; property int maxHeight: parent.height; property alias model: searchFilterProxy.sourceModel; function activate() { searchField.forceActiveFocus(); Qt.inputMethod.show(); // Would be nice if this happened automatically, but... no such luck } signal bookSelected(string filename, int currentPage); clip: true; height: searchField.focus || searchField.text.length > 0 ? searchHeight : 0; - Behavior on height { PropertyAnimation { duration: mainWindow.animationDuration; easing.type: Easing.InOutQuad; } } + Behavior on height { PropertyAnimation { duration: applicationWindow().animationDuration; easing.type: Easing.InOutQuad; } } property int searchHeight: searchField.text.length > 0 ? maxHeight : searchField.height; QtControls.TextField { id: searchField; anchors { top: parent.top; left: parent.left; right: parent.right; } placeholderText: i18nc("placeholder text for the search field", "Tap and type to search"); onTextChanged: { if(text.length > 0) { searchTimer.start(); } else { searchTimer.stop(); } } } Timer { id: searchTimer; interval: 250; repeat: false; running: false; onTriggered: searchFilterProxy.setFilterFixedString(searchField.text); } GridView { id: searchList; clip: true; anchors { top: searchField.bottom; left: parent.left; right: parent.right; bottom: parent.bottom; } footer: Item { width: parent.width; height: Kirigami.Units.iconSizes.large + Kirigami.Units.largeSpacing; } cellWidth: width / 2; cellHeight: Math.max( (root.height * 2 / 7), Math.min(cellWidth, (Kirigami.Units.iconSizes.enormous + Kirigami.Units.largeSpacing * 3 + Kirigami.Theme.defaultFont.pixelSize)) ); currentIndex: -1; model: Peruse.FilterProxy { id: searchFilterProxy; } function previousEntry() { if(currentIndex > 0) { currentIndex--; } } function nextEntry() { if(currentIndex < model.rowCount() - 1) { currentIndex++; } } delegate: Item { height: model.categoryEntriesCount === 0 ? bookTile.neededHeight : categoryTile.neededHeight; width: root.width / 2; ListComponents.CategoryTileTall { id: categoryTile; height: model.categoryEntriesCount > 0 ? neededHeight : 0; width: parent.width; count: model.categoryEntriesCount; title: model.title; entriesModel: model.categoryEntriesModel ? model.categoryEntriesModel : null; selected: searchList.currentIndex === index; } ListComponents.BookTileTall { id: bookTile; height: model.categoryEntriesCount < 1 ? neededHeight : 0; width: parent.width; author: model.author ? model.author : i18nc("used for the author data in book lists if autor is empty", "(unknown)"); title: model.title; filename: model.filename; categoryEntriesCount: model.categoryEntriesCount; currentPage: model.currentPage; totalPages: model.totalPages; onBookSelected: root.bookSelected(filename, currentPage); selected: searchList.currentIndex === index; } } } } diff --git a/src/app/qml/Settings.qml b/src/app/qml/Settings.qml index 0654809..3ee7d62 100644 --- a/src/app/qml/Settings.qml +++ b/src/app/qml/Settings.qml @@ -1,179 +1,179 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.2 import QtQuick.Layouts 1.1 import QtQuick.Controls 1.4 as QtControls import QtQuick.Dialogs 1.0 -import org.kde.kirigami 1.0 as Kirigami +import org.kde.kirigami 2.1 as Kirigami import org.kde.peruse 0.1 as Peruse import "listcomponents" as ListComponents Kirigami.Page { id: root; property string categoryName: "settingsPage"; title: i18nc("title of the settings page", "Settings"); Item { width: root.width - (root.leftPadding + root.rightPadding); height: root.height - (root.topPadding + root.bottomPadding); Column { width: parent.width; height: childrenRect.height; clip: true; Item { width: parent.width; height: Kirigami.Units.largeSpacing; } Item { height: folderHeader.height; width: parent.width - folderAdd.width - Kirigami.Units.smallSpacing; ListComponents.Section { id: folderHeader; text: i18nc("title of the list of search folders", "Search Folders"); } QtControls.ToolButton { id: folderAdd; iconName: "list-add"; onClicked: { if(PLASMA_PLATFORM.substring(0, 5) === "phone") { - mainWindow.pageStack.push(folderDlg); + applicationWindow().pageStack.push(folderDlg); } else { desktopFolderDlg.open(); } } anchors { verticalCenter: parent.verticalCenter; left: parent.right; } } } Item { width: parent.width; height: Kirigami.Units.largeSpacing; } Repeater { model: peruseConfig.bookLocations; delegate: Kirigami.SwipeListItem { id: listItem; actions: [ Kirigami.Action { text: i18nc("remove the search folder from the list", "Delete"); iconName: "list-remove" onTriggered: peruseConfig.removeBookLocation(peruseConfig.bookLocations[index]); } ] Kirigami.Label { anchors { verticalCenter: parent.verticalCenter; left: parent.left; leftMargin: Kirigami.Units.largeSpacing; } text: peruseConfig.bookLocations[index]; } } } } Rectangle { id: addingNewBooksProgress; color: Kirigami.Theme.viewBackgroundColor; anchors.fill: parent; opacity: 0; - Behavior on opacity { PropertyAnimation { duration: mainWindow.animationDuration; } } + Behavior on opacity { PropertyAnimation { duration: applicationWindow().animationDuration; } } Connections { target: contentList.contentModel; onSearchCompleted: addingNewBooksProgress.opacity = 0; } enabled: opacity > 0; MouseArea { enabled: parent.enabled; anchors.fill: parent; } Kirigami.Label { anchors { bottom: parent.verticalCenter; left: parent.left; right: parent.right; } horizontalAlignment: Text.AlignHCenter; text: i18nc("shown with a throbber when searching for books on the device", "Please wait while we find your books..."); } QtControls.BusyIndicator { id: loadingSpinner; anchors { top: parent.verticalCenter; left: parent.left; right: parent.right; } running: addingNewBooksProgress.enabled; } Kirigami.Label { anchors { top: loadingSpinner.bottom; left: parent.left; right: parent.right; } horizontalAlignment: Text.AlignHCenter; text: contentList.count; } } Component { id: folderDlg; Kirigami.Page { id: root; title: "Select a folder" FileFinder { anchors.fill: parent; folder: peruseConfig.homeDir(); showFiles: false; onAccepted: { peruseConfig.addBookLocation(selectedItem()); - mainWindow.pageStack.pop(); + applicationWindow().pageStack.pop(); root.doSearch(); } - onAborted: mainWindow.pageStack.pop(); + onAborted: applicationWindow().pageStack.pop(); } } } } FileDialog { id: desktopFolderDlg; title: i18nc("title for the dialogue used to add a new search folder", "Select a folder"); selectFolder: true; folder: shortcuts.home; onAccepted: { peruseConfig.addBookLocation(desktopFolderDlg.fileUrl); root.doSearch(); } } function doSearch() { // Now search for new items in that locations... var locations = peruseConfig.bookLocations; addingNewBooksProgress.opacity = 1; contentList.contentModel.addLocation(locations[locations.length - 1]); contentList.contentModel.startSearch(); } } diff --git a/src/app/qml/Store.qml b/src/app/qml/Store.qml index eb9c788..03769c0 100644 --- a/src/app/qml/Store.qml +++ b/src/app/qml/Store.qml @@ -1,59 +1,59 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.2 import QtQuick.Layouts 1.1 import QtQuick.Controls 1.4 as QtControls import QtQuick.Dialogs 1.0 -import org.kde.kirigami 1.0 as Kirigami +import org.kde.kirigami 2.1 as Kirigami import org.kde.peruse 0.1 as Peruse import "listcomponents" as ListComponents Kirigami.Page { id: root; property string categoryName: "storePage"; title: i18nc("title of the book store page", "Book Store"); Item { width: root.width - (root.leftPadding + root.rightPadding); height: root.height - (root.topPadding + root.bottomPadding); Loader { id: newStuffLoader; anchors.fill: parent; source: "NewStuffStuff.qml"; } Connections { target: newStuffLoader.item; onDownloadedItemClicked: { if(Array.isArray(installedFiles) && installedFiles.length > 0) { - mainWindow.showBook(installedFiles[0], 0); + applicationWindow().showBook(installedFiles[0], 0); } else if(installedFiles.length > 0) { - mainWindow.showBook(installedFiles, 0); + applicationWindow().showBook(installedFiles, 0); } } } } } diff --git a/src/app/qml/WelcomePage.qml b/src/app/qml/WelcomePage.qml index ecda070..f37e70f 100644 --- a/src/app/qml/WelcomePage.qml +++ b/src/app/qml/WelcomePage.qml @@ -1,514 +1,514 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.2 import QtQuick.Layouts 1.1 import QtQuick.Controls 1.4 as QtControls -import org.kde.kirigami 1.0 as Kirigami +import org.kde.kirigami 2.1 as Kirigami import org.kde.peruse 0.1 as Peruse import "listcomponents" as ListComponents Kirigami.Page { id: root; property string categoryName: "welcomePage"; title: i18nc("title of the welcome page", "Welcome"); signal bookSelected(string filename, int currentPage); function updateRecent() { startWithThese.updateRecentlyRead(); } property list mobileActions; property list desktopActions: [ Kirigami.Action { text: i18n("Open selected book"); shortcut: "Return"; iconName: "document-open"; onTriggered: bookSelected(startWithThese.currentItem.filename, startWithThese.currentItem.currentPage); - enabled: mainWindow.pageStack.currentItem == root && mainWindow.deviceType === mainWindow.deviceTypeDesktop; + enabled: applicationWindow().pageStack.currentItem == root && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop; }, Kirigami.Action { text: i18nc("select the previous book entry in the list", "Previous book"); shortcut: StandardKey.MoveToPreviousChar iconName: "go-previous"; onTriggered: startWithThese.selectPrevious(); - enabled: mainWindow.pageStack.currentItem == root && mainWindow.deviceType === mainWindow.deviceTypeDesktop; + enabled: applicationWindow().pageStack.currentItem == root && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop; }, Kirigami.Action { text: i18nc("select the next book entry in the list", "Next book"); shortcut: StandardKey.MoveToNextChar; iconName: "go-next"; onTriggered: startWithThese.selectNext(); - enabled: mainWindow.pageStack.currentItem == root && mainWindow.deviceType === mainWindow.deviceTypeDesktop; + enabled: applicationWindow().pageStack.currentItem == root && applicationWindow().deviceType === applicationWindow().deviceTypeDesktop; } ] actions { contextualActions: PLASMA_PLATFORM.substring(0, 5) === "phone" ? mobileActions : desktopActions; main: Kirigami.Action { text: i18nc("search in the list of books (not inside the books)", "Search Books"); iconName: "system-search"; onTriggered: searchBox.activate(); - enabled: mainWindow.pageStack.currentItem == root; + enabled: applicationWindow().pageStack.currentItem == root; } } Item { width: root.width - (root.leftPadding + root.rightPadding); height: root.height - root.topPadding; SearchBox { id: searchBox; anchors { top: parent.top; left: parent.left; right: parent.right; } model: contentList.newlyAddedCategoryModel; maxHeight: parent.height - titleContainer.height / 2; onBookSelected: root.bookSelected(filename, currentPage); } Item { id: titleContainer; anchors { top: searchBox.bottom; left: parent.left; right: parent.right; } - height: mainWindow.isLoading ? (parent.height / 2) : (appNameLabel.height + appDescriptionLabel.height + Kirigami.Units.largeSpacing); - Behavior on height { PropertyAnimation { duration: mainWindow.animationDuration; easing.type: Easing.InOutQuad; } } + height: applicationWindow().isLoading ? (parent.height / 2) : (appNameLabel.height + appDescriptionLabel.height + Kirigami.Units.largeSpacing); + Behavior on height { PropertyAnimation { duration: applicationWindow().animationDuration; easing.type: Easing.InOutQuad; } } Kirigami.Heading { id: appNameLabel; anchors { left: parent.left; right: parent.right; bottom: parent.verticalCenter; } text: "Peruse"; horizontalAlignment: Text.AlignHCenter; } Kirigami.Label { id: appDescriptionLabel; anchors { top: parent.verticalCenter; left: parent.left; right: parent.right; } text: i18nc("application subtitle", "Comic Book Reader"); horizontalAlignment: Text.AlignHCenter; } Rectangle { anchors.centerIn: parent; height: 1; color: Kirigami.Theme.textColor; width: appDescriptionLabel.paintedWidth; } } Flickable { id: startWithThese; property QtObject mostRecentlyRead0: fakeBook; property QtObject mostRecentlyRead1: fakeBook; property QtObject mostRecentlyRead2: fakeBook; property QtObject mostRecentlyRead3: fakeBook; property QtObject mostRecentlyRead4: fakeBook; property QtObject mostRecentlyRead5: fakeBook; property int mostRecentlyAdded0: -1; function updateRecentlyRead() { mostRecentlyAdded0 = -1; mostRecentlyRead0 = mostRecentlyRead1 = mostRecentlyRead2 = mostRecentlyRead3 = mostRecentlyRead4 = mostRecentlyRead5 = fakeBook; startWithThese.mostRecentlyRead0 = contentList.bookFromFile(peruseConfig.recentlyOpened[0]); startWithThese.mostRecentlyRead1 = contentList.bookFromFile(peruseConfig.recentlyOpened[1]); startWithThese.mostRecentlyRead2 = contentList.bookFromFile(peruseConfig.recentlyOpened[2]); startWithThese.mostRecentlyRead3 = contentList.bookFromFile(peruseConfig.recentlyOpened[3]); startWithThese.mostRecentlyRead4 = contentList.bookFromFile(peruseConfig.recentlyOpened[4]); startWithThese.mostRecentlyRead5 = contentList.bookFromFile(peruseConfig.recentlyOpened[5]); if(startWithThese.currentItem != null) { startWithThese.currentItem = rread0; } // the model might be null, if we haven't actually got any entries... so, let's check that // and just leave the whole thing empty in that case :) if(contentList.newlyAddedCategoryModel) { startWithThese.mostRecentlyAdded0 = 0; newItemsRepeater.model = Math.min(10, Math.floor((contentList.newlyAddedCategoryModel.rowCount() - 5))); } } Connections { target: peruseConfig; onRecentlyOpenedChanged: startWithThese.updateRecentlyRead(); } Connections { - target: mainWindow; + target: applicationWindow(); onIsLoadingChanged: { - if(mainWindow.isLoading === false) { + if(applicationWindow().isLoading === false) { startWithThese.updateRecentlyRead(); } } } anchors { top: titleContainer.bottom; left: parent.left; right: parent.right; bottom: parent.bottom; } - opacity: mainWindow.isLoading ? 0 : 1; - Behavior on opacity { PropertyAnimation { duration: mainWindow.animationDuration; } } + opacity: applicationWindow().isLoading ? 0 : 1; + Behavior on opacity { PropertyAnimation { duration: applicationWindow().animationDuration; } } contentWidth: width; contentHeight: recentItemsColumn.height; clip: true; property Item currentItem: null; property var itemArray: [rread0, rread1, rread2, rread3, rread4, rread5]; function selectNext() { var index = itemArray.indexOf(currentItem); if(index < itemArray.length) { var nextItem = itemArray[index + 1]; if(nextItem !== undefined && nextItem.height > 0) { currentItem = nextItem; } } } function selectPrevious() { var index = itemArray.indexOf(currentItem); if(index > 0) { currentItem = itemArray[index - 1]; } } Peruse.PropertyContainer { id: fakeBook; property string author: "unnamed"; property string title: "unnamed"; property string filename: ""; property string thumbnail: ""; property string currentPage: "0"; property string totalPages: "0"; } Column { id: recentItemsColumn; width: parent.width; height: childrenRect.height; ListComponents.Section { text: i18nc("title of list of recently opened books", "Continue reading"); width: startWithThese.width; height: rread0.height > 0 ? paintedHeight : 0; visible: height > 0; } Row { anchors.horizontalCenter: parent.horizontalCenter; width: childrenRect.width; height: childrenRect.height; ListComponents.BookTileTall { id: rread0; height: startWithThese.mostRecentlyRead0.readProperty("filename") != "" ? neededHeight : 0; width: startWithThese.width / 2; author: startWithThese.mostRecentlyRead0.readProperty("author"); title: startWithThese.mostRecentlyRead0.readProperty("title"); filename: startWithThese.mostRecentlyRead0.readProperty("filename"); thumbnail: startWithThese.mostRecentlyRead0.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: startWithThese.mostRecentlyRead0.readProperty("currentPage"); totalPages: startWithThese.mostRecentlyRead0.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); selected: startWithThese.currentItem === this; } ListComponents.BookTileTall { id: rread1; height: startWithThese.mostRecentlyRead1.readProperty("filename") != "" ? neededHeight : 0; width: startWithThese.width / 2; author: startWithThese.mostRecentlyRead1.readProperty("author"); title: startWithThese.mostRecentlyRead1.readProperty("title"); filename: startWithThese.mostRecentlyRead1.readProperty("filename"); thumbnail: startWithThese.mostRecentlyRead1.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: startWithThese.mostRecentlyRead1.readProperty("currentPage"); totalPages: startWithThese.mostRecentlyRead1.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); selected: startWithThese.currentItem === this; } } Row { anchors.horizontalCenter: parent.horizontalCenter; width: childrenRect.width; height: childrenRect.height; ListComponents.BookTileTall { id: rread2; height: startWithThese.mostRecentlyRead2.readProperty("filename") != "" ? neededHeight : 0; width: startWithThese.width / 4; author: startWithThese.mostRecentlyRead2.readProperty("author"); title: startWithThese.mostRecentlyRead2.readProperty("title"); filename: startWithThese.mostRecentlyRead2.readProperty("filename"); thumbnail: startWithThese.mostRecentlyRead2.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: startWithThese.mostRecentlyRead2.readProperty("currentPage"); totalPages: startWithThese.mostRecentlyRead2.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); selected: startWithThese.currentItem === this; } ListComponents.BookTileTall { id: rread3; height: startWithThese.mostRecentlyRead3.readProperty("filename") != "" ? neededHeight : 0; width: startWithThese.width / 4; author: startWithThese.mostRecentlyRead3.readProperty("author"); title: startWithThese.mostRecentlyRead3.readProperty("title"); filename: startWithThese.mostRecentlyRead3.readProperty("filename"); thumbnail: startWithThese.mostRecentlyRead3.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: startWithThese.mostRecentlyRead3.readProperty("currentPage"); totalPages: startWithThese.mostRecentlyRead3.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); selected: startWithThese.currentItem === this; } ListComponents.BookTileTall { id: rread4; height: startWithThese.mostRecentlyRead4.readProperty("filename") != "" ? neededHeight : 0; width: startWithThese.width / 4; author: startWithThese.mostRecentlyRead4.readProperty("author"); title: startWithThese.mostRecentlyRead4.readProperty("title"); filename: startWithThese.mostRecentlyRead4.readProperty("filename"); thumbnail: startWithThese.mostRecentlyRead4.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: startWithThese.mostRecentlyRead4.readProperty("currentPage"); totalPages: startWithThese.mostRecentlyRead4.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); selected: startWithThese.currentItem === this; } ListComponents.BookTileTall { id: rread5; height: startWithThese.mostRecentlyRead5.readProperty("filename") != "" ? neededHeight : 0; width: startWithThese.width / 4; author: startWithThese.mostRecentlyRead5.readProperty("author"); title: startWithThese.mostRecentlyRead5.readProperty("title"); filename: startWithThese.mostRecentlyRead5.readProperty("filename"); thumbnail: startWithThese.mostRecentlyRead5.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: startWithThese.mostRecentlyRead5.readProperty("currentPage"); totalPages: startWithThese.mostRecentlyRead5.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); selected: startWithThese.currentItem === this; } } ListComponents.Section { text: i18nc("title of list of recently discovered books", "Recently added"); width: startWithThese.width; height: paintedHeight; } Kirigami.Label { visible: !firstRecentlyAddedBook.visible; height: visible ? paintedHeight : 0; width: startWithThese.width; text: i18nc("description text for the recently discovered list, shown when no items exist in the search paths", "You have no comics on your device. Please put some into your Documents or Downloads folder (for example by downloading some) and they will show up here!"); wrapMode: Text.WordWrap; horizontalAlignment: Text.AlignHCenter; } Row { height: childrenRect.height; width: childrenRect.width; anchors.horizontalCenter: parent.horizontalCenter; ListComponents.BookTileTall { id: firstRecentlyAddedBook; visible: filename !== ""; height: visible ? neededHeight : 0; width: startWithThese.width / 2; property QtObject book: contentList.newlyAddedCategoryModel ? contentList.newlyAddedCategoryModel.get(startWithThese.mostRecentlyAdded0) : fakeBook; author: book.readProperty("author"); title: book.readProperty("title"); filename: book.readProperty("filename"); thumbnail: book.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: book.readProperty("currentPage"); totalPages: book.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); } ListComponents.BookTileTall { visible: filename !== ""; height: visible ? neededHeight : 0; width: startWithThese.width / 2; property QtObject book: contentList.newlyAddedCategoryModel ? contentList.newlyAddedCategoryModel.get(startWithThese.mostRecentlyAdded0 + 1) : fakeBook; author: book.readProperty("author"); title: book.readProperty("title"); filename: book.readProperty("filename"); thumbnail: book.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: book.readProperty("currentPage"); totalPages: book.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); } } Row { height: childrenRect.height; width: childrenRect.width; anchors.horizontalCenter: parent.horizontalCenter; ListComponents.BookTileTall { visible: filename !== ""; height: visible ? neededHeight : 0; width: startWithThese.width / 3; property QtObject book: contentList.newlyAddedCategoryModel ? contentList.newlyAddedCategoryModel.get(startWithThese.mostRecentlyAdded0 + 2) : fakeBook; author: book.readProperty("author"); title: book.readProperty("title"); filename: book.readProperty("filename"); thumbnail: book.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: book.readProperty("currentPage"); totalPages: book.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); } ListComponents.BookTileTall { visible: filename !== ""; height: visible ? neededHeight : 0; width: startWithThese.width / 3; property QtObject book: contentList.newlyAddedCategoryModel ? contentList.newlyAddedCategoryModel.get(startWithThese.mostRecentlyAdded0 + 3) : fakeBook; author: book.readProperty("author"); title: book.readProperty("title"); filename: book.readProperty("filename"); thumbnail: book.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: book.readProperty("currentPage"); totalPages: book.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); } ListComponents.BookTileTall { visible: filename !== ""; height: visible ? neededHeight : 0; width: startWithThese.width / 3; property QtObject book: contentList.newlyAddedCategoryModel ? contentList.newlyAddedCategoryModel.get(startWithThese.mostRecentlyAdded0 + 4) : fakeBook; author: book.readProperty("author"); title: book.readProperty("title"); filename: book.readProperty("filename"); thumbnail: book.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: book.readProperty("currentPage"); totalPages: book.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); } } Repeater { id: newItemsRepeater; model: 0; Row { width: childrenRect.width; height: childrenRect.height; anchors.horizontalCenter: parent.horizontalCenter; ListComponents.BookTileTall { visible: filename != ""; height: visible ? neededHeight : 0; width: startWithThese.width / 4; property QtObject book: contentList.newlyAddedCategoryModel ? contentList.newlyAddedCategoryModel.get((index * 4) + 5) : fakeBook; author: book.readProperty("author"); title: book.readProperty("title"); filename: book.readProperty("filename"); thumbnail: book.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: book.readProperty("currentPage"); totalPages: book.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); } ListComponents.BookTileTall { visible: filename != ""; height: visible ? neededHeight : 0; width: startWithThese.width / 4; property QtObject book: contentList.newlyAddedCategoryModel ? contentList.newlyAddedCategoryModel.get((index * 4) + 6) : fakeBook; author: book.readProperty("author"); title: book.readProperty("title"); filename: book.readProperty("filename"); thumbnail: book.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: book.readProperty("currentPage"); totalPages: book.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); } ListComponents.BookTileTall { visible: filename != ""; height: visible ? neededHeight : 0; width: startWithThese.width / 4; property QtObject book: contentList.newlyAddedCategoryModel ? contentList.newlyAddedCategoryModel.get((index * 4) + 7) : fakeBook; author: book.readProperty("author"); title: book.readProperty("title"); filename: book.readProperty("filename"); thumbnail: book.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: book.readProperty("currentPage"); totalPages: book.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); } ListComponents.BookTileTall { visible: filename != ""; height: visible ? neededHeight : 0; width: startWithThese.width / 4; property QtObject book: contentList.newlyAddedCategoryModel ? contentList.newlyAddedCategoryModel.get((index * 4) + 8) : fakeBook; author: book.readProperty("author"); title: book.readProperty("title"); filename: book.readProperty("filename"); thumbnail: book.readProperty("thumbnail"); categoryEntriesCount: 0; currentPage: book.readProperty("currentPage"); totalPages: book.readProperty("totalPages"); onBookSelected: root.bookSelected(filename, currentPage); } } } Item { width: parent.width; height: Kirigami.Units.iconSizes.large + Kirigami.Units.largeSpacing; } } } Item { id: loadingProgress; anchors { top: parent.verticalCenter; left: parent.left; right: parent.right; bottom: parent.bottom; } - opacity: mainWindow.isLoading ? 1 : 0; - Behavior on opacity { PropertyAnimation { duration: mainWindow.animationDuration; } } + opacity: applicationWindow().isLoading ? 1 : 0; + Behavior on opacity { PropertyAnimation { duration: applicationWindow().animationDuration; } } Kirigami.Label { anchors { bottom: parent.verticalCenter; left: parent.left; right: parent.right; } horizontalAlignment: Text.AlignHCenter; text: i18nc("shown with a throbber when searching for books on the device", "Please wait while we find your books..."); } QtControls.BusyIndicator { id: loadingSpinner; anchors { top: parent.verticalCenter; left: parent.left; right: parent.right; } - running: mainWindow.isLoading; + running: applicationWindow().isLoading; } Kirigami.Label { anchors { top: loadingSpinner.bottom; left: parent.left; right: parent.right; } horizontalAlignment: Text.AlignHCenter; text: contentList.count; } } } } diff --git a/src/app/qml/listcomponents/BookTile.qml b/src/app/qml/listcomponents/BookTile.qml index 76a0323..e274c6d 100644 --- a/src/app/qml/listcomponents/BookTile.qml +++ b/src/app/qml/listcomponents/BookTile.qml @@ -1,248 +1,248 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.1 import QtQuick.Controls 1.4 as QtControls -import org.kde.kirigami 1.0 as Kirigami +import org.kde.kirigami 2.1 as Kirigami Item { id: root; property bool selected: false; property alias title: bookTitle.text; property string author; property string publisher; property alias filename: bookFile.text; property alias thumbnail: coverImage.source; property int categoryEntriesCount; property string currentPage; property string totalPages; signal bookSelected(string filename, int currentPage); signal bookDeleteRequested(); property int neededHeight: bookCover.height;// + bookAuthorLabel.height + bookFile.height + Kirigami.Units.smallSpacing * 4; visible: height > 1; enabled: visible; clip: true; Rectangle { anchors.fill: parent; color: Kirigami.Theme.highlightColor; opacity: root.selected ? 1 : 0; Behavior on opacity { NumberAnimation { duration: Kirigami.Units.shortDuration; } } } Item { id: bookCover; anchors { top: parent.top; left: parent.left; } width: root.width / 3; height: width * 1.5; Image { id: coverImage; anchors { fill: parent; margins: Kirigami.Units.smallSpacing; } asynchronous: true; fillMode: Image.PreserveAspectFit; } MouseArea { anchors.fill: parent; onClicked: root.bookSelected(root.filename, root.currentPage); } } Kirigami.Heading { id: bookTitle; anchors { top: parent.top; leftMargin: Kirigami.Units.smallSpacing; left: bookCover.right; right: parent.right; } maximumLineCount: 1; elide: Text.ElideMiddle; font.weight: Font.Bold; MouseArea { anchors.fill: parent; onClicked: root.bookSelected(root.filename, root.currentPage); } Rectangle { anchors { left: parent.left; top: parent.baseline; topMargin: 2; } height: 2; width: parent.paintedWidth; color: Kirigami.Theme.linkColor; } } Kirigami.Label { id: bookAuthorLabel; anchors { top: bookTitle.bottom; left: bookCover.right; leftMargin: Kirigami.Units.smallSpacing; } width: paintedWidth; text: "Author"; font.bold: true; } Kirigami.Label { id: bookAuthor; anchors { top: bookTitle.bottom; left: bookAuthorLabel.right; leftMargin: Kirigami.Units.smallSpacing; right: parent.right; } elide: Text.ElideRight; text: root.author === "" ? "(unknown)" : root.author; opacity: (text === "(unknown)" || text === "") ? 0.3 : 1; } Kirigami.Label { id: bookPublisherLabel; anchors { top: bookAuthorLabel.bottom; left: bookCover.right; leftMargin: Kirigami.Units.smallSpacing; } width: paintedWidth; text: "Publisher"; font.bold: true; } Kirigami.Label { id: bookPublisher; anchors { top: bookAuthor.bottom; left: bookPublisherLabel.right; leftMargin: Kirigami.Units.smallSpacing; right: parent.right; } elide: Text.ElideRight; text: root.publisher === "" ? "(unknown)" : root.publisher; opacity: (text === "(unknown)" || text === "") ? 0.3 : 1; } Kirigami.Label { id: bookFile; anchors { top: bookPublisherLabel.bottom; left: bookCover.right; leftMargin: Kirigami.Units.smallSpacing; right: parent.right; } elide: Text.ElideMiddle; opacity: 0.3; font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.8; maximumLineCount: 1; } Item { id: descriptionContainer; anchors { top: bookFile.bottom; left: bookCover.right; right: parent.right; bottom: deleteBase.top; margins: Kirigami.Units.smallSpacing; } Kirigami.Label { anchors.fill: parent; verticalAlignment: Text.AlignTop; text: i18nc("Placeholder text for the book description field when no description is set", "(no description available for this book)"); opacity: 0.3; } } Item { id: deleteBase; anchors { left: bookCover.right; leftMargin: Kirigami.Units.smallSpacing; right: parent.right; bottom: parent.bottom; } height: deleteButton.height + Kirigami.Units.smallSpacing * 2; - Behavior on height { PropertyAnimation { duration: mainWindow.animationDuration; easing.type: Easing.InOutQuad; } } + Behavior on height { PropertyAnimation { duration: applicationWindow().animationDuration; easing.type: Easing.InOutQuad; } } states: [ State { name: "confirmDelete"; PropertyChanges { target: deleteButton; opacity: 0; } PropertyChanges { target: deleteConfirmBase; opacity: 1; } PropertyChanges { target: deleteBase; height: deleteConfirmBase.height; } } ] QtControls.Button { id: deleteButton; text: i18nc("Spawn inline dialog box to confirm permanent removal of this book", "Delete from device"); anchors { bottom: parent.bottom; right: parent.right; margins: Kirigami.Units.smallSpacing; } iconName: "edit-delete"; onClicked: deleteBase.state = "confirmDelete"; - Behavior on opacity { PropertyAnimation { duration: mainWindow.animationDuration; easing.type: Easing.InOutQuad; } } + Behavior on opacity { PropertyAnimation { duration: applicationWindow().animationDuration; easing.type: Easing.InOutQuad; } } } Item { id: deleteConfirmBase; opacity: 0; width: parent.width; - Behavior on opacity { PropertyAnimation { duration: mainWindow.animationDuration; easing.type: Easing.InOutQuad; } } + Behavior on opacity { PropertyAnimation { duration: applicationWindow().animationDuration; easing.type: Easing.InOutQuad; } } height: yesDelete.height + confirmDeleteLabel.height + Kirigami.Units.largeSpacing * 2 + Kirigami.Units.smallSpacing; Kirigami.Label { id: confirmDeleteLabel; anchors { top: parent.top; topMargin: Kirigami.Units.largeSpacing; left: parent.left; right: parent.right; } height: paintedHeight; wrapMode: Text.WordWrap; horizontalAlignment: Text.AlignHCenter; text: i18nc("Dialog text for delete book dialog", "Are you sure you want to delete this from your device?"); } QtControls.Button { id: yesDelete; anchors { top: confirmDeleteLabel.bottom; topMargin: Kirigami.Units.smallSpacing; right: parent.horizontalCenter; rightMargin: (parent.width - width) / 4; } text: i18nc("Confirmation button for book delete dialog", "Yes, really delete"); iconName: "dialog-ok"; onClicked: root.bookDeleteRequested(); } QtControls.Button { anchors { top: confirmDeleteLabel.bottom; topMargin: Kirigami.Units.smallSpacing; left: parent.horizontalCenter; leftMargin: (parent.width - width) / 4; } text: i18nc("Cancellation button or book delete dialog", "No, cancel delete"); iconName: "dialog-cancel"; onClicked: deleteBase.state = ""; } } } } diff --git a/src/app/qml/listcomponents/BookTileTall.qml b/src/app/qml/listcomponents/BookTileTall.qml index 9a9bc62..045d19a 100644 --- a/src/app/qml/listcomponents/BookTileTall.qml +++ b/src/app/qml/listcomponents/BookTileTall.qml @@ -1,127 +1,127 @@ /* * Copyright (C) 2016 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.1 import QtQuick.Controls 1.4 as QtControls -import org.kde.kirigami 1.0 as Kirigami +import org.kde.kirigami 2.1 as Kirigami Item { id: root; property bool selected: false; property alias title: bookTitle.text; property string author; property string filename; property int categoryEntriesCount; property string currentPage; property string totalPages; property double progress: currentPage / totalPages; property string thumbnail; signal bookSelected(string filename, int currentPage); /// FIXME This signal will also forward the MouseEvent, but the type is not recognised, so we can't /// add it to the signature. Certainly would be nice if that were possible, though, right? /// @see https://bugreports.qt.io/browse/QTBUG-41441 signal pressAndHold(); property int neededHeight: bookCover.height + bookTitle.height + Kirigami.Units.largeSpacing; visible: height > 0; enabled: visible; clip: true; MouseArea { anchors.fill: parent; onClicked: root.bookSelected(root.filename, root.currentPage); onPressAndHold: root.pressAndHold(mouse); } Item { id: bookCover; anchors { top: parent.top; horizontalCenter: parent.horizontalCenter; margins: Kirigami.Units.largeSpacing; } width: Math.min(parent.width - Kirigami.Units.largeSpacing * 2, Kirigami.Units.iconSizes.enormous + Kirigami.Units.largeSpacing * 2); height: width; Rectangle { anchors { fill: coverOutline; margins: -Kirigami.Units.smallSpacing; } radius: Kirigami.Units.smallSpacing; color: Kirigami.Theme.highlightColor; opacity: root.selected ? 1 : 0; Behavior on opacity { NumberAnimation { duration: Kirigami.Units.shortDuration; } } } Rectangle { id: coverOutline; anchors.centerIn: coverImage; width: Math.max(coverImage.paintedWidth, Kirigami.Units.iconSizes.large) + Kirigami.Units.smallSpacing * 2; height: Math.max(coverImage.paintedHeight, Kirigami.Units.iconSizes.large) + Kirigami.Units.smallSpacing * 2; color: Kirigami.Theme.viewBackgroundColor; border { width: 2; color: Kirigami.Theme.viewTextColor; } radius: 2; } Image { id: coverImage; anchors { fill: parent; margins: Kirigami.Units.largeSpacing; } source: root.thumbnail; asynchronous: true; fillMode: Image.PreserveAspectFit; } QtControls.BusyIndicator { id: loadingSpinner; anchors.centerIn: parent; visible: running; running: coverImage.status === Image.Loading; } } Kirigami.Label { id: bookTitle; anchors { top: bookCover.bottom; left: parent.left; right: parent.right; margins: Kirigami.Units.smallSpacing; topMargin: 0; } height: paintedHeight; maximumLineCount: 2; wrapMode: Text.WrapAtWordBoundaryOrAnywhere; elide: Text.ElideMiddle; horizontalAlignment: Text.AlignHCenter; } QtControls.ProgressBar { anchors { top: bookCover.bottom; topMargin: -Kirigami.Units.smallSpacing; left: bookCover.left; right: bookCover.right; bottom: bookTitle.top; } visible: value > 0; value: root.progress > 0 && root.progress <= 1 ? root.progress : 0; } } diff --git a/src/app/qml/listcomponents/CategoryTile.qml b/src/app/qml/listcomponents/CategoryTile.qml index 5a7f8e2..0468456 100644 --- a/src/app/qml/listcomponents/CategoryTile.qml +++ b/src/app/qml/listcomponents/CategoryTile.qml @@ -1,67 +1,67 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.1 -import org.kde.kirigami 1.0 as Kirigami +import org.kde.kirigami 2.1 as Kirigami Item { id: root; property bool selected: false; property alias count: categoryCount.text; property alias title: categoryTitle.text property QtObject entriesModel; property int neededHeight: categoryTitle.height + Kirigami.Units.smallSpacing * 2; visible: height > 0; enabled: visible; clip: true; MouseArea { anchors.fill: parent; onClicked: { - mainWindow.pageStack.push(bookshelf, { focus: true, headerText: "Comics in folder: " + root.title, model: root.entriesModel }) + applicationWindow().pageStack.push(bookshelf, { focus: true, headerText: "Comics in folder: " + root.title, model: root.entriesModel }) } } Rectangle { anchors.fill: parent; color: Kirigami.Theme.highlightColor; opacity: root.selected ? 1 : 0; Behavior on opacity { NumberAnimation { duration: Kirigami.Units.shortDuration; } } } Kirigami.Header { id: categoryTitle; anchors { margins: Kirigami.Units.smallSpacing; top: parent.top; left: parent.left; right: categoryCount.left; } elide: Text.ElideRight; } Kirigami.Label { id: categoryCount; anchors { margins: Kirigami.Units.smallSpacing; verticalCenter: parent.verticalCenter; right: parent.right; } width: paintedWidth; } } diff --git a/src/app/qml/listcomponents/CategoryTileTall.qml b/src/app/qml/listcomponents/CategoryTileTall.qml index 2af3371..53b3ae8 100644 --- a/src/app/qml/listcomponents/CategoryTileTall.qml +++ b/src/app/qml/listcomponents/CategoryTileTall.qml @@ -1,151 +1,151 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.1 -import org.kde.kirigami 1.0 as Kirigami +import org.kde.kirigami 2.1 as Kirigami Item { id: root; property bool selected: false; property alias count: categoryCount.text; property alias title: categoryTitle.text property QtObject entriesModel; property int neededHeight: categoryImage.height + categoryTitle.height + Kirigami.Units.largeSpacing; visible: height > 0; enabled: visible; clip: true; MouseArea { anchors.fill: parent; onClicked: { - mainWindow.pageStack.push(bookshelf, { focus: true, headerText: root.title, model: root.entriesModel }) + applicationWindow().pageStack.push(bookshelf, { focus: true, headerText: root.title, model: root.entriesModel }) } } Rectangle { anchors.fill: parent; color: Kirigami.Theme.highlightColor; opacity: root.selected ? 1 : 0; Behavior on opacity { NumberAnimation { duration: Kirigami.Units.shortDuration; } } } Item { id: categoryImage; anchors { top: parent.top; horizontalCenter: parent.horizontalCenter; margins: Kirigami.Units.largeSpacing; } width: Math.min(parent.width - Kirigami.Units.largeSpacing * 2, Kirigami.Units.iconSizes.enormous + Kirigami.Units.largeSpacing * 2); height: width; Rectangle { anchors.centerIn: coverImage; width: tileBg.width; height: tileBg.height; color: Kirigami.Theme.viewBackgroundColor; border { width: 2; color: Kirigami.Theme.viewTextColor; } rotation: 16; radius: 2; Rectangle { anchors { fill: parent; margins: Kirigami.Units.smallSpacing; } color: Kirigami.Theme.textColor; } } Rectangle { anchors.centerIn: coverImage; width: tileBg.width; height: tileBg.height; color: Kirigami.Theme.viewBackgroundColor; border { width: 2; color: Kirigami.Theme.viewTextColor; } rotation: 8; radius: 2; Rectangle { anchors { fill: parent; margins: Kirigami.Units.smallSpacing; } color: Kirigami.Theme.textColor; } } Rectangle { id: tileBg; anchors.centerIn: coverImage; width: Math.max(coverImage.paintedWidth, Kirigami.Units.iconSizes.large) + Kirigami.Units.smallSpacing * 2; height: Math.max(coverImage.paintedHeight, Kirigami.Units.iconSizes.large) + Kirigami.Units.smallSpacing * 2; color: Kirigami.Theme.viewBackgroundColor; border { width: 2; color: Kirigami.Theme.viewTextColor; } radius: 2; } Image { id: coverImage; anchors { fill: parent; margins: Kirigami.Units.largeSpacing; } source: root.entriesModel ? root.entriesModel.get(0).readProperty("thumbnail") : ""; asynchronous: true; fillMode: Image.PreserveAspectFit; } Rectangle { anchors { fill: categoryCount; margins: -Kirigami.Units.smallSpacing; } radius: height / 2; color: Kirigami.Theme.highlightColor; } Kirigami.Label { id: categoryCount; anchors { bottom: tileBg.bottom; right: tileBg.right; } height: paintedHeight; width: paintedWidth; color: Kirigami.Theme.highlightedTextColor; } } Kirigami.Label { id: categoryTitle; anchors { top: categoryImage.bottom; left: parent.left; right: parent.right; margins: Kirigami.Units.smallSpacing; topMargin: 0; } height: paintedHeight; maximumLineCount: 2; wrapMode: Text.WrapAtWordBoundaryOrAnywhere; elide: Text.ElideMiddle; horizontalAlignment: Text.AlignHCenter; } } diff --git a/src/app/qml/listcomponents/ListPageHeader.qml b/src/app/qml/listcomponents/ListPageHeader.qml index 3e32045..ac4567e 100644 --- a/src/app/qml/listcomponents/ListPageHeader.qml +++ b/src/app/qml/listcomponents/ListPageHeader.qml @@ -1,41 +1,41 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.1 -import org.kde.kirigami 1.0 as Kirigami +import org.kde.kirigami 2.1 as Kirigami /// FIXME Switch this to using the Kirigami page header/breadcrumbs component instead Row { id: root; property string text; width: parent.width; height: childrenRect.height; Item { width: Kirigami.Units.largeSpacing; height: 1; } Kirigami.Heading { width: parent.width - Kirigami.Units.largeSpacing; text: root.text; height: paintedHeight; } } diff --git a/src/app/qml/listcomponents/Section.qml b/src/app/qml/listcomponents/Section.qml index a816de2..b0c1c0e 100644 --- a/src/app/qml/listcomponents/Section.qml +++ b/src/app/qml/listcomponents/Section.qml @@ -1,51 +1,51 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.1 -import org.kde.kirigami 1.0 as Kirigami +import org.kde.kirigami 2.1 as Kirigami Row { width: parent.width; height: headerText.paintedHeight; property alias text: headerText.text; property alias paintedHeight: headerText.paintedHeight; Item { width: Kirigami.Units.largeSpacing; height: 1; } Kirigami.Heading { id: headerText; width: paintedWidth; text: section; } Item { width: Kirigami.Units.smallSpacing; height: 1; } Rectangle { anchors.bottom: headerText.baseline; height: 2; width: parent.width - headerText.width - Kirigami.Units.smallSpacing - Kirigami.Units.largeSpacing * 2; radius: 2; color: headerText.color; } } diff --git a/src/app/qml/viewers/cbr.qml b/src/app/qml/viewers/cbr.qml index 8153b18..c402bc1 100644 --- a/src/app/qml/viewers/cbr.qml +++ b/src/app/qml/viewers/cbr.qml @@ -1,92 +1,92 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.1 import QtQuick.Layouts 1.1 import QtQuick.Controls 1.0 import org.kde.peruse 0.1 as Peruse ViewerBase { id: root; property string title: imageBrowser.model.title; pagesModel: imageBrowser.model; pageCount: imageBrowser.model.pageCount; onRtlModeChanged: { if(rtlMode === true) { imageBrowser.layoutDirection = Qt.RightToLeft; } else { imageBrowser.layoutDirection = Qt.LeftToRight; } root.restoreCurrentPage(); } onRestoreCurrentPage: { // This is un-pretty, quite obviously. But thanks to the ListView's inability to // stay in place when the geometry changes, well, this makes things simple. imageBrowser.positionViewAtIndex(imageBrowser.currentIndex, ListView.Center); } onCurrentPageChanged: { if(currentPage !== imageBrowser.currentIndex) { pageChangeAnimation.running = false; var currentPos = imageBrowser.contentX; var newPos; imageBrowser.positionViewAtIndex(currentPage, ListView.Center); imageBrowser.currentIndex = currentPage; newPos = imageBrowser.contentX; pageChangeAnimation.from = currentPos; pageChangeAnimation.to = newPos; pageChangeAnimation.running = true; } } - NumberAnimation { id: pageChangeAnimation; target: imageBrowser; property: "contentX"; duration: mainWindow.animationDuration; easing.type: Easing.InOutQuad; } + NumberAnimation { id: pageChangeAnimation; target: imageBrowser; property: "contentX"; duration: applicationWindow().animationDuration; easing.type: Easing.InOutQuad; } Timer { id: initialPageChange; - interval: mainWindow.animationDuration; + interval: applicationWindow().animationDuration; running: false; repeat: false; onTriggered: root.currentPage = imageBrowser.model.currentPage; } ImageBrowser { id: imageBrowser; anchors.fill: parent; model: Peruse.ArchiveBookModel { filename: root.file; qmlEngine: globalQmlEngine; onLoadingCompleted: { root.loadingCompleted(success); initialPageChange.start(); } } onCurrentIndexChanged: { if(root.currentPage !== currentIndex) { root.currentPage = currentIndex; } } onGoNextPage: root.goNextPage(); onGoPreviousPage: root.goPreviousPage(); imageWidth: root.width; imageHeight: root.height; } } diff --git a/src/app/qml/viewers/folderofimages.qml b/src/app/qml/viewers/folderofimages.qml index 9dcb3d3..b22aabb 100644 --- a/src/app/qml/viewers/folderofimages.qml +++ b/src/app/qml/viewers/folderofimages.qml @@ -1,89 +1,89 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.1 import QtQuick.Layouts 1.1 import QtQuick.Controls 1.0 import org.kde.peruse 0.1 as Peruse ViewerBase { id: root; property string title: imageBrowser.model.title; pagesModel: imageBrowser.model; pageCount: imageBrowser.model.pageCount; onRtlModeChanged: { if(rtlMode === true) { imageBrowser.layoutDirection = Qt.RightToLeft; } else { imageBrowser.layoutDirection = Qt.LeftToRight; } } onRestoreCurrentPage: { // This is un-pretty, quite obviously. But thanks to the ListView's inability to // stay in place when the geometry changes, well, this makes things simple. imageBrowser.positionViewAtIndex(imageBrowser.currentIndex, ListView.Center); } onCurrentPageChanged: { if(currentPage !== imageBrowser.currentIndex) { pageChangeAnimation.running = false; var currentPos = imageBrowser.contentX; var newPos; imageBrowser.positionViewAtIndex(currentPage, ListView.Center); imageBrowser.currentIndex = currentPage; newPos = imageBrowser.contentX; pageChangeAnimation.from = currentPos; pageChangeAnimation.to = newPos; pageChangeAnimation.running = true; } } - NumberAnimation { id: pageChangeAnimation; target: imageBrowser; property: "contentX"; duration: mainWindow.animationDuration; easing.type: Easing.InOutQuad; } + NumberAnimation { id: pageChangeAnimation; target: imageBrowser; property: "contentX"; duration: applicationWindow().animationDuration; easing.type: Easing.InOutQuad; } Timer { id: initialPageChange; - interval: mainWindow.animationDuration; + interval: applicationWindow().animationDuration; running: false; repeat: false; onTriggered: root.currentPage = imageBrowser.model.currentPage; } ImageBrowser { id: imageBrowser; anchors.fill: parent; model: Peruse.FolderBookModel { filename: root.file; onLoadingCompleted: { root.loadingCompleted(success); initialPageChange.start(); } } onCurrentIndexChanged: { if(root.currentPage !== currentIndex) { root.currentPage = currentIndex; } } onGoNextPage: root.goNextPage(); onGoPreviousPage: root.goPreviousPage(); imageWidth: root.width; imageHeight: root.height; } } diff --git a/src/app/qml/viewers/okular.qml b/src/app/qml/viewers/okular.qml index 74a00b5..d0b324d 100644 --- a/src/app/qml/viewers/okular.qml +++ b/src/app/qml/viewers/okular.qml @@ -1,243 +1,243 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.1 import QtQuick.Layouts 1.1 import QtQuick.Controls 1.0 -import org.kde.kirigami 1.0 as Kirigami +import org.kde.kirigami 2.1 as Kirigami import org.kde.okular 2.0 as Okular ViewerBase { id: root; property string title: documentItem.windowTitleForDocument; onFileChanged: documentItem.path = file; onCurrentPageChanged: { if(documentItem.currentPage !== currentPage) { documentItem.currentPage = currentPage; } if(currentPage !== imageBrowser.currentIndex) { pageChangeAnimation.running = false; var currentPos = imageBrowser.contentX; var newPos; imageBrowser.positionViewAtIndex(currentPage, ListView.Center); imageBrowser.currentIndex = currentPage; newPos = imageBrowser.contentX; pageChangeAnimation.from = currentPos; pageChangeAnimation.to = newPos; pageChangeAnimation.running = true; } } - NumberAnimation { id: pageChangeAnimation; target: imageBrowser; property: "contentX"; duration: mainWindow.animationDuration; easing.type: Easing.InOutQuad; } + NumberAnimation { id: pageChangeAnimation; target: imageBrowser; property: "contentX"; duration: applicationWindow().animationDuration; easing.type: Easing.InOutQuad; } onRtlModeChanged: { if(rtlMode === true) { imageBrowser.layoutDirection = Qt.RightToLeft; } else { imageBrowser.layoutDirection = Qt.LeftToRight; } root.restoreCurrentPage(); } onRestoreCurrentPage: { // This is un-pretty, quite obviously. But thanks to the ListView's inability to // stay in place when the geometry changes, well, this makes things simple. imageBrowser.positionViewAtIndex(imageBrowser.currentIndex, ListView.Center); } pageCount: documentItem.pageCount; thumbnailComponent: thumbnailComponent; pagesModel: documentItem.matchingPages; Component { id: thumbnailComponent; Item { width: parent.width; height: Kirigami.Units.gridUnit * 6; MouseArea { anchors.fill: parent; onClicked: viewLoader.item.currentPage = model.index; } Rectangle { anchors.fill: parent; color: Kirigami.Theme.highlightColor; opacity: root.currentPage === model.index ? 1 : 0; Behavior on opacity { NumberAnimation { duration: Kirigami.Units.shortDuration; } } } Okular.ThumbnailItem { id: thumbnail anchors { top: parent.top; horizontalCenter: parent.horizontalCenter; margins: Kirigami.Units.smallSpacing; } document: documentItem pageNumber: modelData height: parent.height - pageTitle.height - Kirigami.Units.smallSpacing * 2; function updateWidth() { width = Math.round(height * (implicitWidth / implicitHeight)); } Component.onCompleted: updateWidth(); onHeightChanged: updateWidth(); onImplicitHeightChanged: updateWidth(); } Kirigami.Label { id: pageTitle; anchors { left: parent.left; right: parent.right; bottom: parent.bottom; } height: paintedHeight; text: modelData + 1; elide: Text.ElideMiddle; horizontalAlignment: Text.AlignHCenter; } } } Okular.DocumentItem { id: documentItem // onWindowTitleForDocumentChanged: { // fileBrowserRoot.title = windowTitleForDocument // } onOpenedChanged: { if(opened === true) { root.loadingCompleted(true); initialPageChange.start(); } } onCurrentPageChanged: { if(root.currentPage !== currentPage) { root.currentPage = currentPage; } } } Timer { id: initialPageChange; - interval: mainWindow.animationDuration; + interval: applicationWindow().animationDuration; running: false; repeat: false; onTriggered: root.currentPage = imageBrowser.model.currentIndex; } ListView { id: imageBrowser anchors.fill: parent; model: documentItem.matchingPages; property int imageWidth: root.width + Kirigami.Units.largeSpacing; property int imageHeight: root.height; orientation: ListView.Horizontal snapMode: ListView.SnapOneItem // This ensures that the current index is always up to date, which we need to ensure we can track the current page // as required by the thumbnail navigator, and the resume-reading-from functionality onMovementEnded: { var indexHere = indexAt(contentX + width / 2, contentY + height / 2); if(currentIndex !== indexHere) { currentIndex = indexHere; } } delegate: Flickable { id: flick width: imageBrowser.imageWidth height: imageBrowser.imageHeight contentWidth: imageBrowser.imageWidth contentHeight: imageBrowser.imageHeight interactive: contentWidth > width || contentHeight > height onInteractiveChanged: imageBrowser.interactive = !interactive; z: interactive ? 1000 : 0 PinchArea { width: Math.max(flick.contentWidth, flick.width) height: Math.max(flick.contentHeight, flick.height) property real initialWidth property real initialHeight onPinchStarted: { initialWidth = flick.contentWidth initialHeight = flick.contentHeight } onPinchUpdated: { // adjust content pos due to drag flick.contentX += pinch.previousCenter.x - pinch.center.x flick.contentY += pinch.previousCenter.y - pinch.center.y // resize content flick.resizeContent(Math.max(imageBrowser.imageWidth, initialWidth * pinch.scale), Math.max(imageBrowser.imageHeight, initialHeight * pinch.scale), pinch.center) } onPinchFinished: { // Move its content within bounds. flick.returnToBounds(); } Item { Okular.PageItem { id: page; document: documentItem; pageNumber: index; anchors.centerIn: parent; property real pageRatio: implicitWidth / implicitHeight property bool sameOrientation: root.width / root.height > pageRatio width: sameOrientation ? parent.height * pageRatio : parent.width height: !sameOrientation ? parent.width / pageRatio : parent.height } implicitWidth: page.implicitWidth implicitHeight: page.implicitHeight width: flick.contentWidth height: flick.contentHeight MouseArea { anchors.fill: parent onClicked: startToggleControls(); onDoubleClicked: { abortToggleControls(); if (flick.interactive) { flick.resizeContent(imageBrowser.imageWidth, imageBrowser.imageHeight, {x: imageBrowser.imageWidth/2, y: imageBrowser.imageHeight/2}); } else { flick.resizeContent(imageBrowser.imageWidth * 2, imageBrowser.imageHeight * 2, {x: mouse.x, y: mouse.y}); } } } } } } } MouseArea { anchors { top: parent.top; left: parent.left; bottom: parent.bottom; } width: parent.width / 6; onClicked: root.goPreviousPage(); } MouseArea { anchors { top: parent.top; right: parent.right; bottom: parent.bottom; } width: parent.width / 6; onClicked: root.goNextPage(); } } diff --git a/src/creator/qml/AddPageSheet.qml b/src/creator/qml/AddPageSheet.qml index 7dc093a..cc01deb 100644 --- a/src/creator/qml/AddPageSheet.qml +++ b/src/creator/qml/AddPageSheet.qml @@ -1,82 +1,82 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.2 import QtQuick.Dialogs 1.2 import QtQuick.Controls 1.4 as QtControls -import org.kde.kirigami 1.0 as Kirigami +import org.kde.kirigami 2.1 as Kirigami Kirigami.OverlaySheet { id: root; property int addPageAfter: 0; property QtObject model; Column { height: childrenRect.height; spacing: Kirigami.Units.smallSpacing; Kirigami.Heading { width: parent.width; height: paintedHeight; text: i18nc("title text for the add page sheet", "Add A Page?"); } Kirigami.Label { width: parent.width; height: paintedHeight; text: i18nc("help text for the add page sheet", "Please select the method you want to add the new page. No changes will be made outside of the project by performing these actions."); wrapMode: Text.WrapAtWordBoundaryOrAnywhere; } Item { width: parent.width; height: Kirigami.Units.largeSpacing; } QtControls.Button { anchors.horizontalCenter: parent.horizontalCenter; iconName: "document-open"; text: i18nc("button to add a page by finding an image on the filesystem and copying it into the book", "Copy an image from your device"); onClicked: openDlg.open(); FileDialog { id: openDlg; title: i18nc("Title of a standard file open dialog used to find a page to add to the book", "Please choose an image to add"); folder: mainWindow.homeDir(); property int splitPos: osIsWindows ? 8 : 7; onAccepted: { if(openDlg.fileUrl.toString().substring(0, 7) === "file://") { root.model.addPageFromFile(openDlg.fileUrl.toString().substring(splitPos)); root.close(); } } onRejected: { // Just do nothing, we don't really care... } } } QtControls.Button { anchors.horizontalCenter: parent.horizontalCenter; iconName: "document-new"; text: i18nc("button to add a page by creating a new image using an image editor", "Create a new image using an image editor"); } QtControls.Button { anchors.horizontalCenter: parent.horizontalCenter; iconName: "camera"; text: i18nc("button to add a page by taking a photo with a camera", "Take a photo and add that"); } } } diff --git a/src/creator/qml/Book.qml b/src/creator/qml/Book.qml index 72093f6..5a232c8 100644 --- a/src/creator/qml/Book.qml +++ b/src/creator/qml/Book.qml @@ -1,174 +1,174 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.2 import QtQuick.Controls 1.4 as QtControls -import org.kde.kirigami 1.0 as Kirigami +import org.kde.kirigami 2.1 as Kirigami import org.kde.peruse 0.1 as Peruse Kirigami.Page { id: root; property string categoryName: "book"; title: i18nc("title of the main book editor page", "Editing %1").arg(bookModel.title == "" ? root.filename : bookModel.title); property string filename; actions { left: addPageSheet.opened ? null : saveBookAction; main: addPageSheet.opened ? closeAddPageSheetAction : defaultMainAction; right: addPageSheet.opened ? null : addPageAction; } Kirigami.Action { id: saveBookAction; text: i18nc("Saves the book to a file on disk", "Save Book"); iconName: "document-save"; onTriggered: bookModel.saveBook(); enabled: bookModel.hasUnsavedChanges; } Kirigami.Action { id: addPageAction; text: i18nc("adds a new page at the end of the book", "Add Page"); iconName: "list-add"; onTriggered: addPage(bookModel.pageCount); } Kirigami.Action { id: defaultMainAction; text: i18nc("causes a dialog to show in which the user can edit the meta information for the entire book", "Edit Metainfo"); iconName: "document-edit"; onTriggered: pageStack.push(editMetaInfo); } Kirigami.Action { id: closeAddPageSheetAction; text: i18nc("closes the the add page sheet", "Do Not Add A Page"); iconName: "dialog-cancel"; onTriggered: addPageSheet.close(); } Peruse.ArchiveBookModel { id: bookModel; qmlEngine: globalQmlEngine; readWrite: true; filename: root.filename; } function addPage(afterWhatIndex) { addPageSheet.addPageAfter = afterWhatIndex; addPageSheet.open(); } AddPageSheet { id: addPageSheet; model: bookModel; } Component { id: editMetaInfo; BookMetainfoPage { model: bookModel; } } Item { width: root.width - (root.leftPadding + root.rightPadding); height: root.height - (root.topPadding + root.bottomPadding); ListView { anchors.fill: parent; model: bookModel; delegate: Kirigami.SwipeListItem { id: listItem; height: Kirigami.Units.iconSizes.huge + Kirigami.Units.smallSpacing * 2; supportsMouseEvents: true; onClicked: ; actions: [ Kirigami.Action { text: i18nc("swap the position of this page with the previous one", "Move Up"); iconName: "go-up" onTriggered: { bookModel.swapPages(model.index, model.index - 1); } enabled: model.index > 0; visible: enabled; }, Kirigami.Action { text: i18nc("swap the position of this page with the next one", "Move Down"); iconName: "go-down" onTriggered: { bookModel.swapPages(model.index, model.index + 1); } enabled: model.index < bookModel.pageCount - 1; visible: enabled; }, Kirigami.Action { text: i18nc("remove the page from the book", "Delete Page"); iconName: "list-remove" onTriggered: {} }, Kirigami.Action { text: i18nc("add a page to the book after this one", "Add Page After This"); iconName: "list-add" onTriggered: root.addPage(model.index); } ] Item { anchors.fill: parent; Item { id: bookCover; anchors { top: parent.top; left: parent.left; bottom: parent.bottom; } width: height; Image { id: coverImage; anchors { fill: parent; margins: Kirigami.Units.smallSpacing; } asynchronous: true; fillMode: Image.PreserveAspectFit; source: model.url; } } Kirigami.Label { anchors { verticalCenter: parent.verticalCenter; left: bookCover.right; leftMargin: Kirigami.Units.largeSpacing; } text: model.title; } } } } Rectangle { id: processingBackground; anchors.fill: parent; opacity: bookModel.processing ? 0.5 : 0; Behavior on opacity { PropertyAnimation { duration: mainWindow.animationDuration; } } MouseArea { anchors.fill: parent; enabled: parent.opacity > 0; onClicked: { } } } QtControls.BusyIndicator { anchors.centerIn: processingBackground; running: processingBackground.opacity > 0; visible: running; } } } diff --git a/src/creator/qml/BookMetainfoPage.qml b/src/creator/qml/BookMetainfoPage.qml index 8ded6ec..b844675 100644 --- a/src/creator/qml/BookMetainfoPage.qml +++ b/src/creator/qml/BookMetainfoPage.qml @@ -1,201 +1,201 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.2 import QtQuick.Controls 1.4 as QtControls -import org.kde.kirigami 1.0 as Kirigami +import org.kde.kirigami 2.1 as Kirigami import "metainfoeditors" Kirigami.Page { id: root; title: i18nc("title text for the book meta information editor sheet", "Edit Meta Information"); property QtObject model; actions { main: saveAndCloseAction; } Kirigami.Action { id: saveAndCloseAction; text: i18nc("Saves the remaining unsaved edited fields and closes the metainfo editor", "Save and Close Editor"); iconName: "dialog-ok"; onTriggered: { root.model.setDirty(); pageStack.pop(); } } Column { id: contentColumn; width: root.width - (root.leftPadding + root.rightPadding); height: childrenRect.height; spacing: Kirigami.Units.smallSpacing; Kirigami.Heading { width: parent.width; height: paintedHeight + Kirigami.Units.smallSpacing * 2; text: i18nc("label text for the edit field for the book title", "Titles"); } Repeater { model: root.model.acbfData ? root.model.acbfData.metaData.bookInfo.titleLanguages : 0; delegate: LanguageTextEntryEditor { width: contentColumn.width; title: modelData === "" ? i18nc("label text for the the book title with no language (default)", "Default title") : modelData; text: root.model.acbfData.metaData.bookInfo.title(modelData); onSaveRequested: { root.model.acbfData.metaData.bookInfo.setTitle(text, modelData); root.model.setDirty(); } onRemoveRequested: { root.model.acbfData.metaData.bookInfo.setTitle("", modelData); root.model.setDirty(); } removePossible: title === modelData; } } QtControls.Button { anchors.right: parent.right; width: parent.with; iconName: "list-add"; text: i18nc("Text on the button for adding new titles", "Add a title in another language"); } Kirigami.Heading { width: parent.width; height: paintedHeight + Kirigami.Units.smallSpacing * 2; text: i18nc("label text for the edit field for the genre list", "Genres"); } Repeater { model: root.model.acbfData ? root.model.acbfData.metaData.bookInfo.genres : 0; delegate: Item { width: parent.width; height: childrenRect.height; QtControls.TextField { id: genreText; width: parent.width - removeGenreButton.width - Kirigami.Units.smallSpacing; text: modelData; QtControls.Button { id: removeGenreButton; anchors { left: parent.right; leftMargin: Kirigami.Units.smallSpacing; } iconName: "list-remove"; height: parent.height; width: height; onClicked: { root.model.acbfData.metaData.bookInfo.removeGenre(modelData); root.model.setDirty(); } } } QtControls.Slider { anchors { top: genreText.bottom; topMargin: Kirigami.Units.smallSpacing; } minimumValue: 0; maximumValue: 100; stepSize: 1.0; width: genreText.width; value: root.model.acbfData.metaData.bookInfo.genrePercentage(modelData); onValueChanged: { if(value > 0 && value !== root.model.acbfData.metaData.bookInfo.genrePercentage(modelData)) { root.model.acbfData.metaData.bookInfo.setGenre(modelData, value); root.model.setDirty(); } } } } } Item { width: parent.width; height: Kirigami.Units.smallSpacing; } QtControls.TextField { width: parent.width - addCharacterButton.width - Kirigami.Units.smallSpacing; placeholderText: i18nc("placeholder text for the add new genre text entry", "Write to add new genre"); QtControls.Button { id: addGenreButton; anchors { left: parent.right; leftMargin: Kirigami.Units.smallSpacing; } iconName: "list-add"; height: parent.height; width: height; onClicked: { if(parent.text !== "") { root.model.acbfData.metaData.bookInfo.setGenre(parent.text); root.model.setDirty(); parent.text = ""; } } } } Kirigami.Heading { width: parent.width; height: paintedHeight + Kirigami.Units.smallSpacing * 2; text: i18nc("label text for the edit field for the character list", "Characters"); } Repeater { model: root.model.acbfData ? root.model.acbfData.metaData.bookInfo.characters : 0; delegate: QtControls.TextField { width: parent.width - removeCharacterButton.width - Kirigami.Units.smallSpacing; text: modelData; QtControls.Button { id: removeCharacterButton; anchors { left: parent.right; leftMargin: Kirigami.Units.smallSpacing; } iconName: "list-remove"; height: parent.height; width: height; onClicked: { root.model.acbfData.metaData.bookInfo.removeCharacter(modelData); root.model.setDirty(); } } } } Item { width: parent.width; height: Kirigami.Units.smallSpacing; } QtControls.TextField { width: parent.width - addCharacterButton.width - Kirigami.Units.smallSpacing; placeholderText: i18nc("placeholder text for the add new character text entry", "Write to add new character"); QtControls.Button { id: addCharacterButton; anchors { left: parent.right; leftMargin: Kirigami.Units.smallSpacing; } iconName: "list-add"; height: parent.height; width: height; onClicked: { if(parent.text !== "") { root.model.acbfData.metaData.bookInfo.addCharacter(parent.text); root.model.setDirty(); parent.text = ""; } } } } } } diff --git a/src/creator/qml/BookPage.qml b/src/creator/qml/BookPage.qml index 3c7e0b4..9955582 100644 --- a/src/creator/qml/BookPage.qml +++ b/src/creator/qml/BookPage.qml @@ -1,31 +1,31 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.2 -import org.kde.kirigami 1.0 as Kirigami +import org.kde.kirigami 2.1 as Kirigami Kirigami.Page { id: root; property string categoryName: "bookPage"; title: i18nc("title of the page editing sub-page for the book editor", "Page %1").arg(root.title); property string title; } diff --git a/src/creator/qml/CreateNewBook.qml b/src/creator/qml/CreateNewBook.qml index a0ad5b8..ee058cb 100644 --- a/src/creator/qml/CreateNewBook.qml +++ b/src/creator/qml/CreateNewBook.qml @@ -1,125 +1,125 @@ /* * Copyright (C) 2016 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.2 import QtQuick.Dialogs 1.2 import QtQuick.Controls 1.4 as QtControls -import org.kde.kirigami 1.0 as Kirigami +import org.kde.kirigami 2.1 as Kirigami import org.kde.peruse 0.1 as Peruse Kirigami.Page { id: root; property string categoryName: "createNewBook"; title: i18nc("title of the new book creation page", "Create New Book"); actions { main: Kirigami.Action { text: i18nc("Accept button which will create a new book", "Create Book"); iconName: "dialog-ok"; property int splitPos: osIsWindows ? 8 : 7; onTriggered: { var filename = newBookModel.createBook(getFolderDlg.folder.toString().substring(splitPos), titleEdit.text, getCoverDlg.fileUrl.toString().substring(splitPos)); if(filename.length > 0) { mainWindow.openBook(filename); } } } } Peruse.ArchiveBookModel { id: newBookModel; qmlEngine: globalQmlEngine; } Column { id: contentColumn; width: root.width - (root.leftPadding + root.rightPadding); height: childrenRect.height; spacing: Kirigami.Units.smallSpacing; Kirigami.Heading { width: parent.width; height: paintedHeight + Kirigami.Units.smallSpacing * 2; text: i18nc("label text for the edit field for the book title", "Title"); } QtControls.TextField { id: titleEdit; width: parent.width; text: i18nc("Default name for new books", "Untitled"); } Kirigami.Heading { width: parent.width; height: paintedHeight + Kirigami.Units.smallSpacing * 2; text: i18nc("label text for the edit field for the file system location for the book", "Folder"); } Kirigami.Label { width: parent.width - getFolderButton.width; text: getFolderDlg.folder; QtControls.Button { id: getFolderButton; anchors.left: parent.right; height: parent.height; width: height; iconName: "folder-open" onClicked: getFolderDlg.open(); } FileDialog { id: getFolderDlg; title: i18nc("Title of a folder dialog used to select the location of a new book", "Please choose the location for the book"); folder: mainWindow.homeDir(); selectFolder: true; } } Kirigami.Heading { width: parent.width - getCoverButton.width; height: paintedHeight + Kirigami.Units.smallSpacing * 2; text: i18nc("label text for the edit field for the cover image for the book", "Cover Image"); QtControls.Button { id: getCoverButton; anchors.left: parent.right; height: getFolderButton.height; width: height; iconName: "folder-open" onClicked: getCoverDlg.open(); } FileDialog { id: getCoverDlg; title: i18nc("Title of a file dialog used to select the cover image for a new book", "Please choose your cover image"); folder: mainWindow.homeDir(); nameFilters: [ "JPEG images (*.jpg, *.jpeg)", "All files (*)" ]; } } Item { width: parent.width; height: Kirigami.Units.iconSizes.enormous + Kirigami.Units.smallSpacing; Image { anchors.centerIn: parent; height: Kirigami.Units.iconSizes.enormous; width: Kirigami.Units.iconSizes.enormous; asynchronous: true; fillMode: Image.PreserveAspectFit; source: getCoverDlg.fileUrl; } } } } diff --git a/src/creator/qml/Main.qml b/src/creator/qml/Main.qml index f56d1a2..9c7a526 100644 --- a/src/creator/qml/Main.qml +++ b/src/creator/qml/Main.qml @@ -1,143 +1,143 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.2 import QtQuick.Window 2.2 import QtQuick.Dialogs 1.2 -import org.kde.kirigami 1.0 as Kirigami +import org.kde.kirigami 2.1 as Kirigami import org.kde.peruse 0.1 as Peruse Kirigami.ApplicationWindow { id: mainWindow; property int animationDuration: 200; width: Screen.desktopAvailableWidth * 0.6; height: Screen.desktopAvailableHeight * 0.7; pageStack.initialPage: welcomePage; Peruse.Config { id: peruseConfig; } function homeDir() { return peruseConfig.homeDir(); } function openBook(bookFilename) { currentCategory = ""; peruseConfig.bookOpened(bookFilename); mainWindow.pageStack.clear(); mainWindow.pageStack.push(bookPage, { filename: bookFilename }); } globalDrawer: Kirigami.GlobalDrawer { /// FIXME This causes the text to get cut off on the phone, however if the text is shorter /// it fails to expand the sidebar sufficiently to see all the action labels fully. Revisit /// this when switching to Kirigami title: i18nc("application title for the sidebar", "Peruse Creator"); titleIcon: "peruse-creator"; - opened: true; + drawerOpen: true; modal: false; actions: [ Kirigami.Action { text: "Welcome"; iconName: "start-over"; checked: mainWindow.currentCategory === "welcomePage"; checkable: true; onTriggered: { changeCategory(welcomePage); } }, Kirigami.Action { }, Kirigami.Action { text: i18nc("Create a book", "Create a new book..."); iconName: "document-new"; onTriggered: changeCategory(createNewBookPage); }, Kirigami.Action { text: i18nc("Open a book from somewhere on disk (uses the open dialog, or a drilldown on touch devices)", "Open other..."); iconName: "document-open"; onTriggered: openOther(); }, Kirigami.Action { }, Kirigami.Action { text: i18nc("Open the settings page", "Settings"); iconName: "configure" checked: mainWindow.currentCategory === "settingsPage"; checkable: true; onTriggered: changeCategory(settingsPage); } ] } Component { id: welcomePage; WelcomePage { } } Component { id: createNewBookPage; CreateNewBook { } } Component { id: bookPage; Book { } } Component { id: settingsPage; Settings { } } property string currentCategory: "welcomePage"; function changeCategory(categoryItem) { // Clear all the way to the welcome page if we change the category... mainWindow.pageStack.clear(); mainWindow.pageStack.push(categoryItem); currentCategory = mainWindow.pageStack.currentItem.categoryName; } function openOther() { openDlg.open(); } FileDialog { id: openDlg; title: i18nc("Title of a standard file open dialog used to open a book not in the collection", "Please choose a book to open"); folder: mainWindow.homeDir(); nameFilters: [ "Comic Book Archive zip format (*.cbz)", "All files (*)" ] property int splitPos: osIsWindows ? 8 : 7; onAccepted: { if(openDlg.fileUrl.toString().substring(0, 7) === "file://") { mainWindow.openBook(openDlg.fileUrl.toString().substring(splitPos), 0); } } onRejected: { // Just do nothing, we don't really care... } } } diff --git a/src/creator/qml/Settings.qml b/src/creator/qml/Settings.qml index edc6008..30b8fcf 100644 --- a/src/creator/qml/Settings.qml +++ b/src/creator/qml/Settings.qml @@ -1,31 +1,31 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.2 -import org.kde.kirigami 1.0 as Kirigami +import org.kde.kirigami 2.1 as Kirigami Kirigami.Page { id: root; property string categoryName: "settingsPage"; title: i18nc("title of the settings page", "Settings"); } diff --git a/src/creator/qml/WelcomePage.qml b/src/creator/qml/WelcomePage.qml index f3f2801..a717442 100644 --- a/src/creator/qml/WelcomePage.qml +++ b/src/creator/qml/WelcomePage.qml @@ -1,175 +1,175 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.2 import QtQuick.Layouts 1.1 import QtQuick.Controls 1.4 as QtControls -import org.kde.kirigami 1.0 as Kirigami +import org.kde.kirigami 2.1 as Kirigami Kirigami.Page { id: root; property string categoryName: "welcomePage"; title: i18nc("title of the welcome page", "Welcome"); Item { width: root.width - (root.leftPadding + root.rightPadding); height: root.height - root.topPadding; Item { id: titleContainer; anchors { top: parent.top; left: parent.left; right: parent.right; } height: appNameLabel.height + appDescriptionLabel.height + Kirigami.Units.largeSpacing; Kirigami.Heading { id: appNameLabel; anchors { left: parent.left; right: parent.right; bottom: parent.verticalCenter; } text: "Peruse Creator"; horizontalAlignment: Text.AlignHCenter; } Kirigami.Label { id: appDescriptionLabel; anchors { top: parent.verticalCenter; left: parent.left; right: parent.right; } text: i18nc("application subtitle", "Comic Book Creation Tool"); horizontalAlignment: Text.AlignHCenter; } Rectangle { anchors.centerIn: parent; height: 1; color: Kirigami.Theme.textColor; width: appDescriptionLabel.paintedWidth; } } Item { id: actionsContainer; anchors { top: titleContainer.bottom; left: parent.left; right: parent.right; bottom: parent.bottom; } Item { anchors { top: parent.top; left: parent.left; right: parent.right; bottom: parent.verticalCenter; margins: Kirigami.Units.largeSpacing; } Kirigami.Label { anchors.fill: parent; wrapMode: Text.WrapAtWordBoundaryOrAnywhere; horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter; text: i18nc("Longer introduction text used on the welcome page", "Welcome to Peruse Creator, a tool designed to assist you in creating comic book archives which can be read with any cbz capable comic book reader app. You can either create entirely new comic book archives from scratch, create one from a set of pictures, or editing existing archives. Once you have created them, you can even publish them directly to the online comic book archive at the KDE Store from within the application, or just share the files with your friends."); } } Item { anchors { top: parent.verticalCenter; left: parent.left; right: parent.right; bottom: parent.bottom; } Item { id: continueLast; anchors { top: parent.top; left: parent.left; right: parent.horizontalCenter; bottom: parent.verticalCenter; } QtControls.Button { anchors.centerIn: parent; iconName: "go-next"; text: i18nc("Button to continue working on the most recently opened comic book archive", "Continue %1").arg(continueLast.mostRecentBook.split('/').pop()); onClicked: mainWindow.openBook(continueLast.mostRecentBook); } property string mostRecentBook: ""; Component.onCompleted: { if(peruseConfig.recentlyOpened.length > 0) { for(var i = 0; i < peruseConfig.recentlyOpened.length; ++i) { if(peruseConfig.recentlyOpened[i].toLowerCase().slice(-4) === ".cbz") { continueLast.mostRecentBook = peruseConfig.recentlyOpened[i]; break; } } } } visible: mostRecentBook.length > 0; } Item { anchors { top: parent.top; left: continueLast.visible ? parent.horizontalCenter : parent.left; right: parent.right; bottom: parent.verticalCenter; } QtControls.Button { anchors.centerIn: parent; iconName: "document-open"; text: i18nc("Button to open existing comic book archive", "Open Existing..."); onClicked: mainWindow.openOther(); } } Item { anchors { top: parent.verticalCenter; left: parent.left; right: parent.horizontalCenter; bottom: parent.bottom; } QtControls.Button { anchors.centerIn: parent; iconName: "document-new"; text: i18nc("Button to create a new, empty comic book archive", "Create Blank"); onClicked: mainWindow.createNew(); } } Item { anchors { top: parent.verticalCenter; left: parent.horizontalCenter; right: parent.right; bottom: parent.bottom; } QtControls.Button { anchors.centerIn: parent; iconName: "folder-open"; text: i18nc("Button to create a new comic book archive by copying in a bunch of pictures", "Create From Images..."); } } } } } } diff --git a/src/creator/qml/metainfoeditors/LanguageTextEntryEditor.qml b/src/creator/qml/metainfoeditors/LanguageTextEntryEditor.qml index 574a04b..43b68d7 100644 --- a/src/creator/qml/metainfoeditors/LanguageTextEntryEditor.qml +++ b/src/creator/qml/metainfoeditors/LanguageTextEntryEditor.qml @@ -1,83 +1,83 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ import QtQuick 2.2 -import org.kde.kirigami 1.0 as Kirigami +import org.kde.kirigami 2.1 as Kirigami import QtQuick.Controls 1.4 as QtControls Item { id: root; height: childrenRect.height; property string title; property string text; property bool removePossible: true; signal saveRequested(); signal removeRequested(); onTextChanged: { if(text !== editor.text) { editor.text = text; } } Kirigami.Label { id: titleLabel; width: parent.width; height: paintedHeight + Kirigami.Units.smallSpacing * 2; text: root.title; QtControls.Button { id: removeButton; anchors { top: parent.top; right: parent.right; } height: parent.height; width: height; iconName: "list-remove"; visible: root.removePossible; onClicked: root.removeRequested(); } QtControls.Button { anchors { top: parent.top; right: removeButton.visible ? removeButton.left : parent.right; rightMargin: removeButton.visible ? Kirigami.Units.smallSpacing : 0; } height: parent.height; width: height; iconName: "document-save"; opacity: editor.text !== root.text; Behavior on opacity { PropertyAnimation { duration: mainWindow.animationDuration; } } enabled: opacity > 0; onClicked: { root.text = editor.text; root.saveRequested(); } } } QtControls.TextField { id: editor; width: parent.width; anchors.top: titleLabel.bottom; } }