Unity 8
Drawer.qml
1 /*
2  * Copyright (C) 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 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 General Public License for more details.
12  *
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/>.
15  */
16 
17 import QtQuick 2.4
18 import Ubuntu.Components 1.3
19 import Unity.Launcher 0.1
20 import Utils 0.1
21 import "../Components"
22 import Qt.labs.settings 1.0
23 
24 FocusScope {
25  id: root
26 
27  property int panelWidth: 0
28  readonly property bool moving: listLoader.item && listLoader.item.moving
29 
30  signal applicationSelected(string appId)
31 
32  property bool draggingHorizontally: false
33  property int dragDistance: 0
34 
35  onFocusChanged: {
36  if (focus) {
37  searchField.selectAll();
38  }
39  }
40 
41  function focusInput() {
42  searchField.selectAll();
43  searchField.focus = true;
44  }
45 
46  Settings {
47  property alias selectedTab: sections.selectedIndex
48  }
49 
50  MouseArea {
51  anchors.fill: parent
52  hoverEnabled: true
53  acceptedButtons: Qt.AllButtons
54  onWheel: wheel.accepted = true
55  }
56 
57  Rectangle {
58  anchors.fill: parent
59  color: "#BF000000"
60 
61  AppDrawerModel {
62  id: appDrawerModel
63  }
64 
65  AppDrawerProxyModel {
66  id: sortProxyModel
67  source: appDrawerModel
68  filterString: searchField.displayText
69  sortBy: AppDrawerProxyModel.SortByAToZ
70  }
71 
72  Item {
73  id: contentContainer
74  anchors.fill: parent
75  anchors.leftMargin: root.panelWidth
76 
77  TextField {
78  id: searchField
79  anchors { left: parent.left; top: parent.top; right: parent.right; margins: units.gu(1) }
80  placeholderText: i18n.tr("Search…")
81  focus: true
82  onAccepted: {
83  if (searchField.displayText != "" && listLoader.item && listLoader.item.currentItem) {
84  root.applicationSelected(listLoader.item.getFirstAppId());
85  }
86  }
87  }
88 
89  Item {
90  id: sectionsContainer
91  anchors { left: parent.left; top: searchField.bottom; right: parent.right; }
92  height: sections.height
93  clip: true
94  z: 2
95 
96  Sections {
97  id: sections
98  width: parent.width
99  actions: [
100  Action {
101  text: i18n.ctr("Apps sorted alphabetically", "A-Z")
102  // TODO: Disabling this for now as we don't get the right input from u-a-l yet.
103 // },
104 // Action {
105 // text: i18n.ctr("Most used apps", "Most used")
106  }
107  ]
108 
109  Rectangle {
110  anchors.bottom: parent.bottom
111  height: units.dp(1)
112  color: 'gray'
113  width: contentContainer.width
114  }
115  }
116  }
117 
118  Loader {
119  id: listLoader
120  anchors { left: parent.left; top: sectionsContainer.bottom; right: parent.right; bottom: parent.bottom; leftMargin: units.gu(1); rightMargin: units.gu(1) }
121  sourceComponent: {
122  switch (sections.selectedIndex) {
123  case 0: return aToZComponent;
124  case 1: return mostUsedComponent;
125  }
126  }
127  Binding {
128  target: listLoader.item || null
129  property: "objectName"
130  value: "drawerItemList"
131  }
132  }
133 
134  MouseArea {
135  parent: listLoader.item ? listLoader.item : null
136  anchors.fill: parent
137  propagateComposedEvents: true
138  property int oldX: 0
139  onPressed: {
140  oldX = mouseX;
141  }
142  onMouseXChanged: {
143  var diff = oldX - mouseX;
144  root.draggingHorizontally |= diff > units.gu(2);
145  if (!root.draggingHorizontally) {
146  return;
147  }
148  propagateComposedEvents = false;
149  parent.interactive = false;
150  root.dragDistance += diff;
151  oldX = mouseX
152  }
153  onReleased: {
154  if (root.draggingHorizontally) {
155  root.draggingHorizontally = false;
156  parent.interactive = true;
157  }
158  reactivateTimer.start();
159  }
160  Timer {
161  id: reactivateTimer
162  interval: 0
163  onTriggered: parent.propagateComposedEvents = true;
164  }
165  }
166 
167  Component {
168  id: mostUsedComponent
169  DrawerListView {
170 
171  header: MoreAppsHeader {
172  width: parent.width
173  height: units.gu(6)
174  }
175 
176  model: AppDrawerProxyModel {
177  source: sortProxyModel
178  group: AppDrawerProxyModel.GroupByAll
179  sortBy: AppDrawerProxyModel.SortByUsage
180  }
181 
182  delegate: UbuntuShape {
183  width: parent.width
184  color: "#20ffffff"
185  aspect: UbuntuShape.Flat
186  // NOTE: Cannot use gridView.rows here as it would evaluate to 0 at first and only update later,
187  // which messes up the ListView.
188  height: (Math.ceil(mostUsedGridView.model.count / mostUsedGridView.columns) * mostUsedGridView.delegateHeight) + units.gu(2)
189 
190  readonly property string appId: model.appId
191 
192  DrawerGridView {
193  id: mostUsedGridView
194  anchors.fill: parent
195  topMargin: units.gu(1)
196  bottomMargin: units.gu(1)
197  clip: true
198 
199  model: sortProxyModel
200 
201  delegateWidth: units.gu(8)
202  delegateHeight: units.gu(10)
203  delegate: drawerDelegateComponent
204  }
205  }
206  }
207  }
208 
209  Component {
210  id: aToZComponent
211  DrawerListView {
212 
213  header: MoreAppsHeader {
214  width: parent.width
215  height: units.gu(6)
216  }
217 
218  model: AppDrawerProxyModel {
219  source: sortProxyModel
220  sortBy: AppDrawerProxyModel.SortByAToZ
221  group: AppDrawerProxyModel.GroupByAToZ
222  }
223 
224  delegate: UbuntuShape {
225  width: parent.width
226  color: "#20ffffff"
227  aspect: UbuntuShape.Flat
228 
229  readonly property string appId: model.appId
230 
231  // NOTE: Cannot use gridView.rows here as it would evaluate to 0 at first and only update later,
232  // which messes up the ListView.
233  height: (Math.ceil(gridView.model.count / gridView.columns) * gridView.delegateHeight) +
234  categoryNameLabel.implicitHeight + units.gu(2)
235 
236  Label {
237  id: categoryNameLabel
238  anchors { left: parent.left; top: parent.top; right: parent.right; margins: units.gu(1) }
239  text: model.letter
240  }
241 
242  DrawerGridView {
243  id: gridView
244  anchors { left: parent.left; top: categoryNameLabel.bottom; right: parent.right; topMargin: units.gu(1) }
245  height: rows * delegateHeight
246 
247  interactive: false
248 
249  model: AppDrawerProxyModel {
250  id: categoryModel
251  source: sortProxyModel
252  filterLetter: model.letter
253  }
254  delegateWidth: units.gu(8)
255  delegateHeight: units.gu(10)
256  delegate: drawerDelegateComponent
257  }
258  }
259  }
260  }
261  }
262 
263  Component {
264  id: drawerDelegateComponent
265  AbstractButton {
266  width: GridView.view.cellWidth
267  height: units.gu(10)
268  objectName: "drawerItem_" + model.appId
269 
270  onClicked: root.applicationSelected(model.appId)
271 
272  Column {
273  width: units.gu(8)
274  anchors.horizontalCenter: parent.horizontalCenter
275  height: childrenRect.height
276  spacing: units.gu(1)
277 
278  UbuntuShape {
279  id: appIcon
280  width: units.gu(6)
281  height: 7.5 / 8 * width
282  anchors.horizontalCenter: parent.horizontalCenter
283  backgroundMode: UbuntuShape.SolidColor
284  backgroundColor: UbuntuColors.lightGrey
285  radius: "medium"
286  borderSource: 'undefined'
287  source: Image {
288  id: sourceImage
289  sourceSize.width: appIcon.width
290  source: model.icon
291  }
292  sourceFillMode: UbuntuShape.PreserveAspectCrop
293  }
294 
295  Label {
296  text: model.name
297  width: parent.width
298  horizontalAlignment: Text.AlignHCenter
299  fontSize: "small"
300  elide: Text.ElideRight
301  }
302  }
303  }
304  }
305  }
306 }