diff --git a/framework/qml/CalendarComboBox.qml b/framework/qml/CalendarComboBox.qml index bc66c3a4..bd63c380 100644 --- a/framework/qml/CalendarComboBox.qml +++ b/framework/qml/CalendarComboBox.qml @@ -1,87 +1,88 @@ /* * Copyright (C) 2018 Michael Bohlender, * Copyright (C) 2019 Christian Mollekopf, * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, 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 General Public License for more details. * * You should have received a copy of the GNU 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.4 import org.kube.framework 1.0 as Kube Kube.ComboBox { id: root property alias accountId: calendarModel.accountId + property string contentType: "event" signal selected(var calendar) model: Kube.EntityModel { id: calendarModel type: "calendar" roles: ["name", "color"] sortRole: "name" - filter: {"enabled": true} + filter: {"contentTypes": contentType, enabled: true} //Set initial selection. //onCurrentIndexChanged will not work because the as more items are added the currentIndex changes, //but depending on the sorting it will point to a different item (Which is really a bug of the model or ComboBox). onInitialItemsLoaded: { if (currentIndex >= 0) { root.selected(calendarModel.data(currentIndex).object) } } } textRole: "name" onCurrentIndexChanged: { if (currentIndex >= 0) { root.selected(calendarModel.data(currentIndex).object) } } delegate: Kube.ListDelegate { width: root.popup.width height: Kube.Units.gridUnit * 1.5 contentItem: Row { Item { width: Kube.Units.smallSpacing height: parent.height } Rectangle { anchors.verticalCenter: parent.verticalCenter width: Kube.Units.gridUnit height: Kube.Units.gridUnit radius: Kube.Units.gridUnit / 2 color: model.color } Kube.Label { padding: Kube.Units.smallSpacing text: model[root.textRole] color: root.highlightedIndex === index ? Kube.Colors.highlightedTextColor : Kube.Colors.textColor } } MouseArea { anchors.fill: parent onClicked: { root.currentIndex = calendarSelector.highlightedIndex root.popup.close() } } } } diff --git a/framework/qml/View.qml b/framework/qml/View.qml index 497b5994..f5f7a322 100644 --- a/framework/qml/View.qml +++ b/framework/qml/View.qml @@ -1,167 +1,170 @@ /* * Copyright (C) 2017 Michael Bohlender, * Copyright (C) 2017 Christian Mollekopf, * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, 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 General Public License for more details. * * You should have received a copy of the GNU 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.7 import QtQuick.Controls 2 import QtQuick.Layouts 1.1 import org.kube.framework 1.0 as Kube FocusScope { id: root //Use overlayArea to position an overlay window, parented to ApplicationWindow.window, over just the view-section without the sidebar. property int sidebarWidth: ApplicationWindow.window && ApplicationWindow.window.sidebarWidth ? ApplicationWindow.window.sidebarWidth : 0 property int windowWidth: ApplicationWindow.window && ApplicationWindow.window.width ? ApplicationWindow.window.width : width property int windowHeight: ApplicationWindow.window && ApplicationWindow.window.height ? ApplicationWindow.window.height : height property rect overlayArea: Qt.rect(sidebarWidth, 0, windowWidth - sidebarWidth, windowHeight) //Search property rect searchArea property string filter: "" property var searchObject: null function triggerSearch() { if (!searchObject && StackView.visible) { searchObject = searchComponent.createObject(root) } } function clearSearch() { if (searchObject) { searchObject.close() searchObject = null } } //Help property Component helpViewComponent: null function showHelp() { if (helpViewComponent) { helpViewComponent.createObject(root).open() } } //View columns property int visibleViews: 0 //0 means the feature is disabled entirely property int currentIndex: 0 property int count: contentItems.length default property alias contentItems: content.data property bool __aborted: false + property bool isCurrentView: true //This signal will be emitted to refresh the views contents. Fetch data in here. signal refresh() //This signal will be emitted once all initial properties have been set and the view is ready to load signal setup() StackView.onActivated: { + root.isCurrentView = true root.refresh() } StackView.onDeactivated: { + root.isCurrentView = false clearSearch() } //This signal will be emitted before destruction if the view was not done signal aborted() onAborted: { __aborted = true } //This signal will be emitted when the view is done signal done() onDone: { if (!__aborted) { Kube.Fabric.postMessage(Kube.Messages.componentDone, {}) } } onCurrentIndexChanged: showRelevantSplits() Component.onCompleted: { root.setup() showRelevantSplits() } function incrementCurrentIndex() { if (currentIndex < count) { currentIndex = currentIndex + 1 } } function decrementCurrentIndex() { if (currentIndex > 0) { currentIndex = currentIndex - 1 } } function showRelevantSplits() { if (!visibleViews) { return } var i; for (i = 0; i < count; i++) { if ('visible' in contentItems[i]) { if (i < currentIndex) { contentItems[i].visible = false; } else if (i > (currentIndex + visibleViews - 1)) { contentItems[i].visible = false; } else { contentItems[i].visible = true; } } } } Kube.IconButton { anchors { top: root.top left: root.left } z: 1 color: Kube.Colors.darkBackgroundColor iconName: Kube.Icons.goBack_inverted visible: currentIndex > 0 onClicked: decrementCurrentIndex() } Keys.onEscapePressed: { if (currentIndex > 0) { decrementCurrentIndex() } } RowLayout { id: content anchors.fill: parent spacing: 0 } Component { id: searchComponent Kube.SearchPopup { searchArea: root.searchArea onFilterChanged: { root.filter = filter } } } } diff --git a/views/calendar/qml/EventEditor.qml b/views/calendar/qml/EventEditor.qml index 86767d97..db2cb6a8 100644 --- a/views/calendar/qml/EventEditor.qml +++ b/views/calendar/qml/EventEditor.qml @@ -1,214 +1,215 @@ /* * Copyright (C) 2018 Michael Bohlender, * Copyright (C) 2019 Christian Mollekopf, * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, 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 General Public License for more details. * * You should have received a copy of the GNU 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.4 import QtQuick.Layouts 1.1 import org.kube.framework 1.0 as Kube Item { id: root property bool editMode: false property date start: new Date() property bool allDay: false property var controller: Kube.EventController { allDay: root.allDay } property var accountId: null signal done() implicitWidth: contentLayout.implicitWidth + 2 * Kube.Units.largeSpacing implicitHeight: contentLayout.implicitHeight + buttons.implicitHeight + 2 * Kube.Units.largeSpacing states: [ State { name: "edit" PropertyChanges { target: deleteButton; visible: true } PropertyChanges { target: abortButton; visible: false } PropertyChanges { target: saveButton; visible: true } PropertyChanges { target: discardButton; visible: true } PropertyChanges { target: createButton; visible: false } PropertyChanges { target: calendarSelector; visible: false } }, State { name: "new" PropertyChanges { target: deleteButton; visible: false } PropertyChanges { target: abortButton; visible: true } PropertyChanges { target: saveButton; visible: false } PropertyChanges { target: discardButton; visible: false } PropertyChanges { target: createButton; visible: true } PropertyChanges { target: calendarSelector; visible: true } } ] state: editMode ? "edit" : "new" ColumnLayout { id: contentLayout anchors { fill: parent margins: Kube.Units.largeSpacing } spacing: Kube.Units.largeSpacing ColumnLayout { spacing: Kube.Units.largeSpacing Kube.TextField { id: titleEdit Layout.fillWidth: true placeholderText: qsTr("Event Title") text: controller.summary onTextChanged: controller.summary = text } ColumnLayout { id: dateAndTimeChooser spacing: Kube.Units.smallSpacing RowLayout { Layout.fillWidth: true spacing: Kube.Units.largeSpacing DateTimeChooser { id: startDate objectName: "startDate" enableTime: !controller.allDay initialValue: root.editMode ? controller.start : root.start onDateTimeChanged: controller.start = dateTime } Kube.Label { text: qsTr("until") } DateTimeChooser { id: endDate objectName: "endDate" enableTime: !controller.allDay notBefore: startDate.dateTime initialValue: root.editMode ? controller.end : startDate.dateTime onDateTimeChanged: controller.end = dateTime } } RowLayout { spacing: Kube.Units.smallSpacing Kube.CheckBox { checked: controller.allDay onCheckedChanged: { if (controller.allDay != checked) { controller.allDay = checked } } } Kube.Label { text: qsTr("All day") } } } ColumnLayout { spacing: Kube.Units.smallSpacing Layout.fillWidth: true //FIXME location doesn't exist yet // Kube.TextField { // Layout.fillWidth: true // placeholderText: qsTr("Location") // text: controller.location // onTextChanged: controller.location = text // } Kube.TextEditor { Layout.fillWidth: true Layout.fillHeight: true Layout.minimumHeight: Kube.Units.gridUnit * 4 //TODO placeholderText: "Description" text: controller.description onTextChanged: controller.description = text } Kube.CalendarComboBox { id: calendarSelector Layout.fillWidth: true accountId: root.accountId + contentType: "event" onSelected: { controller.calendar = calendar } } } } RowLayout { id: buttons spacing: Kube.Units.smallSpacing Kube.Button { id: deleteButton text: qsTr("Delete") onClicked: { controller.remove() root.done() } } Kube.Button { id: abortButton text: qsTr("Abort") onClicked: { root.done() } } Item { Layout.fillWidth: true } Kube.Button { id: discardButton text: qsTr("Discard Changes") onClicked: { root.done() } } Kube.PositiveButton { id: saveButton text: qsTr("Save Changes") onClicked: { controller.saveAction.execute() root.done() } } Kube.PositiveButton { id: createButton text: qsTr("Create Event") onClicked: { controller.saveAction.execute() root.done() } } } } } diff --git a/views/conversation/qml/View.qml b/views/conversation/qml/View.qml index f41f58b9..4815304b 100644 --- a/views/conversation/qml/View.qml +++ b/views/conversation/qml/View.qml @@ -1,259 +1,268 @@ /* * Copyright (C) 2017 Michael Bohlender, * Copyright (C) 2017 Christian Mollekopf, * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, 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 General Public License for more details. * * You should have received a copy of the GNU 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.9 import QtQuick.Controls 1.3 as Controls1 import QtQuick.Controls 2 import QtQuick.Layouts 1.1 import org.kube.framework 1.0 as Kube Kube.View { id: root property alias currentAccount: accountFolderview.currentAccount property variant currentFolder: null //We have to hardcode because all the mapToItem/mapFromItem functions are garbage searchArea: Qt.rect(ApplicationWindow.window.sidebarWidth + mailListView.parent.x, 0, (mailView.x + mailView.width) - mailListView.parent.x, (mailView.y + mailView.height) - mailListView.y) onFilterChanged: { mailListView.filter = filter Kube.Fabric.postMessage(Kube.Messages.searchString, {"searchString": filter}) } onRefresh: { if (!!root.currentFolder) { Kube.Fabric.postMessage(Kube.Messages.synchronize, {"folder": root.currentFolder}); Kube.Fabric.postMessage(Kube.Messages.synchronize, {"accountId": root.currentAccount, "type": "folder"}) } else { Kube.Fabric.postMessage(Kube.Messages.synchronize, {"accountId": root.currentAccount}) } } Kube.Listener { filter: Kube.Messages.search onMessageReceived: root.triggerSearch() } helpViewComponent: Kube.HelpPopup { ListModel { ListElement { description: qsTr("Jump to next thread:"); shortcut: "j" } ListElement { description: qsTr("Jump to previous thread:"); shortcut: "k" } ListElement { description: qsTr("Jump to next message:"); shortcut: "n" } ListElement { description: qsTr("Jump to previous message:"); shortcut: "p" } ListElement { description: qsTr("Jump to next folder:"); shortcut: "f,n" } ListElement { description: qsTr("Jump to previous previous folder:"); shortcut: "f,p" } ListElement { description: qsTr("Compose new message:"); shortcut: "c" } ListElement { description: qsTr("Reply to the currently focused message:"); shortcut: "r" } ListElement { description: qsTr("Delete the currently focused message:"); shortcut: "d" } ListElement { description: qsTr("Mark the currently focused message as important:"); shortcut: "i" } ListElement { description: qsTr("Mark the currently focused message as unread:"); shortcut: "u" } ListElement { description: qsTr("Show this help text:"); shortcut: "?" } } } Shortcut { + enabled: root.isCurrentView sequences: ['j'] onActivated: Kube.Fabric.postMessage(Kube.Messages.selectNextConversation, {}) } Shortcut { + enabled: root.isCurrentView sequences: ['k'] onActivated: Kube.Fabric.postMessage(Kube.Messages.selectPreviousConversation, {}) } Shortcut { + enabled: root.isCurrentView sequences: ['Shift+J'] onActivated: Kube.Fabric.postMessage(Kube.Messages.scrollConversationDown, {}) } Shortcut { + enabled: root.isCurrentView sequences: ['Shift+K'] onActivated: Kube.Fabric.postMessage(Kube.Messages.scrollConversationUp, {}) } Shortcut { sequences: ['n'] onActivated: Kube.Fabric.postMessage(Kube.Messages.selectNextMessage, {}) } Shortcut { + enabled: root.isCurrentView sequences: ['p'] onActivated: Kube.Fabric.postMessage(Kube.Messages.selectPreviousMessage, {}) } Shortcut { + enabled: root.isCurrentView sequences: ['f,n'] onActivated: Kube.Fabric.postMessage(Kube.Messages.selectNextFolder, {}) } Shortcut { + enabled: root.isCurrentView sequences: ['f,p'] onActivated: Kube.Fabric.postMessage(Kube.Messages.selectPreviousFolder, {}) } Shortcut { + enabled: root.isCurrentView sequences: ['c'] onActivated: Kube.Fabric.postMessage(Kube.Messages.compose, {}) } Shortcut { + enabled: root.isCurrentView sequence: "?" onActivated: root.showHelp() } Controls1.SplitView { Layout.fillWidth: true Layout.fillHeight: true Rectangle { width: Kube.Units.gridUnit * 10 Layout.fillHeight: parent.height color: Kube.Colors.darkBackgroundColor Kube.PositiveButton { id: newMailButton objectName: "newMailButton" anchors { top: parent.top left: parent.left right: parent.right margins: Kube.Units.largeSpacing } focus: true text: qsTr("New Email") onClicked: Kube.Fabric.postMessage(Kube.Messages.compose, {}) } Kube.InlineAccountSwitcher { id: accountFolderview activeFocusOnTab: true anchors { top: newMailButton.bottom topMargin: Kube.Units.largeSpacing bottom: statusBarContainer.top left: newMailButton.left right: parent.right } delegate: Kube.FolderListView { objectName: "folderListView" accountId: parent.accountId onVisibleChanged: { if (visible) { selectRootIndex() } } onActivated: { if (visible) { Kube.Fabric.postMessage(Kube.Messages.folderSelection, {"folder": model.data(index, Kube.FolderListModel.DomainObject), "trash": model.data(index, Kube.FolderListModel.Trash)}) root.currentFolder = model.data(index, Kube.FolderListModel.DomainObject) } } } } Item { id: statusBarContainer anchors { topMargin: Kube.Units.smallSpacing bottom: parent.bottom left: parent.left right: parent.right } height: childrenRect.height Rectangle { id: border visible: statusBar.visible anchors { right: parent.right left: parent.left margins: Kube.Units.smallSpacing } height: 1 color: Kube.Colors.viewBackgroundColor opacity: 0.3 } Kube.StatusBar { id: statusBar accountId: accountFolderview.currentAccount height: Kube.Units.gridUnit * 2 anchors { top: border.bottom left: statusBarContainer.left right: statusBarContainer.right } } } } Rectangle { width: Kube.Units.gridUnit * 18 Layout.fillHeight: parent.height color: "transparent" border.width: 1 border.color: Kube.Colors.buttonColor Kube.MailListView { id: mailListView objectName: "mailListView" anchors.fill: parent activeFocusOnTab: true Layout.minimumWidth: Kube.Units.gridUnit * 10 Kube.Listener { filter: Kube.Messages.folderSelection onMessageReceived: { root.clearSearch() mailListView.parentFolder = message.folder } } onCurrentMailChanged: { Kube.Fabric.postMessage(Kube.Messages.mailSelection, {"mail": currentMail}) } } } Kube.ConversationView { id: mailView objectName: "mailView" Layout.fillWidth: true Layout.fillHeight: parent.height activeFocusOnTab: true model: Kube.MailListModel { id: mailViewModel } Kube.Listener { filter: Kube.Messages.mailSelection onMessageReceived: { if (!mailListView.threaded) { mailViewModel.singleMail = message.mail } else { mailViewModel.mail = message.mail } } } Kube.Listener { filter: Kube.Messages.folderSelection onMessageReceived: { mailView.hideTrash = !message.trash mailView.hideNonTrash = message.trash } } } } } diff --git a/views/todo/qml/TodoEditor.qml b/views/todo/qml/TodoEditor.qml index bb68ccdc..d449ea19 100644 --- a/views/todo/qml/TodoEditor.qml +++ b/views/todo/qml/TodoEditor.qml @@ -1,205 +1,186 @@ /* * Copyright (C) 2018 Michael Bohlender, * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, 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 General Public License for more details. * * You should have received a copy of the GNU 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.4 import QtQuick.Layouts 1.1 import org.kube.framework 1.0 as Kube Item { id: root property bool editMode: false + property bool doing: false property var controller: Kube.TodoController { complete: false - doing: false + doing: root.doing } property var accountId: null signal done() implicitWidth: contentLayout.implicitWidth + 2 * Kube.Units.largeSpacing implicitHeight: contentLayout.implicitHeight + buttons.implicitHeight + 2 * Kube.Units.largeSpacing states: [ State { name: "edit" PropertyChanges { target: deleteButton; visible: true } PropertyChanges { target: abortButton; visible: false } PropertyChanges { target: saveButton; visible: true } PropertyChanges { target: discardButton; visible: true } PropertyChanges { target: createButton; visible: false } PropertyChanges { target: calendarSelector; visible: false } }, State { name: "new" PropertyChanges { target: deleteButton; visible: false } PropertyChanges { target: abortButton; visible: true } PropertyChanges { target: saveButton; visible: false } PropertyChanges { target: discardButton; visible: false } PropertyChanges { target: createButton; visible: true } PropertyChanges { target: calendarSelector; visible: true } } ] state: editMode ? "edit" : "new" ColumnLayout { id: contentLayout anchors { fill: parent margins: Kube.Units.largeSpacing } spacing: Kube.Units.largeSpacing ColumnLayout { spacing: Kube.Units.largeSpacing Kube.TextField { id: titleEdit + focus: true Layout.fillWidth: true placeholderText: qsTr("Todo Title") text: controller.summary onTextChanged: controller.summary = text + Keys.onReturnPressed: { + controller.saveAction.execute() + root.done() + } } - // ColumnLayout { - // id: dateAndTimeChooser - - // spacing: Kube.Units.smallSpacing - - // RowLayout { - // Layout.fillWidth: true - // spacing: Kube.Units.largeSpacing - // Kube.CheckBox { - // checked: !isNaN(controller.due) - // // onCheckedChanged: { - // // if (controller.important != checked) { - // // controller.important = checked - // // } - // // } - // } - // Kube.Label { - // text: qsTr("Due") - // } - // DateTimeChooser { - // enableTime: false - // // notBefore: startDate.dateTime - // // initialValue: root.editMode ? controller.end : startDate.dateTime - // onDateTimeChanged: controller.dudue dateTime - // } - // } - - // RowLayout { - // spacing: Kube.Units.smallSpacing - // Kube.CheckBox { - // checked: controller.important - // onCheckedChanged: { - // if (controller.important != checked) { - // controller.important = checked - // } - // } - // } - // Kube.Label { - // text: qsTr("Important") - // } - // } - // } + ColumnLayout { + RowLayout { + spacing: Kube.Units.smallSpacing + Kube.CheckBox { + checked: controller.doing + onCheckedChanged: { + if (controller.doing != checked) { + controller.doing = checked + } + } + } + Kube.Label { + text: qsTr("Doing") + } + } + } ColumnLayout { spacing: Kube.Units.smallSpacing Layout.fillWidth: true Kube.TextEditor { Layout.fillWidth: true Layout.fillHeight: true Layout.minimumHeight: Kube.Units.gridUnit * 4 //TODO placeholderText: "Description" text: controller.description onTextChanged: controller.description = text } Kube.CalendarComboBox { id: calendarSelector Layout.fillWidth: true accountId: root.accountId + contentType: "todo" onSelected: { controller.calendar = calendar } } } } RowLayout { id: buttons spacing: Kube.Units.smallSpacing Kube.Button { id: deleteButton text: qsTr("Delete") onClicked: { controller.remove() root.done() } } Kube.Button { id: abortButton text: qsTr("Abort") onClicked: { root.done() } } Item { Layout.fillWidth: true } Kube.Button { id: discardButton text: qsTr("Discard Changes") onClicked: { root.done() } } Kube.PositiveButton { id: saveButton text: qsTr("Save Changes") onClicked: { controller.saveAction.execute() root.done() } } Kube.PositiveButton { id: createButton text: qsTr("Create Todo") onClicked: { controller.saveAction.execute() root.done() } } } } } diff --git a/views/todo/qml/TodoView.qml b/views/todo/qml/TodoView.qml index daf2879c..aa1f6bc8 100644 --- a/views/todo/qml/TodoView.qml +++ b/views/todo/qml/TodoView.qml @@ -1,112 +1,112 @@ /* * Copyright (C) 2018 Michael Bohlender, * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, 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 General Public License for more details. * * You should have received a copy of the GNU 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.4 import QtQuick.Layouts 1.1 import QtQuick.Controls 2.3 import org.kube.framework 1.0 as Kube // import "dateutils.js" as DateUtils FocusScope { id: root property var controller: null signal done() StackView { id: stackView anchors.fill: parent initialItem: eventDetails clip: true visible: controller } Component { id: eventDetails Rectangle { color: Kube.Colors.viewBackgroundColor ColumnLayout { id: contentLayout anchors { fill: parent margins: Kube.Units.largeSpacing } spacing: Kube.Units.smallSpacing Kube.Heading { width: parent.width text: controller.summary } Kube.SelectableLabel { visible: !isNaN(controller.due) text: "Due on: " + controller.due.toLocaleString(Qt.locale(), "dd. MMMM") } Kube.SelectableLabel { visible: !isNaN(controller.start) text: "Start on: " + controller.start.toLocaleString(Qt.locale(), "dd. MMMM") } TextEdit { text: controller.description readOnly: true selectByMouse: true color: Kube.Colors.textColor font.family: Kube.Font.fontFamily } Item { Layout.fillHeight: true width: 1 } RowLayout { Kube.Button { text: qsTr("Remove") onClicked: { root.controller.remove() } } Item { Layout.fillWidth: true } Kube.Button { text: qsTr("Edit") onClicked: { stackView.push(editor, StackView.Immediate) } } } } } } Component { id: editor TodoEditor { controller: root.controller editMode: true - onDone: root.done() + onDone: stackView.pop(StackView.Immediate) } } } diff --git a/views/todo/qml/View.qml b/views/todo/qml/View.qml index 570052c7..6d69792e 100644 --- a/views/todo/qml/View.qml +++ b/views/todo/qml/View.qml @@ -1,392 +1,400 @@ /* * Copyright (C) 2017 Michael Bohlender, * Copyright (C) 2017 Christian Mollekopf, * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, 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 General Public License for more details. * * You should have received a copy of the GNU 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.9 import QtQuick.Controls 1.3 as Controls1 import QtQuick.Controls 2 import QtQuick.Layouts 1.1 import org.kube.framework 1.0 as Kube Kube.View { id: root property alias currentAccount: accountSwitcher.currentAccount // property variant currentFolder: null //We have to hardcode because all the mapToItem/mapFromItem functions are garbage // searchArea: Qt.rect(ApplicationWindow.window.sidebarWidth + mailListView.parent.x, 0, (mailView.x + mailView.width) - mailListView.parent.x, (mailView.y + mailView.height) - mailListView.y) onFilterChanged: { Kube.Fabric.postMessage(Kube.Messages.searchString, {"searchString": filter}) } onRefresh: { // if (!!root.currentFolder) { // Kube.Fabric.postMessage(Kube.Messages.synchronize, {"folder": root.currentFolder}); // Kube.Fabric.postMessage(Kube.Messages.synchronize, {"accountId": root.currentAccount, "type": "folder"}) // } else { Kube.Fabric.postMessage(Kube.Messages.synchronize, {"accountId": root.currentAccount}) // } } Kube.Listener { filter: Kube.Messages.search onMessageReceived: root.triggerSearch() } helpViewComponent: Kube.HelpPopup { ListModel { - ListElement { description: qsTr("Jump to next thread:"); shortcut: "j" } - ListElement { description: qsTr("Jump to previous thread:"); shortcut: "k" } - ListElement { description: qsTr("Jump to next message:"); shortcut: "n" } - ListElement { description: qsTr("Jump to previous message:"); shortcut: "p" } - ListElement { description: qsTr("Jump to next folder:"); shortcut: "f,n" } - ListElement { description: qsTr("Jump to previous previous folder:"); shortcut: "f,p" } - ListElement { description: qsTr("Compose new message:"); shortcut: "c" } - ListElement { description: qsTr("Reply to the currently focused message:"); shortcut: "r" } - ListElement { description: qsTr("Delete the currently focused message:"); shortcut: "d" } - ListElement { description: qsTr("Mark the currently focused message as important:"); shortcut: "i" } - ListElement { description: qsTr("Mark the currently focused message as unread:"); shortcut: "u" } + ListElement { description: qsTr("Go to next todo:"); shortcut: "j" } + ListElement { description: qsTr("Go to previous todo:"); shortcut: "k" } + ListElement { description: qsTr("Create new todo:"); shortcut: "c" } ListElement { description: qsTr("Show this help text:"); shortcut: "?" } } } Shortcut { sequences: ['j'] - onActivated: Kube.Fabric.postMessage(Kube.Messages.selectNextConversation, {}) + onActivated: todoView.incrementCurrentIndex() } Shortcut { sequences: ['k'] - onActivated: Kube.Fabric.postMessage(Kube.Messages.selectPreviousConversation, {}) - } - Shortcut { - sequences: ['f,n'] - onActivated: Kube.Fabric.postMessage(Kube.Messages.selectNextFolder, {}) - } - Shortcut { - sequences: ['f,p'] - onActivated: Kube.Fabric.postMessage(Kube.Messages.selectPreviousFolder, {}) + onActivated: todoView.decrementCurrentIndex() } Shortcut { sequences: ['c'] - onActivated: Kube.Fabric.postMessage(Kube.Messages.compose, {}) + onActivated: editorPopup.createObject(root, {}).open() } Shortcut { + enabled: root.isCurrentView sequence: "?" onActivated: root.showHelp() } ButtonGroup { id: viewButtonGroup } Controls1.SplitView { Layout.fillWidth: true Layout.fillHeight: true Rectangle { width: Kube.Units.gridUnit * 10 Layout.fillHeight: parent.height color: Kube.Colors.darkBackgroundColor Column { id: topLayout anchors { top: parent.top left: parent.left right: parent.right margins: Kube.Units.largeSpacing } Kube.PositiveButton { id: newMailButton objectName: "newMailButton" anchors { left: parent.left right: parent.right } focus: true text: qsTr("New Todo") onClicked: editorPopup.createObject(root, {}).open() } Item { anchors { left: parent.left right: parent.right } height: Kube.Units.gridUnit } Kube.TextButton { + id: doingViewButton anchors { left: parent.left right: parent.right } - text: qsTr("Today") + text: qsTr("Doing") textColor: Kube.Colors.highlightedTextColor checkable: true checked: true horizontalAlignment: Text.AlignHLeft ButtonGroup.group: viewButtonGroup onClicked: todoModel.filter = {"doing": true} } Kube.TextButton { id: allViewButton anchors { left: parent.left right: parent.right } text: qsTr("All") textColor: Kube.Colors.highlightedTextColor checkable: true horizontalAlignment: Text.AlignHLeft ButtonGroup.group: viewButtonGroup onClicked: todoModel.filter = {} } } Kube.CalendarSelector { id: accountSwitcher activeFocusOnTab: true anchors { top: topLayout.bottom topMargin: Kube.Units.largeSpacing bottom: statusBarContainer.top left: topLayout.left right: parent.right } contentType: "todo" } Item { id: statusBarContainer anchors { topMargin: Kube.Units.smallSpacing bottom: parent.bottom left: parent.left right: parent.right } height: childrenRect.height Rectangle { id: border visible: statusBar.visible anchors { right: parent.right left: parent.left margins: Kube.Units.smallSpacing } height: 1 color: Kube.Colors.viewBackgroundColor opacity: 0.3 } Kube.StatusBar { id: statusBar accountId: root.currentAccount height: Kube.Units.gridUnit * 2 anchors { top: border.bottom left: statusBarContainer.left right: statusBarContainer.right } } } } Rectangle { width: Kube.Units.gridUnit * 18 Layout.fillHeight: parent.height color: "transparent" border.width: 1 border.color: Kube.Colors.buttonColor Kube.ListView { id: todoView anchors.fill: parent Layout.minimumWidth: Kube.Units.gridUnit * 10 onCurrentItemChanged: { if (currentItem) { var currentData = currentItem.currentData; todoDetails.controller = controllerComponent.createObject(parent, {"todo": currentData.domainObject}) } } + Column { + anchors.centerIn: parent + visible: todoView.count === 0 + Kube.Label { + text: qsTr("Nothing here yet...") + } + Kube.PositiveButton { + visible: doingViewButton.checked + text: qsTr("Pick some tasks") + onClicked: { + allViewButton.checked = true + allViewButton.clicked() + } + } + Kube.PositiveButton { + visible: allViewButton.checked + text: qsTr("Add a new task") + onClicked: editorPopup.createObject(root, {}).open() + } + } + model: Kube.TodoModel { id: todoModel calendarFilter: accountSwitcher.enabledCalendars filter: {"doing": true} } delegate: Kube.ListDelegate { id: delegateRoot //Required for D&D // property var mail: model.mail property bool buttonsVisible: delegateRoot.hovered width: todoView.availableWidth height: Kube.Units.gridUnit * 3 color: Kube.Colors.viewBackgroundColor border.color: Kube.Colors.backgroundColor border.width: 1 Item { id: content anchors { fill: parent margins: Kube.Units.smallSpacing } Column { anchors { verticalCenter: parent.verticalCenter left: parent.left leftMargin: Kube.Units.largeSpacing } Kube.Label{ id: subject width: content.width - Kube.Units.gridUnit * 3 text: model.summary color: delegateRoot.textColor font.strikeout: model.complete font.bold: model.doing && allViewButton.checked maximumLineCount: 2 wrapMode: Text.WordWrap elide: Text.ElideRight } } Kube.Label { id: date anchors { right: parent.right bottom: parent.bottom } visible: model.date && !delegateRoot.buttonsVisible text: Qt.formatDateTime(model.date, "dd MMM yyyy") font.italic: true color: delegateRoot.disabledTextColor font.pointSize: Kube.Units.tinyFontSize } } Kube.Icon { anchors { right: parent.right verticalCenter: parent.verticalCenter margins: Kube.Units.smallSpacing } visible: model.important && !delegateRoot.buttonsVisible iconName: Kube.Icons.isImportant } Column { id: buttons anchors { right: parent.right margins: Kube.Units.smallSpacing verticalCenter: parent.verticalCenter } visible: delegateRoot.buttonsVisible opacity: 0.7 Kube.IconButton { iconName: model.doing ? Kube.Icons.listRemove : Kube.Icons.addNew activeFocusOnTab: false tooltip: model.doing ? qsTr("Unpick") : qsTr("Pick") onClicked: { var controller = controllerComponent.createObject(parent, {"todo": model.domainObject}); if (controller.complete) { controller.complete = false } controller.doing = !controller.doing; controller.saveAction.execute(); } } Kube.IconButton { iconName: Kube.Icons.checkbox checked: model.complete activeFocusOnTab: false tooltip: qsTr("Done!") onClicked: { var controller = controllerComponent.createObject(parent, {"todo": model.domainObject}); controller.complete = !controller.complete; controller.saveAction.execute(); } } } } } } Rectangle { Layout.fillHeight: parent.height Layout.fillWidth: true color: "transparent" border.width: 1 border.color: Kube.Colors.buttonColor TodoView { id: todoDetails anchors.fill: parent // onDone: popup.close() } } Component { id: controllerComponent Kube.TodoController { } } } Kube.Listener { filter: Kube.Messages.eventEditor onMessageReceived: eventPopup.createObject(root, message).open() } Component { id: editorPopup Kube.Popup { id: popup x: root.width * 0.15 y: root.height * 0.15 width: root.width * 0.7 height: root.height * 0.7 padding: 0 TodoEditor { id: editor anchors.fill: parent accountId: root.currentAccount + doing: doingViewButton.checked onDone: popup.close() } } } }