~lukas-kde/unity8/betterSessionManagement

« back to all changes in this revision

Viewing changes to qml/ApplicationMenus/MenuPopup.qml

  • Committer: Lukáš Tinkl
  • Date: 2017-03-24 11:57:27 UTC
  • mfrom: (2800.1.91 unity8)
  • Revision ID: lukas.tinkl@canonical.com-20170324115727-34c7tvv3qnxvxzdj
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
import QtQuick 2.4
18
18
import QtQuick.Layouts 1.1
19
 
import QtQuick.Window 2.2
20
19
import Ubuntu.Components 1.3
21
20
import Ubuntu.Components.ListItems 1.3 as ListItems
22
21
import "../Components"
 
22
import "../Components/PanelState"
 
23
import "."
23
24
 
24
25
UbuntuShape {
25
26
    id: root
26
27
    objectName: "menu"
27
28
    backgroundColor: theme.palette.normal.overlay
28
29
 
 
30
    signal childActivated()
 
31
 
 
32
    // true for submenus that need to show on the other side of their parent
 
33
    // if they don't fit when growing right
 
34
    property bool substractWidth: false
 
35
 
 
36
    property bool selectFirstOnCountChange: true
 
37
 
 
38
    property real desiredX
 
39
    x: {
 
40
        var dummy = visible; // force recalc when shown/hidden
 
41
        var parentTopLeft = parent.mapToItem(null, 0, 0);
 
42
        var farX = ApplicationMenusLimits.screenWidth;
 
43
        if (parentTopLeft.x + width + desiredX <= farX) {
 
44
            return desiredX;
 
45
        } else {
 
46
            if (substractWidth) {
 
47
                return -width;
 
48
            } else {
 
49
                return farX - parentTopLeft.x - width;
 
50
            }
 
51
        }
 
52
    }
 
53
 
 
54
    property real desiredY
 
55
    y: {
 
56
        var dummy = visible; // force recalc when shown/hidden
 
57
        var parentTopLeft = parent.mapToItem(null, 0, 0);
 
58
        var bottomY = ApplicationMenusLimits.screenHeight;
 
59
        if (parentTopLeft.y + height + desiredY <= bottomY) {
 
60
            return desiredY;
 
61
        } else {
 
62
            return bottomY - parentTopLeft.y - height;
 
63
        }
 
64
    }
 
65
 
29
66
    property alias unityMenuModel: repeater.model
30
67
 
31
68
    function show() {
38
75
        d.currentItem = null;
39
76
    }
40
77
 
41
 
    function select(index) {
42
 
        d.select(index)
 
78
    function selectFirstIndex() {
 
79
        d.selectNext(-1);
43
80
    }
44
81
 
45
82
    function reset() {
64
101
        readonly property int currentIndex: currentItem ? currentItem.__ownIndex : -1
65
102
 
66
103
        property real __minimumWidth: units.gu(20)
67
 
        property real __maximumWidth: Screen.width * 0.7
 
104
        property real __maximumWidth: ApplicationMenusLimits.screenWidth * 0.7
68
105
        property real __minimumHeight: units.gu(2)
69
 
        property real __maximumHeight: Screen.height * 0.7
 
106
        property real __maximumHeight: ApplicationMenusLimits.screenHeight - PanelState.panelHeight
70
107
 
71
108
        signal dismissAll()
72
109
 
76
113
            } else {
77
114
                hoveredItem = null;
78
115
            }
 
116
 
 
117
            submenuHoverTimer.stop();
79
118
        }
80
119
 
81
120
        onSelect: {
147
186
                }
148
187
 
149
188
                MouseArea {
 
189
                    id: previousMA
150
190
                    anchors.fill: parent
151
 
                    onPressed: {
 
191
                    hoverEnabled: enabled
 
192
                    onPressed: progress()
 
193
 
 
194
                    Timer {
 
195
                        running: previousMA.containsMouse && !listView.atYBeginning
 
196
                        interval: 1000
 
197
                        repeat: true
 
198
                        onTriggered: previousMA.progress()
 
199
                    }
 
200
 
 
201
                    function progress() {
152
202
                        var item = menuColumn.childAt(0, listView.contentY);
153
203
                        if (item) {
154
204
                            var previousItem = item;
176
226
                contentHeight: menuColumn.height
177
227
                interactive: height < contentHeight
178
228
 
 
229
                Timer {
 
230
                    id: submenuHoverTimer
 
231
                    interval: 225 // GTK MENU_POPUP_DELAY, Qt SH_Menu_SubMenuPopupDelay in QCommonStyle is 256
 
232
                    onTriggered: d.currentItem.item.trigger();
 
233
                }
 
234
 
179
235
                MouseArea {
180
236
                    anchors.fill: parent
181
237
                    hoverEnabled: true
182
 
                    propagateComposedEvents: true // propogate events so we send clicks to children.
183
238
                    z: 1 // on top so we override any other hovers
184
239
                    onEntered: updateCurrentItemFromPosition(Qt.point(mouseX, mouseY))
185
240
                    onPositionChanged: updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y))
189
244
 
190
245
                        if (!d.hoveredItem || !d.currentItem ||
191
246
                                !d.hoveredItem.contains(Qt.point(pos.x - d.currentItem.x, pos.y - d.currentItem.y))) {
 
247
                            submenuHoverTimer.stop();
 
248
 
192
249
                            d.hoveredItem = menuColumn.childAt(pos.x, pos.y)
193
250
                            if (!d.hoveredItem || !d.hoveredItem.enabled)
194
 
                                return false;
 
251
                                return;
195
252
                            d.currentItem = d.hoveredItem;
196
 
                        }
197
 
                        return true;
 
253
 
 
254
                            if (!d.currentItem.__isSeparator && d.currentItem.item.hasSubmenu && d.currentItem.item.enabled) {
 
255
                                submenuHoverTimer.start();
 
256
                            }
 
257
                        }
 
258
                    }
 
259
 
 
260
                    onClicked: {
 
261
                        var pos = mapToItem(listView.contentItem, mouse.x, mouse.y);
 
262
                        var clickedItem = menuColumn.childAt(pos.x, pos.y);
 
263
                        if (clickedItem.enabled && !clickedItem.__isSeparator) {
 
264
                            clickedItem.item.trigger();
 
265
                        }
198
266
                    }
199
267
                }
200
268
 
210
278
                    }
