on e1659ca help sign in
304 lines 9.4 kB history raw
1/*
2 * Copyright 2016 Canonical Ltd.
3 *
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.
7 *
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.
12 *
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/>.
15 */
17import QtQuick 2.4
18import QtQuick.Layouts 1.1
19import Utils 0.1
20import Ubuntu.Components 1.3
21import GlobalShortcut 1.0
23Item {
24 id: root
25 objectName: "menuBar"
27 property alias unityMenuModel: rowRepeater.model
29 readonly property bool valid: rowRepeater.count > 0
31 property bool enableKeyFilter: false
33 readonly property bool showRequested: d.longAltPressed || d.currentItem != null
35 implicitWidth: row.width
36 height: parent.height
38 function select(index) {
39 d.select(index);
40 }
42 function dismiss() {
43 d.dismissAll();
44 }
46 GlobalShortcut {
47 shortcut: Qt.Key_Alt|Qt.AltModifier
48 active: enableKeyFilter
49 onTriggered: d.startShortcutTimer()
50 onReleased: d.stopSHortcutTimer()
51 }
52 // On an actual keyboard, the AltModifier is not supplied on release.
53 GlobalShortcut {
54 shortcut: Qt.Key_Alt
55 active: enableKeyFilter
56 onTriggered: d.startShortcutTimer()
57 onReleased: d.stopSHortcutTimer()
58 }
60 InverseMouseArea {
61 acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton
62 anchors.fill: parent
63 enabled: d.currentItem != null
64 onPressed: d.dismissAll()
65 }
67 Item {
68 id: clippingItem
70 height: root.height
71 width: root.width
72 clip: true
74 Row {
75 id: row
76 spacing: units.gu(2)
77 height: parent.height
79 ActionContext {
80 id: menuBarContext
81 objectName: "barContext"
82 active: !d.currentItem && enableKeyFilter
83 }
85 Repeater {
86 id: rowRepeater
88 Item {
89 id: visualItem
90 objectName: root.objectName + "-item" + __ownIndex
92 readonly property int __ownIndex: index
93 property Item __popup: null;
94 property bool popupVisible: __popup && __popup.visible
96 implicitWidth: column.implicitWidth
97 implicitHeight: row.height
98 enabled: model.sensitive
100 function show() {
101 if (!__popup) {
102 __popup = menuComponent.createObject(root, { objectName: visualItem.objectName + "-menu" });
103 // force the current item to be the newly popped up menu
104 } else {
105 __popup.show();
106 }
107 d.currentItem = visualItem;
108 }
109 function hide() {
110 if (__popup) {
111 __popup.hide();
113 if (d.currentItem === visualItem) {
114 d.currentItem = null;
115 }
116 }
117 }
118 function dismiss() {
119 if (__popup) {
120 __popup.destroy();
121 __popup = null;
123 if (d.currentItem === visualItem) {
124 d.currentItem = null;
125 }
126 }
127 }
129 Connections {
130 target: d
131 onDismissAll: visualItem.dismiss()
132 }
134 Component {
135 id: menuComponent
136 MenuPopup {
137 x: visualItem.x - units.gu(1)
138 anchors.top: parent.bottom
139 unityMenuModel: root.unityMenuModel.submenu(visualItem.__ownIndex)
141 Component.onCompleted: reset();
142 }
143 }
145 RowLayout {
146 id: column
147 spacing: units.gu(1)
148 anchors {
149 centerIn: parent
150 }
152 Icon {
153 Layout.preferredWidth: units.gu(2)
154 Layout.preferredHeight: units.gu(2)
155 Layout.alignment: Qt.AlignVCenter
157 visible: model.icon || false
158 source: model.icon || ""
159 }
161 ActionItem {
162 id: actionItem
163 width: _title.width
164 height: _title.height
166 action: Action {
167 // FIXME - SDK Action:text modifies menu text with html underline for mnemonic
168 text: model.label.replace("_", "&").replace("<u>", "&").replace("</u>", "")
170 onTriggered: {
171 visualItem.show();
172 }
173 }
175 Label {
176 id: _title
177 text: actionItem.text
178 horizontalAlignment: Text.AlignLeft
179 color: enabled ? "white" : "#5d5d5d"
180 }
181 }
182 }
183 } // Item ( delegate )
184 } // Repeater
185 } // Row
187 MouseArea {
188 anchors.fill: row
189 hoverEnabled: d.currentItem
191 onEntered: {
192 if (d.currentItem) {
193 updateCurrentItemFromPosition(Qt.point(mouseX, mouseY))
194 }
195 }
196 onPositionChanged: {
197 if (d.currentItem) {
198 updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y))
199 }
200 }
201 onClicked: updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y))
203 function updateCurrentItemFromPosition(point) {
204 var pos = mapToItem(row, point.x, point.y);
206 if (!d.hoveredItem || !d.currentItem || !d.hoveredItem.contains(Qt.point(pos.x - d.currentItem.x, pos.y - d.currentItem.y))) {
207 d.hoveredItem = row.childAt(pos.x, pos.y);
208 if (!d.hoveredItem || !d.hoveredItem.enabled)
209 return false;
210 if (d.currentItem != d.hoveredItem) {
211 d.currentItem = d.hoveredItem;
212 }
213 }
214 return true;
215 }
216 }
218 Rectangle {
219 id: underline
220 anchors {
221 bottom: row.bottom
222 }
223 x: d.currentItem ? row.x + d.currentItem.x - units.gu(1) : 0
224 width: d.currentItem ? d.currentItem.width + units.gu(2) : 0
225 height: units.dp(4)
226 color: UbuntuColors.orange
227 visible: d.currentItem
228 }
229 }
231 MenuNavigator {
232 id: d
233 objectName: "d"
234 itemView: rowRepeater
236 property Item currentItem: null
237 property Item hoveredItem: null
238 property Item prevCurrentItem: null
240 readonly property int currentIndex: currentItem ? currentItem.__ownIndex : -1
242 property bool altPressed: false
243 property bool longAltPressed: false
245 signal dismissAll()
247 onSelect: {
248 var delegate = rowRepeater.itemAt(index);
249 if (delegate) {
250 d.currentItem = delegate;
251 }
252 }
254 onCurrentItemChanged: {
255 if (prevCurrentItem && prevCurrentItem != currentItem) {
256 if (currentItem) {
257 prevCurrentItem.hide();
258 } else {
259 prevCurrentItem.dismiss();
260 }
261 }
263 if (currentItem) currentItem.show();
264 prevCurrentItem = currentItem;
265 }
267 function startShortcutTimer() {
268 d.altPressed = true;
269 menuBarShortcutTimer.start();
270 }
272 function stopSHortcutTimer() {
273 menuBarShortcutTimer.stop();
274 d.altPressed = false;
275 d.longAltPressed = false;
276 }
277 }
279 Timer {
280 id: menuBarShortcutTimer
281 interval: 200
282 repeat: false
283 onTriggered: {
284 d.longAltPressed = true;
285 }
286 }
288 Keys.onEscapePressed: {
289 d.dismissAll();
290 event.accepted = true;
291 }
293 Keys.onLeftPressed: {
294 if (d.currentItem) {
295 d.selectPrevious(d.currentIndex);
296 }
297 }
299 Keys.onRightPressed: {
300 if (d.currentItem) {
301 d.selectNext(d.currentIndex);
302 }
303 }