diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2017-07-26 21:39:48 -0600 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2017-07-27 11:01:21 -0600 |
commit | 89f4c68f548b24d983309cc7337b7c0ffe8aa3f3 (patch) | |
tree | b17891a6e956a70c71fb32a63d886852c7caf6ee /framework/qml/ConversationView.qml | |
parent | dff119d85fd4d6ceaf792337fee59300c1b23579 (diff) | |
download | kube-89f4c68f548b24d983309cc7337b7c0ffe8aa3f3.tar.gz kube-89f4c68f548b24d983309cc7337b7c0ffe8aa3f3.zip |
Keyboard navigation for the conversation view
Removed the manual currentIndex handling again as we seem to be able to
use the regular stuff now.
Additionally the listview is now resized if we don't have enough mails,
so the first mail is shown on top.
We can also move from mail to mail using keyboard navigation.
The mail highlight also serves as focus indicator for the conversation
view in general, and as such is cleared when loosing focus.
Diffstat (limited to 'framework/qml/ConversationView.qml')
-rw-r--r-- | framework/qml/ConversationView.qml | 229 |
1 files changed, 111 insertions, 118 deletions
diff --git a/framework/qml/ConversationView.qml b/framework/qml/ConversationView.qml index fb7a435e..1da1a9db 100644 --- a/framework/qml/ConversationView.qml +++ b/framework/qml/ConversationView.qml | |||
@@ -25,13 +25,10 @@ import org.kube.framework 1.0 as Kube | |||
25 | import QtQml 2.2 as QtQml | 25 | import QtQml 2.2 as QtQml |
26 | 26 | ||
27 | 27 | ||
28 | Rectangle { | 28 | FocusScope { |
29 | id: root | 29 | id: root |
30 | 30 | ||
31 | property variant mail; | 31 | property variant mail; |
32 | property int currentIndex: 0; | ||
33 | property bool scrollToEnd: true; | ||
34 | property variant currentMail: null; | ||
35 | property bool hideTrash: true; | 32 | property bool hideTrash: true; |
36 | property bool hideNonTrash: false; | 33 | property bool hideNonTrash: false; |
37 | 34 | ||
@@ -39,6 +36,7 @@ Rectangle { | |||
39 | filter: Kube.Messages.mailSelection | 36 | filter: Kube.Messages.mailSelection |
40 | onMessageReceived: { | 37 | onMessageReceived: { |
41 | root.mail = message.mail | 38 | root.mail = message.mail |
39 | listView.forceLayout() | ||
42 | } | 40 | } |
43 | } | 41 | } |
44 | 42 | ||
@@ -50,143 +48,138 @@ Rectangle { | |||
50 | } | 48 | } |
51 | } | 49 | } |
52 | 50 | ||
53 | onCurrentIndexChanged: { | 51 | Rectangle { |
54 | markAsReadTimer.restart(); | ||
55 | } | ||
56 | onMailChanged: { | ||
57 | scrollToEnd = true; | ||
58 | currentMail = null; | ||
59 | } | ||
60 | |||
61 | color: Kube.Colors.backgroundColor | ||
62 | |||
63 | Kube.ListView { | ||
64 | id: listView | ||
65 | |||
66 | anchors.fill: parent | 52 | anchors.fill: parent |
53 | color: Kube.Colors.backgroundColor | ||
54 | |||
55 | Kube.ListView { | ||
56 | id: listView | ||
57 | focus: true | ||
67 | 58 | ||
68 | ScrollBar.vertical: ScrollBar {} | 59 | anchors { |
69 | verticalLayoutDirection: ListView.BottomToTop | 60 | top: parent.top |
70 | 61 | left: parent.left | |
71 | function setCurrentIndex() | 62 | right: parent.right |
72 | { | ||
73 | /** | ||
74 | * This will detect the index at the "scrollbar-position" (visibleArea.yPosition). | ||
75 | * This ensures that the first and last entry can become the currentIndex, | ||
76 | * but in the middle of the list the item in the middle is set as the current item. | ||
77 | */ | ||
78 | var yPos = 0.5; | ||
79 | if (listView.visibleArea.yPosition < 0.4) { | ||
80 | yPos = 0.2 + (0.2 * listView.visibleArea.yPosition); | ||
81 | } | 63 | } |
82 | if (listView.visibleArea.yPosition > 0.6) { | 64 | //Shrink the listview if the content doesn't fill the full height, so the email appears on top instead of on the bottom. |
83 | yPos = 0.6 + (0.2 * listView.visibleArea.yPosition) | 65 | height: Math.min(contentHeight, parent.height) |
66 | |||
67 | verticalLayoutDirection: ListView.BottomToTop | ||
68 | |||
69 | onActiveFocusChanged: { | ||
70 | if (activeFocus) { | ||
71 | if (currentIndex < 0) { | ||
72 | currentIndex = 0 | ||
73 | } | ||
74 | } else { | ||
75 | currentIndex = -1 | ||
76 | } | ||
84 | } | 77 | } |
85 | var indexAtCenter = listView.indexAt(root.width / 2, contentY + root.height * yPos); | 78 | |
86 | if (indexAtCenter >= 0) { | 79 | //Position view so the last email begins on top of the view |
87 | root.currentIndex = indexAtCenter; | 80 | onContentHeightChanged: { |
88 | } else { | 81 | //FIXME This doesn't work quite correctly when we have headers and footers in the listview and the mail loads to slowly and only one item to show. |
89 | root.currentIndex = count - 1; | 82 | listView.positionViewAtIndex(0, ListView.End) |
83 | //A futile attempt to fix the problem above | ||
84 | listView.returnToBounds() | ||
90 | } | 85 | } |
91 | } | ||
92 | 86 | ||
93 | clip: true | 87 | Keys.onDownPressed: { |
88 | decrementCurrentIndex() | ||
89 | positionViewAtIndex(listView.currentIndex, ListView.End) | ||
90 | } | ||
94 | 91 | ||
95 | model: Kube.MailListModel { | 92 | Keys.onUpPressed: { |
96 | mail: root.mail | 93 | incrementCurrentIndex() |
97 | } | 94 | positionViewAtIndex(listView.currentIndex, ListView.End) |
95 | } | ||
98 | 96 | ||
99 | header: Item { | 97 | onCurrentIndexChanged: { |
100 | height: Kube.Units.gridUnit * 0.5 | 98 | markAsReadTimer.restart(); |
101 | width: parent.width | 99 | } |
102 | 100 | ||
103 | } | 101 | model: Kube.MailListModel { |
102 | mail: root.mail | ||
103 | } | ||
104 | 104 | ||
105 | footer: Item { | 105 | header: Item { |
106 | height: Kube.Units.gridUnit | 106 | height: Kube.Units.gridUnit * 0.5 |
107 | width: parent.width | 107 | width: parent.width |
108 | } | 108 | } |
109 | 109 | ||
110 | delegate: mailDelegate | 110 | footer: Item { |
111 | height: Kube.Units.gridUnit | ||
112 | width: parent.width | ||
113 | } | ||
111 | 114 | ||
112 | //Setting the currentIndex results in further lags. So we don't do that either. | 115 | delegate: FocusScope { |
113 | // currentIndex: root.currentIndex | 116 | id: delegateRoot |
114 | 117 | ||
115 | //Optimize for view quality | 118 | property var currentData: model |
116 | pixelAligned: true | ||
117 | 119 | ||
118 | onContentYChanged: { | 120 | focus: false |
119 | //We have to track our current mail manually | 121 | activeFocusOnTab: false |
120 | setCurrentIndex(); | ||
121 | } | ||
122 | 122 | ||
123 | //The cacheBuffer needs to be large enough to fit the whole thread. | 123 | height: sheet.height + Kube.Units.gridUnit |
124 | //Otherwise the contentHeight will constantly increase and decrease, | 124 | width: parent.width |
125 | //which will break lot's of things. | 125 | visible: !((root.hideTrash && model.trash) || (root.hideNonTrash && !model.trash)) |
126 | cacheBuffer: 100000 | 126 | |
127 | 127 | MouseArea { | |
128 | Timer { | 128 | anchors.fill: parent |
129 | id: markAsReadTimer | 129 | hoverEnabled: true |
130 | interval: 2000 | 130 | onEntered: delegateRoot.ListView.view.currentIndex = index |
131 | running: false | 131 | onClicked: delegateRoot.ListView.view.currentIndex = index |
132 | repeat: false | ||
133 | onTriggered: { | ||
134 | if (!!root.currentMail) { | ||
135 | Kube.Fabric.postMessage(Kube.Messages.markAsRead, {"mail": root.currentMail}) | ||
136 | } | 132 | } |
137 | } | ||
138 | } | ||
139 | 133 | ||
140 | //Intercept all scroll events, | 134 | MailViewer { |
141 | //necessary due to the webengineview | 135 | id: sheet |
142 | Kube.MouseProxy { | 136 | anchors.centerIn: parent |
143 | anchors.fill: parent | 137 | width: parent.width - Kube.Units.gridUnit * 2 |
144 | target: listView.mouseProxy | 138 | |
145 | forwardWheelEvents: true | 139 | message: model.mimeMessage |
146 | } | 140 | subject: model.subject |
147 | } | 141 | sender: model.sender |
148 | Component { | 142 | senderName: model.senderName |
149 | id: mailDelegate | 143 | to: model.to |
150 | 144 | cc: model.cc | |
151 | Item { | 145 | bcc: model.bcc |
152 | id: wrapper | 146 | date: model.date |
153 | property bool isCurrent: root.currentIndex === index; | 147 | trash: model.trash |
154 | onIsCurrentChanged: { | 148 | draft: model.draft |
155 | if (isCurrent) { | 149 | sent: model.sent |
156 | root.currentMail = model.mail | 150 | incomplete: model.incomplete |
151 | current: delegateRoot.ListView.isCurrentItem | ||
157 | } | 152 | } |
158 | } | 153 | } |
159 | 154 | ||
160 | height: sheet.height + Kube.Units.gridUnit | ||
161 | width: parent.width | ||
162 | visible: !((root.hideTrash && model.trash) || (root.hideNonTrash && !model.trash)) | ||
163 | 155 | ||
164 | MouseArea { | 156 | //Optimize for view quality |
165 | anchors.fill: parent | 157 | pixelAligned: true |
166 | enabled: parent.enabled | 158 | |
167 | hoverEnabled: true | 159 | |
168 | onEntered: root.currentIndex = index | 160 | //The cacheBuffer needs to be large enough to fit the whole thread. |
169 | onClicked: root.currentIndex = index | 161 | //Otherwise the contentHeight will constantly increase and decrease, |
162 | //which will break lot's of things. | ||
163 | cacheBuffer: 100000 | ||
164 | |||
165 | Timer { | ||
166 | id: markAsReadTimer | ||
167 | interval: 2000 | ||
168 | running: false | ||
169 | repeat: false | ||
170 | onTriggered: { | ||
171 | if (listView.currentItem && !!listView.currentItem.currentData.mail) { | ||
172 | Kube.Fabric.postMessage(Kube.Messages.markAsRead, {"mail": listView.currentItem.currentData.mail}) | ||
173 | } | ||
174 | } | ||
170 | } | 175 | } |
171 | 176 | ||
172 | MailViewer { | 177 | //Intercept all scroll events, |
173 | id: sheet | 178 | //necessary due to the webengineview |
174 | anchors.centerIn: parent | 179 | Kube.MouseProxy { |
175 | width: parent.width - Kube.Units.gridUnit * 2 | 180 | anchors.fill: parent |
176 | 181 | target: listView.mouseProxy | |
177 | message: model.mimeMessage | 182 | forwardWheelEvents: true |
178 | subject: model.subject | ||
179 | sender: model.sender | ||
180 | senderName: model.senderName | ||
181 | to: model.to | ||
182 | cc: model.cc | ||
183 | bcc: model.bcc | ||
184 | date: model.date | ||
185 | trash: model.trash | ||
186 | draft: model.draft | ||
187 | sent: model.sent | ||
188 | incomplete: model.incomplete | ||
189 | current: isCurrent | ||
190 | } | 183 | } |
191 | } | 184 | } |
192 | } | 185 | } |