1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
|
// Copyright (C) 2026 Danilo M. <danix@danix.xyz>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
import QtQuick
import QtQuick.Controls
Item {
id: page
property string title: ""
// The module's tile glyph, set by the drawer, shown before the title.
property string icon: ""
default property alias content: holder.data
signal back
// Closes the whole drawer, for a page whose action is finished once done
// (the clipboard copies and gets out of the way).
signal dismiss
// Opens another module's page by name, for a page that links to one (the
// user page opens the git page).
signal go(string name)
// The height the content area shows, for content that scrolls itself
// (the wallpaper list) and so wants a fixed height, not the page's scroll.
readonly property real viewHeight: scroll.height
Item {
id: header
anchors { top: parent.top; left: parent.left; right: parent.right }
height: 44
Rectangle {
id: backBtn
anchors { left: parent.left; verticalCenter: parent.verticalCenter }
width: 32; height: 32; radius: 16
color: backArea.containsMouse ? Qt.alpha(Theme.accent, 0.22) : "transparent"
Text {
anchors.centerIn: parent
text: ""
font { family: Theme.iconFamily; pixelSize: 14 }
color: Theme.text
}
MouseArea {
id: backArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: page.back()
}
}
Row {
anchors { left: backBtn.right; leftMargin: 10; verticalCenter: parent.verticalCenter }
spacing: 8
Text {
visible: page.icon !== ""
text: page.icon
font { family: Theme.iconFamily; pixelSize: Theme.fontSize + 2 }
color: Theme.text
}
Text {
text: page.title
font { family: Theme.fontFamily; pixelSize: Theme.fontSize + 2; bold: true }
color: Theme.text
}
}
}
Rectangle {
id: rule
anchors { top: header.bottom; left: parent.left; right: parent.right }
height: 1
color: Qt.alpha(Theme.text, 0.12)
}
// Scrolls to the content row whose objectName is key and flashes it, for
// a settings search hit. Rows usually arrive after the page loads its
// data, so it looks for up to 3s before giving up.
function reveal(key) {
finder.key = key;
finder.tries = 0;
finder.restart();
}
function findNamed(item, key) {
if (item.objectName === key) return item;
for (let i = 0; i < item.children.length; i++) {
const f = page.findNamed(item.children[i], key);
if (f) return f;
}
return null;
}
Timer {
id: finder
property string key: ""
property int tries: 0
interval: 100
repeat: true
onTriggered: {
const it = page.findNamed(holder, key);
if (!it) { if (++tries > 30) stop(); return; }
stop();
const p = it.mapToItem(holder, 0, 0);
const flick = scroll.contentItem;
flick.contentY = Math.max(0, Math.min(p.y - 40, flick.contentHeight - flick.height));
flash.x = p.x - 4;
flash.y = p.y - 2;
flash.width = it.width + 8;
flash.height = it.height + 4;
flashAnim.restart();
}
}
ScrollView {
id: scroll
anchors { top: rule.bottom; left: parent.left; right: parent.right; bottom: parent.bottom }
anchors.topMargin: 12
clip: true
contentWidth: availableWidth
Item {
id: holder
width: parent.width
implicitHeight: childrenRect.height
}
}
// The reveal highlight, over the row and in holder's coordinates.
// Declared out here, not in the ScrollView, which sizes from a single
// child; parent puts it in holder all the same.
Rectangle {
id: flash
parent: holder
z: 10
radius: 8
opacity: 0
color: Qt.alpha(Theme.accent, 0.28)
border.width: 1
border.color: Theme.accent
SequentialAnimation {
id: flashAnim
NumberAnimation { target: flash; property: "opacity"; to: 1; duration: 120 }
PauseAnimation { duration: 1400 }
NumberAnimation { target: flash; property: "opacity"; to: 0; duration: 600 }
}
}
}
|