on 19a9ecd help sign in
318 lines 9.8 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 GlobalShortcut {
61 shortcut: Qt.AltModifier | Qt.Key_F10
62 active: enableKeyFilter && d.currentItem == null
63 onTriggered: {
64 for (var i = 0; i < rowRepeater.count; i++) {
65 var item = rowRepeater.itemAt(i);
66 if (item.enabled) {
67 item.show();
68 break;
69 }
70 }
71 }
72 }
74 InverseMouseArea {
75 acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton
76 anchors.fill: parent
77 enabled: d.currentItem != null
78 onPressed: d.dismissAll()
79 }
81 Item {
82 id: clippingItem
84 height: root.height
85 width: root.width
86 clip: true
88 Row {
89 id: row
90 spacing: units.gu(2)
91 height: parent.height
93 ActionContext {
94 id: menuBarContext
95 objectName: "barContext"
96 active: !d.currentItem && enableKeyFilter
97 }
99 Repeater {
100 id: rowRepeater
102 Item {
103 id: visualItem
104 objectName: root.objectName + "-item" + __ownIndex
106 readonly property int __ownIndex: index
107 property Item __popup: null;
108 property bool popupVisible: __popup && __popup.visible
110 implicitWidth: column.implicitWidth
111 implicitHeight: row.height
112 enabled: model.sensitive
114 function show() {
115 if (!__popup) {
116 __popup = menuComponent.createObject(root, { objectName: visualItem.objectName + "-menu" });
117 // force the current item to be the newly popped up menu
118 } else {
119 __popup.show();
120 }
121 d.currentItem = visualItem;
122 }
123 function hide() {
124 if (__popup) {
125 __popup.hide();
127 if (d.currentItem === visualItem) {
128 d.currentItem = null;
129 }
130 }
131 }
132 function dismiss() {
133 if (__popup) {
134 __popup.destroy();
135 __popup = null;
137 if (d.currentItem === visualItem) {
138 d.currentItem = null;
139 }
140 }
141 }
143 Connections {
144 target: d
145 onDismissAll: visualItem.dismiss()
146 }
148 Component {
149 id: menuComponent
150 MenuPopup {
151 x: visualItem.x - units.gu(1)
152 anchors.top: parent.bottom
153 unityMenuModel: root.unityMenuModel.submenu(visualItem.__ownIndex)
155 Component.onCompleted: reset();
156 }
157 }
159 RowLayout {
160 id: column
161 spacing: units.gu(1)
162 anchors {
163 centerIn: parent
164 }
166 Icon {
167 Layout.preferredWidth: units.gu(2)
168 Layout.preferredHeight: units.gu(2)
169 Layout.alignment: Qt.AlignVCenter
171 visible: model.icon || false
172 source: model.icon || ""
173 }
175 ActionItem {
176 id: actionItem
177 width: _title.width
178 height: _title.height
180 action: Action {
181 // FIXME - SDK Action:text modifies menu text with html underline for mnemonic
182 text: model.label.replace("_", "&").replace("<u>", "&").replace("</u>", "")
184 onTriggered: {
185 visualItem.show();
186 }
187 }
189 Label {
190 id: _title
191 text: actionItem.text
192 horizontalAlignment: Text.AlignLeft
193 color: enabled ? "white" : "#5d5d5d"
194 }
195 }
196 }
197 } // Item ( delegate )
198 } // Repeater
199 } // Row
201 MouseArea {
202 anchors.fill: parent
203 hoverEnabled: d.currentItem
205 onEntered: {
206 if (d.currentItem) {
207 updateCurrentItemFromPosition(Qt.point(mouseX, mouseY))
208 }
209 }
210 onPositionChanged: {
211 if (d.currentItem) {
212 updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y))
213 }
214 }
215 onClicked: updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y))
217 function updateCurrentItemFromPosition(point) {
218 var pos = mapToItem(row, point.x, point.y);
220 if (!d.hoveredItem || !d.currentItem || !d.hoveredItem.contains(Qt.point(pos.x - d.currentItem.x, pos.y - d.currentItem.y))) {
221 d.hoveredItem = row.childAt(pos.x, pos.y);
222 if (!d.hoveredItem || !d.hoveredItem.enabled)
223 return false;
224 if (d.currentItem != d.hoveredItem) {
225 d.currentItem = d.hoveredItem;
226 }
227 }
228 return true;
229 }
230 }
232 Rectangle {
233 id: underline
234 anchors {
235 bottom: row.bottom
236 }
237 x: d.currentItem ? row.x + d.currentItem.x - units.gu(1) : 0
238 width: d.currentItem ? d.currentItem.width + units.gu(2) : 0
239 height: units.dp(4)
240 color: UbuntuColors.orange
241 visible: d.currentItem
242 }
243 }
245 MenuNavigator {
246 id: d
247 objectName: "d"
248 itemView: rowRepeater
250 property Item currentItem: null
251 property Item hoveredItem: null
252 property Item prevCurrentItem: null
254 readonly property int currentIndex: currentItem ? currentItem.__ownIndex : -1
256 property bool altPressed: false
257 property bool longAltPressed: false
259 signal dismissAll()
261 onSelect: {
262 var delegate = rowRepeater.itemAt(index);
263 if (delegate) {
264 d.currentItem = delegate;
265 }
266 }
268 onCurrentItemChanged: {
269 if (prevCurrentItem && prevCurrentItem != currentItem) {
270 if (currentItem) {
271 prevCurrentItem.hide();
272 } else {
273 prevCurrentItem.dismiss();
274 }
275 }
277 if (currentItem) currentItem.show();
278 prevCurrentItem = currentItem;
279 }
281 function startShortcutTimer() {
282 d.altPressed = true;
283 menuBarShortcutTimer.start();
284 }
286 function stopSHortcutTimer() {
287 menuBarShortcutTimer.stop();
288 d.altPressed = false;
289 d.longAltPressed = false;
290 }
291 }
293 Timer {
294 id: menuBarShortcutTimer
295 interval: 200
296 repeat: false
297 onTriggered: {
298 d.longAltPressed = true;
299 }
300 }
302 Keys.onEscapePressed: {
303 d.dismissAll();
304 event.accepted = true;
305 }
307 Keys.onLeftPressed: {
308 if (d.currentItem) {
309 d.selectPrevious(d.currentIndex);
310 }
311 }
313 Keys.onRightPressed: {
314 if (d.currentItem) {
315 d.selectNext(d.currentIndex);
316 }
317 }