From 03fd92efdb0407b34beee13a0d2f4888b4397916 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Wed, 10 May 2017 16:26:46 +0200 Subject: A new composer based on Kube.View Kube.View is a sort of split-view that always only shows a fixed number of splits (and doesn't support manual resizing). --- components/kube/contents/ui/ComposerView.qml | 347 ++++++++++++++++++++++++++- components/kube/contents/ui/Kube.qml | 14 +- framework/qml/Icons.qml | 2 + framework/qml/Messages.qml | 2 + framework/qml/View.qml | 83 +++++++ framework/qmldir | 2 + framework/src/domain/maillistmodel.cpp | 32 +++ framework/src/domain/maillistmodel.h | 4 + 8 files changed, 472 insertions(+), 14 deletions(-) create mode 100644 framework/qml/View.qml diff --git a/components/kube/contents/ui/ComposerView.qml b/components/kube/contents/ui/ComposerView.qml index d1bc2ea5..f6bd8396 100644 --- a/components/kube/contents/ui/ComposerView.qml +++ b/components/kube/contents/ui/ComposerView.qml @@ -19,20 +19,347 @@ import QtQuick 2.7 +import QtQuick.Controls 1.3 +import QtQuick.Controls 2.0 as Controls2 +import QtQuick.Layouts 1.1 + import org.kube.framework 1.0 as Kube -Item { - id:root - signal done - property alias message: composer.message - property alias loadAsDraft: composer.loadAsDraft +Kube.View { + id: root + + property bool loadAsDraft: false + property variant message: {} + + //FIXME mean hack to unfuck hiding + property variant _composerController: Kube.ComposerController { + id: composerController + onDone: Kube.Fabric.postMessage(Kube.Messages.componentDone, {}) + } + + //actions + property variant sendAction: composerController.sendAction + property variant saveAsDraftAction: composerController.saveAsDraftAction + + Component.onCompleted: loadMessage(root.message, root.loadAsDraft) + + function loadMessage(message, loadAsDraft) { + if (message) { + composerController.loadMessage(message, loadAsDraft) + } + } + + function closeFirstSplitIfNecessary() { + //Move the view forward + if (root.currentIndex == 0) { + root.incrementCurrentIndex() + } + } + + //Drafts + Rectangle { + width: Kube.Units.gridUnit * 20 + Layout.minimumWidth: Kube.Units.gridUnit * 5 + anchors { + top: parent.top + bottom: parent.bottom + } + + color: Kube.Colors.textColor + focus: true + + ColumnLayout { + anchors { + fill: parent + margins: Kube.Units.largeSpacing + } + spacing: Kube.Units.smallSpacing + + Kube.PositiveButton { + anchors { + left: parent.left + right: parent.right + margins: Kube.Units.largeSpacing + } + focus: true + text: qsTr("New Mail") + onClicked: { + composerController.clear() + subject.forceActiveFocus() + } + } + Kube.Label{ + text: qsTr("Drafts") + color: Kube.Colors.highlightedTextColor + } + ListView { + id: listView + Layout.fillHeight: true + anchors { + left: parent.left + right: parent.right + } + + clip: true + + // Controls2.ScrollBar.vertical: Controls2.ScrollBar { + // id: scrollbar + // } + + //BEGIN keyboard nav + onActiveFocusChanged: { + if (activeFocus && currentIndex < 0) { + currentIndex = 0 + } + } + + Keys.onDownPressed: { + listView.incrementCurrentIndex() + } + Keys.onUpPressed: { + listView.decrementCurrentIndex() + } + //END keyboard nav + + onCurrentItemChanged: { + root.loadMessage(currentItem.currentData.domainObject, true) + } + + model: Kube.MailListModel { + id: mailListModel + showDrafts: true + } + + delegate: Item { + property variant currentData: model + + width: delegateRoot.width + height: delegateRoot.height + + Item { + id: delegateRoot + + property variant mail : model.domainObject + + // width: scrollbar.visible ? listView.width - scrollbar.width : listView.width + width: listView.width + height: Kube.Units.gridUnit * 4 + + states: [ + State { + name: "selected" + when: listView.currentIndex == index + + PropertyChanges {target: background; color: Kube.Colors.highlightColor} + PropertyChanges {target: subject; color: Kube.Colors.highlightedTextColor} + }, + State { + name: "hovered" + when: ( mouseArea.containsMouse || buttons.containsMouse ) + + PropertyChanges {target: background; color: Kube.Colors.highlightColor; opacity: 0.6} + PropertyChanges {target: subject; color: Kube.Colors.highlightedTextColor} + } + ] - Kube.FocusComposer { - id: composer + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: listView.currentIndex = index + } + + Rectangle { + id: background + anchors.fill: parent + color: Kube.Colors.textColor + } + + Item { + id: content + + anchors { + top: parent.top + bottom: parent.bottom + left: parent.left + right: parent.right + 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.subject + color: Kube.Colors.highlightedTextColor + maximumLineCount: 2 + wrapMode: Text.WrapAnywhere + elide: Text.ElideRight + } + } + + Kube.Label { + id: date + + anchors { + right: parent.right + bottom: parent.bottom + } + text: Qt.formatDateTime(model.date, "dd MMM yyyy") + font.italic: true + color: Kube.Colors.disabledTextColor + font.pointSize: 9 + } + } + } + } + } + } + } + + //Content + Rectangle { + Layout.fillWidth: true + Layout.minimumWidth: Kube.Units.gridUnit * 5 + anchors { + top: parent.top + bottom: parent.bottom + } + color: Kube.Colors.backgroundColor + + ColumnLayout { + anchors { + fill: parent + margins: Kube.Units.largeSpacing + leftMargin: Kube.Units.largeSpacing + Kube.Units.gridUnit * 2 + rightMargin: Kube.Units.largeSpacing + Kube.Units.gridUnit * 2 + } + Kube.TextField { + id: subject + Layout.fillWidth: true + + placeholderText: "Enter Subject..." + text: composerController.subject + onTextChanged: composerController.subject = text; + onActiveFocusChanged: closeFirstSplitIfNecessary() + } + + Controls2.TextArea { + id: content + Layout.fillWidth: true + Layout.fillHeight: true + + text: composerController.body + onTextChanged: composerController.body = text; + onActiveFocusChanged: closeFirstSplitIfNecessary() + } + } + } + + //Recepients + Rectangle { + width: Kube.Units.gridUnit * 20 + Layout.minimumWidth: Kube.Units.gridUnit * 5 anchors { - fill: parent - margins: Kube.Units.smallSpacing + top: parent.top + bottom: parent.bottom + } + color: Kube.Colors.backgroundColor + ColumnLayout { + anchors { + fill: parent + margins: Kube.Units.largeSpacing + } + width: parent.width + + Kube.Label { + text: "Sending Email to" + } + Kube.AutocompleteLineEdit { + id: to + Layout.fillWidth: true + text: composerController.to + onTextChanged: composerController.to = text + model: composerController.recipientCompleter.model + onSearchTermChanged: composerController.recipientCompleter.searchString = searchTerm + } + + Kube.Label { + text: "Sending Copy to (CC)" + } + Kube.AutocompleteLineEdit { + id: cc + Layout.fillWidth: true + text: composerController.cc + onTextChanged: composerController.cc = text + model: composerController.recipientCompleter.model + onSearchTermChanged: composerController.recipientCompleter.searchString = searchTerm + } + + Kube.Label { + text: "Sending Secret Copy to (Bcc)" + } + Kube.AutocompleteLineEdit { + id: bcc + Layout.fillWidth: true + text: composerController.bcc + onTextChanged: composerController.bcc = text; + model: composerController.recipientCompleter.model + onSearchTermChanged: composerController.recipientCompleter.searchString = searchTerm + } + + Item { + Layout.fillHeight: true + } + + + Item { + Layout.fillHeight: true + } + + + Kube.Button { + id: saveDraftButton + + text: "Save as Draft" + //TODO enabled: saveAsDraftAction.enabled + onClicked: { + saveAsDraftAction.execute() + } + } + Kube.Button { + text: "Discard" + onClicked: Kube.Fabric.postMessage(Kube.Messages.componentDone, {}) + } + + Kube.Label { + text: "You are sending this from:" + } + Kube.ComboBox { + id: identityCombo + model: composerController.identitySelector.model + textRole: "displayName" + Layout.fillWidth: true + onCurrentIndexChanged: { + composerController.identitySelector.currentIndex = currentIndex + } + } + + Kube.PositiveButton { + width: saveDraftButton.width + + text: "Send" + enabled: sendAction.enabled + onClicked: { + sendAction.execute() + } + } } - onDone: root.done() } } diff --git a/components/kube/contents/ui/Kube.qml b/components/kube/contents/ui/Kube.qml index 115123b7..21c6f4fd 100644 --- a/components/kube/contents/ui/Kube.qml +++ b/components/kube/contents/ui/Kube.qml @@ -117,10 +117,12 @@ Controls2.ApplicationWindow { Kube.IconButton { iconName: Kube.Icons.search_inverted + onClicked: search.open() + } - onClicked: { - search.open() - } + Kube.IconButton { + iconName: Kube.Icons.edit_inverted + onClicked: kubeViews.openComposer() } Kube.IconButton { @@ -166,6 +168,11 @@ Controls2.ApplicationWindow { Layout.fillWidth: true initialItem: mailView + Kube.Listener { + filter: Kube.Messages.componentDone + onMessageReceived: kubeViews.pop({immediate: true}) + } + function setPeopleView() { //TODO replacing here while a composer is open is destructive kubeViews.push({item: peopleView, replace: true, immediate: true}) @@ -202,7 +209,6 @@ Controls2.ApplicationWindow { Component { id: composerView ComposerView { - onDone: kubeViews.pop({immediate: true}) } } Component { diff --git a/framework/qml/Icons.qml b/framework/qml/Icons.qml index add51534..05cde6c9 100644 --- a/framework/qml/Icons.qml +++ b/framework/qml/Icons.qml @@ -38,6 +38,7 @@ Item { property string undo: "edit-undo-inverted" property string moveToTrash: "kubetrash" property string edit: "document-edit" + property string edit_inverted: "document-edit-inverted" property string replyToSender: "mail-reply-sender" property string outbox: "mail-folder-outbox" property string outbox_inverted: "mail-folder-outbox-inverted" @@ -47,6 +48,7 @@ Item { property string search_inverted: "edit-find-inverted" property string mail_inverted: "mail-message-inverted" property string goBack: "go-previous" + property string goBack_inverted: "go-previous-inverted" property string goDown: "go-down" property string goUp: "go-down" diff --git a/framework/qml/Messages.qml b/framework/qml/Messages.qml index 19d0c402..aa43de76 100644 --- a/framework/qml/Messages.qml +++ b/framework/qml/Messages.qml @@ -41,5 +41,7 @@ Item { property string reply: "reply" property string edit: "edit" property string compose: "compose" + + property string componentDone: "done" } diff --git a/framework/qml/View.qml b/framework/qml/View.qml new file mode 100644 index 00000000..6a0b51b4 --- /dev/null +++ b/framework/qml/View.qml @@ -0,0 +1,83 @@ +/* + * 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 1.3 +import QtQuick.Controls 2.0 as Controls2 +import QtQuick.Layouts 1.1 +import org.kube.framework 1.0 as Kube + +Item { + id: container + + property int visibleViews: 2 + property int currentIndex: 0 + property int count: contentItems.length + default property alias contentItems: content.data + + onCurrentIndexChanged: showRelevantSplits() + Component.onCompleted: showRelevantSplits() + + function incrementCurrentIndex() { + if (currentIndex < count) { + currentIndex = currentIndex + 1 + } + } + + function decrementCurrentIndex() { + if (currentIndex > 0) { + currentIndex = currentIndex - 1 + } + } + + function showRelevantSplits() { + var i; + for (i = 0; i < count; 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: container.top + left: container.left + } + z: 1 + background: Rectangle { + anchors.fill: parent + color: Kube.Colors.textColor + } + iconName: Kube.Icons.goBack_inverted + visible: currentIndex > 0 + onClicked: decrementCurrentIndex() + } + + RowLayout { + id: content + anchors.fill: parent + } +} diff --git a/framework/qmldir b/framework/qmldir index 8b0c52ac..2060a920 100644 --- a/framework/qmldir +++ b/framework/qmldir @@ -22,6 +22,8 @@ ComboBox 1.0 ComboBox.qml PositiveButton 1.0 PositiveButton.qml TextField 1.0 TextField.qml Label 1.0 Label.qml +View 1.0 View.qml +AutocompleteLineEdit 1.0 AutocompleteLineEdit.qml singleton Messages 1.0 Messages.qml singleton Colors 1.0 Colors.qml singleton Icons 1.0 Icons.qml diff --git a/framework/src/domain/maillistmodel.cpp b/framework/src/domain/maillistmodel.cpp index 0f477624..73d686eb 100644 --- a/framework/src/domain/maillistmodel.cpp +++ b/framework/src/domain/maillistmodel.cpp @@ -302,3 +302,35 @@ QVariant MailListModel::mail() const } +void MailListModel::setShowDrafts(bool) +{ + using namespace Sink::ApplicationDomain; + Sink::Query query; + // query.setFlags(Sink::Query::LiveQuery | Sink::Query::UpdateStatus); + query.filter(true); + query.request(); + query.request(); + query.request(); + query.request(); + query.request(); + query.request(); + query.request(); + query.request(); + query.request(); + query.request(); + query.request(); + query.request(); + query.request(); + query.request(); + mFetchMails = true; + mFetchedMails.clear(); + qDebug() << "Running mail query for drafts: "; + //Latest mail at the top + sort(0, Qt::AscendingOrder); + runQuery(query); +} + +bool MailListModel::showDrafts() const +{ + return false; +} diff --git a/framework/src/domain/maillistmodel.h b/framework/src/domain/maillistmodel.h index 5ed081f4..5f593700 100644 --- a/framework/src/domain/maillistmodel.h +++ b/framework/src/domain/maillistmodel.h @@ -31,6 +31,7 @@ class MailListModel : public QSortFilterProxyModel Q_OBJECT Q_PROPERTY (QVariant parentFolder READ parentFolder WRITE setParentFolder) Q_PROPERTY (QVariant mail READ mail WRITE setMail) + Q_PROPERTY (bool showDrafts READ showDrafts WRITE setShowDrafts) Q_PROPERTY (QString filter READ filter WRITE setFilter) Q_PROPERTY (bool isThreaded READ isThreaded NOTIFY isThreadedChanged) @@ -87,6 +88,9 @@ public: void setFilter(const QString &mail); QString filter() const; + void setShowDrafts(bool); + bool showDrafts() const; + signals: void isThreadedChanged(); -- cgit v1.2.3