on xenial help sign in
fix keyboard focus handling
2b4c1c5Michael Zanetti
qml/Launcher/Drawer.qml+162−157
@@ -4,208 +4,213 @@ import Unity.Launcher 0.1
4import Utils 0.1
4import Utils 0.1
5import "../Components"
5import "../Components"
6
6
7−Rectangle {
7+FocusScope {
8 id: root
8 id: root
9− color: "#BF000000"
10
9
11 property int panelWidth: 0
10 property int panelWidth: 0
12 readonly property bool moving: listLoader.item && listLoader.item.moving
11 readonly property bool moving: listLoader.item && listLoader.item.moving
13
12
14 signal applicationSelected(string appId)
13 signal applicationSelected(string appId)
15
14
16− AppDrawerModel {
15+ Rectangle {
17− id: appDrawerModel
18− }
19−
20− AppDrawerProxyModel {
21− id: sortProxyModel
22− source: appDrawerModel
23− filterString: searchField.text
24− }
25−
26− Item {
27− id: contentContainer
28 anchors.fill: parent
16 anchors.fill: parent
29− anchors.leftMargin: root.panelWidth
17+ color: "#BF000000"
30
18
31− TextField {
19+ AppDrawerModel {
32− id: searchField
20+ id: appDrawerModel
33− anchors { left: parent.left; top: parent.top; right: parent.right; margins: units.gu(1) }
34− placeholderText: i18n.tr("Search...")
35 }
21 }
36
22
37− Item {
23+ AppDrawerProxyModel {
38− id: sectionsContainer
24+ id: sortProxyModel
39− anchors { left: parent.left; top: searchField.bottom; right: parent.right; }
25+ source: appDrawerModel
40− height: sections.height
26+ filterString: searchField.text
41− clip: true
42− z: 2
43−
44− Sections {
45− id: sections
46− width: parent.width
47− actions: [
48− Action {
49− text: "A-Z"
50− },
51− Action {
52− text: "Most used"
53− }
54− ]
55−
56− Rectangle {
57− anchors.bottom: parent.bottom
58− height: units.dp(1)
59− color: 'gray'
60− width: contentContainer.width
61− }
62− }
63 }
27 }
64
28
65− Loader {
29+ Item {
66− id: listLoader
30+ id: contentContainer
67− anchors { left: parent.left; top: sectionsContainer.bottom; right: parent.right; bottom: parent.bottom; leftMargin: units.gu(1); rightMargin: units.gu(1) }
31+ anchors.fill: parent
68− sourceComponent: sections.selectedIndex == 0 ? aToZComponent : mostUsedComponent
32+ anchors.leftMargin: root.panelWidth
33+
34+ TextField {
35+ id: searchField
36+ anchors { left: parent.left; top: parent.top; right: parent.right; margins: units.gu(1) }
37+ placeholderText: i18n.tr("Search...")
38+ focus: true
69− }
39+ }
70
40
71− Component {
41+ Item {
72− id: mostUsedComponent
42+ id: sectionsContainer
73− ListView {
43+ anchors { left: parent.left; top: searchField.bottom; right: parent.right; }
74− anchors.fill: parent
44+ height: sections.height
75− topMargin: units.gu(1)
76− bottomMargin: units.gu(1)
77− spacing: units.gu(1)
78 clip: true
45 clip: true
46+ z: 2
79
47
80− header: MoreAppsHeader {
48+ Sections {
49+ id: sections
81 width: parent.width
50 width: parent.width
82− height: units.gu(6)
51+ actions: [
52+ Action {
53+ text: "A-Z"
54+ },
55+ Action {
56+ text: "Most used"
83− }
57+ }
58+ ]
84
59
85− model: AppDrawerProxyModel {
60+ Rectangle {
86− source: sortProxyModel
61+ anchors.bottom: parent.bottom
87− group: AppDrawerProxyModel.GroupByAll
62+ height: units.dp(1)
63+ color: 'gray'
64+ width: contentContainer.width
65+ }
88 }
66 }
67+ }
89
68
90− delegate: UbuntuShape {
69+ Loader {
91− width: parent.width
70+ id: listLoader
92− color: "#20ffffff"
71+ anchors { left: parent.left; top: sectionsContainer.bottom; right: parent.right; bottom: parent.bottom; leftMargin: units.gu(1); rightMargin: units.gu(1) }
93− aspect: UbuntuShape.Flat
72+ sourceComponent: sections.selectedIndex == 0 ? aToZComponent : mostUsedComponent
94− // NOTE: Cannot use gridView.rows here as it would evaluate to 0 at first and only update later,
73+ }
95− // which messes up the ListView.
96− height: (Math.ceil(mostUsedGridView.model.count / mostUsedGridView.columns) * mostUsedGridView.delegateHeight) + units.gu(2)
97
74
98− DrawerGridView {
75+ Component {
99− id: mostUsedGridView
76+ id: mostUsedComponent
77+ ListView {
100− anchors.fill: parent
78+ anchors.fill: parent
101− topMargin: units.gu(1)
79+ topMargin: units.gu(1)
102− bottomMargin: units.gu(1)
80+ bottomMargin: units.gu(1)
81+ spacing: units.gu(1)
103− clip: true
82+ clip: true
83+
84+ header: MoreAppsHeader {
85+ width: parent.width
86+ height: units.gu(6)
87+ }
104
88
89+ model: AppDrawerProxyModel {
105− model: sortProxyModel
90+ source: sortProxyModel
91+ group: AppDrawerProxyModel.GroupByAll
92+ }
106
93
94+ delegate: UbuntuShape {
95+ width: parent.width
96+ color: "#20ffffff"
97+ aspect: UbuntuShape.Flat
98+ // NOTE: Cannot use gridView.rows here as it would evaluate to 0 at first and only update later,
99+ // which messes up the ListView.
100+ height: (Math.ceil(mostUsedGridView.model.count / mostUsedGridView.columns) * mostUsedGridView.delegateHeight) + units.gu(2)
107
101
108− delegateWidth: units.gu(8)
102+ DrawerGridView {
109− delegateHeight: units.gu(10)
103+ id: mostUsedGridView
110− delegate: drawerDelegateComponent
104+ anchors.fill: parent
111− }
105+ topMargin: units.gu(1)
112− }
106+ bottomMargin: units.gu(1)
113− }
107+ clip: true
114− }
115
108
116− Component {
109+ model: sortProxyModel
117− id: aToZComponent
118− ListView {
119− anchors.fill: parent
120− topMargin: units.gu(1)
121− bottomMargin: units.gu(1)
122− spacing: units.gu(1)
123− clip: true
124
110
125− header: MoreAppsHeader {
126− width: parent.width
127− height: units.gu(6)
128− }
129
111
130− model: AppDrawerProxyModel {
112+ delegateWidth: units.gu(8)
131− source: sortProxyModel
113+ delegateHeight: units.gu(10)
132− group: AppDrawerProxyModel.GroupByAToZ
114+ delegate: drawerDelegateComponent
115+ }
116+ }
133 }
117 }
118+ }
134
119
135− delegate: UbuntuShape {
120+ Component {
136− width: parent.width
121+ id: aToZComponent
137− color: "#20ffffff"
138− aspect: UbuntuShape.Flat
139−
140− // NOTE: Cannot use gridView.rows here as it would evaluate to 0 at first and only update later,
141− // which messes up the ListView.
122+ ListView {
142− height: (Math.ceil(gridView.model.count / gridView.columns) * gridView.delegateHeight) +
123+ anchors.fill: parent
143− categoryNameLabel.implicitHeight + units.gu(2)
124+ topMargin: units.gu(1)
125+ bottomMargin: units.gu(1)
126+ spacing: units.gu(1)
127+ clip: true
128+
129+ header: MoreAppsHeader {
130+ width: parent.width
131+ height: units.gu(6)
132+ }
144
133
145− Label {
134+ model: AppDrawerProxyModel {
146− id: categoryNameLabel
135+ source: sortProxyModel
147− anchors { left: parent.left; top: parent.top; right: parent.right; margins: units.gu(1) }
136+ group: AppDrawerProxyModel.GroupByAToZ
148− text: model.letter
149 }
137 }
150
138
151− DrawerGridView {
139+ delegate: UbuntuShape {
152− id: gridView
140+ width: parent.width
153− anchors { left: parent.left; top: categoryNameLabel.bottom; right: parent.right; topMargin: units.gu(1) }
141+ color: "#20ffffff"
142+ aspect: UbuntuShape.Flat
143+
144+ // NOTE: Cannot use gridView.rows here as it would evaluate to 0 at first and only update later,
145+ // which messes up the ListView.
154− height: rows * delegateHeight
146+ height: (Math.ceil(gridView.model.count / gridView.columns) * gridView.delegateHeight) +
147+ categoryNameLabel.implicitHeight + units.gu(2)
155
148
156− interactive: false
149+ Label {
150+ id: categoryNameLabel
151+ anchors { left: parent.left; top: parent.top; right: parent.right; margins: units.gu(1) }
152+ text: model.letter
153+ }
157
154
155+ DrawerGridView {
156+ id: gridView
157+ anchors { left: parent.left; top: categoryNameLabel.bottom; right: parent.right; topMargin: units.gu(1) }
158+ height: rows * delegateHeight
159+
160+ interactive: false
161+
158− model: AppDrawerProxyModel {
162+ model: AppDrawerProxyModel {
159− id: categoryModel
163+ id: categoryModel
160− source: sortProxyModel
164+ source: sortProxyModel
161− filterLetter: model.letter
165+ filterLetter: model.letter
166+ }
167+ delegateWidth: units.gu(8)
168+ delegateHeight: units.gu(10)
169+ delegate: drawerDelegateComponent
162 }
170 }
163− delegateWidth: units.gu(8)
164− delegateHeight: units.gu(10)
165− delegate: drawerDelegateComponent
166 }
171 }
167 }
172 }
168 }
173 }
169 }
174 }
170− }
171−
172− Component {
173− id: drawerDelegateComponent
174− AbstractButton {
175− width: GridView.view.cellWidth
176− height: units.gu(10)
177
175
178− onClicked: root.applicationSelected(model.appId)
176+ Component {
177+ id: drawerDelegateComponent
178+ AbstractButton {
179+ width: GridView.view.cellWidth
180+ height: units.gu(10)
179
181
180− Column {
182+ onClicked: root.applicationSelected(model.appId)
181− width: units.gu(8)
182− anchors.horizontalCenter: parent.horizontalCenter
183− height: childrenRect.height
184− spacing: units.gu(1)
185
183
186− UbuntuShape {
184+ Column {
187− id: appIcon
188− width: units.gu(6)
185+ width: units.gu(8)
189− height: 7.5 / 8 * width
190 anchors.horizontalCenter: parent.horizontalCenter
186 anchors.horizontalCenter: parent.horizontalCenter
187+ height: childrenRect.height
188+ spacing: units.gu(1)
189+
190+ UbuntuShape {
191+ id: appIcon
192+ width: units.gu(6)
193+ height: 7.5 / 8 * width
194+ anchors.horizontalCenter: parent.horizontalCenter
191− backgroundMode: UbuntuShape.SolidColor
195+ backgroundMode: UbuntuShape.SolidColor
192− backgroundColor: UbuntuColors.lightGrey
196+ backgroundColor: UbuntuColors.lightGrey
193− radius: "medium"
197+ radius: "medium"
194− borderSource: 'undefined'
198+ borderSource: 'undefined'
195− source: Image {
199+ source: Image {
196− id: sourceImage
200+ id: sourceImage
197− sourceSize.width: appIcon.width
201+ sourceSize.width: appIcon.width
198− source: model.icon
202+ source: model.icon
203+ }
204+ sourceFillMode: UbuntuShape.PreserveAspectCrop
199 }
205 }
200− sourceFillMode: UbuntuShape.PreserveAspectCrop
201− }
202
206
203− Label {
207+ Label {
204− text: model.name
208+ text: model.name
205− width: parent.width
209+ width: parent.width
206− horizontalAlignment: Text.AlignHCenter
210+ horizontalAlignment: Text.AlignHCenter
207− fontSize: "small"
211+ fontSize: "small"
208− elide: Text.ElideRight
212+ elide: Text.ElideRight
213+ }
209 }
214 }
210 }
215 }
211 }
216 }
qml/Launcher/Launcher.qml+16
@@ -64,6 +64,9 @@ FocusScope {
64 }
64 }
65
65
66 onSuperPressedChanged: {
66 onSuperPressedChanged: {
67+ if (state == "drawer")
68+ return;
69+
67 if (superPressed) {
70 if (superPressed) {
68 superPressTimer.start();
71 superPressTimer.start();
69 superLongPressTimer.start();
72 superLongPressTimer.start();
@@ -145,6 +148,14 @@ FocusScope {
145 switchToNextState("visible")
148 switchToNextState("visible")
146 }
149 }
147
150
151+ function openDrawer() {
152+ superPressTimer.stop();
153+ superLongPressTimer.stop();
154+ root.focus = true;
155+ drawer.focus = true;
156+ switchToNextState("drawer")
157+ }
158+
148 Keys.onPressed: {
159 Keys.onPressed: {
149 switch (event.key) {
160 switch (event.key) {
150 case Qt.Key_Backtab:
161 case Qt.Key_Backtab:
@@ -354,6 +365,11 @@ FocusScope {
354
365
355 onApplicationSelected: root.launcherApplicationSelected(appId)
366 onApplicationSelected: root.launcherApplicationSelected(appId)
356
367
368+ Keys.onEscapePressed: {
369+ switchToNextState("");
370+ root.focus = false;
371+ }
372+
357 Behavior on x {
373 Behavior on x {
358 enabled: !dragArea.dragging && !launcherDragArea.drag.active// && panel.animate;
374 enabled: !dragArea.dragging && !launcherDragArea.drag.active// && panel.animate;
359 NumberAnimation {
375 NumberAnimation {
qml/Shell.qml+6
@@ -544,6 +544,12 @@ StyledItem {
544 }
544 }
545 }
545 }
546
546
547+ GlobalShortcut {
548+ shortcut: Qt.MetaModifier | Qt.Key_A
549+ onTriggered: {
550+ launcher.openDrawer();
551+ }
552+ }
547 GlobalShortcut {
553 GlobalShortcut {
548 shortcut: Qt.AltModifier | Qt.Key_F1
554 shortcut: Qt.AltModifier | Qt.Key_F1
549 onTriggered: {
555 onTriggered: {