@@ -16,16 +16,50 @@
17import QtQuick 2.4
17import QtQuick 2.4
18import QtQuick.Layouts 1.1
18import QtQuick.Layouts 1.1
19−import QtQuick.Window 2.2
20import Ubuntu.Components 1.3
19import Ubuntu.Components 1.3
21import Ubuntu.Components.ListItems 1.3 as ListItems
20import Ubuntu.Components.ListItems 1.3 as ListItems
22import "../Components"
21import "../Components"
24UbuntuShape {
24UbuntuShape {
26 objectName: "menu"
26 objectName: "menu"
27 backgroundColor: theme.palette.normal.overlay
27 backgroundColor: theme.palette.normal.overlay
29+ signal childActivated()
31+ // true for submenus that need to show on the other side of their parent
32+ // if they don't fit when growing right
33+ property bool substractWidth: false
35+ property real desiredX
37+ var dummy = visible; // force recalc when shown/hidden
38+ var parentTopLeft = parent.mapToItem(null, 0, 0);
39+ var farX = ApplicationMenusLimits.screenWidth;
40+ if (parentTopLeft.x + width + desiredX <= farX) {
43+ if (substractWidth) {
46+ return farX - parentTopLeft.x - width;
51+ property real desiredY
53+ var dummy = visible; // force recalc when shown/hidden
54+ var parentTopLeft = parent.mapToItem(null, 0, 0);
55+ var bottomY = ApplicationMenusLimits.screenHeight;
56+ if (parentTopLeft.y + height + desiredY <= bottomY) {
59+ return bottomY - parentTopLeft.y - height;
29 property alias unityMenuModel: repeater.model
63 property alias unityMenuModel: repeater.model
31 function show() {
65 function show() {
@@ -64,9 +98,9 @@ UbuntuShape {
64 readonly property int currentIndex: currentItem ? currentItem.__ownIndex : -1
98 readonly property int currentIndex: currentItem ? currentItem.__ownIndex : -1
66 property real __minimumWidth: units.gu(20)
100 property real __minimumWidth: units.gu(20)
67− property real __maximumWidth: Screen.width * 0.7
101+ property real __maximumWidth: ApplicationMenusLimits.screenWidth * 0.7
68 property real __minimumHeight: units.gu(2)
102 property real __minimumHeight: units.gu(2)
69− property real __maximumHeight: Screen.height - mapToItem(null, 0, y).y
103+ property real __maximumHeight: ApplicationMenusLimits.screenHeight - mapToItem(null, 0, y).y
71 signal dismissAll()
105 signal dismissAll()
@@ -76,6 +110,8 @@ UbuntuShape {
77 hoveredItem = null;
111 hoveredItem = null;
114+ submenuHoverTimer.stop();
81 onSelect: {
117 onSelect: {
@@ -188,10 +224,15 @@ UbuntuShape {
188 contentHeight: menuColumn.height
224 contentHeight: menuColumn.height
189 interactive: height < contentHeight
225 interactive: height < contentHeight
228+ id: submenuHoverTimer
229+ interval: 225 // GTK MENU_POPUP_DELAY, Qt SH_Menu_SubMenuPopupDelay in QCommonStyle is 256
230+ onTriggered: d.currentItem.item.trigger();
191 MouseArea {
233 MouseArea {
192 anchors.fill: parent
234 anchors.fill: parent
193 hoverEnabled: true
235 hoverEnabled: true
194− propagateComposedEvents: true // propogate events so we send clicks to children.
195 z: 1 // on top so we override any other hovers
236 z: 1 // on top so we override any other hovers
196 onEntered: updateCurrentItemFromPosition(Qt.point(mouseX, mouseY))
237 onEntered: updateCurrentItemFromPosition(Qt.point(mouseX, mouseY))
197 onPositionChanged: updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y))
238 onPositionChanged: updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y))
@@ -201,10 +242,24 @@ UbuntuShape {
202 if (!d.hoveredItem || !d.currentItem ||
243 if (!d.hoveredItem || !d.currentItem ||
203 !d.hoveredItem.contains(Qt.point(pos.x - d.currentItem.x, pos.y - d.currentItem.y))) {
244 !d.hoveredItem.contains(Qt.point(pos.x - d.currentItem.x, pos.y - d.currentItem.y))) {
245+ submenuHoverTimer.stop();
204 d.hoveredItem = menuColumn.childAt(pos.x, pos.y)
247 d.hoveredItem = menuColumn.childAt(pos.x, pos.y)
205 if (!d.hoveredItem || !d.hoveredItem.enabled)
248 if (!d.hoveredItem || !d.hoveredItem.enabled)
207 d.currentItem = d.hoveredItem;
250 d.currentItem = d.hoveredItem;
252+ if (!d.currentItem.__isSeparator && d.currentItem.item.hasSubmenu && d.currentItem.item.enabled) {
253+ submenuHoverTimer.start();
259+ var pos = mapToItem(listView.contentItem, mouse.x, mouse.y);
260+ var clickedItem = menuColumn.childAt(pos.x, pos.y);
261+ if (clickedItem.enabled && !clickedItem.__isSeparator) {
262+ clickedItem.item.trigger();
@@ -260,6 +315,8 @@ UbuntuShape {
260 width: MathUtils.clamp(implicitWidth, d.__minimumWidth, d.__maximumWidth)
315 width: MathUtils.clamp(implicitWidth, d.__minimumWidth, d.__maximumWidth)
262 action.onTriggered: {
317 action.onTriggered: {
318+ submenuHoverTimer.stop();
263 d.currentItem = loader;
320 d.currentItem = loader;
265 if (hasSubmenu) {
322 if (hasSubmenu) {
@@ -268,22 +325,29 @@ UbuntuShape {
268 popup = submenuComponent.createObject(focusScope, {
325 popup = submenuComponent.createObject(focusScope, {
269 objectName: loader.objectName + "-",
326 objectName: loader.objectName + "-",
270 unityMenuModel: model,
327 unityMenuModel: model,
328+ substractWidth: true,
271− x: Qt.binding(function() { return root.width }),
329+ desiredX: Qt.binding(function() { return root.width }),
272− y: Qt.binding(function() {
330+ desiredY: Qt.binding(function() {
273 var dummy = listView.contentY; // force a recalc on contentY change.
331 var dummy = listView.contentY; // force a recalc on contentY change.
274 return mapToItem(container, 0, y).y;
332 return mapToItem(container, 0, y).y;
335+ popup.retreat.connect(function() {
338+ menuItem.forceActiveFocus();
340+ popup.childActivated.connect(function() {
343+ root.childActivated();
277 } else if (popup) {
345 } else if (popup) {
278 popup.visible = true;
346 popup.visible = true;
280− popup.retreat.connect(function() {
283− menuItem.forceActiveFocus();
286 root.unityMenuModel.activate(__ownIndex);
349 root.unityMenuModel.activate(__ownIndex);
350+ root.childActivated();
@@ -397,23 +461,28 @@ UbuntuShape {
397 id: submenuLoader
461 id: submenuLoader
398 source: "MenuPopup.qml"
462 source: "MenuPopup.qml"
464+ property real desiredX
465+ property real desiredY
466+ property bool substractWidth
400 property var unityMenuModel: null
467 property var unityMenuModel: null
401 signal retreat()
468 signal retreat()
402−
469+ signal childActivated()
404− target: item
471+ onLoaded: {
405− property: "unityMenuModel"
472+ item.unityMenuModel = Qt.binding(function() { return submenuLoader.unityMenuModel; });
406− value: submenuLoader.unityMenuModel
473+ item.objectName = Qt.binding(function() { return submenuLoader.objectName + "menu"; });
474+ item.desiredX = Qt.binding(function() { return submenuLoader.desiredX; });
475+ item.desiredY = Qt.binding(function() { return submenuLoader.desiredY; });
476+ item.substractWidth = Qt.binding(function() { return submenuLoader.substractWidth; });
409− Binding {
479+ Keys.onLeftPressed: retreat()
410 target: item
482 target: item
411− property: "objectName"
483+ onChildActivated: childActivated();
412− value: submenuLoader.objectName + "menu"
415− Keys.onLeftPressed: retreat()
417 Component.onCompleted: item.select(0);
486 Component.onCompleted: item.select(0);
418 onVisibleChanged: if (visible) { item.select(0); }
487 onVisibleChanged: if (visible) { item.select(0); }