2 * Copyright 2016 Canonical Ltd.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18import QtQuick.Layouts 1.1
20import Ubuntu.Components 1.3
21import GlobalShortcut 1.0
28 property alias unityMenuModel: rowRepeater.model
29 property bool enableKeyFilter: false
30 property real overflowWidth: width
31 property bool windowMoving: false
34 readonly property bool valid: rowRepeater.count > 0
35 readonly property bool showRequested: d.longAltPressed || d.currentItem != null
37 // MoveHandler API for DecoratedWindow
38 signal pressed(var mouse)
39 signal pressedChangedEx(bool pressed, var pressedButtons, real mouseX, real mouseY)
40 signal positionChanged(var mouse)
41 signal released(var mouse)
42 signal doubleClicked(var mouse)
44 implicitWidth: row.width
47 function select(index) {
56 shortcut: Qt.Key_Alt|Qt.AltModifier
57 active: enableKeyFilter
58 onTriggered: d.startShortcutTimer()
59 onReleased: d.stopSHortcutTimer()
61 // On an actual keyboard, the AltModifier is not supplied on release.
64 active: enableKeyFilter
65 onTriggered: d.startShortcutTimer()
66 onReleased: d.stopSHortcutTimer()
70 shortcut: Qt.AltModifier | Qt.Key_F10
71 active: enableKeyFilter && d.currentItem == null
73 for (var i = 0; i < rowRepeater.count; i++) {
74 var item = rowRepeater.itemAt(i);
84 acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton
86 enabled: d.currentItem != null
87 hoverEnabled: enabled && d.currentItem && d.currentItem.__popup != null
88 onPressed: { mouse.accepted = false; d.dismissAll(); }
98 objectName: "barContext"
99 active: !d.currentItem && enableKeyFilter
103 target: root.unityMenuModel
104 onModelReset: d.firstInvisibleIndex = undefined
115 onItemAdded: d.recalcFirstInvisibleIndexAdded(index, item)
116 onCountChanged: d.recalcFirstInvisibleIndex()
120 objectName: root.objectName + "-item" + __ownIndex
122 readonly property int __ownIndex: index
123 property Item __popup: null;
124 readonly property bool popupVisible: __popup && __popup.visible
125 readonly property bool shouldDisplay: x + width + ((__ownIndex < rowRepeater.count-1) ? units.gu(2) : 0) <
126 root.overflowWidth - ((__ownIndex < rowRepeater.count-1) ? overflowButton.width : 0)
128 implicitWidth: column.implicitWidth
129 implicitHeight: row.height
130 enabled: (model.sensitive === true) && shouldDisplay
131 opacity: shouldDisplay ? 1 : 0
135 __popup = menuComponent.createObject(root,
137 objectName: visualItem.objectName + "-menu",
138 desiredX: Qt.binding(function() { return visualItem.x - units.gu(1); }),
139 desiredY: Qt.binding(function() { return root.height; }),
140 unityMenuModel: Qt.binding(function() { return root.unityMenuModel.submenu(visualItem.__ownIndex); })
143 __popup.childActivated.connect(dismiss);
144 // force the current item to be the newly popped up menu
148 d.currentItem = visualItem;
154 if (d.currentItem === visualItem) {
155 d.currentItem = null;
164 if (d.currentItem === visualItem) {
165 d.currentItem = null;
171 if (!visible && __popup) dismiss();
174 onShouldDisplayChanged: {
175 if ((!shouldDisplay && d.firstInvisibleIndex == undefined) || __ownIndex <= d.firstInvisibleIndex) {
176 d.recalcFirstInvisibleIndex();
182 onDismissAll: visualItem.dismiss()
193 Layout.preferredWidth: units.gu(2)
194 Layout.preferredHeight: units.gu(2)
195 Layout.alignment: Qt.AlignVCenter
197 visible: model.icon || false
198 source: model.icon || ""
204 height: _title.height
207 enabled: visualItem.enabled
208 // FIXME - SDK Action:text modifies menu text with html underline for mnemonic
209 text: model.label.replace("_", "&").replace("<u>", "&").replace("</u>", "")
218 text: actionItem.text
219 horizontalAlignment: Text.AlignLeft
220 color: enabled ? theme.palette.normal.backgroundText : theme.palette.disabled.backgroundText
224 } // Item ( delegate )
230 hoverEnabled: d.currentItem
232 property bool moved: false
236 updateCurrentItemFromPosition(Qt.point(mouseX, mouseY))
242 var prevItem = d.currentItem;
243 updateCurrentItemFromPosition(Qt.point(mouseX, mouseY));
244 if (prevItem && d.currentItem == prevItem) {
251 // for the MoveHandler
252 onPressed: root.pressed(mouse)
253 onPressedChanged: root.pressedChangedEx(pressed, pressedButtons, mouseX, mouseY)
254 onReleased: root.released(mouse)
255 onDoubleClicked: root.doubleClicked(mouse)
257 Mouse.ignoreSynthesizedEvents: true
258 Mouse.onPositionChanged: {
259 root.positionChanged(mouse);
260 moved = root.windowMoving;
262 updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y))
266 function updateCurrentItemFromPosition(point) {
267 var pos = mapToItem(row, point.x, point.y);
269 if (!d.hoveredItem || !d.currentItem || !d.hoveredItem.contains(Qt.point(pos.x - d.currentItem.x, pos.y - d.currentItem.y))) {
270 d.hoveredItem = row.childAt(pos.x, pos.y);
271 if (!d.hoveredItem || !d.hoveredItem.enabled)
273 if (d.currentItem != d.hoveredItem) {
274 d.currentItem = d.hoveredItem;
282 objectName: "overflow"
284 hoverEnabled: d.currentItem
285 onEntered: d.currentItem = this
286 onPositionChanged: d.currentItem = this
287 onPressed: d.currentItem = this
289 property Item __popup: null;
290 readonly property bool popupVisible: __popup && __popup.visible
291 readonly property Item firstInvisibleItem: d.firstInvisibleIndex !== undefined ? rowRepeater.itemAt(d.firstInvisibleIndex) : null
293 visible: d.firstInvisibleIndex != undefined
294 x: firstInvisibleItem ? firstInvisibleItem.x : 0
296 height: parent.height
300 if (!visible && __popup) dismiss();
307 anchors.centerIn: parent
308 color: theme.palette.normal.backgroundText
309 name: "toolkit_chevron-down_2gu"
314 __popup = overflowComponent.createObject(root, { objectName: overflowButton.objectName + "-menu" });
315 __popup.childActivated.connect(dismiss);
316 // force the current item to be the newly popped up menu
320 d.currentItem = overflowButton;
326 if (d.currentItem === overflowButton) {
327 d.currentItem = null;
336 if (d.currentItem === overflowButton) {
337 d.currentItem = null;
344 onDismissAll: overflowButton.dismiss()
348 id: overflowComponent
351 desiredX: overflowButton.x - units.gu(1)
352 desiredY: parent.height
353 unityMenuModel: overflowModel
355 ExpressionFilterModel {
357 sourceModel: root.unityMenuModel
358 matchExpression: function(index) {
359 if (d.firstInvisibleIndex === undefined) return false;
360 return index >= d.firstInvisibleIndex;
363 function submenu(index) {
364 return sourceModel.submenu(mapRowToSource(index));
366 function activate(index) {
367 return sourceModel.activate(mapRowToSource(index));
373 onFirstInvisibleIndexChanged: overflowModel.invalidate()
384 x: d.currentItem ? row.x + d.currentItem.x - units.gu(1) : 0
385 width: d.currentItem ? d.currentItem.width + units.gu(2) : 0
387 color: UbuntuColors.orange
388 visible: d.currentItem
394 itemView: rowRepeater
395 hasOverflow: overflowButton.visible
397 property Item currentItem: null
398 property Item hoveredItem: null
399 property Item prevCurrentItem: null
400 property bool altPressed: false
401 property bool longAltPressed: false
402 property var firstInvisibleIndex: undefined
404 readonly property int currentIndex: currentItem && currentItem.hasOwnProperty("__ownIndex") ? currentItem.__ownIndex : -1
408 function recalcFirstInvisibleIndexAdded(index, item) {
409 if (firstInvisibleIndex === undefined) {
410 if (!item.shouldDisplay) {
411 firstInvisibleIndex = index;
413 } else if (index <= firstInvisibleIndex) {
414 if (!item.shouldDisplay) {
415 firstInvisibleIndex = index;
417 firstInvisibleIndex++;
422 function recalcFirstInvisibleIndex() {
423 for (var i = 0; i < rowRepeater.count; i++) {
424 if (!rowRepeater.itemAt(i).shouldDisplay) {
425 firstInvisibleIndex = i;
429 firstInvisibleIndex = undefined;
433 var delegate = rowRepeater.itemAt(index);
435 d.currentItem = delegate;
440 d.currentItem = overflowButton;
443 onCurrentItemChanged: {
444 if (prevCurrentItem && prevCurrentItem != currentItem) {
446 prevCurrentItem.hide();
448 prevCurrentItem.dismiss();
452 if (currentItem) currentItem.show();
453 prevCurrentItem = currentItem;
456 function startShortcutTimer() {
458 menuBarShortcutTimer.start();
461 function stopSHortcutTimer() {
462 menuBarShortcutTimer.stop();
463 d.altPressed = false;
464 d.longAltPressed = false;
469 id: menuBarShortcutTimer
473 d.longAltPressed = true;
477 Keys.onEscapePressed: {
479 event.accepted = true;
482 Keys.onLeftPressed: {
484 d.selectPrevious(d.currentIndex);
488 Keys.onRightPressed: {
490 d.selectNext(d.currentIndex);
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list