2 * Copyright (C) 2016 Canonical, Ltd.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU 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 General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 import Ubuntu.Components 1.3
19 import Unity.Launcher 0.1
21 import "../Components"
22 import Qt.labs.settings 1.0
28 property int panelWidth: 0
29 readonly property bool moving: listLoader.item && listLoader.item.moving
30 readonly property Item searchTextField: searchField
31 readonly property real delegateWidth: units.gu(10)
33 signal applicationSelected(string appId)
35 property bool draggingHorizontally: false
36 property int dragDistance: 0
40 searchField.selectAll();
44 function focusInput() {
45 searchField.selectAll();
46 searchField.focus = true;
50 if (event.text.trim() !== "") {
52 searchField.text = event.text;
54 // Catch all presses here in case the navigation lets something through
55 // We never want to end up in the launcher with focus
56 event.accepted = true;
60 property alias selectedTab: sections.selectedIndex
66 acceptedButtons: Qt.AllButtons
67 onWheel: wheel.accepted = true
80 source: appDrawerModel
81 filterString: searchField.displayText
82 sortBy: AppDrawerProxyModel.SortByAToZ
88 anchors.leftMargin: root.panelWidth
92 objectName: "searchField"
93 anchors { left: parent.left; top: parent.top; right: parent.right; margins: units.gu(1) }
94 placeholderText: i18n.tr("Search…")
97 KeyNavigation.down: sections
100 if (searchField.displayText != "" && listLoader.item) {
101 // In case there is no currentItem (it might have been filtered away) lets reset it to the first item
102 if (!listLoader.item.currentItem) {
103 listLoader.item.currentIndex = 0;
105 root.applicationSelected(listLoader.item.getFirstAppId());
111 id: sectionsContainer
112 anchors { left: parent.left; top: searchField.bottom; right: parent.right; }
113 height: sections.height
119 objectName: "drawerSections"
122 KeyNavigation.up: searchField
123 KeyNavigation.down: headerFocusScope
124 KeyNavigation.backtab: searchField
125 KeyNavigation.tab: headerFocusScope
129 text: i18n.ctr("Apps sorted alphabetically", "A-Z")
130 // TODO: Disabling this for now as we don't get the right input from u-a-l yet.
133 // text: i18n.ctr("Most used apps", "Most used")
138 anchors.bottom: parent.bottom
141 width: contentContainer.width
148 objectName: "headerFocusScope"
149 KeyNavigation.up: sections
150 KeyNavigation.down: listLoader.item
151 KeyNavigation.backtab: sections
152 KeyNavigation.tab: listLoader.item
153 activeFocusOnTab: true
157 schema.id: "com.canonical.Unity8"
166 event.accepted = true;
171 Qt.openUrlExternally(settings.appstoreUri)
177 objectName: "drawerListLoader"
178 anchors { left: parent.left; top: sectionsContainer.bottom; right: parent.right; bottom: parent.bottom }
180 KeyNavigation.up: headerFocusScope
181 KeyNavigation.down: searchField
182 KeyNavigation.backtab: headerFocusScope
183 KeyNavigation.tab: searchField
186 switch (sections.selectedIndex) {
187 case 0: return aToZComponent;
188 case 1: return mostUsedComponent;
192 target: listLoader.item || null
193 property: "objectName"
194 value: "drawerItemList"
199 parent: listLoader.item ? listLoader.item : null
201 propagateComposedEvents: true
207 var diff = oldX - mouseX;
208 root.draggingHorizontally |= diff > units.gu(2);
209 if (!root.draggingHorizontally) {
212 propagateComposedEvents = false;
213 parent.interactive = false;
214 root.dragDistance += diff;
220 if (root.draggingHorizontally) {
221 root.draggingHorizontally = false;
222 parent.interactive = true;
224 reactivateTimer.start();
230 onTriggered: parent.propagateComposedEvents = true;
235 id: mostUsedComponent
239 header: MoreAppsHeader {
242 highlighted: headerFocusScope.activeFocus
243 onClicked: headerFocusScope.trigger();
246 model: AppDrawerProxyModel {
247 source: sortProxyModel
248 group: AppDrawerProxyModel.GroupByAll
249 sortBy: AppDrawerProxyModel.SortByUsage
250 dynamicSortFilter: false
253 delegate: UbuntuShape {
254 width: parent.width - units.gu(2)
255 anchors.horizontalCenter: parent.horizontalCenter
257 aspect: UbuntuShape.Flat
258 // NOTE: Cannot use gridView.rows here as it would evaluate to 0 at first and only update later,
259 // which messes up the ListView.
260 height: (Math.ceil(mostUsedGridView.model.count / mostUsedGridView.columns) * mostUsedGridView.delegateHeight) + units.gu(2)
262 readonly property string appId: model.appId
267 topMargin: units.gu(1)
268 bottomMargin: units.gu(1)
272 focus: index == mostUsedListView.currentIndex
274 model: sortProxyModel
276 delegateWidth: root.delegateWidth
277 delegateHeight: units.gu(10)
278 delegate: drawerDelegateComponent
289 header: MoreAppsHeader {
292 highlighted: headerFocusScope.activeFocus
293 onClicked: headerFocusScope.trigger();
296 model: AppDrawerProxyModel {
297 source: sortProxyModel
298 sortBy: AppDrawerProxyModel.SortByAToZ
299 group: AppDrawerProxyModel.GroupByAToZ
300 dynamicSortFilter: false
303 delegate: UbuntuShape {
304 width: parent.width - units.gu(2)
305 anchors.horizontalCenter: parent.horizontalCenter
307 aspect: UbuntuShape.Flat
309 readonly property string appId: model.appId
311 // NOTE: Cannot use gridView.rows here as it would evaluate to 0 at first and only update later,
312 // which messes up the ListView.
313 height: (Math.ceil(gridView.model.count / gridView.columns) * gridView.delegateHeight) +
314 categoryNameLabel.implicitHeight + units.gu(2)
317 id: categoryNameLabel
318 anchors { left: parent.left; top: parent.top; right: parent.right; margins: units.gu(1) }
324 anchors { left: parent.left; top: categoryNameLabel.bottom; right: parent.right; topMargin: units.gu(1) }
325 height: rows * delegateHeight
328 focus: index == aToZListView.currentIndex
330 model: AppDrawerProxyModel {
332 source: sortProxyModel
333 filterLetter: model.letter
334 dynamicSortFilter: false
336 delegateWidth: root.delegateWidth
337 delegateHeight: units.gu(10)
338 delegate: drawerDelegateComponent
346 id: drawerDelegateComponent
349 width: GridView.view.cellWidth
351 objectName: "drawerItem_" + model.appId
353 readonly property bool focused: index === GridView.view.currentIndex && GridView.view.activeFocus
355 onClicked: root.applicationSelected(model.appId)
356 z: loader.active ? 1 : 0
360 anchors.horizontalCenter: parent.horizontalCenter
361 height: childrenRect.height
367 height: 7.5 / 8 * width
368 anchors.horizontalCenter: parent.horizontalCenter
370 borderSource: 'undefined'
373 sourceSize.width: appIcon.width
376 sourceFillMode: UbuntuShape.PreserveAspectCrop
379 styleName: "FocusShape"
382 visible: drawerDelegate.focused
383 radius: units.gu(2.55)
392 horizontalAlignment: Text.AlignHCenter
394 elide: Text.ElideRight
401 aux = label.width / 2 - item.width / 2;
402 var containerXMap = mapToItem(contentContainer, aux, 0).x
403 if (containerXMap < 0) {
404 aux = aux - containerXMap;
407 if (containerXMap + item.width > contentContainer.width) {
408 aux = aux - (containerXMap + item.width - contentContainer.width);
414 active: label.truncated && (drawerDelegate.hovered || drawerDelegate.focused)
415 sourceComponent: Rectangle {
416 color: UbuntuColors.jet
417 width: fullLabel.contentWidth + units.gu(1)
418 height: fullLabel.height + units.gu(1)
422 width: Math.min(root.delegateWidth * 2, implicitWidth)
424 horizontalAlignment: Text.AlignHCenter
426 elide: Text.ElideRight
427 anchors.centerIn: parent