aboutsummaryrefslogtreecommitdiffstats
path: root/appearance
diff options
context:
space:
mode:
Diffstat (limited to 'appearance')
-rw-r--r--appearance/AppearancePanel.qml35
-rw-r--r--appearance/Field.qml50
-rw-r--r--appearance/SunsetTab.qml169
-rw-r--r--appearance/Toggle.qml56
-rw-r--r--appearance/shell.qml3
5 files changed, 302 insertions, 11 deletions
diff --git a/appearance/AppearancePanel.qml b/appearance/AppearancePanel.qml
index 708682c..a4c12b6 100644
--- a/appearance/AppearancePanel.qml
+++ b/appearance/AppearancePanel.qml
@@ -21,6 +21,12 @@ Scope {
property string tab: "wallpaper"
property string notice: ""
+ readonly property var tabs: ["wallpaper", "theme", "sunset", "idle", "icons"]
+ readonly property var tabLabels: ({
+ wallpaper: "Wallpaper", theme: "Theme", sunset: "Sunset",
+ idle: "Idle", icons: "Icons",
+ })
+
// What the mock screens show. Hovering a thumbnail previews it on the
// targeted screen; the other keeps whatever is currently set.
property string hovering: ""
@@ -35,6 +41,7 @@ Scope {
// started, which for an autostarted one is the whole session.
Udt.refresh();
Wallpapers.refresh();
+ Hyprsunset.refresh();
root.open = true;
}
function close() { root.open = false; }
@@ -99,7 +106,8 @@ Scope {
Keys.onEscapePressed: root.close()
Keys.onPressed: event => {
if (event.key === Qt.Key_Tab) {
- root.tab = root.tab === "theme" ? "wallpaper" : "theme";
+ const i = root.tabs.indexOf(root.tab);
+ root.tab = root.tabs[(i + 1) % root.tabs.length];
event.accepted = true;
}
}
@@ -123,15 +131,14 @@ Scope {
Row {
spacing: 8
- Tab {
- text: "Wallpaper"
- selected: root.tab === "wallpaper"
- onClicked: root.tab = "wallpaper"
- }
- Tab {
- text: "Theme"
- selected: root.tab === "theme"
- onClicked: root.tab = "theme"
+ Repeater {
+ model: root.tabs
+ Tab {
+ required property var modelData
+ text: root.tabLabels[modelData]
+ selected: root.tab === modelData
+ onClicked: root.tab = modelData
+ }
}
Item { width: 12; height: 1 }
Text {
@@ -154,7 +161,13 @@ Scope {
Loader {
width: parent.width
height: parent.height - y
- sourceComponent: root.tab === "theme" ? themeTab : wallpaperTab
+ // Existing tabs are inline Components; the new ones are
+ // files. sourceComponent wins when it is non-null.
+ sourceComponent: root.tab === "theme" ? themeTab
+ : root.tab === "wallpaper" ? wallpaperTab : null
+ source: root.tab === "sunset" ? Qt.resolvedUrl("SunsetTab.qml")
+ : root.tab === "idle" ? Qt.resolvedUrl("IdleTab.qml")
+ : root.tab === "icons" ? Qt.resolvedUrl("IconsTab.qml") : ""
}
}
}
diff --git a/appearance/Field.qml b/appearance/Field.qml
new file mode 100644
index 0000000..004bda6
--- /dev/null
+++ b/appearance/Field.qml
@@ -0,0 +1,50 @@
+// 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
+
+Rectangle {
+ id: field
+ property alias value: input.text
+ property string placeholder: ""
+ signal edited
+
+ implicitWidth: 70
+ height: 24
+ radius: 5
+ color: Qt.alpha(Theme.surface, 0.5)
+ border.width: 1
+ border.color: input.activeFocus ? Qt.alpha(Theme.accent, 0.6) : Qt.alpha(Theme.text, 0.12)
+
+ Text {
+ anchors.fill: parent
+ anchors.leftMargin: 6
+ anchors.rightMargin: 6
+ verticalAlignment: Text.AlignVCenter
+ visible: input.text === ""
+ text: field.placeholder
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 }
+ color: Theme.overlay
+ }
+
+ TextInput {
+ id: input
+ anchors.fill: parent
+ anchors.leftMargin: 6
+ anchors.rightMargin: 6
+ verticalAlignment: TextInput.AlignVCenter
+ clip: true
+ selectByMouse: true
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 }
+ color: Theme.text
+ onEditingFinished: field.edited()
+ }
+}
diff --git a/appearance/SunsetTab.qml b/appearance/SunsetTab.qml
new file mode 100644
index 0000000..50f6ad6
--- /dev/null
+++ b/appearance/SunsetTab.qml
@@ -0,0 +1,169 @@
+// 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
+
+Flickable {
+ contentHeight: col.implicitHeight
+ clip: true
+
+ Column {
+ id: col
+ width: parent.width
+ spacing: 12
+
+ Row {
+ spacing: 8
+ Text {
+ anchors.verticalCenter: parent.verticalCenter
+ text: "Lat"
+ color: Theme.subtext
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 }
+ }
+ Field { width: 90; value: Hyprsunset.lat; onEdited: Hyprsunset.lat = value; placeholder: "lat" }
+ Text {
+ anchors.verticalCenter: parent.verticalCenter
+ text: "Lon"
+ color: Theme.subtext
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 }
+ }
+ Field { width: 90; value: Hyprsunset.lon; onEdited: Hyprsunset.lon = value; placeholder: "lon" }
+ Toggle {
+ anchors.verticalCenter: parent.verticalCenter
+ checked: Hyprsunset.autoDetect
+ label: "auto"
+ onToggled: Hyprsunset.autoDetect = checked
+ }
+ Tab { text: "Detect"; onClicked: Hyprsunset.detect() }
+ Tab { text: Hyprsunset.busy ? "fetching…" : "Fetch sun times"; onClicked: Hyprsunset.fetchSun() }
+ Text {
+ anchors.verticalCenter: parent.verticalCenter
+ text: Hyprsunset.sunSummary
+ color: Theme.overlay
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 }
+ }
+ }
+
+ Repeater {
+ model: Hyprsunset.profiles
+ Rectangle {
+ required property var modelData
+ required property int index
+ width: col.width
+ implicitHeight: 40
+ radius: 8
+ color: Qt.alpha(Theme.surface, 0.35)
+
+ Row {
+ anchors.fill: parent
+ anchors.margins: 8
+ spacing: 8
+
+ Text {
+ anchors.verticalCenter: parent.verticalCenter
+ text: index === Hyprsunset.dayIndex(Hyprsunset.profiles) ? "day"
+ : index === Hyprsunset.nightIndex(Hyprsunset.profiles) ? "night" : "profile"
+ color: Theme.subtext
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 }
+ }
+ Field {
+ anchors.verticalCenter: parent.verticalCenter
+ width: 60
+ value: modelData.time
+ placeholder: "HH:MM"
+ onEdited: Hyprsunset.profiles = Hyprsunset.profiles.map(
+ (p, i) => i === index ? Object.assign({}, p, { time: value }) : p)
+ }
+ Toggle {
+ anchors.verticalCenter: parent.verticalCenter
+ checked: modelData.identity
+ label: "identity"
+ onToggled: Hyprsunset.profiles = Hyprsunset.profiles.map(
+ (p, i) => i === index ? Object.assign({}, p, { identity: checked }) : p)
+ }
+ Toggle {
+ anchors.verticalCenter: parent.verticalCenter
+ checked: modelData.temperature !== null && modelData.temperature !== undefined
+ label: "temp"
+ onToggled: Hyprsunset.profiles = Hyprsunset.profiles.map(
+ (p, i) => i === index ? Object.assign({}, p,
+ { temperature: checked ? (p.temperature ?? 5500) : null }) : p)
+ }
+ Field {
+ anchors.verticalCenter: parent.verticalCenter
+ width: 70
+ value: modelData.temperature === null || modelData.temperature === undefined
+ ? "" : String(modelData.temperature)
+ placeholder: "5500"
+ onEdited: Hyprsunset.profiles = Hyprsunset.profiles.map(
+ (p, i) => i === index ? Object.assign({}, p,
+ { temperature: value === "" ? null : parseInt(value, 10) }) : p)
+ }
+ Toggle {
+ anchors.verticalCenter: parent.verticalCenter
+ checked: modelData.gamma !== null && modelData.gamma !== undefined
+ label: "gamma"
+ onToggled: Hyprsunset.profiles = Hyprsunset.profiles.map(
+ (p, i) => i === index ? Object.assign({}, p,
+ { gamma: checked ? (p.gamma ?? 1.0) : null }) : p)
+ }
+ Field {
+ anchors.verticalCenter: parent.verticalCenter
+ width: 60
+ value: modelData.gamma === null || modelData.gamma === undefined
+ ? "" : String(modelData.gamma)
+ placeholder: "0.8"
+ onEdited: Hyprsunset.profiles = Hyprsunset.profiles.map(
+ (p, i) => i === index ? Object.assign({}, p,
+ { gamma: value === "" ? null : parseFloat(value) }) : p)
+ }
+ Item { width: 8; height: 1 }
+ Tab {
+ anchors.verticalCenter: parent.verticalCenter
+ text: "✕"
+ onClicked: Hyprsunset.profiles =
+ Hyprsunset.profiles.filter((p, i) => i !== index)
+ }
+ // The Row is pinned to the card width, so parent.width is
+ // real, but bound it anyway: a narrow enough panel would
+ // make the subtraction negative and put the Row in a
+ // negative-width state.
+ Item { width: Math.max(0, parent.width - 640); height: 1 }
+ }
+ }
+ }
+
+ Row {
+ spacing: 8
+ Tab {
+ text: "+ Add profile"
+ onClicked: Hyprsunset.profiles =
+ Hyprsunset.profiles.concat([{ time: "0:00", identity: false,
+ temperature: null, gamma: null }])
+ }
+ Tab { text: "Live preview"; onClicked: Hyprsunset.preview() }
+ Tab {
+ text: "Save + Restart"
+ selected: true
+ onClicked: Hyprsunset.save()
+ }
+ }
+
+ Text {
+ width: parent.width
+ wrapMode: Text.Wrap
+ visible: Hyprsunset.notice !== ""
+ text: Hyprsunset.notice
+ color: Hyprsunset.notice.indexOf("invalid") === 0 ? Theme.red : Theme.green
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 }
+ }
+ }
+}
diff --git a/appearance/Toggle.qml b/appearance/Toggle.qml
new file mode 100644
index 0000000..2bb56a2
--- /dev/null
+++ b/appearance/Toggle.qml
@@ -0,0 +1,56 @@
+// 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
+
+Rectangle {
+ id: toggle
+ property bool checked: false
+ property string label: ""
+ signal toggled
+
+ implicitWidth: row.implicitWidth
+ implicitHeight: 22
+ color: "transparent"
+
+ Row {
+ id: row
+ anchors.verticalCenter: parent.verticalCenter
+ spacing: 6
+ Rectangle {
+ anchors.verticalCenter: parent.verticalCenter
+ width: 16; height: 16; radius: 4
+ color: toggle.checked ? Qt.alpha(Theme.accent, 0.7) : Qt.alpha(Theme.surface, 0.5)
+ border.width: 1
+ border.color: toggle.checked ? Qt.alpha(Theme.accent, 0.9) : Qt.alpha(Theme.text, 0.2)
+ Text {
+ anchors.centerIn: parent
+ visible: toggle.checked
+ text: "✓"
+ font { family: Theme.iconFamily; pixelSize: Theme.fontSize - 6; bold: true }
+ color: Theme.base
+ }
+ }
+ Text {
+ anchors.verticalCenter: parent.verticalCenter
+ visible: toggle.label !== ""
+ text: toggle.label
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 }
+ color: Theme.subtext
+ }
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ cursorShape: Qt.PointingHandCursor
+ onClicked: { toggle.checked = !toggle.checked; toggle.toggled(); }
+ }
+}
diff --git a/appearance/shell.qml b/appearance/shell.qml
index c0356a8..ea87fc3 100644
--- a/appearance/shell.qml
+++ b/appearance/shell.qml
@@ -20,6 +20,9 @@ ShellRoot {
function toggle() { panel.toggle(""); }
function wallpaper() { panel.toggle("wallpaper"); }
function theme() { panel.toggle("theme"); }
+ function sunset() { panel.toggle("sunset"); }
+ function idle() { panel.toggle("idle"); }
+ function icons() { panel.toggle("icons"); }
function selftest(): string {
return Hyprsunset.selftest() + "\n" + Hypridle.selftest() + "\n" + Icons.selftest();
}