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