1/*
2    SPDX-FileCopyrightText: 2015 Eike Hein <hein@kde.org>
3
4    SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7import QtQuick 2.4
8
9import org.kde.plasma.core 2.0 as PlasmaCore
10import org.kde.plasma.extras 2.0 as PlasmaExtras
11
12import org.kde.plasma.private.kicker 0.1 as Kicker
13
14PlasmaExtras.ScrollArea {
15    id: itemMultiGrid
16
17    anchors {
18        top: parent.top
19    }
20
21    width: parent.width
22
23    implicitHeight: itemColumn.implicitHeight
24
25    signal keyNavLeft(int subGridIndex)
26    signal keyNavRight(int subGridIndex)
27    signal keyNavUp()
28    signal keyNavDown()
29
30    property bool grabFocus: false
31
32    property alias model: repeater.model
33    property alias count: repeater.count
34
35    flickableItem.flickableDirection: Flickable.VerticalFlick
36
37    onFocusChanged: {
38        if (!focus) {
39            for (var i = 0; i < repeater.count; i++) {
40                subGridAt(i).focus = false;
41            }
42        }
43    }
44
45    function subGridAt(index) {
46        return repeater.itemAt(index).itemGrid;
47    }
48
49    function tryActivate(row, col) { // FIXME TODO: Cleanup messy algo.
50        if (flickableItem.contentY > 0) {
51            row = 0;
52        }
53
54        var target = null;
55        var rows = 0;
56
57        for (var i = 0; i < repeater.count; i++) {
58            var grid = subGridAt(i);
59
60            if (rows <= row) {
61                target = grid;
62                rows += grid.lastRow() + 2; // Header counts as one.
63            } else {
64                break;
65            }
66        }
67
68        if (target) {
69            rows -= (target.lastRow() + 2);
70            target.tryActivate(row - rows, col);
71        }
72    }
73
74    Column {
75        id: itemColumn
76
77        width: itemMultiGrid.width - PlasmaCore.Units.gridUnit
78
79        Repeater {
80            id: repeater
81
82            delegate: Item {
83                width: itemColumn.width - PlasmaCore.Units.gridUnit
84                height: headerHeight + gridView.height + (index == repeater.count - 1 ? 0 : footerHeight)
85
86                property int headerHeight: (gridViewLabel.height
87                    + gridViewLabelUnderline.height + PlasmaCore.Units.largeSpacing)
88                property int footerHeight: (Math.ceil(headerHeight / cellSize) * cellSize) - headerHeight
89
90                property Item itemGrid: gridView
91
92                PlasmaExtras.Heading {
93                    id: gridViewLabel
94
95                    anchors.top: parent.top
96
97                    x: PlasmaCore.Units.smallSpacing
98                    width: parent.width - x
99                    height: dummyHeading.height
100
101                    elide: Text.ElideRight
102                    wrapMode: Text.NoWrap
103                    opacity: 1.0
104
105                    color: "white" // FIXME TODO: Respect theming?
106
107                    level: 1
108
109                    text: repeater.model.modelForRow(index).description
110                }
111
112                PlasmaCore.SvgItem {
113                    id: gridViewLabelUnderline
114
115                    anchors.top: gridViewLabel.bottom
116
117                    width: parent.width - PlasmaCore.Units.gridUnit
118                    height: lineSvg.horLineHeight
119
120                    svg: lineSvg
121                    elementId: "horizontal-line"
122                }
123
124                MouseArea {
125                    width: parent.width
126                    height: parent.height
127
128                    onClicked: root.toggle()
129                }
130
131                ItemGridView {
132                    id: gridView
133
134                    anchors {
135                        top: gridViewLabelUnderline.bottom
136                        topMargin: PlasmaCore.Units.largeSpacing
137                    }
138
139                    width: parent.width
140                    height: Math.ceil(count / Math.floor(width / cellSize)) * cellSize
141
142                    cellWidth: cellSize
143                    cellHeight: cellSize
144                    iconSize: root.iconSize
145
146                    verticalScrollBarPolicy: Qt.ScrollBarAlwaysOff
147
148                    model: repeater.model.modelForRow(index)
149
150                    onFocusChanged: {
151                        if (focus) {
152                            itemMultiGrid.focus = true;
153                        }
154                    }
155
156                    onCountChanged: {
157                        if (itemMultiGrid.grabFocus && index == 0 && count > 0) {
158                            currentIndex = 0;
159                            focus = true;
160                        }
161                    }
162
163                    onCurrentItemChanged: {
164                        if (!currentItem) {
165                            return;
166                        }
167
168                        if (index == 0 && currentRow() === 0) {
169                            itemMultiGrid.flickableItem.contentY = 0;
170                            return;
171                        }
172
173                        var y = currentItem.y;
174                        y = contentItem.mapToItem(itemMultiGrid.flickableItem.contentItem, 0, y).y;
175
176                        if (y < itemMultiGrid.flickableItem.contentY) {
177                            itemMultiGrid.flickableItem.contentY = y;
178                        } else {
179                            y += cellSize;
180                            y -= itemMultiGrid.flickableItem.contentY;
181                            y -= itemMultiGrid.viewport.height;
182
183                            if (y > 0) {
184                                itemMultiGrid.flickableItem.contentY += y;
185                            }
186                        }
187                    }
188
189                    onKeyNavLeft: {
190                        itemMultiGrid.keyNavLeft(index);
191                    }
192
193                    onKeyNavRight: {
194                        itemMultiGrid.keyNavRight(index);
195                    }
196
197                    onKeyNavUp: {
198                        if (index > 0) {
199                            var prevGrid = subGridAt(index - 1);
200                            prevGrid.tryActivate(prevGrid.lastRow(), currentCol());
201                        } else {
202                            itemMultiGrid.keyNavUp();
203                        }
204                    }
205
206                    onKeyNavDown: {
207                        if (index < repeater.count - 1) {
208                            subGridAt(index + 1).tryActivate(0, currentCol());
209                        } else {
210                            itemMultiGrid.keyNavDown();
211                        }
212                    }
213                }
214
215                // HACK: Steal wheel events from the nested grid view and forward them to
216                // the ScrollView's internal WheelArea.
217                Kicker.WheelInterceptor {
218                    anchors.fill: gridView
219                    z: 1
220
221                    destination: findWheelArea(itemMultiGrid.flickableItem)
222                }
223            }
224        }
225    }
226}
227