@@ -26,18 +26,18 @@ UbuntuShape {
26 objectName: "menu"
26 objectName: "menu"
27 backgroundColor: theme.palette.normal.overlay
27 backgroundColor: theme.palette.normal.overlay
29− property alias unityMenuModel: listView.model
29+ property alias unityMenuModel: repeater.model
31 readonly property real __ajustedMinimumHeight: {
31 readonly property real __ajustedMinimumHeight: {
32 if (listView.contentHeight > __minimumHeight) {
32 if (listView.contentHeight > __minimumHeight) {
33− return units.gu(30);
33+ return __minimumHeight;
35 return Math.max(listView.contentHeight, units.gu(2));
35 return Math.max(listView.contentHeight, units.gu(2));
38 readonly property real __minimumWidth: units.gu(20)
38 readonly property real __minimumWidth: units.gu(20)
39− readonly property real __minimumHeight: units.gu(30)
40 readonly property real __maximumWidth: Screen.width * 0.7
39 readonly property real __maximumWidth: Screen.width * 0.7
40+ readonly property real __minimumHeight: units.gu(30)
41 readonly property real __maximumHeight: Screen.height * 0.7
41 readonly property real __maximumHeight: Screen.height * 0.7
43 function show() {
43 function show() {
@@ -63,13 +63,13 @@ UbuntuShape {
63 d.dismissAll();
63 d.dismissAll();
66− implicitWidth: container.width
66+ implicitWidth: focusScope.width
67− implicitHeight: MathUtils.clamp(listView.contentHeight, __ajustedMinimumHeight, __maximumHeight)
67+ implicitHeight: focusScope.height
69 MenuNavigator {
69 MenuNavigator {
71 objectName: "d"
71 objectName: "d"
72− itemView: listView
72+ itemView: repeater
74 property Item currentItem: null
74 property Item currentItem: null
75 property Item hoveredItem: null
75 property Item hoveredItem: null
@@ -86,13 +86,26 @@ UbuntuShape {
88 onSelect: {
88 onSelect: {
89− currentItem = listView.itemAt(index);
89+ currentItem = repeater.itemAt(index);
91+ if (currentItem.y < listView.contentY) {
92+ listView.contentY = currentItem.y;
93+ } else if (currentItem.y + currentItem.height > listView.contentY + listView.height) {
94+ listView.contentY = currentItem.y + currentItem.height - listView.height;
102+ anchors.fill: parent
94 id: focusScope
106 id: focusScope
95− anchors.fill: parent
107+ width: container.width
108+ height: container.height
96 focus: visible
109 focus: visible
98 Keys.onUpPressed: d.selectPrevious(d.currentIndex)
111 Keys.onUpPressed: d.selectPrevious(d.currentIndex)
@@ -108,17 +121,17 @@ UbuntuShape {
108 id: container
121 id: container
109 objectName: "container"
122 objectName: "container"
111− width: listView.contentWidth
124+ height: MathUtils.clamp(listView.contentHeight, __ajustedMinimumHeight, __maximumHeight)
112− height: parent.height
125+ width: menuColumn.width
113 spacing: 0
126 spacing: 0
115− // FIXME use ListView.header - tried but was flaky with positionViewAtIndex.
128+ // Header - scroll up
117− Layout.fillWidth: true;
130+ Layout.fillWidth: true
118− Layout.maximumHeight: units.gu(3)
131+ height: units.gu(3)
119− Layout.minimumHeight: units.gu(3)
120 visible: listView.contentHeight > root.height
132 visible: listView.contentHeight > root.height
121 enabled: !listView.atYBeginning
133 enabled: !listView.atYBeginning
123 Rectangle {
136 Rectangle {
124 color: enabled ? theme.palette.normal.overlayText :
137 color: enabled ? theme.palette.normal.overlayText :
@@ -143,47 +156,35 @@ UbuntuShape {
143 MouseArea {
156 MouseArea {
144 anchors.fill: parent
157 anchors.fill: parent
145 onPressed: {
158 onPressed: {
146− var index = listView.indexAt(0, listView.contentY);
159+ var item = menuColumn.childAt(0, listView.contentY);
147− listView.positionViewAtIndex(index-1, ListView.Beginning);
160+ if (item) {
161+ var previousItem = item;
163+ previousItem = repeater.itemAt(previousItem.__ownIndex-1);
164+ if (!previousItem) {
165+ listView.contentY = 0;
168+ } while (previousItem.__isSeparator);
170+ listView.contentY = previousItem.y
152− ListView {
176+ // Menu Items
153 id: listView
178 id: listView
154− objectName: "listView"
155− Layout.fillHeight: true
156− Layout.fillWidth: true
157− contentWidth: MathUtils.clamp(contentItem.childrenRect.width,
161− orientation: Qt.Vertical
162− interactive: contentHeight > height
163 clip: interactive
179 clip: interactive
164− highlightFollowsCurrentItem: false
166− highlight: Rectangle {
167− color: "transparent"
168− border.width: units.dp(1)
169− border.color: UbuntuColors.orange
172− width: listView.width
181+ Layout.fillHeight: true
173− height: d.currentItem ? d.currentItem.height : 0
182+ Layout.fillWidth: true
174− y: d.currentItem ? d.currentItem.y : 0
183+ contentHeight: menuColumn.height
175− visible: d.currentItem
184+ interactive: height < contentHeight
178− function itemAt(index) {
179− if (index > count || index < 0) return null;
180− currentIndex = index;
184 MouseArea {
186 MouseArea {
186− anchors.fill: listView
187+ anchors.fill: parent
187 hoverEnabled: true
188 hoverEnabled: true
188 propagateComposedEvents: true // propogate events so we send clicks to children.
189 propagateComposedEvents: true // propogate events so we send clicks to children.
189 z: 1 // on top so we override any other hovers
190 z: 1 // on top so we override any other hovers
@@ -195,7 +196,7 @@ UbuntuShape {
196 if (!d.hoveredItem || !d.currentItem ||
197 if (!d.hoveredItem || !d.currentItem ||
197 !d.hoveredItem.contains(Qt.point(pos.x - d.currentItem.x, pos.y - d.currentItem.y))) {
198 !d.hoveredItem.contains(Qt.point(pos.x - d.currentItem.x, pos.y - d.currentItem.y))) {
198− d.hoveredItem = listView.itemAt(listView.indexAt(pos.x, pos.y));
199+ d.hoveredItem = menuColumn.childAt(pos.x, pos.y)
199 if (!d.hoveredItem || !d.hoveredItem.enabled)
200 if (!d.hoveredItem || !d.hoveredItem.enabled)
200 return false;
201 return false;
201 d.currentItem = d.hoveredItem;
202 d.currentItem = d.hoveredItem;
@@ -216,89 +217,121 @@ UbuntuShape {
219− delegate: Loader {
220+ ColumnLayout {
220− id: loader
221+ id: menuColumn
221− objectName: root.objectName + "-item" + __ownIndex
222+ spacing: 0
223− property int __ownIndex: index
224+ Repeater {
225− width: root.width
227+ Loader {
226− enabled: model.isSeparator ? false : model.sensitive
228+ id: loader
229+ objectName: root.objectName + "-item" + __ownIndex
228− sourceComponent: {
231+ property int __ownIndex: index
229− if (model.isSeparator) {
232+ property bool __isSeparator: model.isSeparator
230− return separatorComponent;
232− return menuItemComponent;
235− property Item popup: null
234+ enabled: __isSeparator ? false : model.sensitive
237− Component {
236+ sourceComponent: {
238− id: menuItemComponent
237+ if (model.isSeparator) {
239− MenuItem {
238+ return separatorComponent;
242− objectName: loader.objectName + "-actionItem"
244− action.onTriggered: {
245− d.currentItem = loader;
249− var model = root.unityMenuModel.submenu(__ownIndex);
250− popup = submenuComponent.createObject(focusScope, {
251− objectName: loader.objectName + "-",
252− unityMenuModel: model,
253− x: Qt.binding(function() { return root.width }),
254− y: Qt.binding(function() { return loader.y })
257− popup.visible = true;
259− popup.retreat.connect(function() {
262− menuItem.forceActiveFocus();
265− root.unityMenuModel.activate(__ownIndex);
240+ return menuItemComponent;
269− Connections {
243+ property Item popup: null
271− onCurrentIndexChanged: {
245+ Layout.fillWidth: true
272− if (popup && d.currentIndex != __ownIndex) {
246+
248+ id: menuItemComponent
252+ objectName: loader.objectName + "-actionItem"
254+ width: MathUtils.clamp(implicitWidth, __minimumWidth, __maximumWidth)
256+ action.onTriggered: {
257+ d.currentItem = loader;
261+ var model = root.unityMenuModel.submenu(__ownIndex);
262+ popup = submenuComponent.createObject(focusScope, {
263+ objectName: loader.objectName + "-",
264+ unityMenuModel: model,
265+ x: Qt.binding(function() { return root.width }),
266+ y: Qt.binding(function() {
267+ var dummy = listView.contentY; // force a recalc on contentY change.
268+ return mapToItem(container, 0, y).y;
273− popup.visible = false;
272+ popup.visible = true;
274+ popup.retreat.connect(function() {
277+ menuItem.forceActiveFocus();
280+ root.unityMenuModel.activate(__ownIndex);
286+ onCurrentIndexChanged: {
287+ if (popup && d.currentIndex != __ownIndex) {
288+ popup.visible = false;
276− onDismissAll: {
291+ onDismissAll: {
277− if (popup) {
292+ if (popup) {
278− popup.destroy();
293+ popup.destroy();
279− popup = null;
294+ popup = null;
286− Component {
301+ Component {
287− id: separatorComponent
302+ id: separatorComponent
288− ListItems.ThinDivider {
303+ ListItems.ThinDivider {
289− objectName: loader.objectName + "-separator"
304+ objectName: loader.objectName + "-separator"
305+ implicitHeight: units.dp(2)
295− // FIXME use ListView.footer - tried but was flaky with positionViewAtIndex.
313+ // Highlight
315+ color: "transparent"
316+ border.width: units.dp(1)
317+ border.color: UbuntuColors.orange
320+ width: listView.width
321+ height: d.currentItem ? d.currentItem.height : 0
322+ y: d.currentItem ? d.currentItem.y : 0
323+ visible: d.currentItem
328+ // Header - scroll down
297− Layout.fillWidth: true;
330+ Layout.fillWidth: true
298− Layout.maximumHeight: units.gu(3)
331+ height: units.gu(3)
299− Layout.minimumHeight: units.gu(3)
300 visible: listView.contentHeight > root.height
332 visible: listView.contentHeight > root.height
301 enabled: !listView.atYEnd
333 enabled: !listView.atYEnd
303 Rectangle {
336 Rectangle {
304 color: enabled ? theme.palette.normal.overlayText :
337 color: enabled ? theme.palette.normal.overlayText :
@@ -323,8 +356,19 @@ UbuntuShape {
323 MouseArea {
356 MouseArea {
324 anchors.fill: parent
357 anchors.fill: parent
325 onPressed: {
358 onPressed: {
326− var index = listView.indexAt(0, listView.contentY);
359+ var item = menuColumn.childAt(0, listView.contentY + listView.height);
327− listView.positionViewAtIndex(index+1, ListView.Beginning);
360+ if (item) {
361+ var nextItem = item;
363+ nextItem = repeater.itemAt(nextItem.__ownIndex+1);
365+ listView.contentY = listView.contentHeight - listView.height;
368+ } while (nextItem.__isSeparator);
370+ listView.contentY = nextItem.y - listView.height