DashboardRepresentation.qml 37.3 KB
Newer Older
1
2
3
4
5
/*
    SPDX-FileCopyrightText: 2015 Eike Hein <hein@kde.org>

    SPDX-License-Identifier: GPL-2.0-or-later
*/
6
7
8

import QtQuick 2.4
import QtGraphicalEffects 1.0
Konrad Materka's avatar
Konrad Materka committed
9
import QtQml 2.15
10

11
import org.kde.plasma.core 2.1 as PlasmaCore
12
import org.kde.plasma.components 2.0 as PlasmaComponents
13
import org.kde.plasma.core 2.0 as PlasmaCore
14
15
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.kquickcontrolsaddons 2.0
16
17
18
import org.kde.kwindowsystem 1.0

import org.kde.plasma.private.shell 2.0
19
20
21

import org.kde.plasma.private.kicker 0.1 as Kicker

22
import "code/tools.js" as Tools
23
24

/* TODO
Eike Hein's avatar
Eike Hein committed
25
26
27
 * Reverse middleRow layout + keyboard nav + filter list text alignment in rtl locales.
 * Keep cursor column when arrow'ing down past non-full trailing rows into a lower grid.
 * Make DND transitions cleaner by performing an item swap instead of index reinsertion.
28
29
*/

Eike Hein's avatar
Eike Hein committed
30
Kicker.DashboardWindow {
31
32
    id: root

33
    property bool smallScreen: ((Math.floor(width / PlasmaCore.Units.iconSizes.huge) <= 22) || (Math.floor(height / PlasmaCore.Units.iconSizes.huge) <= 14))
34

35
    property int iconSize: smallScreen ? PlasmaCore.Units.iconSizes.large : PlasmaCore.Units.iconSizes.huge
Nate Graham's avatar
Nate Graham committed
36
    property int cellSize: iconSize + PlasmaCore.Theme.mSize(PlasmaCore.Theme.defaultFont).height
37
        + (2 * PlasmaCore.Units.smallSpacing)
38
39
        + (2 * Math.max(highlightItemSvg.margins.top + highlightItemSvg.margins.bottom,
                        highlightItemSvg.margins.left + highlightItemSvg.margins.right))
Eike Hein's avatar
Eike Hein committed
40
    property int columns: Math.floor(((smallScreen ? 85 : 80)/100) * Math.ceil(width / cellSize))
41
    property bool searching: (searchField.text != "")
42
    property var widgetExplorer: null
43

Eike Hein's avatar
Eike Hein committed
44
    keyEventProxy: searchField
45
    backgroundColor: Qt.rgba(0, 0, 0, 0.737)
Eike Hein's avatar
Eike Hein committed
46

47
48
49
50
51
52
53
54
    onKeyEscapePressed: {
        if (searching) {
            searchField.clear();
        } else {
            root.toggle();
        }
    }

55
    onVisibleChanged: {
56
        tabBar.activeTab = 0;
57
58
59
60
61
62
63
64
65
66
67
68
        reset();

        if (visible) {
            preloadAllAppsTimer.restart();
        }
    }

    onSearchingChanged: {
        if (!searching) {
            reset();
        } else {
            filterList.currentIndex = -1;
69
70
71
72
73

            if (tabBar.activeTab == 1) {
                widgetExplorer.widgetsModel.filterQuery = "";
                widgetExplorer.widgetsModel.filterType = "";
            }
74
75
76
77
78
79
80
81
82
        }
    }

    function reset() {
        searchField.clear();
        globalFavoritesGrid.currentIndex = -1;
        systemFavoritesGrid.currentIndex = -1;
        filterList.currentIndex = 0;
        funnelModel.sourceModel = rootModel.modelForRow(0);
83
        mainGrid.model = (tabBar.activeTab == 0) ? funnelModel : root.widgetExplorer.widgetsModel;
84
85
        mainGrid.currentIndex = -1;
        filterListScrollArea.focus = true;
86
        filterList.model = (tabBar.activeTab == 0) ? rootModel : root.widgetExplorer.filterModel;
87
88
89
    }

    function updateWidgetExplorer() {
90
91
        if (tabBar.activeTab == 1 /* Widgets */ || tabBar.hoveredTab == 1) {
            if (!root.widgetExplorer) {
92
93
94
                root.widgetExplorer = widgetExplorerComponent.createObject(root, {
                    containment: containmentInterface.screenContainment(plasmoid)
                });
95
            }
96
97
98
99
        } else if (root.widgetExplorer) {
            root.widgetExplorer.destroy();
            root.widgetExplorer = null;
        }
100
101
102
    }

    mainItem: MouseArea {
103
104
        id: rootItem

105
106
107
        anchors.fill: parent

        acceptedButtons: Qt.LeftButton | Qt.RightButton
Marco Martin's avatar
Marco Martin committed
108
109
110
        
        LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft
        LayoutMirroring.childrenInherit: true
111
112
113
114

        Connections {
            target: kicker

Konrad Materka's avatar
Konrad Materka committed
115
            function onReset() {
116
                if (!root.searching) {
117
                    filterList.applyFilter();
118
119
120
121

                    if (tabBar.activeTab == 0) {
                        funnelModel.reset();
                    }
122
123
                }
            }
Eike Hein's avatar
Eike Hein committed
124

Konrad Materka's avatar
Konrad Materka committed
125
126
            function onDragSourceChanged() {
                if (!kicker.dragSource) {
127
                    // FIXME TODO HACK: Reset all views post-DND to work around
Eike Hein's avatar
Eike Hein committed
128
129
130
                    // mouse grab bug despite QQuickWindow::mouseGrabberItem==0x0.
                    // Needs a more involved hunt through Qt Quick sources later since
                    // it's not happening with near-identical code in the menu repr.
131
                    rootModel.refresh();
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
                } else if (tabBar.activeTab == 1) {
                    root.toggle();
                    containmentInterface.ensureMutable(containmentInterface.screenContainment(plasmoid));
                    kwindowsystem.showingDesktop = true;
                }
            }
        }

        KWindowSystem {
            id: kwindowsystem
        }

        Component {
            id: widgetExplorerComponent

147
            WidgetExplorer { showSpecialFilters: false }
148
149
        }

150
151
        Connections {
            target: plasmoid
Konrad Materka's avatar
Konrad Materka committed
152
            function onUserConfiguringChanged() {
153
154
155
                if (plasmoid.userConfiguring) {
                    root.hide()
                }
Eike Hein's avatar
Eike Hein committed
156
            }
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
        }

        PlasmaComponents.Menu {
            id: contextMenu

            PlasmaComponents.MenuItem {
                action: plasmoid.action("configure")
            }
        }

        PlasmaExtras.Heading {
            id: dummyHeading

            visible: false

            width: 0

            level: 1
        }

        TextMetrics {
            id: headingMetrics

            font: dummyHeading.font
        }

        Kicker.FunnelModel {
            id: funnelModel

            onSourceModelChanged: {
                if (mainColumn.visible) {
                    mainGrid.currentIndex = -1;
                    mainGrid.forceLayout();
                }
            }
        }

        Timer {
            id: preloadAllAppsTimer

            property bool done: false

199
            interval: 1000
200
201
202
            repeat: false

            onTriggered: {
203
                if (done || root.searching) {
204
205
206
207
208
209
                    return;
                }

                for (var i = 0; i < rootModel.count; ++i) {
                    var model = rootModel.modelForRow(i);

Laurent Montel's avatar
Laurent Montel committed
210
                    if (model.description === "KICKER_ALL_MODEL") {
211
212
213
214
215
216
                        allAppsGrid.model = model;
                        done = true;
                        break;
                    }
                }
            }
217
218
219
220
221
222

            function defer() {
                if (running && !done) {
                    restart();
                }
            }
223
224
        }

225
226
227
228
229
230
231
232
233
234
235
236
237
238
        Kicker.ContainmentInterface {
            id: containmentInterface
        }

        DashboardTabBar {
            id: tabBar

            y: 0

            anchors.horizontalCenter: parent.horizontalCenter

            visible: (plasmoid.immutability !== PlasmaCore.Types.SystemImmutable)

            onActiveTabChanged: {
239
240
                root.updateWidgetExplorer();
                root.reset();
241
242
            }

243
            onHoveredTabChanged: root.updateWidgetExplorer()
244
245
246
247
248
249

            Keys.onDownPressed: {
                mainColumn.tryActivate(0, 0);
            }
        }

250
        TextEdit {
251
252
253
254
255
256
257
            id: searchField

            width: 0
            height: 0

            visible: false

258
259
            persistentSelection: true

260
            onTextChanged: {
261
262
263
                if (tabBar.activeTab == 0) {
                    runnerModel.query = searchField.text;
                } else {
264
                    root.widgetExplorer.widgetsModel.searchTerm = searchField.text;
265
                }
266
267
268
269
270
            }

            function clear() {
                text = "";
            }
271
272
273

            onSelectionStartChanged: Qt.callLater(searchHeading.updateSelection)
            onSelectionEndChanged: Qt.callLater(searchHeading.updateSelection)
274
275
        }

276
        TextEdit {
277
278
279
280
281
282
            id: searchHeading

            anchors {
                horizontalCenter: parent.horizontalCenter
            }

283
            y: (middleRow.anchors.topMargin / 2) - (root.smallScreen ? (height/10) : 0)
284
285
286
287
288

            font.pointSize: dummyHeading.font.pointSize * 1.5
            wrapMode: Text.NoWrap
            opacity: 1.0

289
290
            selectByMouse: false
            cursorVisible: false
291

292
            color: "white"
293

294
            text: root.searching ? i18n("Searching for '%1'", searchField.text) : i18n("Type to search…")
295
296
297
298
299
300
301
302
303

            function updateSelection() {
                if (!searchField.selectedText) {
                    return;
                }

                var delta = text.lastIndexOf(searchField.text, text.length - 2);
                searchHeading.select(searchField.selectionStart + delta, searchField.selectionEnd + delta);
            }
304
305
306
307
308
309
310
        }

        PlasmaComponents.ToolButton {
            id: cancelSearchButton

            anchors {
                left: searchHeading.right
311
                leftMargin: PlasmaCore.Units.largeSpacing
312
313
314
                verticalCenter: searchHeading.verticalCenter
            }

315
            width: PlasmaCore.Units.iconSizes.large
316
317
318
319
320
321
322
323
            height: width

            visible: (searchField.text != "")

            iconName: "dialog-close"
            flat: false

            onClicked: searchField.clear();
Eike Hein's avatar
Eike Hein committed
324

325
            Keys.onPressed: event => {
Laurent Montel's avatar
Laurent Montel committed
326
                if (event.key === Qt.Key_Tab) {
Eike Hein's avatar
Eike Hein committed
327
328
329
330
331
332
333
                    event.accepted = true;

                    if (runnerModel.count) {
                        mainColumn.tryActivate(0, 0);
                    } else {
                        systemFavoritesGrid.tryActivate(0, 0);
                    }
Laurent Montel's avatar
Laurent Montel committed
334
                } else if (event.key === Qt.Key_Backtab) {
Eike Hein's avatar
Eike Hein committed
335
336
337
338
339
340
341
342
343
344
345
                    event.accepted = true;

                    if (tabBar.visible) {
                        tabBar.focus = true;
                    } else if (globalFavoritesGrid.enabled) {
                        globalFavoritesGrid.tryActivate(0, 0);
                    } else {
                        systemFavoritesGrid.tryActivate(0, 0);
                    }
                }
            }
346
347
348
349
350
351
352
        }

        Row {
            id: middleRow

            anchors {
                top: parent.top
353
                topMargin: PlasmaCore.Units.gridUnit * (smallScreen ? 8 : 10)
354
                bottom: parent.bottom
355
                bottomMargin: (PlasmaCore.Units.gridUnit * 2)
356
357
358
                horizontalCenter: parent.horizontalCenter
            }

359
            width: (root.columns * root.cellSize) + (2 * spacing)
360

361
            spacing: PlasmaCore.Units.gridUnit * 2
362
363
364
365
366
367
368
369
370

            Item {
                id: favoritesColumn

                anchors {
                    top: parent.top
                    bottom: parent.bottom
                }

371
                width: (columns * root.cellSize) + PlasmaCore.Units.gridUnit
372
373
374
375
376
377

                property int columns: 3

                PlasmaExtras.Heading {
                    id: favoritesColumnLabel

378
379
                    enabled: (tabBar.activeTab == 0)

380
381
382
383
                    anchors {
                        top: parent.top
                    }

384
                    x: PlasmaCore.Units.smallSpacing
385
386
387
388
389
390
391
392
393
394
                    width: parent.width - x

                    elide: Text.ElideRight
                    wrapMode: Text.NoWrap

                    color: "white"

                    level: 1

                    text: i18n("Favorites")
395
396
397

                    opacity: (enabled ? 1.0 : 0.3)

398
                    Behavior on opacity { SmoothedAnimation { duration: PlasmaCore.Units.longDuration; velocity: 0.01 } }
399
400
401
402
403
                }

                PlasmaCore.SvgItem {
                    id: favoritesColumnLabelUnderline

404
405
                    enabled: (tabBar.activeTab == 0)

406
407
408
409
                    anchors {
                        top: favoritesColumnLabel.bottom
                    }

410
                    width: parent.width - PlasmaCore.Units.gridUnit
411
412
413
414
                    height: lineSvg.horLineHeight

                    svg: lineSvg
                    elementId: "horizontal-line"
415
416
417

                    opacity: (enabled ? 1.0 : 0.3)

418
                    Behavior on opacity { SmoothedAnimation { duration: PlasmaCore.Units.longDuration; velocity: 0.01 } }
419
420
421
422
423
                }

                ItemGridView {
                    id: globalFavoritesGrid

424
425
                    enabled: (tabBar.activeTab == 0)

426
                    anchors {
Eike Hein's avatar
Eike Hein committed
427
                        top: favoritesColumnLabelUnderline.bottom
428
                        topMargin: PlasmaCore.Units.largeSpacing
429
430
431
                    }

                    property int rows: (Math.floor((parent.height - favoritesColumnLabel.height
432
                        - favoritesColumnLabelUnderline.height - PlasmaCore.Units.largeSpacing) / root.cellSize)
433
434
435
                        - systemFavoritesGrid.rows)

                    width: parent.width
436
                    height: rows * root.cellSize
437

438
439
                    cellWidth: root.cellSize
                    cellHeight: root.cellSize
440
                    iconSize: root.iconSize
441
442
443

                    model: globalFavorites

444
                    dropEnabled: true
445
                    usesPlasmaTheme: false
446

447
448
                    opacity: (enabled ? 1.0 : 0.3)

449
                    Behavior on opacity { SmoothedAnimation { duration: PlasmaCore.Units.longDuration; velocity: 0.01 } }
450

451
452
453
454
                    onCurrentIndexChanged: {
                        preloadAllAppsTimer.defer();
                    }

455
456
457
458
459
460
461
462
                    onKeyNavRight: {
                        mainColumn.tryActivate(currentRow(), 0);
                    }

                    onKeyNavDown: {
                        systemFavoritesGrid.tryActivate(0, currentCol());
                    }

463
                    Keys.onPressed: event => {
Laurent Montel's avatar
Laurent Montel committed
464
                        if (event.key === Qt.Key_Tab) {
Eike Hein's avatar
Eike Hein committed
465
466
467
468
                            event.accepted = true;

                            if (tabBar.visible) {
                                tabBar.focus = true;
469
                            } else if (root.searching) {
Eike Hein's avatar
Eike Hein committed
470
471
472
473
                                cancelSearchButton.focus = true;
                            } else {
                                mainColumn.tryActivate(0, 0);
                            }
Laurent Montel's avatar
Laurent Montel committed
474
                        } else if (event.key === Qt.Key_Backtab) {
Eike Hein's avatar
Eike Hein committed
475
476
477
478
479
                            event.accepted = true;
                            systemFavoritesGrid.tryActivate(0, 0);
                        }
                    }

480
481
482
                    Binding {
                        target: globalFavorites
                        property: "iconSize"
483
                        value: root.iconSize
Konrad Materka's avatar
Konrad Materka committed
484
                        restoreMode: Binding.RestoreBinding
485
486
487
488
489
490
491
492
493
494
                    }
                }

                ItemGridView {
                    id: systemFavoritesGrid

                    anchors {
                        top: globalFavoritesGrid.bottom
                    }

495
                    property int rows: Math.ceil(count / Math.floor(width / root.cellSize))
496
497

                    width: parent.width
498
                    height: rows * root.cellSize
499

500
501
                    cellWidth: root.cellSize
                    cellHeight: root.cellSize
502
                    iconSize: root.iconSize
503
504
505

                    model: systemFavorites

506
                    dropEnabled: true
507
                    usesPlasmaTheme: true
508

509
510
511
512
                    onCurrentIndexChanged: {
                        preloadAllAppsTimer.defer();
                    }

513
514
515
516
517
518
519
                    onKeyNavRight: {
                        mainColumn.tryActivate(globalFavoritesGrid.rows + currentRow(), 0);
                    }

                    onKeyNavUp: {
                        globalFavoritesGrid.tryActivate(globalFavoritesGrid.rows - 1, currentCol());
                    }
Eike Hein's avatar
Eike Hein committed
520

521
                    Keys.onPressed: event => {
Laurent Montel's avatar
Laurent Montel committed
522
                        if (event.key === Qt.Key_Tab) {
Eike Hein's avatar
Eike Hein committed
523
524
525
526
527
528
                            event.accepted = true;

                            if (globalFavoritesGrid.enabled) {
                                globalFavoritesGrid.tryActivate(0, 0);
                            } else if (tabBar.visible) {
                                tabBar.focus = true;
529
                            } else if (root.searching && !runnerModel.count) {
Eike Hein's avatar
Eike Hein committed
530
531
532
533
                                cancelSearchButton.focus = true;
                            } else {
                                mainColumn.tryActivate(0, 0);
                            }
Laurent Montel's avatar
Laurent Montel committed
534
                        } else if (event.key === Qt.Key_Backtab) {
Eike Hein's avatar
Eike Hein committed
535
536
537
538
                            event.accepted = true;

                            if (filterList.enabled) {
                                filterList.forceActiveFocus();
539
                            } else if (root.searching && !runnerModel.count) {
Eike Hein's avatar
Eike Hein committed
540
541
542
543
544
545
                                cancelSearchButton.focus = true;
                            } else {
                                mainColumn.tryActivate(0, 0);
                            }
                        }
                    }
546
547
548
549
550
551
552
553
                }
            }

            Item {
                id: mainColumn

                anchors.top: parent.top

554
                width: (columns * root.cellSize) + PlasmaCore.Units.gridUnit
555
                height: Math.floor(parent.height / root.cellSize) * root.cellSize + mainGridContainer.headerHeight
556
557
558
559
560
561
562
563
564
565
566
567
568

                property int columns: root.columns - favoritesColumn.columns - filterListColumn.columns
                property Item visibleGrid: mainGrid

                function tryActivate(row, col) {
                    if (visibleGrid) {
                        visibleGrid.tryActivate(row, col);
                    }
                }

                Item {
                    id: mainGridContainer

569
                    anchors.fill: parent
570
571
                    z: (opacity == 1.0) ? 1 : 0

572
                    visible: opacity != 0.0
573

574
                    property int headerHeight: mainColumnLabel.height + mainColumnLabelUnderline.height + PlasmaCore.Units.largeSpacing
575

576
                    opacity: {
577
                        if (tabBar.activeTab == 0 && root.searching) {
578
579
580
581
582
583
584
585
586
                            return 0.0;
                        }

                        if (filterList.allApps) {
                            return 0.0;
                        }

                        return 1.0;
                    }
587
588
589
590
591
592
593
594
595
596
597
598
599
600

                    onOpacityChanged: {
                        if (opacity == 1.0) {
                            mainColumn.visibleGrid = mainGrid;
                        }
                    }

                    PlasmaExtras.Heading {
                        id: mainColumnLabel

                        anchors {
                            top: parent.top
                        }

601
                        x: PlasmaCore.Units.smallSpacing
602
603
604
605
606
607
608
609
610
611
                        width: parent.width - x

                        elide: Text.ElideRight
                        wrapMode: Text.NoWrap
                        opacity: 1.0

                        color: "white"

                        level: 1

612
                        text: (tabBar.activeTab == 0) ? funnelModel.description : i18n("Widgets")
613
614
615
616
617
618
619
620
621
622
623
                    }

                    PlasmaCore.SvgItem {
                        id: mainColumnLabelUnderline

                        visible: mainGrid.count

                        anchors {
                            top: mainColumnLabel.bottom
                        }

624
                        width: parent.width - PlasmaCore.Units.gridUnit
625
626
627
628
629
630
631
632
633
634
635
                        height: lineSvg.horLineHeight

                        svg: lineSvg
                        elementId: "horizontal-line"
                    }

                    ItemGridView {
                        id: mainGrid

                        anchors {
                            top: mainColumnLabelUnderline.bottom
636
                            topMargin: PlasmaCore.Units.largeSpacing
637
638
639
                        }

                        width: parent.width
Eike Hein's avatar
Eike Hein committed
640
                        height: systemFavoritesGrid.y + systemFavoritesGrid.height - mainGridContainer.headerHeight
641

642
                        cellWidth: (tabBar.activeTab == 0 ? root.cellSize : root.cellSize * 2)
643
                        cellHeight: cellWidth
644
                        iconSize: (tabBar.activeTab == 0 ? root.iconSize : cellWidth - (PlasmaCore.Units.largeSpacing * 2))
645
646
647

                        model: funnelModel

648
649
650
651
                        onCurrentIndexChanged: {
                            preloadAllAppsTimer.defer();
                        }

652
                        onKeyNavLeft: {
653
654
655
656
657
658
                            if (tabBar.activeTab == 0) {
                                var row = currentRow();
                                var target = row + 1 > globalFavoritesGrid.rows ? systemFavoritesGrid : globalFavoritesGrid;
                                var targetRow = row + 1 > globalFavoritesGrid.rows ? row - globalFavoritesGrid.rows : row;
                                target.tryActivate(targetRow, favoritesColumn.columns - 1);
                            }
659
660
661
662
663
                        }

                        onKeyNavRight: {
                            filterListScrollArea.focus = true;
                        }
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678

                        onKeyNavUp: {
                            if (tabBar.visible) {
                                tabBar.focus = true;
                            }
                        }

                        onItemActivated: {
                            if (tabBar.activeTab == 1) {
                                containmentInterface.ensureMutable(containmentInterface.screenContainment(plasmoid));
                                root.widgetExplorer.addApplet(currentItem.m.pluginName);
                                root.toggle();
                                kwindowsystem.showingDesktop = true;
                            }
                        }
679
680
681
682
683
684
685
686
687
688
689
690
                    }
                }

                ItemMultiGridView {
                    id: allAppsGrid

                    anchors {
                        top: parent.top
                    }

                    z: (opacity == 1.0) ? 1 : 0
                    width: parent.width
Eike Hein's avatar
Eike Hein committed
691
                    height: systemFavoritesGrid.y + systemFavoritesGrid.height
692

693
                    visible: opacity != 0.0
694

695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
                    opacity: filterList.allApps ? 1.0 : 0.0

                    onOpacityChanged: {
                        if (opacity == 1.0) {
                            allAppsGrid.flickableItem.contentY = 0;
                            mainColumn.visibleGrid = allAppsGrid;
                        }
                    }

                    onKeyNavLeft: {
                        var row = 0;

                        for (var i = 0; i < subGridIndex; i++) {
                            row += subGridAt(i).lastRow() + 2; // Header counts as one.
                        }

                        row += subGridAt(subGridIndex).currentRow();

                        var target = row + 1 > globalFavoritesGrid.rows ? systemFavoritesGrid : globalFavoritesGrid;
                        var targetRow = row + 1 > globalFavoritesGrid.rows ? row - globalFavoritesGrid.rows : row;
                        target.tryActivate(targetRow, favoritesColumn.columns - 1);
                    }

                    onKeyNavRight: {
                        filterListScrollArea.focus = true;
                    }
                }

                ItemMultiGridView {
                    id: runnerGrid

                    anchors {
                        top: parent.top
                    }

                    z: (opacity == 1.0) ? 1 : 0
                    width: parent.width
732
                    height: Math.min(implicitHeight, systemFavoritesGrid.y + systemFavoritesGrid.height)
733

734
                    visible: opacity != 0.0
735

736
737
738
739
                    model: runnerModel

                    grabFocus: true

740
                    opacity: (tabBar.activeTab == 0 && root.searching) ? 1.0 : 0.0
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761

                    onOpacityChanged: {
                        if (opacity == 1.0) {
                            mainColumn.visibleGrid = runnerGrid;
                        }
                    }

                    onKeyNavLeft: {
                        var row = 0;

                        for (var i = 0; i < subGridIndex; i++) {
                            row += subGridAt(i).lastRow() + 2; // Header counts as one.
                        }

                        row += subGridAt(subGridIndex).currentRow();

                        var target = row + 1 > globalFavoritesGrid.rows ? systemFavoritesGrid : globalFavoritesGrid;
                        var targetRow = row + 1 > globalFavoritesGrid.rows ? row - globalFavoritesGrid.rows : row;
                        target.tryActivate(targetRow, favoritesColumn.columns - 1);
                    }
                }
Eike Hein's avatar
Eike Hein committed
762

763
                Keys.onPressed: event => {
Laurent Montel's avatar
Laurent Montel committed
764
                    if (event.key === Qt.Key_Tab) {
Eike Hein's avatar
Eike Hein committed
765
766
767
768
769
770
771
                        event.accepted = true;

                        if (filterList.enabled) {
                            filterList.forceActiveFocus();
                        } else {
                            systemFavoritesGrid.tryActivate(0, 0);
                        }
Laurent Montel's avatar
Laurent Montel committed
772
                    } else if (event.key === Qt.Key_Backtab) {
Eike Hein's avatar
Eike Hein committed
773
774
                        event.accepted = true;

775
                        if (root.searching) {
Eike Hein's avatar
Eike Hein committed
776
777
778
779
780
781
782
783
784
785
                            cancelSearchButton.focus = true;
                        } else if (tabBar.visible) {
                            tabBar.focus = true;
                        } else if (globalFavoritesGrid.enabled) {
                            globalFavoritesGrid.tryActivate(0, 0);
                        } else {
                            systemFavoritesGrid.tryActivate(0, 0);
                        }
                    }
                }
786
787
788
789
790
791
792
            }

            Item {
                id: filterListColumn

                anchors {
                    top: parent.top
793
                    topMargin: mainColumnLabelUnderline.y + mainColumnLabelUnderline.height + PlasmaCore.Units.largeSpacing
794
795
796
                    bottom: parent.bottom
                }

797
                width: columns * root.cellSize
798
799
800
801
802
803

                property int columns: 3

                PlasmaExtras.ScrollArea {
                    id: filterListScrollArea

804
                    x: root.visible ? 0 : PlasmaCore.Units.gridUnit
805

806
                    Behavior on x { SmoothedAnimation { duration: PlasmaCore.Units.longDuration; velocity: 0.01 } }
807
808

                    width: parent.width
Eike Hein's avatar
Eike Hein committed
809
                    height: mainGrid.height
810

811
                    enabled: !root.searching
812
813
814

                    property alias currentIndex: filterList.currentIndex

815
                    opacity: root.visible ? (root.searching ? 0.30 : 1.0) : 0.3
816

817
                    Behavior on opacity { SmoothedAnimation { duration: PlasmaCore.Units.longDuration; velocity: 0.01 } }
818

819
820
                    verticalScrollBarPolicy: (opacity == 1.0) ? Qt.ScrollBarAsNeeded : Qt.ScrollBarAlwaysOff

821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
                    onEnabledChanged: {
                        if (!enabled) {
                            filterList.currentIndex = -1;
                        }
                    }

                    onCurrentIndexChanged: {
                        focus = (currentIndex != -1);
                    }

                    ListView {
                        id: filterList

                        focus: true

                        property bool allApps: false
                        property int eligibleWidth: width
838
839
                        property int hItemMargins: Math.max(highlightItemSvg.margins.left + highlightItemSvg.margins.right,
                            listItemSvg.margins.left + listItemSvg.margins.right)
840
841
842
843
844
845

                        model: rootModel

                        boundsBehavior: Flickable.StopAtBounds
                        snapMode: ListView.SnapToItem
                        spacing: 0
846
                        keyNavigationWraps: true
847
848
849
850
851
852
853

                        delegate: MouseArea {
                            id: item

                            signal actionTriggered(string actionId, variant actionArgument)
                            signal aboutToShowActionMenu(variant actionMenu)

854
                            property var m: model
855
856
                            property int textWidth: label.contentWidth
                            property int mouseCol
Laurent Montel's avatar
Laurent Montel committed
857
858
                            property bool hasActionList: ((model.favoriteId !== null)
                                || (("hasActionList" in model) && (model.hasActionList === true)))
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
                            property Item menu: actionMenu

                            width: parent.width
                            height: Math.ceil((label.paintedHeight
                                + Math.max(highlightItemSvg.margins.top + highlightItemSvg.margins.bottom,
                                listItemSvg.margins.top + listItemSvg.margins.bottom)) / 2) * 2

                            Accessible.role: Accessible.MenuItem
                            Accessible.name: model.display

                            acceptedButtons: Qt.LeftButton | Qt.RightButton

                            hoverEnabled: true

                            onContainsMouseChanged: {
                                if (!containsMouse) {
                                    updateCurrentItemTimer.stop();
                                }
                            }

879
                            onPositionChanged: mouse => { // Lazy menu implementation.
880
881
                                mouseCol = mouse.x;

Laurent Montel's avatar
Laurent Montel committed
882
                                if (justOpenedTimer.running || ListView.view.currentIndex === 0 || index === ListView.view.currentIndex) {
883
884
885
886
887
888
889
890
891
892
893
894
895
896
                                    updateCurrentItem();
                                } else if ((index == ListView.view.currentIndex - 1) && mouse.y < (height - 6)
                                    || (index == ListView.view.currentIndex + 1) && mouse.y > 5) {

                                    if (mouse.x > ListView.view.eligibleWidth - 5) {
                                        updateCurrentItem();
                                    }
                                } else if (mouse.x > ListView.view.eligibleWidth) {
                                    updateCurrentItem();
                                }

                                updateCurrentItemTimer.restart();
                            }

897
                            onPressed: mouse => {
898
899
900
901
902
903
904
                                if (mouse.buttons & Qt.RightButton) {
                                    if (hasActionList) {
                                        openActionMenu(item, mouse.x, mouse.y);
                                    }
                                }
                            }

905
                            onClicked: mouse => {
906
907
908
909
910
                                if (mouse.button == Qt.LeftButton) {
                                    updateCurrentItem();
                                }
                            }

911
912
                            onAboutToShowActionMenu: {
                                var actionList = hasActionList ? model.actionList : [];
913
                                Tools.fillActionMenu(i18n, actionMenu, actionList, ListView.view.model.favoritesModel, model.favoriteId);
914
915
916
                            }

                            onActionTriggered: {
917
918
919
                                if (Tools.triggerAction(ListView.view.model, model.index, actionId, actionArgument) === true) {
                                    plasmoid.expanded = false;
                                }
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
                            }

                            function openActionMenu(visualParent, x, y) {
                                aboutToShowActionMenu(actionMenu);
                                actionMenu.visualParent = visualParent;
                                actionMenu.open(x, y);
                            }

                            function updateCurrentItem() {
                                ListView.view.currentIndex = index;
                                ListView.view.eligibleWidth = Math.min(width, mouseCol);
                            }

                            ActionMenu {
                                id: actionMenu

                                onActionClicked: {
                                    actionTriggered(actionId, actionArgument);
                                }
                            }

                            Timer {
                                id: updateCurrentItemTimer

                                interval: 50
                                repeat: false

                                onTriggered: parent.updateCurrentItem()
                            }

                            PlasmaExtras.Heading {
                                id: label

                                anchors {
                                    fill: parent
                                    leftMargin: highlightItemSvg.margins.left
                                    rightMargin: highlightItemSvg.margins.right
                                }

                                elide: Text.ElideRight
                                wrapMode: Text.NoWrap
                                opacity: 1.0

                                color: "white"

                                level: 1

                                text: model.display
                            }
                        }

                        highlight: PlasmaComponents.Highlight {
                            anchors {
                                top: filterList.currentItem ? filterList.currentItem.top : undefined
                                left: filterList.currentItem ? filterList.currentItem.left : undefined
                                bottom: filterList.currentItem ? filterList.currentItem.bottom : undefined
                            }

                            opacity: filterListScrollArea.focus ? 1.0 : 0.7

                            width: (highlightItemSvg.margins.left
                                + filterList.currentItem.textWidth
                                + highlightItemSvg.margins.right
983
                                + PlasmaCore.Units.smallSpacing)
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004

                            visible: filterList.currentItem
                        }

                        highlightFollowsCurrentItem: false
                        highlightMoveDuration: 0
                        highlightResizeDuration: 0

                        onCurrentIndexChanged: applyFilter()

                        onCountChanged: {
                            var width = 0;

                            for (var i = 0; i < rootModel.count; ++i) {
                                headingMetrics.text = rootModel.labelForRow(i);

                                if (headingMetrics.width > width) {
                                    width = headingMetrics.width;
                                }
                            }

1005
                            filterListColumn.columns = Math.ceil(width / root.cellSize);
1006
                            filterListScrollArea.width = width + hItemMargins + (PlasmaCore.Units.gridUnit * 2);
1007
1008
1009
                        }

                        function applyFilter() {
1010
                            if (!root.searching && currentIndex >= 0) {
1011
1012
1013
1014
                                if (tabBar.activeTab == 1) {
                                    root.widgetExplorer.widgetsModel.filterQuery = currentItem.m.filterData;
                                    root.widgetExplorer.widgetsModel.filterType = currentItem.m.filterType;

1015
1016
1017
                                    allApps = false;
                                    funnelModel.sourceModel = model;

1018
1019
1020
                                    return;
                                }

1021
1022
1023
1024
                                if (preloadAllAppsTimer.running) {
                                    preloadAllAppsTimer.stop();
                                }

1025
1026
                                var model = rootModel.modelForRow(currentIndex);

Laurent Montel's avatar
Laurent Montel committed
1027
                                if (model.description === "KICKER_ALL_MODEL") {
1028
1029
1030
                                    allAppsGrid.model = model;
                                    allApps = true;
                                    funnelModel.sourceModel = null;
1031
                                    preloadAllAppsTimer.done = true;
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
                                } else {
                                    funnelModel.sourceModel = model;
                                    allApps = false;
                                }
                            } else {
                                funnelModel.sourceModel = null;
                                allApps = false;
                            }
                        }

1042
                        Keys.onPressed: event => {
Laurent Montel's avatar
Laurent Montel committed
1043
                            if (event.key === Qt.Key_Left) {
1044
1045
                                event.accepted = true;

1046
                                var currentRow = Math.max(0, Math.ceil(currentItem.y / mainGrid.cellHeight) - 1);
1047
                                mainColumn.tryActivate(currentRow, mainColumn.columns - 1);
Laurent Montel's avatar
Laurent Montel committed
1048
                            } else if (event.key === Qt.Key_Tab) {
Eike Hein's avatar
Eike Hein committed
1049
1050
                                event.accepted = true;
                                systemFavoritesGrid.tryActivate(0, 0);
Laurent Montel's avatar
Laurent Montel committed
1051
                            } else if (event.key === Qt.Key_Backtab) {
Eike Hein's avatar
Eike Hein committed
1052
1053
                                event.accepted = true;
                                mainColumn.tryActivate(0, 0);
1054
1055
1056
1057
1058
1059
1060
                            }
                        }
                    }
                }
            }
        }

1061
        onPressed: mouse => {
1062
1063
1064
1065
1066
            if (mouse.button == Qt.RightButton) {
                contextMenu.open(mouse.x, mouse.y);
            }
        }

1067
        onClicked: mouse => {
1068
1069
1070
1071
1072
1073
            if (mouse.button == Qt.LeftButton) {
                root.toggle();
            }
        }
    }
}