@@ -24,12 +24,13 @@ Item {
25 objectName: "menuBar"
25 objectName: "menuBar"
27 property alias unityMenuModel: rowRepeater.model
28 property alias unityMenuModel: rowRepeater.model
29− readonly property bool valid: rowRepeater.count > 0
31 property bool enableKeyFilter: false
29 property bool enableKeyFilter: false
30+ property real overflowWidth: width
33+ readonly property bool valid: rowRepeater.count > 0
33 readonly property bool showRequested: d.longAltPressed || d.currentItem != null
34 readonly property bool showRequested: d.longAltPressed || d.currentItem != null
35 implicitWidth: row.width
36 implicitWidth: row.width
@@ -75,197 +76,334 @@ Item {
75 acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton
76 acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton
76 anchors.fill: parent
77 anchors.fill: parent
77 enabled: d.currentItem != null
78 enabled: d.currentItem != null
78− hoverEnabled: enabled && d.currentItem.__popup != null
79+ hoverEnabled: enabled && d.currentItem && d.currentItem.__popup != null
79 onPressed: d.dismissAll()
80 onPressed: d.dismissAll()
91− spacing: units.gu(2)
85+ spacing: units.gu(2)
92− height: parent.height
86+ height: parent.height
94− ActionContext {
88+ ActionContext {
95− id: menuBarContext
89+ id: menuBarContext
96− objectName: "barContext"
90+ objectName: "barContext"
97− active: !d.currentItem && enableKeyFilter
91+ active: !d.currentItem && enableKeyFilter
105− objectName: root.objectName + "-item" + __ownIndex
107− readonly property int __ownIndex: index
108− property Item __popup: null;
109− property bool popupVisible: __popup && __popup.visible
111− implicitWidth: column.implicitWidth
94+ Connections {
112− implicitHeight: row.height
95+ target: root.unityMenuModel
113− enabled: model.sensitive
96+ onModelReset: d.firstInvisibleIndex = undefined
102+ onItemAdded: d.recalcFirstInvisibleIndexAdded(index, item)
103+ onCountChanged: d.recalcFirstInvisibleIndex()
107+ objectName: root.objectName + "-item" + __ownIndex
109+ readonly property int __ownIndex: index
110+ property Item __popup: null;
111+ readonly property bool popupVisible: __popup && __popup.visible
112+ readonly property bool shouldDisplay: x + width + ((__ownIndex < rowRepeater.count-1) ? units.gu(2) : 0) <
113+ root.overflowWidth - ((__ownIndex < rowRepeater.count-1) ? overflowButton.width : 0)
115+ implicitWidth: column.implicitWidth
116+ implicitHeight: row.height
117+ enabled: model.sensitive && shouldDisplay
118+ opacity: shouldDisplay ? 1 : 0
115− function show() {
120+ function show() {
116− if (!__popup) {
121+ if (!__popup) {
117− __popup = menuComponent.createObject(root, { objectName: visualItem.objectName + "-menu" });
122+ __popup = menuComponent.createObject(root, { objectName: visualItem.objectName + "-menu" });
118− __popup.childActivated.connect(dismiss);
123+ __popup.childActivated.connect(dismiss);
119− // force the current item to be the newly popped up menu
124+ // force the current item to be the newly popped up menu
120− } else {
125+ } else {
121− __popup.show();
126+ __popup.show();
123− d.currentItem = visualItem;
128+ d.currentItem = visualItem;
125− function hide() {
130+ function hide() {
126− if (__popup) {
131+ if (__popup) {
127− __popup.hide();
132+ __popup.hide();
129− if (d.currentItem === visualItem) {
134+ if (d.currentItem === visualItem) {
130− d.currentItem = null;
135+ d.currentItem = null;
134− function dismiss() {
139+ function dismiss() {
135− if (__popup) {
140+ if (__popup) {
136− __popup.destroy();
141+ __popup.destroy();
137− __popup = null;
142+ __popup = null;
139− if (d.currentItem === visualItem) {
144+ if (d.currentItem === visualItem) {
140− d.currentItem = null;
145+ d.currentItem = null;
151+ if (!visible && __popup) dismiss();
145− Connections {
154+ onShouldDisplayChanged: {
146− target: d
155+ if ((!shouldDisplay && d.firstInvisibleIndex == undefined) || __ownIndex <= d.firstInvisibleIndex) {
147− onDismissAll: visualItem.dismiss()
156+ d.recalcFirstInvisibleIndex();
150− Component {
160+ Connections {
151− id: menuComponent
161+ target: d
152− MenuPopup {
162+ onDismissAll: visualItem.dismiss()
153− desiredX: visualItem.x - units.gu(1)
163+ }
154− desiredY: parent.height
155− unityMenuModel: root.unityMenuModel.submenu(visualItem.__ownIndex)
168+ desiredX: visualItem.x - units.gu(1)
169+ desiredY: parent.height
170+ unityMenuModel: root.unityMenuModel.submenu(visualItem.__ownIndex)
157− Component.onCompleted: reset();
172+ Component.onCompleted: reset();
161− RowLayout {
176+ RowLayout {
162− id: column
177+ id: column
163− spacing: units.gu(1)
178+ spacing: units.gu(1)
164− anchors {
179+ anchors {
165− centerIn: parent
180+ centerIn: parent
169− Layout.preferredWidth: units.gu(2)
184+ Layout.preferredWidth: units.gu(2)
170− Layout.preferredHeight: units.gu(2)
185+ Layout.preferredHeight: units.gu(2)
171− Layout.alignment: Qt.AlignVCenter
186+ Layout.alignment: Qt.AlignVCenter
173− visible: model.icon || false
188+ visible: model.icon || false
174− source: model.icon || ""
189+ source: model.icon || ""
177− ActionItem {
192+ ActionItem {
178− id: actionItem
193+ id: actionItem
179− width: _title.width
194+ width: _title.width
180− height: _title.height
195+ height: _title.height
182− action: Action {
197+ action: Action {
198+ enabled: visualItem.enabled
183− // FIXME - SDK Action:text modifies menu text with html underline for mnemonic
199+ // FIXME - SDK Action:text modifies menu text with html underline for mnemonic
184− text: model.label.replace("_", "&").replace("<u>", "&").replace("</u>", "")
200+ text: model.label.replace("_", "&").replace("<u>", "&").replace("</u>", "")
186− onTriggered: {
202+ onTriggered: {
187− visualItem.show();
203+ visualItem.show();
192− id: _title
208+ id: _title
193− text: actionItem.text
209+ text: actionItem.text
194− horizontalAlignment: Text.AlignLeft
210+ horizontalAlignment: Text.AlignLeft
195− color: enabled ? "white" : "#5d5d5d"
211+ color: enabled ? "white" : "#5d5d5d"
199− } // Item ( delegate )
215+ } // Item ( delegate )
200− } // Repeater
216+ } // Repeater
201− } // Row
217+ } // Row
203− MouseArea {
219+ MouseArea {
204− anchors.fill: parent
220+ anchors.fill: parent
205− hoverEnabled: d.currentItem
221+ hoverEnabled: d.currentItem
207− onEntered: {
223+ onEntered: {
208− if (d.currentItem) {
224+ if (d.currentItem) {
209− updateCurrentItemFromPosition(Qt.point(mouseX, mouseY))
225+ updateCurrentItemFromPosition(Qt.point(mouseX, mouseY))
228+ onPositionChanged: {
229+ if (d.currentItem) {
230+ updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y))
234+ var prevItem = d.currentItem;
235+ updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y))
236+ if (prevItem && d.currentItem == prevItem) {
241+ function updateCurrentItemFromPosition(point) {
242+ var pos = mapToItem(row, point.x, point.y);
244+ if (!d.hoveredItem || !d.currentItem || !d.hoveredItem.contains(Qt.point(pos.x - d.currentItem.x, pos.y - d.currentItem.y))) {
245+ d.hoveredItem = row.childAt(pos.x, pos.y);
246+ if (!d.hoveredItem || !d.hoveredItem.enabled)
248+ if (d.currentItem != d.hoveredItem) {
249+ d.currentItem = d.hoveredItem;
257+ objectName: "overflow"
259+ hoverEnabled: d.currentItem
260+ onEntered: d.currentItem = this
212− onPositionChanged: {
261+ onPositionChanged: d.currentItem = this
262+ onClicked: d.currentItem = this
264+ property Item __popup: null;
265+ readonly property bool popupVisible: __popup && __popup.visible
266+ readonly property Item firstInvisibleItem: d.firstInvisibleIndex !== undefined ? rowRepeater.itemAt(d.firstInvisibleIndex) : null
268+ visible: d.firstInvisibleIndex != undefined
269+ x: firstInvisibleItem ? firstInvisibleItem.x : 0
271+ height: parent.height
275+ if (!visible && __popup) dismiss();
282+ anchors.centerIn: parent
283+ color: theme.palette.normal.overlayText
284+ name: "toolkit_chevron-down_2gu"
289+ __popup = overflowComponent.createObject(root, { objectName: overflowButton.objectName + "-menu" });
290+ __popup.childActivated.connect(dismiss);
291+ // force the current item to be the newly popped up menu
295+ d.currentItem = overflowButton;
213− if (d.currentItem) {
301+ if (d.currentItem === overflowButton) {
214− updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y))
302+ d.currentItem = null;
218− var prevItem = d.currentItem;
306+ function dismiss() {
219− updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y))
307+ if (__popup) {
220− if (prevItem && d.currentItem == prevItem) {
311+ if (d.currentItem === overflowButton) {
221− prevItem.hide();
312+ d.currentItem = null;
225− function updateCurrentItemFromPosition(point) {
317+ Connections {
226− var pos = mapToItem(row, point.x, point.y);
318+ target: d
319+ onDismissAll: overflowButton.dismiss()
323+ id: overflowComponent
326+ desiredX: overflowButton.x - units.gu(1)
327+ desiredY: parent.height
328+ unityMenuModel: overflowModel
330+ ExpressionFilterModel {
332+ sourceModel: root.unityMenuModel
333+ matchExpression: function(index) {
334+ if (d.firstInvisibleIndex === undefined) return false;
335+ return index >= d.firstInvisibleIndex;
228− if (!d.hoveredItem || !d.currentItem || !d.hoveredItem.contains(Qt.point(pos.x - d.currentItem.x, pos.y - d.currentItem.y))) {
338+ function submenu(index) {
229− d.hoveredItem = row.childAt(pos.x, pos.y);
339+ return sourceModel.submenu(mapRowToSource(index));
230− if (!d.hoveredItem || !d.hoveredItem.enabled)
232− if (d.currentItem != d.hoveredItem) {
233− d.currentItem = d.hoveredItem;
341+ function activate(index) {
342+ return sourceModel.activate(mapRowToSource(index));
348+ onFirstInvisibleIndexChanged: overflowModel.invalidate()
240− Rectangle {
354+ Rectangle {
241− id: underline
355+ id: underline
242− anchors {
356+ anchors {
243− bottom: row.bottom
357+ bottom: row.bottom
245− x: d.currentItem ? row.x + d.currentItem.x - units.gu(1) : 0
246− width: d.currentItem ? d.currentItem.width + units.gu(2) : 0
248− color: UbuntuColors.orange
249− visible: d.currentItem
359+ x: d.currentItem ? row.x + d.currentItem.x - units.gu(1) : 0
360+ width: d.currentItem ? d.currentItem.width + units.gu(2) : 0
362+ color: UbuntuColors.orange
363+ visible: d.currentItem
253 MenuNavigator {
366 MenuNavigator {
255 objectName: "d"
368 objectName: "d"
256 itemView: rowRepeater
369 itemView: rowRepeater
370+ hasOverflow: overflowButton.visible
258 property Item currentItem: null
372 property Item currentItem: null
259 property Item hoveredItem: null
373 property Item hoveredItem: null
260 property Item prevCurrentItem: null
374 property Item prevCurrentItem: null
262− readonly property int currentIndex: currentItem ? currentItem.__ownIndex : -1
264 property bool altPressed: false
375 property bool altPressed: false
265 property bool longAltPressed: false
376 property bool longAltPressed: false
377+ property var firstInvisibleIndex: undefined
379+ readonly property int currentIndex: currentItem && currentItem.hasOwnProperty("__ownIndex") ? currentItem.__ownIndex : -1
267 signal dismissAll()
381 signal dismissAll()
383+ function recalcFirstInvisibleIndexAdded(index, item) {
384+ if (firstInvisibleIndex === undefined) {
385+ if (!item.shouldDisplay) {
386+ firstInvisibleIndex = index;
388+ } else if (index <= firstInvisibleIndex) {
389+ if (!item.shouldDisplay) {
390+ firstInvisibleIndex = index;
392+ firstInvisibleIndex++;
397+ function recalcFirstInvisibleIndex() {
398+ for (var i = 0; i < rowRepeater.count; i++) {
399+ if (!rowRepeater.itemAt(i).shouldDisplay) {
400+ firstInvisibleIndex = i;
404+ firstInvisibleIndex = undefined;
269 onSelect: {
407 onSelect: {
270 var delegate = rowRepeater.itemAt(index);
408 var delegate = rowRepeater.itemAt(index);
271 if (delegate) {
409 if (delegate) {
@@ -273,6 +411,10 @@ Item {
415+ d.currentItem = overflowButton;
276 onCurrentItemChanged: {
418 onCurrentItemChanged: {
277 if (prevCurrentItem && prevCurrentItem != currentItem) {
419 if (prevCurrentItem && prevCurrentItem != currentItem) {
278 if (currentItem) {
420 if (currentItem) {