@@ -19,6 +19,7 @@ import Ubuntu.Components 1.3
1919import Ubuntu.Components.ListItems 1.3 as ListItems
2020import Unity.Launcher 0.1
2121import Ubuntu.Components.Popups 1.3
22+import GlobalShortcut 1.0
2223import "../Components/ListItems"
2324import "../Components/"
2425
@@ -33,7 +34,7 @@ Rectangle {
3334 property bool dragging: false
3435 property bool moving: launcherListView.moving || launcherListView.flicking
3536 property bool preventHiding: moving || dndArea.draggedIndex >= 0 || quickList.state === "open" || dndArea.pressed
36− property int highlightIndex: -1
37+ property int highlightIndex: -2
3738
3839 signal applicationSelected(string appId)
3940 signal showDashHome()
@@ -44,6 +45,26 @@ Rectangle {
4445 }
4546 }
4647
48+ function highlightNext() {
49+ highlightIndex++;
50+ if (highlightIndex >= launcherListView.count) {
51+ highlightIndex = -1;
52+ }
53+ moveAnimation.moveToIndex(Math.max(highlightIndex, 0));
54+ }
55+ function highlightPrevious() {
56+ highlightIndex--;
57+ if (highlightIndex <= -2) {
58+ highlightIndex = launcherListView.count - 1;
59+ }
60+ moveAnimation.moveToIndex(Math.max(highlightIndex, 0));
61+ }
62+ function openQuicklist(index) {
63+ quickList.open(index);
64+ quickList.selectedIndex = 0;
65+ quickList.focus = true;
66+ }
67+
4768 Column {
4869 id: mainColumn
4970 anchors {
@@ -172,6 +193,16 @@ Rectangle {
172193 to = contentY;
173194 start();
174195 }
196+ function moveToIndex(index) {
197+ var itemPosition = index * launcherListView.itemHeight;
198+ var height = launcherListView.height - launcherListView.topMargin - launcherListView.bottomMargin
199+ var distanceToEnd = index == 0 || index == launcherListView.count - 1 ? 0 : launcherListView.itemHeight
200+ if (itemPosition + launcherListView.itemHeight + distanceToEnd > launcherListView.contentY + launcherListView.topMargin + height) {
201+ moveAnimation.moveTo(itemPosition + launcherListView.itemHeight - launcherListView.topMargin - height + distanceToEnd);
202+ } else if (itemPosition - distanceToEnd < launcherListView.contentY + launcherListView.topMargin) {
203+ moveAnimation.moveTo(itemPosition - distanceToEnd - launcherListView.topMargin);
204+ }
205+ }
175206 }
176207
177208 displaced: Transition {
@@ -197,6 +228,7 @@ Rectangle {
197228 itemFocused: model.focused
198229 inverted: root.inverted
199230 alerting: model.alerting
231+ highlighted: root.highlightIndex == index
200232 z: -Math.abs(offset)
201233 maxAngle: 55
202234 property bool dragging: false
@@ -234,14 +266,7 @@ Rectangle {
234266 onAlertingChanged: {
235267 if(alerting) {
236268 if (!dragging && (launcherListView.peekingIndex === -1 || launcher.visibleWidth > 0)) {
237− var itemPosition = index * launcherListView.itemHeight;
238− var height = launcherListView.height - launcherListView.topMargin - launcherListView.bottomMargin
239− var distanceToEnd = index == 0 || index == launcherListView.count - 1 ? 0 : launcherListView.itemHeight
240− if (itemPosition + launcherListView.itemHeight + distanceToEnd > launcherListView.contentY + launcherListView.topMargin + height) {
241− moveAnimation.moveTo(itemPosition + launcherListView.itemHeight - launcherListView.topMargin - height + distanceToEnd);
242− } else if (itemPosition - distanceToEnd < launcherListView.contentY + launcherListView.topMargin) {
243− moveAnimation.moveTo(itemPosition - distanceToEnd - launcherListView.topMargin);
244− }
269+ moveAnimation.moveToIndex(index)
245270 if (!dragging && launcher.state !== "visible") {
246271 peekingAnimation.start()
247272 }
@@ -392,10 +417,7 @@ Rectangle {
392417
393418 if (mouse.button & Qt.RightButton) { // context menu
394419 // Opening QuickList
395− quickList.item = clickedItem;
396− quickList.model = launcherListView.model.get(index).quickList;
397− quickList.appId = launcherListView.model.get(index).appId;
398− quickList.state = "open";
420+ quickList.open(index);
399421 return
400422 }
401423
@@ -465,11 +487,7 @@ Rectangle {
465487
466488 draggedIndex = Math.floor((mouseY + launcherListView.realContentY) / launcherListView.realItemHeight);
467489
468− // Opening QuickList
469− quickList.item = selectedItem;
470− quickList.model = launcherListView.model.get(draggedIndex).quickList;
471− quickList.appId = launcherListView.model.get(draggedIndex).appId;
472− quickList.state = "open";
490+ quickList.open(draggedIndex)
473491
474492 launcherListView.interactive = false
475493
@@ -647,12 +665,55 @@ Rectangle {
647665 property var model
648666 property string appId
649667 property var item
668+ property int selectedIndex: -1
669+
670+ Keys.onPressed: {
671+ switch (event.key) {
672+ case Qt.Key_Down:
673+ selectedIndex++;
674+ if (selectedIndex >= popoverRepeater.count) {
675+ selectedIndex = 0;
676+ }
677+ event.accepted = true;
678+ break;
679+ case Qt.Key_Up:
680+ selectedIndex--;
681+ if (selectedIndex < 0) {
682+ selectedIndex = popoverRepeater.count - 1;
683+ }
684+ event.accepted = true;
685+ break;
686+ case Qt.Key_Left:
687+ case Qt.Key_Escape:
688+ quickList.selectedIndex = -1;
689+ // Falling through intentionally
690+ case Qt.Key_Enter:
691+ case Qt.Key_Return:
692+ case Qt.Key_Space:
693+ if (quickList.selectedIndex >= 0) {
694+ LauncherModel.quickListActionInvoked(quickList.appId, quickList.selectedIndex)
695+ }
696+ quickList.focus = false;
697+ quickList.state = ""
698+ // Don't consume the event. We want to close the Launcher too, not just the quicklist.
699+ break;
700+ }
701+ }
650702
651703 // internal
652704 property int itemCenter: item ? root.mapFromItem(quickList.item).y + (item.height / 2) : units.gu(1)
653705 property int offset: itemCenter + (height/2) + units.gu(1) > parent.height ? -itemCenter - (height/2) - units.gu(1) + parent.height :
654706 itemCenter - (height/2) < units.gu(1) ? (height/2) - itemCenter + units.gu(1) : 0
655707
708+ function open(index) {
709+ var itemPosition = index * launcherListView.itemHeight;
710+ var height = launcherListView.height - launcherListView.topMargin - launcherListView.bottomMargin
711+ item = launcherListView.itemAt(launcherListView.width / 2, itemPosition + launcherListView.itemHeight / 2);
712+ quickList.model = launcherListView.model.get(index).quickList;
713+ quickList.appId = launcherListView.model.get(index).appId;
714+ quickList.state = "open";
715+ }
716+
656717 Column {
657718 id: quickListColumn
658719 width: parent.width
@@ -666,6 +727,7 @@ Rectangle {
666727 objectName: "quickListEntry" + index
667728 text: (model.clickable ? "" : "<b>") + model.label + (model.clickable ? "" : "</b>")
668729 highlightWhenPressed: model.clickable
730+ selected: index === quickList.selectedIndex
669731
670732 // FIXME: This is a workaround for the theme not being context sensitive. I.e. the
671733 // ListItems don't know that they are sitting in a themed Popover where the color