@@ -44,6 +44,10 @@ UbuntuShape {
4444 d.select(index)
4545 }
4646
47+ function reset() {
48+ d.currentItem = null;
49+ }
50+
4751 function dismiss() {
4852 d.dismissAll()
4953 }
@@ -51,37 +55,26 @@ UbuntuShape {
5155 implicitWidth: container.width
5256 implicitHeight: MathUtils.clamp(listView.contentHeight, __ajustedMinimumHeight, __maximumHeight)
5357
54− focus: true
55−
56− Keys.onUpPressed: d.selectPrevious(d.currentIndex)
57− Keys.onDownPressed: d.selectNext(d.currentIndex)
58− Keys.onRightPressed: {
59− // Don't let right keypresses fall through if the current item has a visible popup.
60− if (!d.currentItem || !d.currentItem.popup || !d.currentItem.popup.visible) {
61− event.accepted = false;
62− }
63− }
64−
6558 onVisibleChanged: {
6659 if (!visible) {
6760 d.currentItem = null;
6861 }
6962 }
7063
71− QtObject {
64+ MenuNavigator {
7265 id: d
7366 objectName: "d"
67+ itemView: listView
7468
7569 property Item currentItem: null
7670 property Item hoveredItem: null
7771 readonly property int currentIndex: currentItem ? currentItem.__ownIndex : -1
7872
79− signal select(int index)
8073 signal dismissAll()
8174
8275 onCurrentItemChanged: {
8376 if (currentItem) {
84− currentItem.forceActiveFocus();
77+ currentItem.item.forceActiveFocus();
8578 } else {
8679 hoveredItem = null;
8780 }
@@ -90,314 +83,272 @@ UbuntuShape {
9083 onSelect: {
9184 currentItem = listView.itemAt(index);
9285 }
86+ }
9387
94− function selectNext(currentIndex) {
95− var menu;
96− var newIndex = 0;
97− if (currentIndex === -1 && listView.count > 0) {
98− while (listView.count > newIndex) {
99− menu = listView.itemAt(newIndex);
100− if (!!menu["enabled"]) {
101− select(newIndex);
102− break;
103− }
104− newIndex++;
105− }
106− } else if (currentIndex !== -1 && listView.count > 1) {
107− var startIndex = (currentIndex + 1) % listView.count;
108− newIndex = startIndex;
109− do {
110− menu = listView.itemAt(newIndex);
111− if (!!menu["enabled"]) {
112− select(newIndex);
113− break;
114− }
115− newIndex = (newIndex + 1) % listView.count;
116− } while (newIndex !== startIndex)
88+ FocusScope {
89+ id: focusScope
90+ anchors.fill: parent
91+ focus: visible
92+
93+ Keys.onUpPressed: d.selectPrevious(d.currentIndex)
94+ Keys.onDownPressed: d.selectNext(d.currentIndex)
95+ Keys.onRightPressed: {
96+ // Don't let right keypresses fall through if the current item has a visible popup.
97+ if (!d.currentItem || !d.currentItem.popup || !d.currentItem.popup.visible) {
98+ event.accepted = false;
11799 }
118100 }
119101
120− function selectPrevious(currentIndex) {
121− var menu;
122− var newIndex = listView.count-1;
123− if (currentIndex === -1 && listView.count > 0) {
124− while (listView.count > newIndex) {
125− menu = listView.itemAt(newIndex);
126− if (!!menu["enabled"]) {
127− select(newIndex);
128− break;
102+ ColumnLayout {
103+ id: container
104+ objectName: "container"
105+
106+ width: listView.contentWidth
107+ height: parent.height
108+ spacing: 0
109+
110+ // FIXME use ListView.header - tried but was flaky with positionViewAtIndex.
111+ Item {
112+ Layout.fillWidth: true;
113+ Layout.maximumHeight: units.gu(3)
114+ Layout.minimumHeight: units.gu(3)
115+ visible: listView.contentHeight > root.height
116+ enabled: !listView.atYBeginning
117+
118+ Rectangle {
119+ color: enabled ? theme.palette.normal.overlayText :
120+ theme.palette.disabled.overlayText
121+ height: units.dp(1)
122+ anchors {
123+ bottom: parent.bottom
124+ left: parent.left
125+ right: parent.right
129126 }
130− newIndex--;
131127 }
132− } else if (currentIndex !== -1 && listView.count > 1) {
133− var startIndex = currentIndex - 1;
134− newIndex = startIndex;
135− do {
136− if (newIndex < 0) {
137− newIndex = listView.count - 1;
138− }
139− menu = listView.itemAt(newIndex);
140− if (!!menu["enabled"]) {
141− select(newIndex);
142− break;
143− }
144− newIndex--;
145− } while (newIndex !== startIndex)
146− }
147− }
148− }
149128
150− ColumnLayout {
151− id: container
152− objectName: "container"
153−
154− width: listView.contentWidth
155− height: parent.height
156− spacing: 0
157−
158− // FIXME use ListView.header - tried but was flaky with positionViewAtIndex.
159− Item {
160− Layout.fillWidth: true;
161− Layout.maximumHeight: units.gu(3)
162− Layout.minimumHeight: units.gu(3)
163− visible: listView.contentHeight > root.height
164− enabled: !listView.atYBeginning
165−
166− Rectangle {
167− color: enabled ? theme.palette.normal.overlayText :
168− theme.palette.disabled.overlayText
169− height: units.dp(1)
170− anchors {
171− bottom: parent.bottom
172− left: parent.left
173− right: parent.right
129+ Icon {
130+ anchors.centerIn: parent
131+ width: units.gu(2)
132+ height: units.gu(2)
133+ name: "up"
134+ color: enabled ? theme.palette.normal.overlayText :
135+ theme.palette.disabled.overlayText
174136 }
175− }
176137
177− Icon {
178− anchors.centerIn: parent
179− width: units.gu(2)
180− height: units.gu(2)
181− name: "up"
182− color: enabled ? theme.palette.normal.overlayText :
183− theme.palette.disabled.overlayText
184− }
185−
186− MouseArea {
187− anchors.fill: parent
188− onPressed: {
189− var index = listView.indexAt(0, listView.contentY);
190− listView.positionViewAtIndex(index-1, ListView.Beginning);
138+ MouseArea {
139+ anchors.fill: parent
140+ onPressed: {
141+ var index = listView.indexAt(0, listView.contentY);
142+ listView.positionViewAtIndex(index-1, ListView.Beginning);
143+ }
191144 }
192145 }
193− }
194146
195− ListView {
196− id: listView
197− objectName: "listView"
198− Layout.fillHeight: true
199− Layout.fillWidth: true
200− contentWidth: MathUtils.clamp(contentItem.childrenRect.width,
201− __minimumWidth,
202− __maximumWidth)
203−
204− orientation: Qt.Vertical
205− interactive: contentHeight > height
206− clip: interactive
207− highlightFollowsCurrentItem: false
208−
209− highlight: Rectangle {
210− color: "transparent"
211− border.width: units.dp(1)
212− border.color: UbuntuColors.orange
213− z: 1
214−
215− width: listView.width
216− height: d.currentItem ? d.currentItem.height : 0
217− y: d.currentItem ? d.currentItem.y : 0
218− visible: d.currentItem
219− }
147+ ListView {
148+ id: listView
149+ objectName: "listView"
150+ Layout.fillHeight: true
151+ Layout.fillWidth: true
152+ contentWidth: MathUtils.clamp(contentItem.childrenRect.width,
153+ __minimumWidth,
154+ __maximumWidth)
155+
156+ orientation: Qt.Vertical
157+ interactive: contentHeight > height
158+ clip: interactive
159+ highlightFollowsCurrentItem: false
160+
161+ highlight: Rectangle {
162+ color: "transparent"
163+ border.width: units.dp(1)
164+ border.color: UbuntuColors.orange
165+ z: 1
166+
167+ width: listView.width
168+ height: d.currentItem ? d.currentItem.height : 0
169+ y: d.currentItem ? d.currentItem.y : 0
170+ visible: d.currentItem
171+ }
220172
221− function itemAt(index) {
222− if (index > count || index < 0) return null;
223− currentIndex = index;
224− return currentItem;
225− }
173+ function itemAt(index) {
174+ if (index > count || index < 0) return null;
175+ currentIndex = index;
176+ return currentItem;
177+ }
226178
227− MouseArea {
228− id: menuMouseArea
229− anchors.fill: parent
230− hoverEnabled: true
231− z: -1
179+ MouseArea {
180+ id: menuMouseArea
181+ anchors.fill: parent
182+ hoverEnabled: true
183+ z: -1
232184
233− onPositionChanged: updateCurrentItemFromPosition(mouse)
185+ onPositionChanged: updateCurrentItemFromPosition(mouse)
234186
235− function updateCurrentItemFromPosition(point) {
236− var pos = mapToItem(listView.contentItem, point.x, point.y);
187+ function updateCurrentItemFromPosition(point) {
188+ var pos = mapToItem(listView.contentItem, point.x, point.y);
237189
238− if (!d.hoveredItem || !d.currentItem ||
239− !d.hoveredItem.contains(Qt.point(pos.x - d.currentItem.x, pos.y - d.currentItem.y))) {
240− d.hoveredItem = listView.itemAt(listView.indexAt(pos.x, pos.y));
241− if (!d.hoveredItem || !d.hoveredItem.enabled)
242− return false;
243− d.currentItem = d.hoveredItem;
190+ if (!d.hoveredItem || !d.currentItem ||
191+ !d.hoveredItem.contains(Qt.point(pos.x - d.currentItem.x, pos.y - d.currentItem.y))) {
192+ d.hoveredItem = listView.itemAt(listView.indexAt(pos.x, pos.y));
193+ if (!d.hoveredItem || !d.hoveredItem.enabled)
194+ return false;
195+ d.currentItem = d.hoveredItem;
196+ }
197+ return true;
244198 }
245− return true;
246199 }
247− }
248200
249− ActionContext {
250− id: menuBarContext
251− objectName: "menuContext"
252− active: {
253− if (!root.visible) return false;
254− if (d.currentItem && d.currentItem.popup && d.currentItem.popup.visible) {
255− return false;
201+ ActionContext {
202+ id: menuBarContext
203+ objectName: "menuContext"
204+ active: {
205+ if (!root.visible) return false;
206+ if (d.currentItem && d.currentItem.popup && d.currentItem.popup.visible) {
207+ return false;
208+ }
209+ return true;
256210 }
257− return true;
258211 }
259− }
260212
261− delegate: Loader {
262− id: loader
263− objectName: root.objectName + "-item" + __ownIndex
213+ delegate: Loader {
214+ id: loader
215+ objectName: root.objectName + "-item" + __ownIndex
264216
265− property int __ownIndex: index
217+ property int __ownIndex: index
266218
267− width: root.width
268− enabled: model.isSeparator ? false : model.sensitive
269− Keys.forwardTo: [ item ]
219+ width: root.width
220+ enabled: model.isSeparator ? false : model.sensitive
270221
271− sourceComponent: {
272− if (model.isSeparator) {
273− return separatorComponent;
222+ sourceComponent: {
223+ if (model.isSeparator) {
224+ return separatorComponent;
225+ }
226+ return menuItemComponent;
274227 }
275− return menuItemComponent;
276− }
277228
278− property Item popup: null
279−
280− Component {
281− id: menuItemComponent
282− MenuItem {
283− id: menuItem
284− menuData: model
285− objectName: loader.objectName + "-actionItem"
286−
287− action.onTriggered: {
288− d.currentItem = loader;
289−
290− if (hasSubmenu) {
291− if (!popup) {
292− var model = root.unityMenuModel.submenu(__ownIndex);
293− popup = submenuComponent.createObject(root, {
294− objectName: loader.objectName + "-",
295− unityMenuModel: model,
296− x: Qt.binding(function() { return root.width }),
297− y: Qt.binding(function() { return loader.y })
298− });
299− } else if (popup) {
300− popup.visible = true;
229+ property Item popup: null
230+
231+ Component {
232+ id: menuItemComponent
233+ MenuItem {
234+ id: menuItem
235+ menuData: model
236+ objectName: loader.objectName + "-actionItem"
237+
238+ action.onTriggered: {
239+ d.currentItem = loader;
240+
241+ if (hasSubmenu) {
242+ if (!popup) {
243+ var model = root.unityMenuModel.submenu(__ownIndex);
244+ popup = submenuComponent.createObject(focusScope, {
245+ objectName: loader.objectName + "-",
246+ unityMenuModel: model,
247+ x: Qt.binding(function() { return root.width }),
248+ y: Qt.binding(function() { return loader.y })
249+ });
250+ } else if (popup) {
251+ popup.visible = true;
252+ }
253+ popup.dismiss.connect(function() {
254+ popup.destroy();
255+ popup = null;
256+ menuItem.forceActiveFocus();
257+ })
258+ } else {
259+ root.unityMenuModel.activate(__ownIndex);
301260 }
302− popup.forceActiveFocus();
303− popup.dismiss.connect(function() {
304− popup.destroy();
305− popup = null;
306− loader.forceActiveFocus();
307− })
308− } else {
309− root.unityMenuModel.activate(__ownIndex);
310261 }
311− }
312262
313− Connections {
314− target: d
315− onCurrentIndexChanged: {
316− if (popup && d.currentIndex != __ownIndex) {
317− popup.visible = false;
263+ Connections {
264+ target: d
265+ onCurrentIndexChanged: {
266+ if (popup && d.currentIndex != __ownIndex) {
267+ popup.visible = false;
268+ }
318269 }
319270 }
320271 }
321272 }
322− }
323273
324− Component {
325− id: separatorComponent
326− ListItems.ThinDivider {
327− objectName: loader.objectName + "-separator"
274+ Component {
275+ id: separatorComponent
276+ ListItems.ThinDivider {
277+ objectName: loader.objectName + "-separator"
278+ }
328279 }
329280 }
330− }
331− } // ListView
332−
333− // FIXME use ListView.footer - tried but was flaky with positionViewAtIndex.
334− Item {
335− Layout.fillWidth: true;
336− Layout.maximumHeight: units.gu(3)
337− Layout.minimumHeight: units.gu(3)
338− visible: listView.contentHeight > root.height
339− enabled: !listView.atYEnd
340−
341− Rectangle {
342− color: enabled ? theme.palette.normal.overlayText :
343− theme.palette.disabled.overlayText
344− height: units.dp(1)
345− anchors {
346− top: parent.top
347− left: parent.left
348− right: parent.right
281+ } // ListView
282+
283+ // FIXME use ListView.footer - tried but was flaky with positionViewAtIndex.
284+ Item {
285+ Layout.fillWidth: true;
286+ Layout.maximumHeight: units.gu(3)
287+ Layout.minimumHeight: units.gu(3)
288+ visible: listView.contentHeight > root.height
289+ enabled: !listView.atYEnd
290+
291+ Rectangle {
292+ color: enabled ? theme.palette.normal.overlayText :
293+ theme.palette.disabled.overlayText
294+ height: units.dp(1)
295+ anchors {
296+ top: parent.top
297+ left: parent.left
298+ right: parent.right
299+ }
349300 }
350− }
351301
352− Icon {
353− anchors.centerIn: parent
354− width: units.gu(2)
355− height: units.gu(2)
356− name: "down"
357− color: enabled ? theme.palette.normal.overlayText :
358− theme.palette.disabled.overlayText
359− }
302+ Icon {
303+ anchors.centerIn: parent
304+ width: units.gu(2)
305+ height: units.gu(2)
306+ name: "down"
307+ color: enabled ? theme.palette.normal.overlayText :
308+ theme.palette.disabled.overlayText
309+ }
360310
361− MouseArea {
362− anchors.fill: parent
363− onPressed: {
364− var index = listView.indexAt(0, listView.contentY);
365− listView.positionViewAtIndex(index+1, ListView.Beginning);
311+ MouseArea {
312+ anchors.fill: parent
313+ onPressed: {
314+ var index = listView.indexAt(0, listView.contentY);
315+ listView.positionViewAtIndex(index+1, ListView.Beginning);
316+ }
366317 }
367318 }
368− }
369− } // Column
319+ } // Column
370320
371− Component {
372− id: submenuComponent
373− Loader {
374− id: submenuLoader
375− source: "MenuPopup.qml"
321+ Component {
322+ id: submenuComponent
323+ Loader {
324+ id: submenuLoader
325+ source: "MenuPopup.qml"
376326
377− property var unityMenuModel: null
378− signal dismiss()
327+ property var unityMenuModel: null
328+ signal dismiss()
379329
380− Binding {
381− target: item
382− property: "unityMenuModel"
383− value: submenuLoader.unityMenuModel
384− }
330+ Binding {
331+ target: item
332+ property: "unityMenuModel"
333+ value: submenuLoader.unityMenuModel
334+ }
385335
386− Binding {
387− target: item
388− property: "objectName"
389− value: submenuLoader.objectName + "menu"
390− }
336+ Binding {
337+ target: item
338+ property: "objectName"
339+ value: submenuLoader.objectName + "menu"
340+ }
391341
392− Connections {
393− target: d
394− onDismissAll: submenuLoader.dismiss()
395− }
342+ Connections {
343+ target: d
344+ onDismissAll: submenuLoader.dismiss()
345+ }
396346
397− Keys.onLeftPressed: dismiss()
347+ Keys.onLeftPressed: dismiss()
398348
399− Component.onCompleted: item.select(0);
400− onVisibleChanged: if (visible) { item.select(0); }
349+ Component.onCompleted: item.select(0);
350+ onVisibleChanged: if (visible) { item.select(0); }
351+ }
401352 }
402353 }
403354}