diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2018-01-09 09:35:59 +0100 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2018-01-10 12:42:13 +0100 |
commit | 6d726bb10386b3d7f5481d41b735ec06cb2163ad (patch) | |
tree | 4d591b67b54c5a83f9f1d718a4576c8ccf05859b | |
parent | 2d9944bd0b5cd1dd202d9dc6318d612e1aca4241 (diff) | |
download | kube-6d726bb10386b3d7f5481d41b735ec06cb2163ad.tar.gz kube-6d726bb10386b3d7f5481d41b735ec06cb2163ad.zip |
Install composer/converations/people as separate views and load them
dynamically.
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | components/kube/qml/Kube.qml | 228 | ||||
-rw-r--r-- | components/kube/qml/LoginView.qml | 52 | ||||
-rw-r--r-- | components/kube/qml/ViewManager.qml | 84 | ||||
-rw-r--r-- | framework/qml/LoginAccount.qml | 4 | ||||
-rw-r--r-- | framework/src/extensionmodel.cpp | 63 | ||||
-rw-r--r-- | framework/src/extensionmodel.h | 14 | ||||
-rw-r--r-- | views/CMakeLists.txt | 11 | ||||
-rw-r--r-- | views/accounts/metadata.json | 4 | ||||
-rw-r--r-- | views/accounts/qml/View.qml (renamed from components/kube/qml/AccountsView.qml) | 0 | ||||
-rw-r--r-- | views/accounts/tests/tst_accountsview.qml (renamed from components/kube/tests/tst_accountsview.qml) | 2 | ||||
-rw-r--r-- | views/composer/metadata.json | 4 | ||||
-rw-r--r-- | views/composer/qml/AddresseeListEditor.qml (renamed from components/kube/qml/AddresseeListEditor.qml) | 0 | ||||
-rw-r--r-- | views/composer/qml/View.qml (renamed from components/kube/qml/ComposerView.qml) | 0 | ||||
-rw-r--r-- | views/composer/tests/tst_composerview.qml (renamed from components/kube/tests/tst_composerview.qml) | 2 | ||||
-rw-r--r-- | views/conversation/metadata.json | 4 | ||||
-rw-r--r-- | views/conversation/qml/View.qml (renamed from components/kube/qml/MailView.qml) | 0 | ||||
-rw-r--r-- | views/conversation/tests/tst_conversationview.qml (renamed from components/kube/tests/tst_mailview.qml) | 2 | ||||
-rw-r--r-- | views/log/metadata.json | 4 | ||||
-rw-r--r-- | views/log/qml/View.qml (renamed from components/kube/qml/LogView.qml) | 0 | ||||
-rw-r--r-- | views/log/tests/tst_logview.qml (renamed from components/kube/tests/tst_logview.qml) | 2 | ||||
-rw-r--r-- | views/people/metadata.json | 4 | ||||
-rw-r--r-- | views/people/qml/View.qml (renamed from components/kube/qml/PeopleView.qml) | 0 | ||||
-rw-r--r-- | views/people/tests/tst_peopleview.qml (renamed from components/kube/tests/tst_peopleview.qml) | 2 |
24 files changed, 254 insertions, 233 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 7933a9e6..48120389 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
@@ -36,5 +36,6 @@ add_subdirectory(framework) | |||
36 | add_subdirectory(components) | 36 | add_subdirectory(components) |
37 | add_subdirectory(icons) | 37 | add_subdirectory(icons) |
38 | add_subdirectory(applications) | 38 | add_subdirectory(applications) |
39 | add_subdirectory(views) | ||
39 | add_subdirectory(accounts) | 40 | add_subdirectory(accounts) |
40 | add_subdirectory(tests) | 41 | add_subdirectory(tests) |
diff --git a/components/kube/qml/Kube.qml b/components/kube/qml/Kube.qml index 9a31ba58..be3f7e34 100644 --- a/components/kube/qml/Kube.qml +++ b/components/kube/qml/Kube.qml | |||
@@ -86,35 +86,6 @@ Controls2.ApplicationWindow { | |||
86 | } | 86 | } |
87 | } | 87 | } |
88 | 88 | ||
89 | Kube.Listener { | ||
90 | filter: Kube.Messages.reply | ||
91 | onMessageReceived: { | ||
92 | kubeViews.openComposerWithMail(message.mail, false) | ||
93 | } | ||
94 | } | ||
95 | |||
96 | Kube.Listener { | ||
97 | filter: Kube.Messages.edit | ||
98 | onMessageReceived: { | ||
99 | kubeViews.openComposerWithMail(message.mail, true) | ||
100 | } | ||
101 | } | ||
102 | |||
103 | Kube.Listener { | ||
104 | filter: Kube.Messages.compose | ||
105 | onMessageReceived: kubeViews.openComposer(true, message.recipients) | ||
106 | } | ||
107 | |||
108 | Kube.Listener { | ||
109 | filter: Kube.Messages.requestLogin | ||
110 | onMessageReceived: kubeViews.setLoginView(message.accountId) | ||
111 | } | ||
112 | |||
113 | Kube.Listener { | ||
114 | filter: Kube.Messages.requestAccountsConfiguration | ||
115 | onMessageReceived: kubeViews.setAccountsView() | ||
116 | } | ||
117 | |||
118 | //BEGIN Shortcuts | 89 | //BEGIN Shortcuts |
119 | Shortcut { | 90 | Shortcut { |
120 | sequence: StandardKey.Quit | 91 | sequence: StandardKey.Quit |
@@ -174,48 +145,20 @@ Controls2.ApplicationWindow { | |||
174 | 145 | ||
175 | spacing: Kube.Units.largeSpacing - Kube.Units.smallSpacing | 146 | spacing: Kube.Units.largeSpacing - Kube.Units.smallSpacing |
176 | 147 | ||
177 | Kube.IconButton { | ||
178 | id: composerButton | ||
179 | iconName: Kube.Icons.edit_inverted | ||
180 | onClicked: kubeViews.openComposer(false, []) | ||
181 | activeFocusOnTab: true | ||
182 | checkable: true | ||
183 | Controls2.ButtonGroup.group: viewButtonGroup | ||
184 | tooltip: qsTr("composer") | ||
185 | } | ||
186 | |||
187 | Kube.IconButton { | ||
188 | id: mailButton | ||
189 | iconName: Kube.Icons.mail_inverted | ||
190 | onClicked: kubeViews.setMailView() | ||
191 | activeFocusOnTab: true | ||
192 | checkable: true | ||
193 | checked: true | ||
194 | Controls2.ButtonGroup.group: viewButtonGroup | ||
195 | tooltip: qsTr("mails") | ||
196 | } | ||
197 | |||
198 | Kube.IconButton { | ||
199 | id: peopleButton | ||
200 | iconName: Kube.Icons.user_inverted | ||
201 | onClicked: kubeViews.setPeopleView() | ||
202 | activeFocusOnTab: true | ||
203 | checkable: true | ||
204 | Controls2.ButtonGroup.group: viewButtonGroup | ||
205 | tooltip: qsTr("people") | ||
206 | } | ||
207 | Repeater { | 148 | Repeater { |
208 | model: Kube.ExtensionModel {} | 149 | model: Kube.ExtensionModel { |
150 | id: extensionModel | ||
151 | sortOrder: ["composer", "conversation", "people"] | ||
152 | } | ||
209 | Kube.IconButton { | 153 | Kube.IconButton { |
154 | id: button | ||
210 | iconName: model.icon | 155 | iconName: model.icon |
211 | onClicked: { | 156 | onClicked: kubeViews.showView(model.name) |
212 | var component = Qt.createComponent(model.source) | ||
213 | kubeViews.pushView(component, {}) | ||
214 | } | ||
215 | activeFocusOnTab: true | 157 | activeFocusOnTab: true |
216 | checkable: true | 158 | checkable: true |
217 | Controls2.ButtonGroup.group: viewButtonGroup | 159 | Controls2.ButtonGroup.group: viewButtonGroup |
218 | tooltip: model.tooltip | 160 | tooltip: model.tooltip |
161 | checked: kubeViews.currentViewName == model.name | ||
219 | } | 162 | } |
220 | } | 163 | } |
221 | } | 164 | } |
@@ -240,10 +183,11 @@ Controls2.ApplicationWindow { | |||
240 | Kube.IconButton { | 183 | Kube.IconButton { |
241 | id: logButton | 184 | id: logButton |
242 | iconName: Kube.Icons.info_inverted | 185 | iconName: Kube.Icons.info_inverted |
243 | onClicked: kubeViews.setLogView() | 186 | onClicked: kubeViews.showView("log") |
244 | activeFocusOnTab: true | 187 | activeFocusOnTab: true |
245 | checkable: true | 188 | checkable: true |
246 | alert: logView.pendingError | 189 | alert: kubeViews.getView("log").pendingError |
190 | checked: kubeViews.currentViewName == "log" | ||
247 | Controls2.ButtonGroup.group: viewButtonGroup | 191 | Controls2.ButtonGroup.group: viewButtonGroup |
248 | tooltip: qsTr("logview") | 192 | tooltip: qsTr("logview") |
249 | } | 193 | } |
@@ -251,141 +195,95 @@ Controls2.ApplicationWindow { | |||
251 | Kube.IconButton { | 195 | Kube.IconButton { |
252 | id: accountsButton | 196 | id: accountsButton |
253 | iconName: Kube.Icons.menu_inverted | 197 | iconName: Kube.Icons.menu_inverted |
254 | onClicked: kubeViews.setAccountsView() | 198 | onClicked: kubeViews.showView("accounts") |
255 | activeFocusOnTab: true | 199 | activeFocusOnTab: true |
256 | checkable: true | 200 | checkable: true |
201 | checked: kubeViews.currentViewName == "accounts" | ||
257 | Controls2.ButtonGroup.group: viewButtonGroup | 202 | Controls2.ButtonGroup.group: viewButtonGroup |
258 | tooltip: qsTr("settings") | 203 | tooltip: qsTr("settings") |
259 | } | 204 | } |
260 | } | 205 | } |
261 | } | 206 | } |
262 | Controls2.StackView { | 207 | ViewManager { |
263 | id: kubeViews | 208 | id: kubeViews |
264 | |||
265 | anchors { | 209 | anchors { |
266 | top: mainContent.top | 210 | top: mainContent.top |
267 | bottom: mainContent.bottom | 211 | bottom: mainContent.bottom |
268 | } | 212 | } |
269 | Layout.fillWidth: true | 213 | Layout.fillWidth: true |
270 | 214 | ||
271 | Kube.Listener { | 215 | extensionModel: extensionModel |
272 | filter: Kube.Messages.componentDone | ||
273 | onMessageReceived: { | ||
274 | //Return to the mailview if we try to pop everything off | ||
275 | if (kubeViews.depth == 1) { | ||
276 | kubeViews.setMailView() | ||
277 | } else { | ||
278 | kubeViews.pop(Controls2.StackView.Immediate) | ||
279 | } | ||
280 | } | ||
281 | } | ||
282 | |||
283 | onCurrentItemChanged: { | ||
284 | if (currentItem) { | ||
285 | currentItem.forceActiveFocus() | ||
286 | } | ||
287 | } | ||
288 | 216 | ||
289 | Component.onCompleted: { | 217 | Component.onCompleted: { |
290 | //Setup the initial item stack | 218 | dontFocus = true |
291 | if (!currentItem) { | 219 | showView("conversation") |
292 | setMailView() | 220 | if (startupCheck.noAccount) { |
293 | if (startupCheck.noAccount) { | 221 | showView("accounts") |
294 | setAccountsView() | ||
295 | } | ||
296 | } | 222 | } |
223 | dontFocus = false | ||
297 | } | 224 | } |
298 | 225 | ||
299 | ///Replace the current view (we can't go back to the old view, and we destroy the old view) | 226 | Kube.Listener { |
300 | function replaceView(view) { | 227 | filter: Kube.Messages.reply |
301 | if (currentItem != view) { | 228 | onMessageReceived: kubeViews.replaceView("composer", {message: message.mail, loadAsDraft: false}) |
302 | kubeViews.replace(null, view, {}, Controls2.StackView.Immediate) | ||
303 | } | ||
304 | } | ||
305 | |||
306 | ///Push a new view on the stack (the old view remains, and we can go back once done) | ||
307 | function pushView(view, properties) { | ||
308 | kubeViews.push(view, properties, Controls2.StackView.Immediate) | ||
309 | } | ||
310 | |||
311 | //TODO replacing here while a composer is open is destructive | ||
312 | function setPeopleView() { | ||
313 | replaceView(peopleView) | ||
314 | } | 229 | } |
315 | 230 | ||
316 | function setMailView() { | 231 | Kube.Listener { |
317 | replaceView(mailView) | 232 | filter: Kube.Messages.edit |
233 | onMessageReceived: kubeViews.replaceView("composer", {message: message.mail, loadAsDraft: true}) | ||
318 | } | 234 | } |
319 | 235 | ||
320 | function setAccountsView() { | 236 | Kube.Listener { |
321 | pushView(accountsView, {}) | 237 | filter: Kube.Messages.compose |
238 | onMessageReceived: kubeViews.replaceView("composer", {newMessage: true, recipients: message.recipients}) | ||
322 | } | 239 | } |
323 | 240 | ||
324 | function setLogView() { | 241 | Kube.Listener { |
325 | replaceView(logView) | 242 | filter: Kube.Messages.requestAccountsConfiguration |
243 | onMessageReceived: kubeViews.showView("accounts") | ||
326 | } | 244 | } |
327 | 245 | ||
328 | function setLoginView(account) { | 246 | Kube.Listener { |
329 | if (currentItem != loginView) { | 247 | filter: Kube.Messages.componentDone |
330 | pushView(loginView, {accountId: account}) | 248 | onMessageReceived: { |
249 | kubeViews.closeView() | ||
331 | } | 250 | } |
332 | } | 251 | } |
333 | 252 | ||
334 | function openComposer(newMessage, recipients) { | 253 | Kube.Listener { |
335 | pushView(composerView, {newMessage: newMessage, recipients: recipients}) | 254 | filter: Kube.Messages.requestLogin |
336 | } | 255 | onMessageReceived: { |
337 | 256 | loginView.createObject(kubeViews, {accountId: message.accountId}) | |
338 | function openComposerWithMail(mail, openAsDraft) { | ||
339 | pushView(composerView, {message: mail, loadAsDraft: openAsDraft}) | ||
340 | } | ||
341 | |||
342 | |||
343 | //These items are not visible until pushed onto the stack, so we keep them in resources instead of items | ||
344 | resources: [ | ||
345 | //Not components so we maintain state | ||
346 | MailView { | ||
347 | id: mailView | ||
348 | anchors.fill: parent | ||
349 | Controls2.StackView.onActivated: mailButton.checked = true | ||
350 | Controls2.StackView.onDeactivated: mailButton.checked = false | ||
351 | }, | ||
352 | PeopleView { | ||
353 | id: peopleView | ||
354 | anchors.fill: parent | ||
355 | Controls2.StackView.onActivated: peopleButton.checked = true | ||
356 | Controls2.StackView.onDeactivated: peopleButton.checked = false | ||
357 | }, | ||
358 | //Not a component because otherwise we can't log stuff | ||
359 | LogView { | ||
360 | id: logView | ||
361 | anchors.fill: parent | ||
362 | Controls2.StackView.onActivated: logButton.checked = true | ||
363 | Controls2.StackView.onDeactivated: logButton.checked = false | ||
364 | } | ||
365 | ] | ||
366 | //A component so it's always destroyed when we're done | ||
367 | Component { | ||
368 | id: composerView | ||
369 | ComposerView { | ||
370 | anchors.fill: parent | ||
371 | Controls2.StackView.onActivated: composerButton.checked = true | ||
372 | Controls2.StackView.onDeactivated: composerButton.checked = false | ||
373 | } | ||
374 | } | ||
375 | Component { | ||
376 | id: accountsView | ||
377 | AccountsView { | ||
378 | anchors.fill: parent | ||
379 | Controls2.StackView.onActivated: accountsButton.checked = true | ||
380 | Controls2.StackView.onDeactivated: accountsButton.checked = false | ||
381 | } | 257 | } |
382 | } | 258 | } |
259 | |||
383 | Component { | 260 | Component { |
384 | id: loginView | 261 | id: loginView |
385 | LoginView { | 262 | Kube.Popup { |
386 | anchors.fill: parent | 263 | id: popup |
264 | property alias accountId: login.accountId | ||
265 | visible: true | ||
266 | parent: Controls2.ApplicationWindow.overlay | ||
267 | height: app.height | ||
268 | width: app.width - app.sidebarWidth | ||
269 | x: app.sidebarWidth | ||
270 | y: 0 | ||
271 | modal: true | ||
272 | closePolicy: Controls2.Popup.NoAutoClose | ||
273 | Kube.LoginAccount { | ||
274 | id: login | ||
275 | anchors { | ||
276 | fill: parent | ||
277 | bottomMargin: Kube.Units.largeSpacing | ||
278 | } | ||
279 | onDone: { | ||
280 | popup.close() | ||
281 | kubeViews.currentItem.forceActiveFocus() | ||
282 | } | ||
283 | } | ||
387 | } | 284 | } |
388 | } | 285 | } |
286 | |||
389 | } | 287 | } |
390 | } | 288 | } |
391 | //END Main content | 289 | //END Main content |
diff --git a/components/kube/qml/LoginView.qml b/components/kube/qml/LoginView.qml deleted file mode 100644 index dbbed11c..00000000 --- a/components/kube/qml/LoginView.qml +++ /dev/null | |||
@@ -1,52 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2017 Michael Bohlender, <michael.bohlender@kdemail.net> | ||
3 | * Copyright (C) 2017 Christian Mollekopf, <mollekopf@kolabsys.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License along | ||
16 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
18 | */ | ||
19 | |||
20 | import QtQuick 2.4 | ||
21 | import QtQuick.Layouts 1.1 | ||
22 | import QtQuick.Controls 2.0 | ||
23 | import org.kube.framework 1.0 as Kube | ||
24 | |||
25 | FocusScope { | ||
26 | id: root | ||
27 | property alias accountId: login.accountId | ||
28 | onActiveFocusChanged: { | ||
29 | if (activeFocus) { | ||
30 | popup.forceActiveFocus() | ||
31 | } | ||
32 | } | ||
33 | |||
34 | Kube.Popup { | ||
35 | id: popup | ||
36 | visible: true | ||
37 | parent: ApplicationWindow.overlay | ||
38 | height: app.height | ||
39 | width: app.width - app.sidebarWidth | ||
40 | x: app.sidebarWidth | ||
41 | y: 0 | ||
42 | modal: true | ||
43 | closePolicy: Popup.NoAutoClose | ||
44 | Kube.LoginAccount { | ||
45 | id: login | ||
46 | anchors { | ||
47 | fill: parent | ||
48 | bottomMargin: Kube.Units.largeSpacing | ||
49 | } | ||
50 | } | ||
51 | } | ||
52 | } | ||
diff --git a/components/kube/qml/ViewManager.qml b/components/kube/qml/ViewManager.qml new file mode 100644 index 00000000..6874107a --- /dev/null +++ b/components/kube/qml/ViewManager.qml | |||
@@ -0,0 +1,84 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2018 Christian Mollekopf, <mollekopf@kolabsys.com> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along | ||
15 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
17 | */ | ||
18 | |||
19 | |||
20 | import QtQuick 2.7 | ||
21 | import QtQuick.Controls 2.0 | ||
22 | /* | ||
23 | * TODO | ||
24 | * * Only replace composer if necessary (on reply, edit draft, ...) | ||
25 | * * Shutdown procedure to save draft before destruction | ||
26 | * * Separate logging from view and and make accessible to log (initialize()) call? | ||
27 | */ | ||
28 | StackView { | ||
29 | id: root | ||
30 | property string currentViewName: currentItem ? currentItem.objectName : "" | ||
31 | property variant extensionModel: null | ||
32 | property bool dontFocus: false | ||
33 | |||
34 | property var viewDict: new Object | ||
35 | function getView(name, replaceView) { | ||
36 | if (!replaceView && name in viewDict) { | ||
37 | var item = viewDict[name] | ||
38 | if (item) { | ||
39 | return item | ||
40 | } | ||
41 | } | ||
42 | var v = Qt.createComponent(extensionModel.findSource(name, "View.qml")) | ||
43 | v = v.createObject(root) | ||
44 | viewDict[name] = v | ||
45 | return v; | ||
46 | } | ||
47 | |||
48 | onCurrentItemChanged: { | ||
49 | if (currentItem && !dontFocus) { | ||
50 | currentItem.forceActiveFocus() | ||
51 | } | ||
52 | } | ||
53 | |||
54 | function showOrReplaceView(name, properties, replace) { | ||
55 | if (currentItem && currentItem.objectName == name) { | ||
56 | return | ||
57 | } | ||
58 | if (root.depth > 0) { | ||
59 | root.pop(StackView.Immediate) | ||
60 | } | ||
61 | var view = getView(name, replace) | ||
62 | var item = push(view, properties, StackView.Immediate) | ||
63 | item.parent = root | ||
64 | item.anchors.fill = root | ||
65 | item.objectName = name | ||
66 | } | ||
67 | |||
68 | function showView(name, properties) { | ||
69 | showOrReplaceView(name, properties, false) | ||
70 | } | ||
71 | |||
72 | function replaceView(name, properties) { | ||
73 | showOrReplaceView(name, properties, true) | ||
74 | } | ||
75 | |||
76 | function closeView() { | ||
77 | //The initial view remains | ||
78 | if (kubeViews.depth > 1) { | ||
79 | var item = kubeViews.pop(StackView.Immediate) | ||
80 | viewDict[item.objectName] = null | ||
81 | item.destroy() | ||
82 | } | ||
83 | } | ||
84 | } | ||
diff --git a/framework/qml/LoginAccount.qml b/framework/qml/LoginAccount.qml index f02050ae..261746d5 100644 --- a/framework/qml/LoginAccount.qml +++ b/framework/qml/LoginAccount.qml | |||
@@ -26,10 +26,12 @@ Item { | |||
26 | property string accountId | 26 | property string accountId |
27 | property bool canRemove: true | 27 | property bool canRemove: true |
28 | 28 | ||
29 | signal done() | ||
30 | |||
29 | function login() { | 31 | function login() { |
30 | loader.item.login() | 32 | loader.item.login() |
31 | Kube.Fabric.postMessage(Kube.Messages.synchronize, {"accountId": loader.item.accountIdentifier}); | 33 | Kube.Fabric.postMessage(Kube.Messages.synchronize, {"accountId": loader.item.accountIdentifier}); |
32 | Kube.Fabric.postMessage(Kube.Messages.componentDone, {}) | 34 | root.done() |
33 | } | 35 | } |
34 | 36 | ||
35 | Kube.AccountFactory { | 37 | Kube.AccountFactory { |
diff --git a/framework/src/extensionmodel.cpp b/framework/src/extensionmodel.cpp index f64a0e17..e3fab7d8 100644 --- a/framework/src/extensionmodel.cpp +++ b/framework/src/extensionmodel.cpp | |||
@@ -22,12 +22,17 @@ | |||
22 | #include <QDir> | 22 | #include <QDir> |
23 | #include <QDebug> | 23 | #include <QDebug> |
24 | #include <QTimer> | 24 | #include <QTimer> |
25 | #include <QJsonDocument> | ||
26 | #include <QJsonObject> | ||
25 | 27 | ||
26 | using namespace Kube; | 28 | using namespace Kube; |
27 | 29 | ||
28 | ExtensionModel::ExtensionModel(QObject *parent) | 30 | ExtensionModel::ExtensionModel(QObject *parent) |
29 | : QSortFilterProxyModel(parent) | 31 | : QSortFilterProxyModel(parent) |
30 | { | 32 | { |
33 | setDynamicSortFilter(true); | ||
34 | sort(0, Qt::DescendingOrder); | ||
35 | setFilterCaseSensitivity(Qt::CaseInsensitive); | ||
31 | QTimer::singleShot(0, this, &ExtensionModel::load); | 36 | QTimer::singleShot(0, this, &ExtensionModel::load); |
32 | } | 37 | } |
33 | 38 | ||
@@ -36,8 +41,7 @@ QHash<int, QByteArray> ExtensionModel::roleNames() const | |||
36 | return { | 41 | return { |
37 | {Name, "name"}, | 42 | {Name, "name"}, |
38 | {Tooltip, "tooltip"}, | 43 | {Tooltip, "tooltip"}, |
39 | {Icon, "icon"}, | 44 | {Icon, "icon"} |
40 | {Source, "source"} | ||
41 | }; | 45 | }; |
42 | } | 46 | } |
43 | 47 | ||
@@ -48,21 +52,55 @@ void ExtensionModel::load() | |||
48 | auto engine = qmlEngine(this); | 52 | auto engine = qmlEngine(this); |
49 | Q_ASSERT(engine); | 53 | Q_ASSERT(engine); |
50 | for (const auto &path : engine->importPathList()) { | 54 | for (const auto &path : engine->importPathList()) { |
51 | QDir dir{path + "/org/kube/viewextensions"}; | 55 | QDir dir{path + "/org/kube/views"}; |
52 | for (const auto &pluginName : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { | 56 | for (const auto &pluginName : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { |
53 | auto viewPath = dir.path() + pluginName + "/View.qml"; | 57 | const auto pluginPath = dir.path() + "/" + pluginName; |
54 | qWarning() << "Plugin path: " << dir.path() + pluginName + "/View.qml"; | 58 | mPaths.insert(pluginName, pluginPath); |
55 | auto item = new QStandardItem; | 59 | auto item = new QStandardItem; |
56 | item->setData(viewPath, Source); | ||
57 | item->setData(pluginName, Name); | 60 | item->setData(pluginName, Name); |
58 | item->setData(pluginName, Tooltip); | 61 | item->setData(pluginName, Tooltip); |
59 | item->setData("document-decrypt", Icon); | 62 | item->setData("kdocumentinfo-inverted", Icon); |
63 | |||
64 | if (QFileInfo::exists(pluginPath + "/metadata.json")) { | ||
65 | QFile file{pluginPath + "/metadata.json"}; | ||
66 | file.open(QIODevice::ReadOnly); | ||
67 | auto json = QJsonDocument::fromJson(file.readAll()); | ||
68 | auto map = json.object().toVariantMap(); | ||
69 | item->setData(map.value("icon").toString(), Icon); | ||
70 | item->setData(map.value("tooltip").toString(), Tooltip); | ||
71 | if (map.value("hidden", false).toBool()) { | ||
72 | delete item; | ||
73 | continue; | ||
74 | } | ||
75 | } | ||
76 | |||
60 | model->appendRow(item); | 77 | model->appendRow(item); |
61 | } | 78 | } |
62 | } | 79 | } |
63 | setSourceModel(model); | 80 | setSourceModel(model); |
64 | } | 81 | } |
65 | 82 | ||
83 | QString ExtensionModel::findSource(const QString &extensionName, const QString &sourceName) | ||
84 | { | ||
85 | if (mPaths.isEmpty()) { | ||
86 | load(); | ||
87 | } | ||
88 | return mPaths.value(extensionName) + "/" + sourceName; | ||
89 | } | ||
90 | |||
91 | void ExtensionModel::setSortOrder(const QVariantList &order) | ||
92 | { | ||
93 | mSortOrder.clear(); | ||
94 | for (const auto &e : order) { | ||
95 | mSortOrder << e.toString(); | ||
96 | } | ||
97 | } | ||
98 | |||
99 | QVariantList ExtensionModel::sortOrder() const | ||
100 | { | ||
101 | return {}; | ||
102 | } | ||
103 | |||
66 | QVariant ExtensionModel::data(const QModelIndex &idx, int role) const | 104 | QVariant ExtensionModel::data(const QModelIndex &idx, int role) const |
67 | { | 105 | { |
68 | return QSortFilterProxyModel::data(idx, role); | 106 | return QSortFilterProxyModel::data(idx, role); |
@@ -70,5 +108,14 @@ QVariant ExtensionModel::data(const QModelIndex &idx, int role) const | |||
70 | 108 | ||
71 | bool ExtensionModel::lessThan(const QModelIndex &left, const QModelIndex &right) const | 109 | bool ExtensionModel::lessThan(const QModelIndex &left, const QModelIndex &right) const |
72 | { | 110 | { |
73 | return QSortFilterProxyModel::lessThan(left, right); | 111 | auto leftIndex = mSortOrder.indexOf(left.data(Name).toString()); |
112 | auto rightIndex = mSortOrder.indexOf(right.data(Name).toString()); | ||
113 | if (leftIndex >= 0 && rightIndex >= 0) { | ||
114 | //Higher index is less than | ||
115 | return leftIndex > rightIndex; | ||
116 | } | ||
117 | if (leftIndex < 0 && rightIndex < 0) { | ||
118 | return QSortFilterProxyModel::lessThan(left, right); | ||
119 | } | ||
120 | return leftIndex < rightIndex; | ||
74 | } | 121 | } |
diff --git a/framework/src/extensionmodel.h b/framework/src/extensionmodel.h index 6e984005..8e5580f7 100644 --- a/framework/src/extensionmodel.h +++ b/framework/src/extensionmodel.h | |||
@@ -27,6 +27,8 @@ namespace Kube { | |||
27 | class ExtensionModel : public QSortFilterProxyModel | 27 | class ExtensionModel : public QSortFilterProxyModel |
28 | { | 28 | { |
29 | Q_OBJECT | 29 | Q_OBJECT |
30 | |||
31 | Q_PROPERTY(QVariantList sortOrder WRITE setSortOrder READ sortOrder CONSTANT) | ||
30 | public: | 32 | public: |
31 | 33 | ||
32 | ExtensionModel(QObject *parent = Q_NULLPTR); | 34 | ExtensionModel(QObject *parent = Q_NULLPTR); |
@@ -39,14 +41,22 @@ public: | |||
39 | enum Roles { | 41 | enum Roles { |
40 | Name = Qt::UserRole + 1, | 42 | Name = Qt::UserRole + 1, |
41 | Tooltip, | 43 | Tooltip, |
42 | Icon, | 44 | Icon |
43 | Source | ||
44 | }; | 45 | }; |
45 | 46 | ||
46 | QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE; | 47 | QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE; |
47 | 48 | ||
49 | void setSortOrder(const QVariantList &order); | ||
50 | QVariantList sortOrder() const; | ||
51 | |||
52 | Q_INVOKABLE QString findSource(const QString &extensionName, const QString &sourceName); | ||
53 | |||
48 | private slots: | 54 | private slots: |
49 | void load(); | 55 | void load(); |
56 | |||
57 | private: | ||
58 | QStringList mSortOrder; | ||
59 | QHash<QString, QString> mPaths; | ||
50 | }; | 60 | }; |
51 | 61 | ||
52 | } | 62 | } |
diff --git a/views/CMakeLists.txt b/views/CMakeLists.txt new file mode 100644 index 00000000..4004e310 --- /dev/null +++ b/views/CMakeLists.txt | |||
@@ -0,0 +1,11 @@ | |||
1 | macro(install_view name) | ||
2 | install(DIRECTORY ${name}/qml/ DESTINATION ${QML_INSTALL_DIR}/org/kube/views/${name}) | ||
3 | install(FILES ${name}/metadata.json DESTINATION ${QML_INSTALL_DIR}/org/kube/views/${name}) | ||
4 | add_test(NAME viewtest-${name} COMMAND kubetestrunner WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${name}/tests) | ||
5 | endmacro() | ||
6 | |||
7 | install_view(composer) | ||
8 | install_view(conversation) | ||
9 | install_view(people) | ||
10 | install_view(log) | ||
11 | install_view(accounts) | ||
diff --git a/views/accounts/metadata.json b/views/accounts/metadata.json new file mode 100644 index 00000000..55091f97 --- /dev/null +++ b/views/accounts/metadata.json | |||
@@ -0,0 +1,4 @@ | |||
1 | { | ||
2 | "tooltip": "Account configuration.", | ||
3 | "hidden": true | ||
4 | } | ||
diff --git a/components/kube/qml/AccountsView.qml b/views/accounts/qml/View.qml index 9b774907..9b774907 100644 --- a/components/kube/qml/AccountsView.qml +++ b/views/accounts/qml/View.qml | |||
diff --git a/components/kube/tests/tst_accountsview.qml b/views/accounts/tests/tst_accountsview.qml index 428bb4cc..096ca9eb 100644 --- a/components/kube/tests/tst_accountsview.qml +++ b/views/accounts/tests/tst_accountsview.qml | |||
@@ -27,7 +27,7 @@ TestCase { | |||
27 | height: 400 | 27 | height: 400 |
28 | name: "AccountsView" | 28 | name: "AccountsView" |
29 | 29 | ||
30 | AccountsView { | 30 | View { |
31 | id: accountsView | 31 | id: accountsView |
32 | } | 32 | } |
33 | 33 | ||
diff --git a/views/composer/metadata.json b/views/composer/metadata.json new file mode 100644 index 00000000..c4654fba --- /dev/null +++ b/views/composer/metadata.json | |||
@@ -0,0 +1,4 @@ | |||
1 | { | ||
2 | "icon": "document-edit-inverted", | ||
3 | "tooltip": "Compose new messages." | ||
4 | } | ||
diff --git a/components/kube/qml/AddresseeListEditor.qml b/views/composer/qml/AddresseeListEditor.qml index 8f9862e7..8f9862e7 100644 --- a/components/kube/qml/AddresseeListEditor.qml +++ b/views/composer/qml/AddresseeListEditor.qml | |||
diff --git a/components/kube/qml/ComposerView.qml b/views/composer/qml/View.qml index 1eb88bb4..1eb88bb4 100644 --- a/components/kube/qml/ComposerView.qml +++ b/views/composer/qml/View.qml | |||
diff --git a/components/kube/tests/tst_composerview.qml b/views/composer/tests/tst_composerview.qml index 67db6ef6..eac391c1 100644 --- a/components/kube/tests/tst_composerview.qml +++ b/views/composer/tests/tst_composerview.qml | |||
@@ -32,7 +32,7 @@ TestCase { | |||
32 | 32 | ||
33 | Component { | 33 | Component { |
34 | id:composerComponent | 34 | id:composerComponent |
35 | ComposerView { | 35 | View { |
36 | focus: true | 36 | focus: true |
37 | } | 37 | } |
38 | } | 38 | } |
diff --git a/views/conversation/metadata.json b/views/conversation/metadata.json new file mode 100644 index 00000000..870ff2aa --- /dev/null +++ b/views/conversation/metadata.json | |||
@@ -0,0 +1,4 @@ | |||
1 | { | ||
2 | "icon": "mail-message-inverted", | ||
3 | "tooltip": "Follow conversations." | ||
4 | } | ||
diff --git a/components/kube/qml/MailView.qml b/views/conversation/qml/View.qml index 8b2b0caf..8b2b0caf 100644 --- a/components/kube/qml/MailView.qml +++ b/views/conversation/qml/View.qml | |||
diff --git a/components/kube/tests/tst_mailview.qml b/views/conversation/tests/tst_conversationview.qml index f31d574d..467c049a 100644 --- a/components/kube/tests/tst_mailview.qml +++ b/views/conversation/tests/tst_conversationview.qml | |||
@@ -27,7 +27,7 @@ TestCase { | |||
27 | height: 400 | 27 | height: 400 |
28 | name: "MailView" | 28 | name: "MailView" |
29 | 29 | ||
30 | MailView { | 30 | View { |
31 | id: mailView | 31 | id: mailView |
32 | } | 32 | } |
33 | 33 | ||
diff --git a/views/log/metadata.json b/views/log/metadata.json new file mode 100644 index 00000000..1bcba1fb --- /dev/null +++ b/views/log/metadata.json | |||
@@ -0,0 +1,4 @@ | |||
1 | { | ||
2 | "tooltip": "Log messages.", | ||
3 | "hidden": true | ||
4 | } | ||
diff --git a/components/kube/qml/LogView.qml b/views/log/qml/View.qml index 4ae1a67c..4ae1a67c 100644 --- a/components/kube/qml/LogView.qml +++ b/views/log/qml/View.qml | |||
diff --git a/components/kube/tests/tst_logview.qml b/views/log/tests/tst_logview.qml index d3326db1..a78d71cb 100644 --- a/components/kube/tests/tst_logview.qml +++ b/views/log/tests/tst_logview.qml | |||
@@ -30,7 +30,7 @@ TestCase { | |||
30 | height: 400 | 30 | height: 400 |
31 | name: "LogView" | 31 | name: "LogView" |
32 | 32 | ||
33 | LogView { | 33 | View { |
34 | id: logView | 34 | id: logView |
35 | } | 35 | } |
36 | 36 | ||
diff --git a/views/people/metadata.json b/views/people/metadata.json new file mode 100644 index 00000000..44296a7e --- /dev/null +++ b/views/people/metadata.json | |||
@@ -0,0 +1,4 @@ | |||
1 | { | ||
2 | "icon": "im-user-inverted", | ||
3 | "tooltip": "Your contacts." | ||
4 | } | ||
diff --git a/components/kube/qml/PeopleView.qml b/views/people/qml/View.qml index 3f1b9261..3f1b9261 100644 --- a/components/kube/qml/PeopleView.qml +++ b/views/people/qml/View.qml | |||
diff --git a/components/kube/tests/tst_peopleview.qml b/views/people/tests/tst_peopleview.qml index e224b527..263247ad 100644 --- a/components/kube/tests/tst_peopleview.qml +++ b/views/people/tests/tst_peopleview.qml | |||
@@ -27,7 +27,7 @@ TestCase { | |||
27 | height: 400 | 27 | height: 400 |
28 | name: "PeopleView" | 28 | name: "PeopleView" |
29 | 29 | ||
30 | PeopleView { | 30 | View { |
31 | id: peopleView | 31 | id: peopleView |
32 | } | 32 | } |
33 | 33 | ||