@@ -16,16 +16,50 @@
1616
1717import QtQuick 2.4
1818import QtQuick.Layouts 1.1
19−import QtQuick.Window 2.2
2019import Ubuntu.Components 1.3
2120import Ubuntu.Components.ListItems 1.3 as ListItems
2221import "../Components"
22+import "."
2323
2424UbuntuShape {
2525 id: root
2626 objectName: "menu"
2727 backgroundColor: theme.palette.normal.overlay
2828
29+ signal childActivated()
30+
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
34+
35+ property real desiredX
36+ x: {
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) {
41+ return desiredX;
42+ } else {
43+ if (substractWidth) {
44+ return -width;
45+ } else {
46+ return farX - parentTopLeft.x - width;
47+ }
48+ }
49+ }
50+
51+ property real desiredY
52+ y: {
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) {
57+ return desiredY;
58+ } else {
59+ return bottomY - parentTopLeft.y - height;
60+ }
61+ }
62+
2963 property alias unityMenuModel: repeater.model
3064
3165 function show() {
@@ -64,9 +98,9 @@ UbuntuShape {
6498 readonly property int currentIndex: currentItem ? currentItem.__ownIndex : -1
6599
66100 property real __minimumWidth: units.gu(20)
67− property real __maximumWidth: Screen.width * 0.7
101+ property real __maximumWidth: ApplicationMenusLimits.screenWidth * 0.7
68102 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
70104
71105 signal dismissAll()
72106
@@ -76,6 +110,8 @@ UbuntuShape {
76110 } else {
77111 hoveredItem = null;
78112 }
113+
114+ submenuHoverTimer.stop();
79115 }
80116
81117 onSelect: {
@@ -188,10 +224,15 @@ UbuntuShape {
188224 contentHeight: menuColumn.height
189225 interactive: height < contentHeight
190226
227+ Timer {
228+ id: submenuHoverTimer
229+ interval: 225 // GTK MENU_POPUP_DELAY, Qt SH_Menu_SubMenuPopupDelay in QCommonStyle is 256
230+ onTriggered: d.currentItem.item.trigger();
231+ }
232+
191233 MouseArea {
192234 anchors.fill: parent
193235 hoverEnabled: true
194− propagateComposedEvents: true // propogate events so we send clicks to children.
195236 z: 1 // on top so we override any other hovers
196237 onEntered: updateCurrentItemFromPosition(Qt.point(mouseX, mouseY))
197238 onPositionChanged: updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y))
@@ -201,10 +242,24 @@ UbuntuShape {
201242
202243 if (!d.hoveredItem || !d.currentItem ||
203244 !d.hoveredItem.contains(Qt.point(pos.x - d.currentItem.x, pos.y - d.currentItem.y))) {
245+ submenuHoverTimer.stop();
246+
204247 d.hoveredItem = menuColumn.childAt(pos.x, pos.y)
205248 if (!d.hoveredItem || !d.hoveredItem.enabled)
206249 return;
207250 d.currentItem = d.hoveredItem;
251+
252+ if (!d.currentItem.__isSeparator && d.currentItem.item.hasSubmenu && d.currentItem.item.enabled) {
253+ submenuHoverTimer.start();
254+ }
255+ }
256+ }
257+
258+ onClicked: {
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();
208263 }
209264 }
210265 }
@@ -260,6 +315,8 @@ UbuntuShape {
260315 width: MathUtils.clamp(implicitWidth, d.__minimumWidth, d.__maximumWidth)
261316
262317 action.onTriggered: {
318+ submenuHoverTimer.stop();
319+
263320 d.currentItem = loader;
264321
265322 if (hasSubmenu) {
@@ -268,22 +325,29 @@ UbuntuShape {
268325 popup = submenuComponent.createObject(focusScope, {
269326 objectName: loader.objectName + "-",
270327 unityMenuModel: model,
271− x: Qt.binding(function() { return root.width }),
272− y: Qt.binding(function() {
328+ substractWidth: true,
329+ desiredX: Qt.binding(function() { return root.width }),
330+ desiredY: Qt.binding(function() {
273331 var dummy = listView.contentY; // force a recalc on contentY change.
274332 return mapToItem(container, 0, y).y;
275333 })
276334 });
335+ popup.retreat.connect(function() {
336+ popup.destroy();
337+ popup = null;
338+ menuItem.forceActiveFocus();
339+ });
340+ popup.childActivated.connect(function() {
341+ popup.destroy();
342+ popup = null;
343+ root.childActivated();
344+ });
277345 } else if (popup) {
278346 popup.visible = true;
279347 }
280− popup.retreat.connect(function() {
281− popup.destroy();
282− popup = null;
283− menuItem.forceActiveFocus();
284− })
285348 } else {
286349 root.unityMenuModel.activate(__ownIndex);
350+ root.childActivated();
287351 }
288352 }
289353
@@ -397,23 +461,28 @@ UbuntuShape {
397461 id: submenuLoader
398462 source: "MenuPopup.qml"
399463
464+ property real desiredX
465+ property real desiredY
466+ property bool substractWidth
400467 property var unityMenuModel: null
401468 signal retreat()
402−
403− Binding {
404− target: item
405− property: "unityMenuModel"
406− value: submenuLoader.unityMenuModel
469+ signal childActivated()
470+
471+ onLoaded: {
472+ item.unityMenuModel = Qt.binding(function() { return 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; });
407477 }
408478
409− Binding {
479+ Keys.onLeftPressed: retreat()
480+
481+ Connections {
410482 target: item
411− property: "objectName"
412− value: submenuLoader.objectName + "menu"
483+ onChildActivated: childActivated();
413484 }
414485
415− Keys.onLeftPressed: retreat()
416−
417486 Component.onCompleted: item.select(0);
418487 onVisibleChanged: if (visible) { item.select(0); }
419488 }