211
279
                }
212
280
 
 
281
                Component {
 
282
                    id: separatorComponent
 
283
                    ListItems.ThinDivider {
 
284
                        // Parent will be loader
 
285
                        objectName: parent.objectName + "-separator"
 
286
                        implicitHeight: units.dp(2)
 
287
                    }
 
288
                }
 
289
 
 
290
                Component {
 
291
                    id: menuItemComponent
 
292
                    MenuItem {
 
293
                        // Parent will be loader
 
294
                        id: menuItem
 
295
                        menuData: parent.__menuData
 
296
                        objectName: parent.objectName + "-actionItem"
 
297
 
 
298
                        width: MathUtils.clamp(implicitWidth, d.__minimumWidth, d.__maximumWidth)
 
299
 
 
300
                        action.onTriggered: {
 
301
                            submenuHoverTimer.stop();
 
302
 
 
303
                            d.currentItem = parent;
 
304
 
 
305
                            if (hasSubmenu) {
 
306
                                if (!popup) {
 
307
                                    root.unityMenuModel.aboutToShow(__ownIndex);
 
308
                                    var model = root.unityMenuModel.submenu(__ownIndex);
 
309
                                    popup = submenuComponent.createObject(focusScope, {
 
310
                                                                                objectName: parent.objectName + "-",
 
311
                                                                                unityMenuModel: model,
 
312
                                                                                substractWidth: true,
 
313
                                                                                desiredX: Qt.binding(function() { return root.width }),
 
314
                                                                                desiredY: Qt.binding(function() {
 
315
                                                                                    var dummy = listView.contentY; // force a recalc on contentY change.
 
316
                                                                                    return mapToItem(container, 0, y).y;
 
317
                                                                                })
 
318
                                                                            });
 
319
                                    popup.retreat.connect(function() {
 
320
                                        popup.destroy();
 
321
                                        popup = null;
 
322
                                        menuItem.forceActiveFocus();
 
323
                                    });
 
324
                                    popup.childActivated.connect(function() {
 
325
                                        popup.destroy();
 
326
                                        popup = null;
 
327
                                        root.childActivated();
 
328
                                    });
 
329
                                } else if (!popup.visible) {
 
330
                                    root.unityMenuModel.aboutToShow(__ownIndex);
 
331
                                    popup.visible = true;
 
332
                                    popup.item.selectFirstIndex();
 
333
                                }
 
334
                            } else {
 
335
                                root.unityMenuModel.activate(__ownIndex);
 
336
                                root.childActivated();
 
337
                            }
 
338
                        }
 
339
 
 
340
                        Connections {
 
341
                            target: d
 
342
                            onCurrentIndexChanged: {
 
343
                                if (popup && d.currentIndex != __ownIndex) {
 
344
                                    popup.visible = false;
 
345
                                }
 
346
                            }
 
347
                            onDismissAll: {
 
348
                                if (popup) {
 
349
                                    popup.destroy();
 
350
                                    popup = null;
 
351
                                }
 
352
                            }
 
353
                        }
 
354
                    }
 
355
                }
 
356
 
