me like nix
1import QtQuick
2import QtQuick.Layouts
3import Quickshell
4import "../../config" as Config
5import "../../components"
6import "../../services" as Services
7import "tabs"
8
9PanelWindow {
10 id: topTrigger
11
12 required property var modelData
13 screen: modelData
14
15 anchors {
16 top: true
17 left: true
18 right: true
19 }
20 margins.left: 60
21 margins.right: 8
22 margins.top: 0
23 implicitHeight: 2
24 exclusiveZone: 0
25 color: "transparent"
26
27 property bool dashboardOpen: false
28 property int activeTab: 0
29
30 MouseArea {
31 anchors.fill: parent
32 hoverEnabled: true
33 onEntered: { closeTimer.stop(); topTrigger.dashboardOpen = true }
34 onExited: closeTimer.restart()
35 }
36
37 onDashboardOpenChanged: {
38 if (dashboardOpen) {
39 contentRect._opening = true
40 fadeInTimer.start()
41 } else {
42 fadeInTimer.stop()
43 contentRect._opening = false
44 contentRect.opacity = 0
45 activeTab = 0
46 }
47 }
48
49 Timer {
50 id: closeTimer
51 interval: 400
52 onTriggered: topTrigger.dashboardOpen = false
53 }
54
55 PopupWindow {
56 id: dashPanel
57 anchor.window: topTrigger
58 anchor.onAnchoring: {
59 anchor.rect.x = (topTrigger.width - implicitWidth) / 2
60 anchor.rect.y = topTrigger.height + 4
61 }
62
63 visible: topTrigger.dashboardOpen || contentRect.opacity > 0
64 implicitWidth: Math.min(topTrigger.width, 900)
65 implicitHeight: contentCol.implicitHeight + 32
66 color: "transparent"
67
68 Timer {
69 id: fadeInTimer
70 interval: 16
71 onTriggered: contentRect.opacity = 1
72 }
73
74 Rectangle {
75 id: contentRect
76 anchors.fill: parent
77 color: Config.Colours.base
78 radius: Config.Appearance.rounding.large
79 border.width: 1
80 border.color: Config.Colours.surface0
81 opacity: 0
82
83 property bool _opening: false
84 Behavior on opacity {
85 Anim {
86 duration: contentRect._opening ? Config.Appearance.anim.durations.normal : Config.Appearance.anim.durations.small
87 easing.bezierCurve: contentRect._opening ? Config.Appearance.anim.curves.standardDecel : Config.Appearance.anim.curves.standardAccel
88 }
89 }
90
91 HoverHandler {
92 onHoveredChanged: {
93 if (hovered) closeTimer.stop()
94 else closeTimer.restart()
95 }
96 }
97
98 ColumnLayout {
99 id: contentCol
100 anchors.fill: parent
101 anchors.margins: 16
102 spacing: 0
103
104 // Tab bar
105 RowLayout {
106 id: tabBar
107 Layout.fillWidth: true
108 spacing: 0
109
110 Repeater {
111 model: [
112 { icon: "\uf0e4", label: "Dashboard" },
113 { icon: "\uf001", label: "Media" },
114 { icon: "\uf2db", label: "Performance" },
115 { icon: "\uf0c2", label: "Weather" }
116 ]
117 delegate: Rectangle {
118 required property var modelData
119 required property int index
120 Layout.fillWidth: true
121 height: 36
122 radius: Config.Appearance.rounding.normal
123 color: topTrigger.activeTab === index
124 ? Config.Colours.surface0
125 : (tabMouse.containsMouse ? Config.Colours.surface0 : "transparent")
126 opacity: topTrigger.activeTab === index ? 1.0 : (tabMouse.containsMouse ? 0.7 : 0.5)
127 Behavior on color { CAnim {} }
128 Behavior on opacity { Anim { duration: Config.Appearance.anim.durations.small } }
129
130 RowLayout {
131 anchors.centerIn: parent
132 spacing: 6
133 Text {
134 color: topTrigger.activeTab === index ? Config.Colours.blue : Config.Colours.subtext0
135 font.family: Config.Appearance.font.family
136 font.pixelSize: 14
137 text: modelData.icon
138 }
139 Text {
140 color: topTrigger.activeTab === index ? Config.Colours.text : Config.Colours.subtext0
141 font.family: Config.Appearance.font.family
142 font.pixelSize: Config.Appearance.font.size.small
143 text: modelData.label
144 }
145 }
146
147 MouseArea {
148 id: tabMouse
149 anchors.fill: parent
150 hoverEnabled: true
151 cursorShape: Qt.PointingHandCursor
152 onClicked: topTrigger.activeTab = index
153 }
154 }
155 }
156 }
157
158 // Tab indicator
159 Rectangle {
160 Layout.fillWidth: true
161 height: 2
162 color: "transparent"
163 Layout.topMargin: 4
164 Layout.bottomMargin: 8
165
166 Rectangle {
167 height: 2
168 radius: 1
169 color: Config.Colours.blue
170 width: parent.width / 4 - 16
171 x: (parent.width / 4) * topTrigger.activeTab + 8
172 Behavior on x {
173 Anim {
174 duration: Config.Appearance.anim.durations.normal
175 easing.bezierCurve: Config.Appearance.anim.curves.emphasized
176 }
177 }
178 }
179 }
180
181 // Tab content
182 Item {
183 id: tabContent
184 Layout.fillWidth: true
185 clip: true
186 implicitHeight: {
187 if (topTrigger.activeTab === 0) return dashTab.implicitHeight
188 if (topTrigger.activeTab === 1) return mediaTab.implicitHeight
189 if (topTrigger.activeTab === 2) return perfTab.implicitHeight
190 return weatherTab.implicitHeight
191 }
192
193 DashTab {
194 id: dashTab
195 width: parent.width
196 visible: topTrigger.activeTab === 0
197 opacity: visible ? 1 : 0
198 isOpen: topTrigger.dashboardOpen
199 Behavior on opacity { Anim { duration: Config.Appearance.anim.durations.small } }
200 }
201
202 MediaTab {
203 id: mediaTab
204 width: parent.width
205 visible: topTrigger.activeTab === 1
206 opacity: visible ? 1 : 0
207 Behavior on opacity { Anim { duration: Config.Appearance.anim.durations.small } }
208 }
209
210 PerformanceTab {
211 id: perfTab
212 width: parent.width
213 visible: topTrigger.activeTab === 2
214 opacity: visible ? 1 : 0
215 Behavior on opacity { Anim { duration: Config.Appearance.anim.durations.small } }
216 }
217
218 WeatherTab {
219 id: weatherTab
220 width: parent.width
221 visible: topTrigger.activeTab === 3
222 opacity: visible ? 1 : 0
223 Behavior on opacity { Anim { duration: Config.Appearance.anim.durations.small } }
224 }
225 }
226 }
227
228 Keys.onEscapePressed: topTrigger.dashboardOpen = false
229 }
230
231 // Click outside to close
232 MouseArea {
233 anchors.fill: parent
234 z: -1
235 onClicked: { closeTimer.stop(); topTrigger.dashboardOpen = false }
236 }
237 }
238}