on xenial help sign in
Make the menus and submenus not go outside the screen when popping out
2d8c8aaAlbert Astals Cid
qml/ApplicationMenus/MenuBar.qml+2−2
@@ -134,8 +134,8 @@ Item {
134 Component {
134 Component {
135 id: menuComponent
135 id: menuComponent
136 MenuPopup {
136 MenuPopup {
137− x: visualItem.x - units.gu(1)
137+ desiredX: visualItem.x - units.gu(1)
138− anchors.top: parent.bottom
138+ desiredY: parent.height
139 unityMenuModel: root.unityMenuModel.submenu(visualItem.__ownIndex)
139 unityMenuModel: root.unityMenuModel.submenu(visualItem.__ownIndex)
140
140
141 Component.onCompleted: reset();
141 Component.onCompleted: reset();
qml/ApplicationMenus/MenuPopup.qml+47−15
@@ -16,16 +16,48 @@
16
16
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"
22+import "."
23
23
24UbuntuShape {
24UbuntuShape {
25 id: root
25 id: root
26 objectName: "menu"
26 objectName: "menu"
27 backgroundColor: theme.palette.normal.overlay
27 backgroundColor: theme.palette.normal.overlay
28
28
29+ // true for submenus that need to show on the other side of their parent
30+ // if they don't fit when growing right
31+ property bool substractWidth: false
32+
33+ property real desiredX
34+ x: {
35+ var dummy = visible; // force recalc when shown/hidden
36+ var parentTopLeft = parent.mapToItem(null, 0, 0);
37+ var farX = ApplicationMenusLimits.screenWidth;
38+ if (parentTopLeft.x + width + desiredX <= farX) {
39+ return desiredX;
40+ } else {
41+ if (substractWidth) {
42+ return -width;
43+ } else {
44+ return farX - parentTopLeft.x - width;
45+ }
46+ }
47+ }
48+
49+ property real desiredY
50+ y: {
51+ var dummy = visible; // force recalc when shown/hidden
52+ var parentTopLeft = parent.mapToItem(null, 0, 0);
53+ var bottomY = ApplicationMenusLimits.screenHeight;
54+ if (parentTopLeft.y + height + desiredY <= bottomY) {
55+ return desiredY;
56+ } else {
57+ return bottomY - parentTopLeft.y - height;
58+ }
59+ }
60+
29 property alias unityMenuModel: repeater.model
61 property alias unityMenuModel: repeater.model
30
62
31 function show() {
63 function show() {
@@ -64,9 +96,9 @@ UbuntuShape {
64 readonly property int currentIndex: currentItem ? currentItem.__ownIndex : -1
96 readonly property int currentIndex: currentItem ? currentItem.__ownIndex : -1
65
97
66 property real __minimumWidth: units.gu(20)
98 property real __minimumWidth: units.gu(20)
67− property real __maximumWidth: Screen.width * 0.7
99+ property real __maximumWidth: ApplicationMenusLimits.screenWidth * 0.7
68 property real __minimumHeight: units.gu(2)
100 property real __minimumHeight: units.gu(2)
69− property real __maximumHeight: Screen.height * 0.7
101+ property real __maximumHeight: ApplicationMenusLimits.screenHeight * 0.7
70
102
71 signal dismissAll()
103 signal dismissAll()
72
104
@@ -257,8 +289,9 @@ UbuntuShape {
257 popup = submenuComponent.createObject(focusScope, {
289 popup = submenuComponent.createObject(focusScope, {
258 objectName: loader.objectName + "-",
290 objectName: loader.objectName + "-",
259 unityMenuModel: model,
291 unityMenuModel: model,
292+ substractWidth: true,
260− x: Qt.binding(function() { return root.width }),
293+ desiredX: Qt.binding(function() { return root.width }),
261− y: Qt.binding(function() {
294+ desiredY: Qt.binding(function() {
262 var dummy = listView.contentY; // force a recalc on contentY change.
295 var dummy = listView.contentY; // force a recalc on contentY change.
263 return mapToItem(container, 0, y).y;
296 return mapToItem(container, 0, y).y;
264 })
297 })
@@ -375,19 +408,18 @@ UbuntuShape {
375 id: submenuLoader
408 id: submenuLoader
376 source: "MenuPopup.qml"
409 source: "MenuPopup.qml"
377
410
411+ property real desiredX
412+ property real desiredY
413+ property bool substractWidth
378 property var unityMenuModel: null
414 property var unityMenuModel: null
379 signal retreat()
415 signal retreat()
380
416
381− Binding {
417+ onLoaded: {
382− target: item
383− property: "unityMenuModel"
384− value: submenuLoader.unityMenuModel
418+ item.unityMenuModel = Qt.binding(function() { return submenuLoader.unityMenuModel; });
385− }
386−
387− Binding {
388− target: item
389− property: "objectName"
390− value: submenuLoader.objectName + "menu"
419+ item.objectName = Qt.binding(function() { return submenuLoader.objectName + "menu"; });
420+ item.desiredX = Qt.binding(function() { return submenuLoader.desiredX; });
421+ item.desiredY = Qt.binding(function() { return submenuLoader.desiredY; });
422+ item.substractWidth = Qt.binding(function() { return submenuLoader.substractWidth; });
391 }
423 }
392
424
393 Keys.onLeftPressed: retreat()
425 Keys.onLeftPressed: retreat()
tests/qmltests/Stage/tst_DesktopStage.qml+54
@@ -29,6 +29,7 @@ import ".." // For EdgeBarrierControls
29import "../../../qml/Stage"
29import "../../../qml/Stage"
30import "../../../qml/Components"
30import "../../../qml/Components"
31import "../../../qml/Components/PanelState"
31import "../../../qml/Components/PanelState"
32+import "../../../qml/ApplicationMenus"
32
33
33Item {
34Item {
34 id: root
35 id: root
@@ -44,6 +45,8 @@ Item {
44 }
45 }
45
46
46 Component.onCompleted: {
47 Component.onCompleted: {
48+ ApplicationMenusLimits.screenWidth = Qt.binding( function() { return stageLoader.width; } );
49+ ApplicationMenusLimits.screenHeight = Qt.binding( function() { return stageLoader.height; } );
47 QuickUtils.keyboardAttached = true;
50 QuickUtils.keyboardAttached = true;
48 theme.name = "Ubuntu.Components.Themes.SuruDark";
51 theme.name = "Ubuntu.Components.Themes.SuruDark";
49 resetGeometry();
52 resetGeometry();
@@ -830,5 +833,56 @@ Item {
830 mouseRelease(decoration);
833 mouseRelease(decoration);
831 tryCompare(Mir, "cursorName", "");
834 tryCompare(Mir, "cursorName", "");
832 }
835 }
836+
837+ function test_menuPositioning_data() {
838+ return [
839+ {tag: "good", windowPosition: Qt.point(units.gu(10), units.gu(10)), expectedMenuCoordinates: Qt.point(units.gu(21.5), units.gu(3)) },
840+ {tag: "collides right", windowPosition: Qt.point(units.gu(100), units.gu(10)), expectedMenuCoordinates: Qt.point(units.gu(12), units.gu(3)) },
841+ {tag: "collides bottom", windowPosition: Qt.point(units.gu(10), units.gu(80)), expectedMenuCoordinates: Qt.point(units.gu(21.5), units.gu(-5.25)) },
842+ ]
843+ }
844+
845+ function test_menuPositioning(data) {
846+ var appDelegate = startApplication("dialer-app");
847+ appDelegate.windowedX = data.windowPosition.x;
848+ appDelegate.windowedY = data.windowPosition.y;
849+
850+ var menuItem = findChild(appDelegate, "menuBar-item3");
851+ menuItem.show();
852+
853+ var menu = findChild(appDelegate, "menuBar-item3-menu");
854+
855+ tryCompare(menu, "x", data.expectedMenuCoordinates.x);
856+ tryCompare(menu, "y", data.expectedMenuCoordinates.y);
857+ }
858+
859+ function test_submenuPositioning_data() {
860+ return [
861+ {tag: "good", windowPosition: Qt.point(units.gu(10), units.gu(10)), expectedMenuCoordinates: Qt.point(units.gu(20), units.gu(10.25)) },
862+ {tag: "collides right", windowPosition: Qt.point(units.gu(100), units.gu(10)), expectedMenuCoordinates: Qt.point(units.gu(-20), units.gu(10.25)) },
863+ {tag: "collides bottom", windowPosition: Qt.point(units.gu(10), units.gu(80)), expectedMenuCoordinates: Qt.point(units.gu(20), units.gu(0)) },
864+ ]
865+ }
866+
867+ function test_submenuPositioning(data) {
868+ var appDelegate = startApplication("dialer-app");
869+ appDelegate.windowedX = data.windowPosition.x;
870+ appDelegate.windowedY = data.windowPosition.y;
871+
872+ var menuItem = findChild(appDelegate, "menuBar-item3");
873+ menuItem.show();
874+
875+ var menu = findChild(appDelegate, "menuBar-item3-menu");
876+ menuItem = findChild(menu, "menuBar-item3-menu-item3-actionItem");
877+ tryCompare(menuItem, "visible", true);
878+ mouseMove(menuItem);
879+ mouseClick(menuItem);
880+
881+ menu = findChild(appDelegate, "menuBar-item3-menu-item3-menu");
882+
883+ console.log(menu, menu.x);
884+ tryCompare(menu, "x", data.expectedMenuCoordinates.x);
885+ tryCompare(menu, "y", data.expectedMenuCoordinates.y);
886+ }
833 }
887 }
834}
888}