213
357
                ColumnLayout {
214
358
                    id: menuColumn
215
359
                    spacing: 0
219
363
                    Repeater {
220
364
                        id: repeater
221
365
 
 
366
                        onCountChanged: {
 
367
                            if (root.selectFirstOnCountChange && !d.currentItem && count > 0) {
 
368
                                root.selectFirstIndex();
 
369
                            }
 
370
                        }
 
371
 
222
372
                        Loader {
223
373
                            id: loader
224
374
                            objectName: root.objectName + "-item" + __ownIndex
225
375
 
 
376
                            property Item popup: null
 
377
                            property var __menuData: model
226
378
                            property int __ownIndex: index
227
379
                            property bool __isSeparator: model.isSeparator
228
380
 
235
387
                                return menuItemComponent;
236
388
                            }
237
389
 
238
 
                            property Item popup: null
239
 
 
240
390
                            Layout.fillWidth: true
241
 
 
242
 
                            Component {
243
 
                                id: menuItemComponent
244
 
                                MenuItem {
245
 
                                    id: menuItem
246
 
                                    menuData: model
247
 
                                    objectName: loader.objectName + "-actionItem"
248
 
 
249
 
                                    width: MathUtils.clamp(implicitWidth, d.__minimumWidth, d.__maximumWidth)
250
 
 
251
 
                                    action.onTriggered: {
252
 
                                        d.currentItem = loader;
253
 
 
254
 
                                        if (hasSubmenu) {
255
 
                                            if (!popup) {
256
 
                                                var model = root.unityMenuModel.submenu(__ownIndex);
257
 
                                                popup = submenuComponent.createObject(focusScope, {
258
 
                                                                                          objectName: loader.objectName + "-",
259
 
                                                                                          unityMenuModel: model,
260
 
                                                                                          x: Qt.binding(function() { return root.width }),
261
 
                                                                                          y: Qt.binding(function() {
262
 
                                                                                              var dummy = listView.contentY; // force a recalc on contentY change.
263
 
                                                                                              return mapToItem(container, 0, y).y;
264
 
                                                                                          })
265
 
                                                                                      });
266
 
                                            } else if (popup) {
267
 
                                                popup.visible = true;
268
 
                                            }
269
 
                                            popup.retreat.connect(function() {
270
 
                                                popup.destroy();
271
 
                                                popup = null;
272
 
                                                menuItem.forceActiveFocus();
273
 
                                            })
274
 
                                        } else {
275
 
                                            root.unityMenuModel.activate(__ownIndex);
276
 
                                        }
277
 
                                    }
278
 
 
279
 
                                    Connections {
280
 
                                        target: d
281
 
                                        onCurrentIndexChanged: {
282
 
                                            if (popup && d.currentIndex != __ownIndex) {
283
 
                                                popup.visible = false;
284
 
                                            }
285
 
                                        }
286
 
                                        onDismissAll: {
287
 
                                            if (popup) {
288
 
                                                popup.destroy();
289
 
                                                popup = null;
290
 
                                            }
291
 
                                        }
292
 
                                    }
293
 
                                }
294
 
                            }
295
 
 
296
 
                            Component {
297
 
                                id: separatorComponent
298
 
                                ListItems.ThinDivider {
299
 
                                    objectName: loader.objectName + "-separator"
300
 
                                    implicitHeight: units.dp(2)
301
 
                                }
302
 
                            }
303
391
                        }
304
392
 
305
393
                    }
349
437
                }
350
438
 
351
439
                MouseArea {
 
440
                    id: nextMA
352
441
                    anchors.fill: parent
353
 
                    onPressed: {
 
442
                    hoverEnabled: enabled
 
443
                    onPressed: progress()
 
444
 
 
445
                    Timer {
 
446
                        running: nextMA.containsMouse && !listView.atYEnd
 
447
                        interval: 1000
 
448
                        repeat: true
 
449
                        onTriggered: nextMA.progress()
 
450
                    }
 
451
 
 
452
                    function progress() {
354
453
                        var item = menuColumn.childAt(0, listView.contentY + listView.height);
355
454
                        if (item) {
356
455
                            var nextItem = item;
375
474
                id: submenuLoader
376
475
                source: "MenuPopup.qml"
377
476
 
 
477
                property real desiredX
 
478
                property real desiredY
 
479
                property bool substractWidth
378
480
                property var unityMenuModel: null
379
481
                signal retreat()
380
 
 
381
 
                Binding {
382
 
                    target: item
383
 
                    property: "unityMenuModel"
384
 
                    value: submenuLoader.unityMenuModel
385
 
                }
386
 
 
387
 
                Binding {
388
 
                    target: item
389
 
                    property: "objectName"
390
 
                    value: submenuLoader.objectName + "menu"
 
482
                signal childActivated()
 
483
 
 
484
                onLoaded: {
 
485
                    item.unityMenuModel = Qt.binding(function() { return submenuLoader.unityMenuModel; });
 
486
                    item.objectName = Qt.binding(function() { return submenuLoader.objectName + "menu"; });
 
487
                    item.desiredX = Qt.binding(function() { return submenuLoader.desiredX; });
 
488
                    item.desiredY = Qt.binding(function() { return submenuLoader.desiredY; });
 
489
                    item.substractWidth = Qt.binding(function() { return submenuLoader.substractWidth; });
391
490
                }
392
491
 
393
492
                Keys.onLeftPressed: retreat()
394
493
 
395
 
                Component.onCompleted: item.select(0);
396
 
                onVisibleChanged: if (visible) { item.select(0); }
 
494
                Connections {
 
495
                    target: item
 
496
                    onChildActivated: childActivated();
 
497
                }
397
498
            }
398
499
        }
399
500
    }