diff --git a/bar/Bar.qml b/bar/Bar.qml index 90914b9..87807df 100644 --- a/bar/Bar.qml +++ b/bar/Bar.qml @@ -112,6 +112,7 @@ PanelWindow { required property string modelData source: Qt.resolvedUrl("modules/" + modelData + ".qml") onLoaded: barContent.bindModule(item, "left") + visible: !item || item.moduleEnabled !== false } } } @@ -139,6 +140,7 @@ PanelWindow { required property string modelData source: Qt.resolvedUrl("modules/" + modelData + ".qml") onLoaded: barContent.bindModule(item, "right") + visible: !item || item.moduleEnabled !== false } } } @@ -170,6 +172,7 @@ PanelWindow { required property string modelData source: Qt.resolvedUrl("modules/" + modelData + ".qml") onLoaded: barContent.bindModule(item, "center") + visible: !item || item.moduleEnabled !== false } } } diff --git a/bar/modules/Battery.qml b/bar/modules/Battery.qml new file mode 100644 index 0000000..eb74ccc --- /dev/null +++ b/bar/modules/Battery.qml @@ -0,0 +1,589 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import Quickshell.Wayland +import Quickshell.Services.UPower + +import "../../config" as Cfg + +Rectangle { + id: root + + property real panelRadius: Cfg.Config.radius / 2 + property int borderWidth: Cfg.Config.panelBorderWidth + + property bool isTop: true + property var targetScreen: null + property int barHeight: Cfg.Config.barHeight + property int barMargin: Cfg.Config.margin + + readonly property var t: Cfg.Config.theme + + readonly property var bat: UPower.displayDevice + readonly property bool batReady: bat && bat.ready && bat.isLaptopBattery + + readonly property bool moduleEnabled: batready + + readonly property real pct: batReady ? bat.percentage * 100 : 72 + readonly property int state: batReady ? bat.state : UPowerDeviceState.Unknown + readonly property bool charging: state === UPowerDeviceState.Charging + readonly property bool fullCharge: state === UPowerDeviceState.FullyCharged + + readonly property string batIcon: { + if (!batReady) return "󱉞" + if (fullCharge) return "󱟢" + if (charging) { + if (pct >= 90) return "󰂋" + if (pct >= 80) return "󰂊" + if (pct >= 70) return "󰢞" + if (pct >= 60) return "󰂉" + if (pct >= 50) return "󰢝" + if (pct >= 40) return "󰂈" + if (pct >= 30) return "󰂇" + if (pct >= 20) return "󰂆" + if (pct >= 10) return "󰢜" + return "󰢟" + } + if (pct >= 90) return "󰂂" + if (pct >= 80) return "󰂁" + if (pct >= 70) return "󰂀" + if (pct >= 60) return "󰁿" + if (pct >= 50) return "󰁾" + if (pct >= 40) return "󰁽" + if (pct >= 30) return "󰁼" + if (pct >= 20) return "󰁻" + if (pct >= 10) return "󰁺" + return "󰂎" + } + + readonly property bool isCritical: batReady && !charging && pct <= 15 + readonly property bool isLow: batReady && !charging && pct <= 30 + + readonly property color iconColor: { + if (fullCharge) return t.batteryFull ?? t.accent + if (charging) return t.batteryCharging ?? t.accent + if (isCritical) return t.batteryCritical ?? "#ff5555" + if (isLow) return t.batteryLow ?? "#ffb86c" + return t.batteryIcon ?? t.clockIcon + } + + readonly property var up: UPower + readonly property var pp: PowerProfiles + + readonly property string typeText: { + if (!batReady) return "—" + switch (bat.type) { + case UPowerDeviceType.Battery: return "Battery" + case UPowerDeviceType.Ups: return "UPS" + case UPowerDeviceType.Mouse: return "Mouse" + case UPowerDeviceType.Keyboard: return "Keyboard" + case UPowerDeviceType.Phone: return "Phone" + case UPowerDeviceType.Tablet: return "Tablet" + default: return "Other" + } + } + + readonly property string degradationText: { + if (pp.degradationReason === PerformanceDegradationReason.None) return "" + if (pp.degradationReason === PerformanceDegradationReason.LapDetected) return "Lap detected" + if (pp.degradationReason === PerformanceDegradationReason.HighTemperature) return "High temp" + return "" + } + + readonly property var profileHolds: { + let names = [] + for (let i = 0; i < pp.holds.length; i++) { + let h = pp.holds[i] + let label = PowerProfile.toString(h.profile) + if (h.applicationId) label += " (" + h.applicationId + ")" + names.push(label) + } + return names + } + + width: batLayout.implicitWidth + 24 + height: Cfg.Config.moduleHeight + radius: panelRadius + + color: hoverHandler.hovered ? t.batBgHover : t.batBg + border.color: hoverHandler.hovered ? t.batBorderHover : t.batBorder + border.width: borderWidth + + Behavior on width { NumberAnimation { duration: 250; easing.type: Easing.OutCubic } } + Behavior on color { ColorAnimation { duration: 150 } } + Behavior on border.color { ColorAnimation { duration: 150 } } + + HoverHandler { id: hoverHandler; cursorShape: Qt.PointingHandCursor } + TapHandler { onTapped: batteryPopup.toggle() } + + RowLayout { + id: batLayout + anchors.centerIn: parent + spacing: 0 + + Text { + text: root.batIcon + color: root.iconColor + font.pixelSize: 16 + Layout.alignment: Qt.AlignVCenter + Layout.rightMargin: 7 + + SequentialAnimation on opacity { + running: root.isCritical + loops: Animation.Infinite + NumberAnimation { to: 0.3; duration: 700; easing.type: Easing.InOutSine } + NumberAnimation { to: 1.0; duration: 700; easing.type: Easing.InOutSine } + } + opacity: 1.0 + } + + Text { + text: root.batReady ? Math.round(root.pct) + "%" : "—" + color: root.t.batText + font.pixelSize: 13 + font.bold: true + verticalAlignment: Text.AlignVCenter + } + + Text { + id: chargingLabel + text: root.charging ? " +" : (root.fullCharge ? " ✓" : "") + color: root.iconColor + font.pixelSize: 13 + font.bold: true + verticalAlignment: Text.AlignVCenter + + opacity: (hoverHandler.hovered || batteryPopup.popupOpen) ? 1.0 : 0.0 + Layout.preferredWidth: (hoverHandler.hovered || batteryPopup.popupOpen) + ? implicitWidth : 0 + clip: true + + Behavior on opacity { NumberAnimation { duration: 200 } } + Behavior on Layout.preferredWidth { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } } + } + } + + PanelWindow { + id: batteryPopup + screen: root.targetScreen + visible: false + color: "transparent" + + property bool popupOpen: false + + function open() { hideTimer.stop(); visible = true; popupOpen = true } + function close() { popupOpen = false; hideTimer.restart() } + function toggle() { popupOpen ? close() : open() } + + Timer { + id: hideTimer + interval: Cfg.Config.popupAnimDuration + 70 + onTriggered: batteryPopup.visible = false + } + + anchors { top: true; bottom: true; left: true; right: true } + WlrLayershell.layer: WlrLayer.Top + WlrLayershell.namespace: "main-shell-battery" + WlrLayershell.exclusiveZone: -1 + mask: popupOpen ? null : _noInput + Region { id: _noInput } + + TapHandler { onTapped: batteryPopup.close() } + + Item { + id: popupClipContainer + readonly property int cardW: 260 + readonly property int screenPad: root.barMargin + 10 + + x: { + let centerX = root.mapToItem(null, 0, 0).x + root.width / 2 + let desiredX = centerX - cardW / 2 + return Math.max(screenPad, + Math.min(desiredX, parent.width - cardW - screenPad)) + } + width: cardW + + anchors.top: root.isTop ? parent.top : undefined + anchors.bottom: root.isTop ? undefined : parent.bottom + anchors.topMargin: root.isTop ? (root.barHeight + 10) : 0 + anchors.bottomMargin: root.isTop ? 0 : (root.barHeight + 10) + height: parent.height - root.barHeight - 10 + + clip: true + + Rectangle { + id: cardShadow + anchors.fill: card + anchors.margins: -3 + radius: card.radius + 3 + color: "transparent" + border.color: Qt.rgba(0, 0, 0, 0.35) + border.width: 6 + visible: batteryPopup.popupOpen + opacity: batteryPopup.popupOpen ? 0.6 : 0 + Behavior on opacity { NumberAnimation { duration: 200 } } + } + + Rectangle { + id: card + width: parent.width + height: cardCol.implicitHeight + 28 + + anchors.top: root.isTop ? parent.top : undefined + anchors.bottom: root.isTop ? undefined : parent.bottom + + transform: Translate { + y: batteryPopup.popupOpen ? 0 + : (root.isTop ? -card.height : card.height) + Behavior on y { + NumberAnimation { + duration: Cfg.Config.popupAnimDuration + easing.type: Easing.OutExpo + } + enabled: batteryPopup.visible + } + } + + opacity: batteryPopup.popupOpen ? 1.0 : 0.0 + Behavior on opacity { NumberAnimation { duration: 220 } } + + color: root.t.batPopupBg + radius: Cfg.Config.popupRadius + border.color: root.t.batPopupBorder + border.width: Cfg.Config.popupBorderWidth + layer.enabled: true + + MouseArea { anchors.fill: parent; propagateComposedEvents: false } + + ColumnLayout { + id: cardCol + anchors { + left: parent.left + right: parent.right + top: parent.top + margins: 18 + topMargin: 20 + } + spacing: 12 + + RowLayout { + Layout.fillWidth: true + spacing: 14 + + Item { + width: 52 + height: 52 + + Rectangle { + anchors.centerIn: parent + width: 46 + height: 46 + radius: 23 + color: Qt.rgba( + Qt.darker(root.iconColor, 1.0).r, + Qt.darker(root.iconColor, 1.0).g, + Qt.darker(root.iconColor, 1.0).b, + 0.15 + ) + } + + Text { + anchors.centerIn: parent + text: root.batIcon + color: root.iconColor + font.pixelSize: 26 + + SequentialAnimation on opacity { + running: root.isCritical + loops: Animation.Infinite + NumberAnimation { to: 0.3; duration: 700; easing.type: Easing.InOutSine } + NumberAnimation { to: 1.0; duration: 700; easing.type: Easing.InOutSine } + } + opacity: 1.0 + } + } + + ColumnLayout { + spacing: 2 + Layout.fillWidth: true + + Text { + text: root.batReady ? Math.round(root.pct) + "%" : "—" + color: root.t.batPopupHeader + font.pixelSize: 30 + font.bold: true + } + + Text { + text: { + if (!root.batReady) return "No battery" + if (root.fullCharge) return "Fully charged" + if (root.charging) return "Charging" + if (root.isCritical) return "Critical" + if (root.isLow) return "Low" + return "Discharging" + } + color: root.iconColor + font.pixelSize: 11 + font.capitalization: Font.AllUppercase + font.letterSpacing: 0.8 + font.bold: true + } + } + } + + Item { + Layout.fillWidth: true + height: 10 + + Rectangle { + anchors.fill: parent + radius: 5 + color: root.t.batProgressTrack + } + + Rectangle { + width: Math.max(height, parent.width * (root.pct / 100)) + height: parent.height + radius: 5 + clip: false + + gradient: Gradient { + orientation: Gradient.Horizontal + GradientStop { + position: 0.0 + color: Qt.darker(root.iconColor, 1.25) + } + GradientStop { + position: 1.0 + color: root.iconColor + } + } + + Behavior on width { NumberAnimation { duration: 450; easing.type: Easing.OutCubic } } + } + + Rectangle { + anchors.fill: parent + radius: 5 + color: "transparent" + border.color: Qt.rgba(root.iconColor.r, + root.iconColor.g, + root.iconColor.b, 0.2) + border.width: 1 + } + } + + Rectangle { + Layout.fillWidth: true + height: 1 + color: root.t.batPopupSeparator + opacity: 0.6 + } + + component DetailRow: RowLayout { + required property string label + required property string value + Layout.fillWidth: true + + Text { + text: label + color: root.t.batPopupDim + font.pixelSize: 12 + Layout.fillWidth: true + } + Text { + text: value + color: root.t.batPopupText + font.pixelSize: 12 + font.bold: true + font.family: "monospace" + } + } + + DetailRow { + label: root.charging ? "Time to full" : "Time left" + value: { + if (!root.batReady || root.fullCharge) return "—" + let secs = root.charging ? bat.timeToFull : bat.timeToEmpty + if (secs <= 0) return "—" + let h = Math.floor(secs / 3600) + let m = Math.floor((secs % 3600) / 60) + return h > 0 ? h + "h " + m + "m" : m + "m" + } + } + + DetailRow { + label: "Rate" + value: { + if (!root.batReady) return "—" + let r = bat.changeRate + return (r >= 0 ? "+" : "") + r.toFixed(1) + " W" + } + } + + DetailRow { + label: "Health" + value: { + if (!root.batReady) return "—" + if (!bat.healthSupported) return "Undetected" + return Math.round(bat.healthPercentage) + "%" + } + } + + DetailRow { + label: "Energy" + value: { + if (!root.batReady) return "—" + return bat.energy.toFixed(1) + " / " + + bat.energyCapacity.toFixed(1) + " Wh" + } + } + + Item { + Layout.fillWidth: true + height: 16 + + Rectangle { + anchors.verticalCenter: parent.verticalCenter + width: parent.width + height: 1 + color: root.t.batPopupSeparator + opacity: 0.6 + } + + Rectangle { + anchors.centerIn: parent + width: sectionLabel.implicitWidth + 14 + height: sectionLabel.implicitHeight + 4 + radius: 4 + color: root.t.batPopupBg + + Text { + id: sectionLabel + anchors.centerIn: parent + text: "Power Profile" + color: root.t.batPopupDim + font.pixelSize: 10 + font.capitalization: Font.AllUppercase + font.letterSpacing: 0.8 + } + } + } + + component ProfileButton: Rectangle { + required property string text + required property var profileValue + property bool interactive: true + + property bool isActive: interactive && (root.pp.profile === profileValue) + + height: 38 + radius: 10 + color: isActive ? root.iconColor + : Qt.rgba(root.iconColor.r, root.iconColor.g, root.iconColor.b, + (profileHover.containsMouse && interactive) ? 0.14 : 0.06) + border.color: isActive + ? Qt.lighter(root.iconColor, 1.2) + : Qt.rgba(root.iconColor.r, root.iconColor.g, root.iconColor.b, 0.12) + border.width: 1 + + Behavior on color { ColorAnimation { duration: 150 } } + Behavior on border.color { ColorAnimation { duration: 150 } } + + Text { + anchors.centerIn: parent + text: parent.text + font.pixelSize: 10 + font.bold: parent.isActive + font.letterSpacing: 0.4 + color: parent.isActive ? root.t.batProfileActiveText : root.t.batPopupText + } + + HoverHandler { id: profileHover; enabled: parent.interactive } + + MouseArea { + anchors.fill: parent + cursorShape: parent.interactive ? Qt.PointingHandCursor : Qt.ArrowCursor + onClicked: { + if (parent.interactive) root.pp.profile = parent.profileValue + } + } + } + + RowLayout { + Layout.fillWidth: true + spacing: 6 + + ProfileButton { + text: "Power Saver" + profileValue: PowerProfile.PowerSaver + Layout.fillWidth: true + } + ProfileButton { + text: "Balanced" + profileValue: PowerProfile.Balanced + Layout.fillWidth: true + } + ProfileButton { + text: "Performance" + profileValue: PowerProfile.Performance + interactive: root.pp.hasPerformanceProfile + Layout.fillWidth: true + } + } + + DetailRow { + label: "Degradation" + value: root.degradationText || "None" + } + + DetailRow { + label: "Holds" + value: root.profileHolds.length > 0 ? root.profileHolds[0] : "—" + visible: root.profileHolds.length > 0 + } + + Item { + Layout.fillWidth: true + height: 16 + + Rectangle { + anchors.verticalCenter: parent.verticalCenter + width: parent.width + height: 1 + color: root.t.batPopupSeparator + opacity: 0.6 + } + + Rectangle { + anchors.centerIn: parent + width: devLabel.implicitWidth + 14 + height: devLabel.implicitHeight + 4 + radius: 4 + color: root.t.batPopupBg + + Text { + id: devLabel + anchors.centerIn: parent + text: "Device" + color: root.t.batPopupDim + font.pixelSize: 10 + font.capitalization: Font.AllUppercase + font.letterSpacing: 0.8 + } + } + } + + DetailRow { label: "Type"; value: root.typeText } + DetailRow { label: "Model"; value: root.batReady ? (bat.model || "Undetected") : "—" } + DetailRow { label: "Power supply"; value: root.batReady ? (bat.powerSupply ? "Yes" : "No") : "—" } + DetailRow { label: "On battery"; value: root.up.onBattery ? "Yes" : "No" } + DetailRow { label: "Devices"; value: root.up.devices.values.length + " total" } + + Item { height: 2 } + } + } + } + } +} diff --git a/bar/modules/Clock.qml b/bar/modules/Clock.qml index ab6bbe4..6d21977 100644 --- a/bar/modules/Clock.qml +++ b/bar/modules/Clock.qml @@ -2,6 +2,7 @@ import QtQuick import QtQuick.Layouts import Quickshell import Quickshell.Wayland +import Quickshell.Io import "../../config" as Cfg @@ -17,8 +18,44 @@ Rectangle { property int barMargin: Cfg.Config.margin readonly property var t: Cfg.Config.theme + readonly property string tz: Cfg.Config.timezone || "" - readonly property int tzOffsetHours: -timeManager.now.getTimezoneOffset() / 60 + + property int tzOffsetMin: 0 + + Process { + id: tzProc + command: ["bash", "-c", "TZ=" + (root.tz || "UTC") + " date +%z"] + running: root.tz !== "" + stdout: SplitParser { + onRead: data => { + var s = data.trim() + if (s.length < 5) return + var sign = (s[0] === "-") ? -1 : 1 + var h = parseInt(s.substring(1, 3), 10) + var m = parseInt(s.substring(3, 5), 10) + root.tzOffsetMin = sign * (h * 60 + m) + } + } + } + + Timer { + interval: 1800000; running: root.tz !== ""; repeat: true + onTriggered: tzProc.running = true + } + + function toTz(date) { + if (!tz) return date + + return new Date(date.getTime() + + date.getTimezoneOffset() * 60000 + + tzOffsetMin * 60000) + } + + function tzOffsetHours() { + if (!tz) return -timeManager.now.getTimezoneOffset() / 60 + return tzOffsetMin / 60 + } width: clockLayout.implicitWidth + 24 height: Cfg.Config.moduleHeight @@ -66,7 +103,7 @@ Rectangle { } Text { - text: timeManager.now.toLocaleTimeString(Qt.locale(), "HH:mm") + text: root.toTz(timeManager.now).toLocaleTimeString(Qt.locale(), "HH:mm") color: root.t.clockTime font.pixelSize: 13 font.bold: true @@ -75,7 +112,7 @@ Rectangle { Text { id: secondsText - text: ":" + timeManager.now.toLocaleTimeString(Qt.locale(), "ss") + text: ":" + root.toTz(timeManager.now).toLocaleTimeString(Qt.locale(), "ss") color: root.t.clockSeconds font.pixelSize: 13 font.bold: true @@ -230,7 +267,7 @@ Rectangle { Layout.fillWidth: true Text { - text: timeManager.now.toLocaleDateString(Qt.locale(), "dddd") + text: root.toTz(timeManager.now).toLocaleDateString(Qt.locale(), "dddd") color: root.t.clockPopupDim font.pixelSize: 12 font.capitalization: Font.AllUppercase @@ -239,7 +276,7 @@ Rectangle { } Text { - text: timeManager.now.toLocaleDateString(Qt.locale(), "d MMMM yyyy") + text: root.toTz(timeManager.now).toLocaleDateString(Qt.locale(), "d MMMM yyyy") color: root.t.clockPopupHeader font.pixelSize: 22 font.bold: true @@ -269,8 +306,8 @@ Rectangle { id: cal Layout.fillWidth: true implicitHeight: gridCol.implicitHeight - property int displayYear: timeManager.now.getFullYear() - property int displayMonth: timeManager.now.getMonth() + property int displayYear: root.toTz(timeManager.now).getFullYear() + property int displayMonth: root.toTz(timeManager.now).getMonth() property int pendingYear: displayYear property int pendingMonth: displayMonth property int slideDir: 1 @@ -279,8 +316,9 @@ Rectangle { target: calendarPopup function onPopupOpenChanged() { if (calendarPopup.popupOpen) { - cal.displayYear = timeManager.now.getFullYear() - cal.displayMonth = timeManager.now.getMonth() + let tzd = root.toTz(timeManager.now) + cal.displayYear = tzd.getFullYear() + cal.displayMonth = tzd.getMonth() } } } @@ -339,7 +377,12 @@ Rectangle { width: (card.width - 32) / 7; height: 26 readonly property int dayNum: (weekRow.weekIndex * 7 + index) - ((new Date(cal.displayYear, cal.displayMonth, 1).getDay() + 6) % 7) + 1 readonly property bool inMonth: dayNum >= 1 && dayNum <= new Date(cal.displayYear, cal.displayMonth + 1, 0).getDate() - readonly property bool isToday: inMonth && dayNum === timeManager.now.getDate() && cal.displayYear === timeManager.now.getFullYear() && cal.displayMonth === timeManager.now.getMonth() + readonly property bool isToday: { + let tzd = root.toTz(timeManager.now) + return inMonth && dayNum === tzd.getDate() + && cal.displayYear === tzd.getFullYear() + && cal.displayMonth === tzd.getMonth() + } Rectangle { anchors.centerIn: parent; width: 22; height: 22; radius: 11 @@ -371,7 +414,7 @@ Rectangle { Layout.rightMargin: 6 Text { - text: "UTC " + (root.tzOffsetHours >= 0 ? "+" : "") + root.tzOffsetHours + text: "UTC " + (root.tzOffsetHours() >= 0 ? "+" : "") + root.tzOffsetHours() color: root.t.clockPopupUtc font.pixelSize: 13 font.bold: true @@ -382,11 +425,11 @@ Rectangle { Text { text: { - let d = timeManager.now + let d = root.toTz(timeManager.now) let month = d.getMonth() + 1 let day = d.getDate() let year = d.getFullYear() - if (Cfg.Config.clockDateFormat === "DMY") + if (Cfg.Config.dateFormat === "DMY") return day + "/" + month + "/" + year return month + "/" + day + "/" + year } diff --git a/bar/modules/Crypto.qml b/bar/modules/Crypto.qml new file mode 100644 index 0000000..c1f13f4 --- /dev/null +++ b/bar/modules/Crypto.qml @@ -0,0 +1,1200 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import Quickshell.Wayland +import Quickshell.Io + +import "../../config" as Cfg + +Rectangle { + id: root + + property real panelRadius: Cfg.Config.radius / 2 + property int borderWidth: Cfg.Config.panelBorderWidth + property bool isTop: true + property var targetScreen: null + property int barHeight: Cfg.Config.barHeight + property int barMargin: Cfg.Config.margin + + readonly property var t: Cfg.Config.theme + readonly property var cfg: Cfg.Config + + readonly property string configPath: + Quickshell.env("HOME") + "/.config/quickshell/config/config.qml" + + Process { + id: cryptoConfigWriter + running: false + onExited: (code) => { + if (code !== 0) + console.warn("[Crypto] Failed to patch config.qml (exit " + code + ")") + } + } + + width: chipRow.implicitWidth + 24 + height: cfg.moduleHeight + radius: panelRadius + + color: hoverHandler.hovered ? (t.cryptoBgHover ?? "#15ffffff") : (t.cryptoBg ?? "transparent") + border.color: (hoverHandler.hovered || cryptoPopup.popupOpen) ? (t.cryptoBorderHover ?? t.accent ?? "#ffc19375") : (t.cryptoBorder ?? "transparent") + border.width: borderWidth + + Behavior on width { NumberAnimation { duration: 250; easing.type: Easing.OutCubic } } + Behavior on color { ColorAnimation { duration: 150 } } + Behavior on border.color { ColorAnimation { duration: 150 } } + + property var localPairs: [{ coin: cfg.cryptoCoinId ?? "bitcoin", currency: cfg.cryptoVsCurrency ?? "usd" }] + property int activeCoinIndex: 0 + readonly property string localCoinId: localPairs.length > 0 + ? localPairs[activeCoinIndex % localPairs.length].coin + : (cfg.cryptoCoinId ?? "bitcoin") + readonly property string localActiveCurrency: localPairs.length > 0 + ? localPairs[activeCoinIndex % localPairs.length].currency + : (cfg.cryptoVsCurrency ?? "usd") + + property string localVsCur: cfg.cryptoVsCurrency ?? "usd" + property int localDecimals: cfg.cryptoDecimals ?? 1 + property int localRefreshSec: cfg.cryptoRefreshSec ?? 60 + property bool localShowIcon: cfg.cryptoShowIcon ?? true + + function resolveWithPersistence() { + const cfgCoin = cfg.cryptoCoinId ?? "bitcoin" + const cfgCur = cfg.cryptoVsCurrency ?? "usd" + + let cfgPairs + if (typeof cfg.cryptoPairs !== "undefined" && Array.isArray(cfg.cryptoPairs) && cfg.cryptoPairs.length > 0) { + cfgPairs = cfg.cryptoPairs + } else if (typeof cfg.cryptoCoinIds !== "undefined" && Array.isArray(cfg.cryptoCoinIds) && cfg.cryptoCoinIds.length > 0) { + cfgPairs = cfg.cryptoCoinIds.map(c => ({ coin: c, currency: cfgCur })) + } else { + cfgPairs = [{ coin: cfgCoin, currency: cfgCur }] + } + + root.localPairs = cfgPairs + root.localVsCur = cfgPairs.length > 0 ? cfgPairs[0].currency : cfgCur + root.displayCurrency = cfgPairs.length > 0 ? cfgPairs[0].currency : cfgCur + root.activeCoinIndex = 0 + root.localDecimals = cfg.cryptoDecimals ?? 1 + root.localRefreshSec = cfg.cryptoRefreshSec ?? 60 + root.localShowIcon = cfg.cryptoShowIcon ?? true + } + + function persistSettings() { + const pairs = root.localPairs + const decimals = Math.round(root.localDecimals) + const refreshSec = Math.round(root.localRefreshSec) + const showIcon = root.localShowIcon + const cfgPath = root.configPath + + const pairsQml = "[" + pairs.map(p => + '{ coin: "' + p.coin + '", currency: "' + p.currency + '" }' + ).join(", ") + "]" + + + const sh = + "CFG=" + JSON.stringify(cfgPath) + "\n" + + "sed -i" + + " -e 's/\\(property int[ ]\\+cryptoDecimals:[ ]\\+\\)[0-9.]\\+/\\1" + decimals.toString() + "/'" + + " -e 's/\\(property int[ ]\\+cryptoRefreshSec:[ ]\\+\\)[0-9.]\\+/\\1" + refreshSec.toString() + "/'" + + " -e 's/\\(property bool[ ]\\+cryptoShowIcon:[ ]\\+\\)\\(true\\|false\\)/\\1" + (showIcon ? "true" : "false") + "/'" + + " \"$CFG\"\n" + + "QS_PAIRS='" + pairsQml + "'\n" + + "awk -v pairs=\"$QS_PAIRS\" '" + + "/property var cryptoPairs:/{print \" property var cryptoPairs: \"pairs;sk=/\\]/?0:1;next}" + + "sk&&/\\]/{sk=0;next}" + + "sk{next}" + + "{print}'" + + " \"$CFG\" > \"${CFG}.tmp\" && mv \"${CFG}.tmp\" \"$CFG\"\n" + + cryptoConfigWriter.command = ["/bin/sh", "-c", sh] + cryptoConfigWriter.running = true + } + + property real price: -1 + property real change24h: 0 + property real high24h: 0 + property real low24h: 0 + property real marketCap: 0 + property real volume24h: 0 + property int rank: 0 + property real circulatingSupply: 0 + property real ath: 0 + property real athChange: 0 + property string coinSymbol: localCoinId.toUpperCase().slice(0, 4) + property string coinName: "" + property string lastUpdated: "" + property string displayCurrency: cfg.cryptoVsCurrency ?? "usd" + property bool loading: false + property bool loadingVisible: false + property bool hasError: false + + Timer { + id: minLoadTimer + interval: 2000 + repeat: false + onTriggered: { if (!root.loading) root.loadingVisible = false } + } + + onLoadingChanged: { + if (loading) { + loadingVisible = true + minLoadTimer.restart() + } else { + if (!minLoadTimer.running) + loadingVisible = false + } + } + + function compactPrice(val) { + if (val < 0) return "—" + const factor = Math.pow(10, localDecimals) + if (val >= 1_000_000) { + const n = Math.trunc(val * factor / 1_000_000) + return (n / factor).toFixed(localDecimals) + "m" + } + if (val >= 1_000) { + const n = Math.trunc(val * factor / 1_000) + return (n / factor).toFixed(localDecimals) + "k" + } + return val.toFixed(Math.max(localDecimals, 2)) + } + function fullPrice(val) { + if (val < 0) return "—" + return val.toLocaleString(Qt.locale(), "f", 2) + } + function compactLarge(val) { + if (val <= 0) return "—" + if (val >= 1_000_000_000) return (val / 1_000_000_000).toFixed(2) + "B" + if (val >= 1_000_000) return (val / 1_000_000).toFixed(2) + "M" + if (val >= 1_000) return (val / 1_000).toFixed(2) + "k" + return val.toFixed(2) + } + + function fetchPrice() { + if (loading) return + loading = true + hasError = false + + const url = + "https://api.coingecko.com/api/v3/coins/markets" + + "?vs_currency=" + encodeURIComponent(localActiveCurrency || "usd") + + "&ids=" + encodeURIComponent(localCoinId || "bitcoin") + + "&order=market_cap_desc&per_page=1&page=1" + + "&sparkline=false&price_change_percentage=24h" + + const xhr = new XMLHttpRequest() + xhr.onreadystatechange = function () { + if (xhr.readyState !== XMLHttpRequest.DONE) return + loading = false + if (xhr.status !== 200) { hasError = true; return } + try { + const data = JSON.parse(xhr.responseText) + if (!data || data.length === 0) { hasError = true; return } + const coin = data[0] + price = coin.current_price ?? -1 + change24h = coin.price_change_percentage_24h ?? 0 + high24h = coin.high_24h ?? 0 + low24h = coin.low_24h ?? 0 + marketCap = coin.market_cap ?? 0 + volume24h = coin.total_volume ?? 0 + rank = coin.market_cap_rank ?? 0 + circulatingSupply = coin.circulating_supply ?? 0 + ath = coin.ath ?? 0 + athChange = coin.ath_change_percentage ?? 0 + coinSymbol = (coin.symbol ?? localCoinId).toUpperCase() + coinName = coin.name ?? "" + lastUpdated = new Date().toLocaleTimeString(Qt.locale(), "HH:mm:ss") + root.displayCurrency = root.localActiveCurrency + root.activeCoinIndex = (root.activeCoinIndex + 1) % Math.max(1, root.localPairs.length) + } catch (_) { + hasError = true + } + } + xhr.open("GET", url) + xhr.setRequestHeader("Accept", "application/json") + xhr.send() + } + + Component.onCompleted: { resolveWithPersistence(); fetchPrice() } + + Timer { + id: refreshTimer + interval: root.localRefreshSec * 1000 + running: true + repeat: true + onTriggered: root.fetchPrice() + } + + onLocalRefreshSecChanged: refreshTimer.restart() + + HoverHandler { + id: hoverHandler + cursorShape: Qt.PointingHandCursor + } + TapHandler { onTapped: cryptoPopup.toggle() } + + RowLayout { + id: chipRow + anchors.centerIn: parent + spacing: 0 + + SequentialAnimation on opacity { + running: root.loadingVisible + loops: Animation.Infinite + NumberAnimation { from: 1; to: 0.25; duration: 500; easing.type: Easing.InOutSine } + NumberAnimation { from: 0.25; to: 1; duration: 500; easing.type: Easing.InOutSine } + } + + Connections { + target: root + function onLoadingVisibleChanged() { + if (!root.loadingVisible) chipRow.opacity = 1 + } + } + + Text { + visible: root.localShowIcon + text: "󰿤" + color: t.cryptoIcon ?? t.accent ?? "#ffc19375" + font.pixelSize: 14 + Layout.alignment: Qt.AlignVCenter + Layout.rightMargin: 7 + } + + Text { + text: root.coinSymbol + "/" + root.displayCurrency.toUpperCase() + ": " + color: t.cryptoLabel ?? t.textDim ?? "#997a8996" + font.pixelSize: 12 + verticalAlignment: Text.AlignVCenter + } + + Text { + text: root.hasError ? "err" : root.price < 0 ? "…" : root.compactPrice(root.price) + color: t.cryptoValue ?? t.textMain ?? "#ffe8eef2" + font.pixelSize: 13 + font.bold: true + verticalAlignment: Text.AlignVCenter + } + + Text { + visible: root.price >= 0 + text: root.change24h >= 0 ? "▲" : "▼" + color: root.change24h >= 0 ? (t.cryptoUp ?? "#ffb4c9a1") : (t.cryptoDown ?? "#fff38ba8") + font.pixelSize: 9 + verticalAlignment: Text.AlignVCenter + Layout.alignment: Qt.AlignVCenter + Layout.leftMargin: 4 + Behavior on color { ColorAnimation { duration: 200 } } + } + + } + + PanelWindow { + id: cryptoPopup + screen: root.targetScreen + visible: false + color: "transparent" + + property bool popupOpen: false + property real popupCenterX: 0 + + function open() { popupCenterX = root.mapToItem(null, 0, 0).x + root.width / 2; hideTimer.stop(); visible = true; popupOpen = true } + function close() { popupOpen = false; hideTimer.restart() } + function toggle() { popupOpen ? close() : open() } + + Timer { + id: hideTimer + interval: Cfg.Config.popupAnimDuration + 70 + onTriggered: cryptoPopup.visible = false + } + + anchors { top: true; bottom: true; left: true; right: true } + WlrLayershell.layer: WlrLayer.Top + WlrLayershell.namespace: "main-shell-crypto" + WlrLayershell.exclusiveZone: -1 + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + mask: popupOpen ? null : _noInput + Region { id: _noInput } + + TapHandler { onTapped: cryptoPopup.close() } + + Item { + id: popClip + readonly property int cardW: 300 + readonly property int screenPad: root.barMargin + 10 + + x: { + let cx = cryptoPopup.popupCenterX + return Math.max(screenPad, + Math.min(cx - cardW / 2, parent.width - cardW - screenPad)) + } + width: cardW + + anchors.top: root.isTop ? parent.top : undefined + anchors.bottom: root.isTop ? undefined : parent.bottom + anchors.topMargin: root.isTop ? (root.barHeight + 10) : 0 + anchors.bottomMargin: root.isTop ? 0 : (root.barHeight + 10) + height: parent.height - root.barHeight - 10 + clip: true + + Rectangle { + id: card + width: parent.width + height: cardCol.implicitHeight + 24 + + anchors.top: root.isTop ? parent.top : undefined + anchors.bottom: root.isTop ? undefined : parent.bottom + + transform: Translate { + y: cryptoPopup.popupOpen ? 0 : (root.isTop ? -card.height : card.height) + Behavior on y { + NumberAnimation { duration: Cfg.Config.popupAnimDuration; easing.type: Easing.OutExpo } + enabled: cryptoPopup.visible + } + } + + opacity: cryptoPopup.popupOpen ? 1.0 : 0.0 + Behavior on opacity { NumberAnimation { duration: 200 } } + Behavior on height { NumberAnimation { duration: 220; easing.type: Easing.OutCubic } } + + color: t.cryptoPopupBg ?? t.calCardBg ?? "#cc0c0e12" + radius: Cfg.Config.popupRadius + border.color: t.cryptoPopupBorder ?? t.calCardBorder ?? "transparent" + border.width: Cfg.Config.popupBorderWidth + layer.enabled: true + + MouseArea { anchors.fill: parent; propagateComposedEvents: false } + + property int activeTab: 0 + + ColumnLayout { + id: cardCol + anchors { left: parent.left; right: parent.right; top: parent.top } + anchors.margins: 16 + anchors.topMargin: 16 + spacing: 10 + + Rectangle { + Layout.fillWidth: true + height: 30 + radius: 10 + color: "#15ffffff" + + RowLayout { + anchors.fill: parent + anchors.margins: 3 + spacing: 3 + + component TabBtn: Rectangle { + required property string label + required property int tabIndex + Layout.fillWidth: true + height: 24 + radius: 8 + + readonly property bool active: card.activeTab === tabIndex + color: active + ? (t.accent ?? "#ffc19375") + : (tm.containsMouse ? "#22ffffff" : "transparent") + Behavior on color { ColorAnimation { duration: 130 } } + + Text { + anchors.centerIn: parent + text: parent.label + color: parent.active ? "#ff0c0e12" : (t.cryptoPopupDim ?? t.textDim ?? "#997a8996") + font.pixelSize: 11 + font.bold: parent.active + Behavior on color { ColorAnimation { duration: 130 } } + } + MouseArea { + id: tm + anchors.fill: parent; hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: card.activeTab = tabIndex + } + } + + TabBtn { label: "Price"; tabIndex: 0 } + TabBtn { label: "Settings"; tabIndex: 1 } + } + } + + Item { + id: tabContainer + Layout.fillWidth: true + clip: true + + Layout.preferredHeight: Math.max(priceContent.implicitHeight, + settingsContent.implicitHeight) + + ColumnLayout { + id: priceContent + width: tabContainer.width + x: card.activeTab === 0 ? 0 : -tabContainer.width + opacity: card.activeTab === 0 ? 1.0 : 0.0 + Behavior on x { NumberAnimation { duration: 260; easing.type: Easing.OutCubic } } + Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } } + spacing: 10 + + RowLayout { + Layout.fillWidth: true + + Text { + text: "󰿤" + color: t.cryptoIcon ?? t.accent ?? "#ffc19375" + font.pixelSize: 22 + Layout.alignment: Qt.AlignVCenter + } + + ColumnLayout { + spacing: 1 + Layout.fillWidth: true + Layout.leftMargin: 8 + + Text { + text: root.coinSymbol + " / " + root.displayCurrency.toUpperCase() + color: t.cryptoPopupHeader ?? t.clockPopupHeader ?? "#ffc19375" + font.pixelSize: 15 + font.bold: true + } + Text { + text: root.coinName !== "" ? root.coinName : (root.localCoinId.charAt(0).toUpperCase() + root.localCoinId.slice(1)) + color: t.cryptoPopupDim ?? t.textDim ?? "#997a8996" + font.pixelSize: 11 + } + } + + Rectangle { + width: badge.implicitWidth + 12 + height: 22; radius: 11 + color: root.change24h >= 0 ? "#22b4c9a1" : "#22f38ba8" + + Text { + id: badge + anchors.centerIn: parent + text: (root.change24h >= 0 ? "▲ +" : "▼ ") + root.change24h.toFixed(2) + "%" + color: root.change24h >= 0 ? (t.cryptoUp ?? "#ffb4c9a1") : (t.cryptoDown ?? "#fff38ba8") + font.pixelSize: 11 + font.bold: true + } + } + } + + Text { + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + text: root.price < 0 ? "Loading…" : root.displayCurrency.toUpperCase() + " " + root.fullPrice(root.price) + color: t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2" + font.pixelSize: 24 + font.bold: true + font.family: "monospace" + } + + Rectangle { Layout.fillWidth: true; height: 1; color: t.calSeparator ?? "#334a5d6e" } + + component StatRow: RowLayout { + property string label: "" + property string value: "" + Layout.fillWidth: true + Text { text: label; color: t.cryptoPopupDim ?? t.textDim ?? "#997a8996"; font.pixelSize: 12; Layout.fillWidth: true } + Text { text: value; color: t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2"; font.pixelSize: 12; font.bold: true; font.family: "monospace" } + } + + StatRow { label: "Rank"; value: root.rank > 0 ? "#" + root.rank : "—" } + StatRow { label: "24h High"; value: root.high24h > 0 ? root.fullPrice(root.high24h) : "—" } + StatRow { label: "24h Low"; value: root.low24h > 0 ? root.fullPrice(root.low24h) : "—" } + StatRow { label: "Market Cap"; value: root.compactLarge(root.marketCap) } + StatRow { label: "24h Volume"; value: root.compactLarge(root.volume24h) } + StatRow { label: "Circ. Supply"; value: root.circulatingSupply > 0 ? root.compactLarge(root.circulatingSupply) + " " + root.coinSymbol : "—" } + + RowLayout { + Layout.fillWidth: true + Text { text: "ATH"; color: t.cryptoPopupDim ?? t.textDim ?? "#997a8996"; font.pixelSize: 12; Layout.fillWidth: true } + Text { + text: root.ath > 0 ? root.fullPrice(root.ath) : "—" + color: t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2" + font.pixelSize: 12; font.bold: true; font.family: "monospace" + } + Text { + visible: root.ath > 0 + text: "(" + root.athChange.toFixed(2) + "%)" + color: t.cryptoDown ?? "#fff38ba8" + font.pixelSize: 11; font.bold: true; font.family: "monospace" + } + } + + ColumnLayout { + Layout.fillWidth: true + spacing: 5 + + RowLayout { + Layout.fillWidth: true + Text { + text: "24h Range" + color: t.cryptoPopupDim ?? t.textDim ?? "#997a8996" + font.pixelSize: 12 + Layout.fillWidth: true + } + Text { + visible: root.high24h > root.low24h && root.price >= 0 + text: ((root.price - root.low24h) / (root.high24h - root.low24h) * 100).toFixed(1) + "% of range" + color: t.cryptoPopupDim ?? t.textDim ?? "#997a8996" + font.pixelSize: 11 + } + } + + Item { + Layout.fillWidth: true + height: 6 + + Rectangle { + anchors.fill: parent + radius: 3 + color: "#1affffff" + } + + Rectangle { + height: parent.height + radius: 3 + color: root.change24h >= 0 + ? (t.cryptoUp ?? "#ffb4c9a1") + : (t.cryptoDown ?? "#fff38ba8") + width: (root.high24h > root.low24h && root.price >= 0) + ? Math.max(radius * 2, + Math.min(parent.width, + (root.price - root.low24h) / (root.high24h - root.low24h) + * parent.width)) + : 0 + Behavior on width { NumberAnimation { duration: 500; easing.type: Easing.OutCubic } } + } + } + + RowLayout { + Layout.fillWidth: true + Text { + text: root.low24h > 0 ? root.fullPrice(root.low24h) : "—" + color: t.cryptoDown ?? "#fff38ba8" + font.pixelSize: 10 + font.family: "monospace" + } + Item { Layout.fillWidth: true } + Text { + text: root.high24h > 0 ? root.fullPrice(root.high24h) : "—" + color: t.cryptoUp ?? "#ffb4c9a1" + font.pixelSize: 10 + font.family: "monospace" + } + } + } + + Rectangle { Layout.fillWidth: true; height: 1; color: t.calSeparator ?? "#334a5d6e" } + + RowLayout { + Layout.fillWidth: true + + Text { + text: root.hasError ? "⚠ fetch error" : "Updated " + (root.lastUpdated || "—") + color: t.cryptoPopupDim ?? t.textDim ?? "#997a8996" + font.pixelSize: 11 + Layout.fillWidth: true + } + + Rectangle { + id: refreshBtn + width: 26; height: 26; radius: 8 + color: rMouse.containsMouse + ? (rMouse.pressed ? (t.accent ?? "#ffc19375") : "#22ffffff") + : "transparent" + Behavior on color { ColorAnimation { duration: 120 } } + + Text { + id: refreshIcon + anchors.centerIn: parent + text: "󰑐" + color: t.cryptoIcon ?? t.accent ?? "#ffc19375" + font.pixelSize: 14 + + RotationAnimator { + id: spinAnim + target: refreshIcon + from: 0; to: 360 + duration: 600 + easing.type: Easing.OutCubic + running: false + } + } + + MouseArea { + id: rMouse + anchors.fill: parent; hoverEnabled: true; cursorShape: Qt.PointingHandCursor + onClicked: { + spinAnim.restart() + root.fetchPrice() + } + } + } + } + } + + ColumnLayout { + id: settingsContent + width: tabContainer.width + x: card.activeTab === 1 ? 0 : tabContainer.width + opacity: card.activeTab === 1 ? 1.0 : 0.0 + Behavior on x { NumberAnimation { duration: 260; easing.type: Easing.OutCubic } } + Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } } + spacing: 12 + + component FieldRow: ColumnLayout { + property string label: "" + property string placeholder: "" + property alias fieldText: fi.text + spacing: 4 + Layout.fillWidth: true + + Text { + text: label + color: t.cryptoPopupDim ?? t.textDim ?? "#997a8996" + font.pixelSize: 11 + font.capitalization: Font.AllUppercase + } + + Rectangle { + Layout.fillWidth: true + height: 30 + radius: 8 + color: fi.activeFocus ? "#22ffffff" : "#15ffffff" + border.color: fi.activeFocus ? (t.accent ?? "#ffc19375") : "transparent" + border.width: 1 + Behavior on color { ColorAnimation { duration: 120 } } + Behavior on border.color { ColorAnimation { duration: 120 } } + + TextInput { + id: fi + anchors { fill: parent; leftMargin: 10; rightMargin: 10; topMargin: 7; bottomMargin: 7 } + color: t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2" + font.pixelSize: 12 + font.family: "monospace" + selectionColor: t.accent ?? "#ffc19375" + clip: true + + Text { + anchors.fill: parent + text: parent.parent.parent.placeholder + color: "#55" + (t.textDim ?? "#7a8996").slice(1) + font.pixelSize: 12 + font.family: "monospace" + visible: fi.text === "" && !fi.activeFocus + verticalAlignment: Text.AlignVCenter + } + } + } + } + + component StepRow: RowLayout { + property string label: "" + property int value: 1 + property int minVal: 0 + property int maxVal: 999 + signal stepped(int v) + Layout.fillWidth: true + + Text { text: label; color: t.cryptoPopupDim ?? t.textDim ?? "#997a8996"; font.pixelSize: 12; Layout.fillWidth: true } + + Rectangle { + width: 24; height: 24; radius: 7 + color: mm.containsMouse ? (mm.pressed ? (t.accent ?? "#ffc19375") : "#22ffffff") : "#15ffffff" + Behavior on color { ColorAnimation { duration: 100 } } + Text { anchors.centerIn: parent; text: "−"; font.pixelSize: 14; font.bold: true; color: t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2" } + MouseArea { id: mm; anchors.fill: parent; hoverEnabled: true; cursorShape: Qt.PointingHandCursor; onClicked: if (value > minVal) stepped(value - 1) } + } + + Rectangle { + width: 54; height: 24; radius: 6; color: "#15ffffff" + Text { anchors.centerIn: parent; text: value; color: t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2"; font.pixelSize: 12; font.bold: true; font.family: "monospace" } + } + + Rectangle { + width: 24; height: 24; radius: 7 + color: pm.containsMouse ? (pm.pressed ? (t.accent ?? "#ffc19375") : "#22ffffff") : "#15ffffff" + Behavior on color { ColorAnimation { duration: 100 } } + Text { anchors.centerIn: parent; text: "+"; font.pixelSize: 14; font.bold: true; color: t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2" } + MouseArea { id: pm; anchors.fill: parent; hoverEnabled: true; cursorShape: Qt.PointingHandCursor; onClicked: if (value < maxVal) stepped(value + 1) } + } + } + + component ToggleRow: RowLayout { + property string label: "" + property bool checked: false + signal toggled(bool v) + Layout.fillWidth: true + + Text { text: label; color: t.cryptoPopupDim ?? t.textDim ?? "#997a8996"; font.pixelSize: 12; Layout.fillWidth: true } + + Rectangle { + width: 36; height: 20; radius: 10 + color: checked ? (t.accent ?? "#ffc19375") : "#22ffffff" + Behavior on color { ColorAnimation { duration: 150 } } + + Rectangle { + width: 14; height: 14; radius: 7; color: "#ffffffff" + anchors.verticalCenter: parent.verticalCenter + x: checked ? parent.width - 17 : 3 + Behavior on x { NumberAnimation { duration: 150; easing.type: Easing.OutCubic } } + } + + MouseArea { anchors.fill: parent; cursorShape: Qt.PointingHandCursor; onClicked: toggled(!checked) } + } + } + + ColumnLayout { + Layout.fillWidth: true + spacing: 4 + + Text { + text: "Pairs to cycle" + color: t.cryptoPopupDim ?? t.textDim ?? "#997a8996" + font.pixelSize: 11 + font.capitalization: Font.AllUppercase + } + + ColumnLayout { + Layout.fillWidth: true + spacing: 4 + + Item { + Layout.fillWidth: true + height: 24 + clip: true + + Rectangle { + anchors { left: parent.left; top: parent.top; bottom: parent.bottom } + width: 12; z: 2 + gradient: Gradient { + orientation: Gradient.Horizontal + GradientStop { position: 0.0; color: t.cryptoPopupBg ?? "#cc0c0e12" } + GradientStop { position: 1.0; color: "transparent" } + } + visible: chipScroll.contentX > 0 + } + Rectangle { + anchors { right: parent.right; top: parent.top; bottom: parent.bottom } + width: 12; z: 2 + gradient: Gradient { + orientation: Gradient.Horizontal + GradientStop { position: 0.0; color: "transparent" } + GradientStop { position: 1.0; color: t.cryptoPopupBg ?? "#cc0c0e12" } + } + visible: chipScroll.contentX < chipScroll.contentWidth - chipScroll.width - 1 + } + + Flickable { + id: chipScroll + anchors.fill: parent + contentWidth: chipRow2.implicitWidth + contentHeight: height + clip: true + flickableDirection: Flickable.HorizontalFlick + boundsBehavior: Flickable.StopAtBounds + + Row { + id: chipRow2 + spacing: 8 + + Repeater { + model: root.localPairs + delegate: Item { + readonly property bool isActive: + index === root.activeCoinIndex % Math.max(1, root.localPairs.length) + + height: 24 + width: chipLabel.implicitWidth + dotSpace.width + 6 + + (root.localPairs.length > 1 ? 18 : 0) + + Rectangle { + id: dotSpace + anchors.verticalCenter: parent.verticalCenter + x: 0; width: 5; height: 5; radius: 3 + color: isActive ? (t.accent ?? "#ffc19375") : "#33ffffff" + Behavior on color { ColorAnimation { duration: 150 } } + } + + Text { + id: chipLabel + anchors.verticalCenter: parent.verticalCenter + x: dotSpace.width + 5 + text: (modelData.coin ?? "?") + "/" + (modelData.currency ?? "?").toUpperCase() + color: isActive + ? (t.accent ?? "#ffc19375") + : (t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2") + font.pixelSize: 12 + font.bold: isActive + font.family: "monospace" + Behavior on color { ColorAnimation { duration: 150 } } + } + + Rectangle { + visible: root.localPairs.length > 1 + anchors { right: parent.right + verticalCenter: parent.verticalCenter } + width: 14; height: 14; radius: 4 + color: rmChipMouse.containsMouse ? "#44f38ba8" : "transparent" + Behavior on color { ColorAnimation { duration: 100 } } + Text { + anchors.centerIn: parent + text: "×"; font.pixelSize: 11; font.bold: true + color: t.cryptoDown ?? "#fff38ba8" + } + MouseArea { + id: rmChipMouse + anchors.fill: parent; hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + let arr = root.localPairs.slice() + arr.splice(index, 1) + root.localPairs = arr + if (root.activeCoinIndex >= arr.length) + root.activeCoinIndex = 0 + } + } + } + } + } + } + } + } + + Item { + Layout.fillWidth: true + height: 3 + visible: chipScroll.contentWidth > chipScroll.width + + Rectangle { + anchors.fill: parent + radius: 2 + color: "#15ffffff" + } + + Rectangle { + height: parent.height + radius: 2 + color: t.accent ?? "#ffc19375" + opacity: 0.7 + + readonly property real ratio: chipScroll.width / Math.max(1, chipScroll.contentWidth) + width: Math.max(16, parent.width * ratio) + + x: (parent.width - width) * + (chipScroll.contentWidth > chipScroll.width + ? chipScroll.contentX / (chipScroll.contentWidth - chipScroll.width) + : 0) + + Behavior on x { NumberAnimation { duration: 80 } } + } + } + } + + ColumnLayout { + Layout.fillWidth: true + spacing: 4 + + Text { + text: "Coin ID" + color: t.cryptoPopupDim ?? t.textDim ?? "#997a8996" + font.pixelSize: 11 + font.capitalization: Font.AllUppercase + } + + Rectangle { + Layout.fillWidth: true + height: 30; radius: 8 + color: addCoinInput.activeFocus ? "#22ffffff" : "#15ffffff" + border.color: addCoinInput.activeFocus ? (t.accent ?? "#ffc19375") : "transparent" + border.width: 1 + Behavior on color { ColorAnimation { duration: 120 } } + Behavior on border.color { ColorAnimation { duration: 120 } } + + TextInput { + id: addCoinInput + anchors { fill: parent; leftMargin: 10; rightMargin: 10; topMargin: 7; bottomMargin: 7 } + color: t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2" + font.pixelSize: 12; font.family: "monospace" + selectionColor: t.accent ?? "#ffc19375" + clip: true + + Text { + anchors.fill: parent + text: "e.g. bitcoin, monero, solana" + color: "#55" + (t.textDim ?? "#7a8996").slice(1) + font.pixelSize: 12; font.family: "monospace" + visible: parent.text === "" && !parent.activeFocus + verticalAlignment: Text.AlignVCenter + } + + onAccepted: addCurrencyInput.forceActiveFocus() + } + } + } + + ColumnLayout { + Layout.fillWidth: true + spacing: 4 + + Text { + text: "Currency" + color: t.cryptoPopupDim ?? t.textDim ?? "#997a8996" + font.pixelSize: 11 + font.capitalization: Font.AllUppercase + } + + RowLayout { + Layout.fillWidth: true + spacing: 6 + + Rectangle { + Layout.fillWidth: true + height: 30; radius: 8 + color: addCurrencyInput.activeFocus ? "#22ffffff" : "#15ffffff" + border.color: addCurrencyInput.activeFocus ? (t.accent ?? "#ffc19375") : "transparent" + border.width: 1 + Behavior on color { ColorAnimation { duration: 120 } } + Behavior on border.color { ColorAnimation { duration: 120 } } + + TextInput { + id: addCurrencyInput + anchors { fill: parent; leftMargin: 10; rightMargin: 10; topMargin: 7; bottomMargin: 7 } + color: t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2" + font.pixelSize: 12; font.family: "monospace" + selectionColor: t.accent ?? "#ffc19375" + clip: true + + Text { + anchors.fill: parent + text: "e.g. usd, eur, brl" + color: "#55" + (t.textDim ?? "#7a8996").slice(1) + font.pixelSize: 12; font.family: "monospace" + visible: parent.text === "" && !parent.activeFocus + verticalAlignment: Text.AlignVCenter + } + + onAccepted: { + const c = addCoinInput.text.trim().toLowerCase() + const cur = addCurrencyInput.text.trim().toLowerCase() || root.localVsCur + if (c !== "") { + root.localPairs = root.localPairs.concat([{ coin: c, currency: cur }]) + addCoinInput.text = "" + addCurrencyInput.text = "" + } + } + } + } + + Rectangle { + width: 30; height: 30; radius: 8 + color: addPairMouse.containsMouse + ? (addPairMouse.pressed ? (t.accent ?? "#ffc19375") : "#22ffffff") + : "#15ffffff" + Behavior on color { ColorAnimation { duration: 100 } } + Text { + anchors.centerIn: parent; text: "+" + font.pixelSize: 14; font.bold: true + color: t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2" + } + MouseArea { + id: addPairMouse + anchors.fill: parent; hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + const c = addCoinInput.text.trim().toLowerCase() + const cur = addCurrencyInput.text.trim().toLowerCase() || root.localVsCur + if (c !== "") { + root.localPairs = root.localPairs.concat([{ coin: c, currency: cur }]) + addCoinInput.text = "" + addCurrencyInput.text = "" + } + } + } + } + } + } + } + + StepRow { + label: "Decimals in label" + value: root.localDecimals + minVal: 0 + maxVal: 8 + onStepped: (v) => root.localDecimals = v + } + + RowLayout { + Layout.fillWidth: true + + Text { + text: "Refresh (seconds)" + color: t.cryptoPopupDim ?? t.textDim ?? "#997a8996" + font.pixelSize: 12 + Layout.fillWidth: true + } + + Rectangle { + width: 24; height: 24; radius: 7 + color: decMouse.containsMouse + ? (decMouse.pressed ? (t.accent ?? "#ffc19375") : "#22ffffff") + : "#15ffffff" + Behavior on color { ColorAnimation { duration: 100 } } + Text { anchors.centerIn: parent; text: "−"; font.pixelSize: 14; font.bold: true; color: t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2" } + MouseArea { + id: decMouse + anchors.fill: parent; hoverEnabled: true; cursorShape: Qt.PointingHandCursor + onClicked: if (root.localRefreshSec > 10) root.localRefreshSec -= 1 + } + } + + Rectangle { + width: 54; height: 24; radius: 6 + color: refreshInput.activeFocus ? "#22ffffff" : "#15ffffff" + border.color: refreshInput.activeFocus ? (t.accent ?? "#ffc19375") : "transparent" + border.width: 1 + Behavior on color { ColorAnimation { duration: 120 } } + Behavior on border.color { ColorAnimation { duration: 120 } } + + TextInput { + id: refreshInput + anchors { fill: parent; leftMargin: 6; rightMargin: 6; topMargin: 4; bottomMargin: 4 } + text: root.localRefreshSec.toString() + color: t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2" + font.pixelSize: 12 + font.bold: true + font.family: "monospace" + selectionColor: t.accent ?? "#ffc19375" + horizontalAlignment: TextInput.AlignHCenter + inputMethodHints: Qt.ImhDigitsOnly + validator: IntValidator { bottom: 10; top: 3600 } + clip: true + + onActiveFocusChanged: if (!activeFocus) { + const v = parseInt(text) + if (!isNaN(v) && v >= 10 && v <= 3600) + root.localRefreshSec = v + else + text = root.localRefreshSec.toString() + } + onAccepted: { + const v = parseInt(text) + if (!isNaN(v) && v >= 10 && v <= 3600) + root.localRefreshSec = v + else + text = root.localRefreshSec.toString() + focus = false + } + + Connections { + target: root + function onLocalRefreshSecChanged() { + if (!refreshInput.activeFocus) + refreshInput.text = root.localRefreshSec.toString() + } + } + } + } + + Rectangle { + width: 24; height: 24; radius: 7 + color: incMouse.containsMouse + ? (incMouse.pressed ? (t.accent ?? "#ffc19375") : "#22ffffff") + : "#15ffffff" + Behavior on color { ColorAnimation { duration: 100 } } + Text { anchors.centerIn: parent; text: "+"; font.pixelSize: 14; font.bold: true; color: t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2" } + MouseArea { + id: incMouse + anchors.fill: parent; hoverEnabled: true; cursorShape: Qt.PointingHandCursor + onClicked: if (root.localRefreshSec < 3600) root.localRefreshSec += 1 + } + } + } + + ToggleRow { + label: "Show icon on bar" + checked: root.localShowIcon + onToggled: (v) => root.localShowIcon = v + } + + Rectangle { Layout.fillWidth: true; height: 1; color: t.calSeparator ?? "#334a5d6e" } + + Rectangle { + Layout.fillWidth: true + height: 32 + radius: 9 + color: applyMouse.containsMouse + ? (applyMouse.pressed + ? Qt.darker(t.accent ?? "#ffc19375", 1.15) + : (t.accent ?? "#ffc19375")) + : (t.cryptoApplyIdleBg ?? "#33c19375") + Behavior on color { ColorAnimation { duration: 130 } } + + Text { + anchors.centerIn: parent + text: "Apply & Refresh" + color: applyMouse.containsMouse ? "#ff0c0e12" : (t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2") + font.pixelSize: 12 + font.bold: true + Behavior on color { ColorAnimation { duration: 130 } } + } + + MouseArea { + id: applyMouse + anchors.fill: parent; hoverEnabled: true; cursorShape: Qt.PointingHandCursor + onClicked: { + root.price = -1 + root.activeCoinIndex = 0 + root.coinSymbol = root.localCoinId.toUpperCase().slice(0, 4) + root.persistSettings() + root.fetchPrice() + card.activeTab = 0 + } + } + } + + Text { + Layout.alignment: Qt.AlignHCenter + text: "Reset to defaults" + color: t.cryptoPopupDim ?? t.textDim ?? "#997a8996" + font.pixelSize: 11 + font.underline: true + + MouseArea { + anchors.fill: parent; cursorShape: Qt.PointingHandCursor + onClicked: { + const cfgCoin = cfg.cryptoCoinId ?? "bitcoin" + const cfgCur = cfg.cryptoVsCurrency ?? "usd" + let cfgPairs + if (typeof cfg.cryptoPairs !== "undefined" && Array.isArray(cfg.cryptoPairs) && cfg.cryptoPairs.length > 0) { + cfgPairs = cfg.cryptoPairs + } else if (typeof cfg.cryptoCoinIds !== "undefined" && Array.isArray(cfg.cryptoCoinIds) && cfg.cryptoCoinIds.length > 0) { + cfgPairs = cfg.cryptoCoinIds.map(c => ({ coin: c, currency: cfgCur })) + } else { + cfgPairs = [{ coin: cfgCoin, currency: cfgCur }] + } + root.localPairs = cfgPairs + root.localVsCur = cfgCur + root.displayCurrency = cfgPairs.length > 0 ? cfgPairs[0].currency : cfgCur + root.activeCoinIndex = 0 + root.localDecimals = cfg.cryptoDecimals ?? 1 + root.localRefreshSec = cfg.cryptoRefreshSec ?? 60 + root.localShowIcon = cfg.cryptoShowIcon ?? true + root.persistSettings() + root.price = -1 + card.activeTab = 0 + } + } + } + + Item { height: 2 } + } + } + } + } + } + } +} diff --git a/bar/modules/ArchLogo.qml b/bar/modules/Logo.qml similarity index 90% rename from bar/modules/ArchLogo.qml rename to bar/modules/Logo.qml index 0957440..966778a 100644 --- a/bar/modules/ArchLogo.qml +++ b/bar/modules/Logo.qml @@ -1,6 +1,4 @@ import QtQuick -import QtQuick.Layouts -import Quickshell import "../../config" as Cfg @@ -37,10 +35,10 @@ Rectangle { width: Cfg.Config.logoIconSize * 2 + 4 height: parent.height - color: isHovered ? t.archLogoBgHover : t.archLogoBg + color: isHovered ? t.logoBgHover : t.logoBg radius: panelRadius - border.color: launcherActive ? t.archLogoBorderActive : t.archLogoBorder + border.color: launcherActive ? t.logoBorderActive : t.logoBorder border.width: borderWidth Behavior on color { ColorAnimation { duration: 250 } } @@ -69,7 +67,7 @@ Rectangle { anchors.centerIn: parent text: Cfg.Config.logoIcon - color: launcherActive ? t.archLogoIconActive : t.archLogoIcon + color: launcherActive ? t.logoIconActive : t.logoIcon font.pixelSize: Cfg.Config.logoIconSize Behavior on color { ColorAnimation { duration: 250 } } diff --git a/bar/modules/Network.qml b/bar/modules/Network.qml new file mode 100644 index 0000000..1d1d131 --- /dev/null +++ b/bar/modules/Network.qml @@ -0,0 +1,728 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import Quickshell.Wayland +import Quickshell.Networking +import Quickshell.Services.Polkit + +import "../../config" as Cfg + +Rectangle { + id: root + + property real panelRadius: Cfg.Config.radius / 2 + property int borderWidth: Cfg.Config.panelBorderWidth + property bool isTop: true + property var targetScreen: null + property int barHeight: Cfg.Config.barHeight + property int barMargin: Cfg.Config.margin + + readonly property var t: Cfg.Config.theme + + readonly property var wifiDevice: { + for (const dev of (Networking.devices?.values ?? [])) + if (dev.type === DeviceType.Wifi) return dev + return null + } + + readonly property var wiredDevice: { + for (const dev of (Networking.devices?.values ?? [])) + if (dev.type === DeviceType.Wired) return dev + return null + } + + readonly property bool hasWifi: wifiDevice !== null + readonly property bool hasWired: wiredDevice !== null + readonly property bool wifiUp: hasWifi && wifiDevice.connected + readonly property bool wiredUp: hasWired && wiredDevice.connected + + readonly property string connectivityLabel: { + switch (Networking.connectivity) { + case NetworkConnectivity.Portal: return "Captive Portal" + case NetworkConnectivity.Limited: return "Limited" + case NetworkConnectivity.None: return "No Internet" + default: return "" + } + } + + readonly property string wifiModeLabel: { + if (!wifiUp || !wifiDevice) return "" + switch (wifiDevice.mode) { + case WifiDeviceMode.Infra: return "Infra" + case WifiDeviceMode.Ap: return "AP" + case WifiDeviceMode.Adhoc: return "Ad-Hoc" + case WifiDeviceMode.Mesh: return "Mesh" + default: return "" + } + } + + + + readonly property var activeAP: { + for (const n of (wifiDevice?.networks?.values ?? [])) + if (n.connected) return n + return null + } + readonly property string ssid: activeAP ? (activeAP.name || "") : "" + readonly property int strength: activeAP ? Math.round((activeAP.signalStrength || 0) * 100) : 0 + + readonly property string wifiIcon: { + if (!wifiUp) return "󰤭" + if (strength >= 80) return "󰤨" + if (strength >= 60) return "󰤥" + if (strength >= 40) return "󰤢" + if (strength >= 20) return "󰤟" + return "󰤯" + } + + readonly property string displayIcon: hasWifi ? wifiIcon : "" + + readonly property color iconColor: { + if (hasWifi) return wifiUp ? t.statusOk : t.statusErr + if (hasWired) return wiredUp ? t.statusOk : t.statusErr + return t.textDim + } + + readonly property string chipLabel: { + if (hasWifi) return (wifiUp && ssid !== "") ? ssid : "Wi-Fi" + if (hasWired) return wiredUp ? "Ethernet" : "Offline" + return "No NIC" + } + + property var pendingAP: null + property string pendingSSID: "" + property bool showPwdPrompt: false + property string enteredPwd: "" + + function attemptConnect() { + if (!pendingAP) return + if (enteredPwd.length > 0) + pendingAP.connectWithPsk(enteredPwd) + else + pendingAP.connect() + pendingAP = null + pendingSSID = "" + enteredPwd = "" + showPwdPrompt = false + } + + function cancelConnect() { + pendingAP = null + pendingSSID = "" + enteredPwd = "" + showPwdPrompt = false + } + + implicitWidth: chipRow.implicitWidth + 24 + height: Cfg.Config.moduleHeight + radius: panelRadius + color: chipHover.hovered ? t.netBgHover : t.netBg + border.color: chipHover.hovered ? t.netBorderHover : t.netBorder + border.width: borderWidth + + Behavior on implicitWidth { NumberAnimation { duration: 250; easing.type: Easing.OutCubic } } + Behavior on color { ColorAnimation { duration: 150 } } + Behavior on border.color { ColorAnimation { duration: 150 } } + + HoverHandler { id: chipHover; cursorShape: Qt.PointingHandCursor } + TapHandler { onTapped: netPopup.toggle() } + + RowLayout { + id: chipRow + anchors.centerIn: parent + spacing: 6 + + Text { + text: root.displayIcon + color: root.iconColor + font.pixelSize: 16 + Layout.alignment: Qt.AlignVCenter + + SequentialAnimation on opacity { + running: root.hasWifi && root.wifiDevice + && (root.wifiDevice.scannerEnabled || false) + loops: Animation.Infinite + NumberAnimation { to: 0.4; duration: 600; easing.type: Easing.InOutSine } + NumberAnimation { to: 1.0; duration: 600; easing.type: Easing.InOutSine } + } + opacity: 1.0 + } + + Text { + text: root.chipLabel + color: root.t.netText + font.pixelSize: 12 + font.bold: true + verticalAlignment: Text.AlignVCenter + Layout.maximumWidth: 110 + elide: Text.ElideRight + } + } + + PanelWindow { + id: netPopup + screen: root.targetScreen + visible: false + color: "transparent" + + property bool popupOpen: false + + function open() { + root.cancelConnect() + hideTimer.stop() + visible = true + popupOpen = true + if (root.hasWifi && root.wifiDevice) + root.wifiDevice.scannerEnabled = true + } + function close() { popupOpen = false; hideTimer.restart() } + function toggle() { popupOpen ? close() : open() } + + Timer { + id: hideTimer + interval: Cfg.Config.popupAnimDuration + 70 + onTriggered: netPopup.visible = false + } + + anchors { top: true; bottom: true; left: true; right: true } + WlrLayershell.layer: WlrLayer.Top + WlrLayershell.namespace: "main-shell-network" + WlrLayershell.exclusiveZone: -1 + mask: netPopup.popupOpen ? null : _noInput + Region { id: _noInput } + + TapHandler { onTapped: netPopup.close() } + + Item { + id: popupClip + readonly property int cardW: 310 + readonly property int screenPad: root.barMargin + 10 + + x: { + var cx = root.mapToItem(null, 0, 0).x + root.width / 2 + var dx = cx - cardW / 2 + return Math.max(screenPad, Math.min(dx, parent.width - cardW - screenPad)) + } + width: cardW + + anchors.top: root.isTop ? parent.top : undefined + anchors.bottom: root.isTop ? undefined : parent.bottom + anchors.topMargin: root.isTop ? (root.barHeight + 10) : 0 + anchors.bottomMargin: root.isTop ? 0 : (root.barHeight + 10) + height: parent.height - root.barHeight - 10 + clip: true + + Rectangle { + id: card + width: parent.width + height: cardBody.implicitHeight + 24 + + anchors.top: root.isTop ? parent.top : undefined + anchors.bottom: root.isTop ? undefined : parent.bottom + + transform: Translate { + y: netPopup.popupOpen ? 0 : (root.isTop ? -card.height : card.height) + Behavior on y { + NumberAnimation { + duration: Cfg.Config.popupAnimDuration + easing.type: Easing.OutExpo + } + enabled: netPopup.visible + } + } + + opacity: netPopup.popupOpen ? 1.0 : 0.0 + Behavior on opacity { NumberAnimation { duration: 200 } } + + color: root.t.netPopupBg + radius: Cfg.Config.popupRadius + border.color: root.t.netPopupBorder + border.width: Cfg.Config.popupBorderWidth + layer.enabled: true + + MouseArea { anchors.fill: parent; propagateComposedEvents: false } + + ColumnLayout { + id: cardBody + anchors { + left: parent.left + right: parent.right + top: parent.top + margins: 16 + topMargin: 18 + } + spacing: 12 + + RowLayout { + Layout.fillWidth: true + spacing: 12 + + Text { + text: root.displayIcon + color: root.iconColor + font.pixelSize: 36 + Layout.alignment: Qt.AlignVCenter + } + + ColumnLayout { + spacing: 2 + Layout.fillWidth: true + + Text { + text: { + if (root.hasWifi) + return root.wifiUp + ? (root.ssid !== "" ? root.ssid : "Connected") + : "Wi-Fi" + if (root.hasWired) + return root.wiredUp ? "Ethernet" : "Disconnected" + return "No Network" + } + color: root.t.netPopupHeader + font.pixelSize: 17 + font.bold: true + elide: Text.ElideRight + Layout.fillWidth: true + } + + Text { + text: { + if (root.hasWifi && root.wifiDevice) { + var st = root.wifiDevice.state + if (st === ConnectionState.Activating) return "Connecting..." + if (st === ConnectionState.Deactivating) return "Disconnecting..." + if (root.wifiUp && root.activeAP) { + var s = root.strength + "% signal" + if (root.wifiModeLabel !== "") s += " · " + root.wifiModeLabel + return s + } + if (st === ConnectionState.Deactivated) + return "Disconnected" + return "Not connected" + } + if (root.hasWired && root.wiredDevice) + return root.wiredDevice.name || "—" + return "No hardware found" + } + color: root.t.netPopupDim + font.pixelSize: 12 + } + + Text { + visible: root.connectivityLabel !== "" + text: root.connectivityLabel + color: root.t.statusErr + font.pixelSize: 11 + } + } + + Rectangle { + visible: root.hasWifi + width: 28; height: 28 + radius: 8 + color: scanHover.hovered ? root.t.netApHoverBg : "transparent" + border.color: root.t.netPopupBorder + border.width: 1 + Behavior on color { ColorAnimation { duration: 120 } } + + Text { + anchors.centerIn: parent + text: "\uf0450" + font.pixelSize: 15 + color: scanHover.hovered ? root.t.accent : root.t.netPopupDim + + RotationAnimator on rotation { + id: scanSpin; running: false + from: 0; to: 360; duration: 700; loops: 2 + easing.type: Easing.InOutCubic + } + } + + HoverHandler { id: scanHover; cursorShape: Qt.PointingHandCursor } + TapHandler { + onTapped: { + scanSpin.restart() + if (root.wifiDevice) root.wifiDevice.scannerEnabled = true + } + } + } + } + + + Rectangle { Layout.fillWidth: true; height: 1; color: root.t.netPopupSeparator } + + ColumnLayout { + visible: root.hasWifi + Layout.fillWidth: true + spacing: 4 + + Text { + text: "AVAILABLE NETWORKS" + color: root.t.netPopupDim + font.pixelSize: 10 + font.letterSpacing: 1.2 + Layout.fillWidth: true + } + + Text { + visible: root.hasWifi && root.wifiDevice + && root.wifiDevice.networks.values.length === 0 + text: (root.wifiDevice && (root.wifiDevice.scannerEnabled || false)) + ? "Scanning..." : "No networks found" + color: root.t.netPopupDim + font.pixelSize: 12 + Layout.alignment: Qt.AlignHCenter + Layout.topMargin: 4 + } + + Repeater { + model: (root.hasWifi && root.wifiDevice) ? root.wifiDevice.networks.values : [] + + delegate: Rectangle { + required property var modelData + + Layout.fillWidth: true + height: 50 + radius: 10 + color: { + var active = modelData.connected || false + if (active) return root.t.netApActiveBg + if (apHover.hovered) return root.t.netApHoverBg + return "transparent" + } + border.color: (modelData.connected || false) ? root.t.accent : "transparent" + border.width: 1 + Behavior on color { ColorAnimation { duration: 120 } } + Behavior on border.color { ColorAnimation { duration: 120 } } + + HoverHandler { id: apHover; cursorShape: Qt.PointingHandCursor } + TapHandler { + onTapped: { + if (modelData.connected || false) return + if (modelData.security !== WifiSecurityType.None) { + root.pendingAP = modelData + root.pendingSSID = modelData.name || "" + root.enteredPwd = "" + root.showPwdPrompt = true + } else { + modelData.connect() + } + } + } + + RowLayout { + anchors { fill: parent; leftMargin: 12; rightMargin: 12 } + spacing: 10 + + Text { + text: { + var s = Math.round((modelData.signalStrength || 0) * 100) + if (s >= 80) return "\uf0ec8" + if (s >= 60) return "\uf0ec5" + if (s >= 40) return "\uf0ec2" + if (s >= 20) return "\uf0ebf" + return "\uf0eef" + } + color: (modelData.connected || false) + ? root.t.accent : root.t.netPopupDim + font.pixelSize: 18 + Layout.alignment: Qt.AlignVCenter + } + + ColumnLayout { + spacing: 2 + Layout.fillWidth: true + + Text { + text: modelData.name || "(hidden)" + color: (modelData.connected || false) + ? root.t.netPopupHeader + : root.t.netPopupText + font.pixelSize: 13 + font.bold: (modelData.connected || false) + elide: Text.ElideRight + Layout.fillWidth: true + } + + Text { + text: { + if (modelData.stateChanging) + return modelData.state === ConnectionState.Activating + ? "Connecting..." : "Disconnecting..." + var parts = [] + if (modelData.known) parts.push("Saved") + if (modelData.security !== WifiSecurityType.None) parts.push("Secured") + else parts.push("Open") + parts.push(Math.round((modelData.signalStrength || 0) * 100) + "%") + return parts.join(" · ") + } + color: root.t.netPopupDim + font.pixelSize: 11 + } + } + + Text { + visible: (modelData.security !== WifiSecurityType.None) && !(modelData.connected || false) + text: "\uf033e" + color: root.t.netPopupDim + font.pixelSize: 13 + Layout.alignment: Qt.AlignVCenter + } + + Text { + visible: (modelData.connected || false) + text: "\uf012c" + color: root.t.statusOk + font.pixelSize: 15 + Layout.alignment: Qt.AlignVCenter + } + } + } + } + + Rectangle { + visible: root.wifiUp + Layout.fillWidth: true + Layout.topMargin: 4 + height: 36; radius: 10 + color: discoHover.hovered + ? root.t.netDisconnectBg : "transparent" + border.color: root.t.netDisconnectBorder + border.width: 1 + Behavior on color { ColorAnimation { duration: 120 } } + + HoverHandler { id: discoHover; cursorShape: Qt.PointingHandCursor } + TapHandler { + onTapped: { + if (root.activeAP) + root.activeAP.disconnect() + } + } + Text { + anchors.centerIn: parent + text: "Disconnect" + color: root.t.statusErr + font.pixelSize: 12 + font.bold: true + } + } + } + + ColumnLayout { + visible: !root.hasWifi && root.hasWired + Layout.fillWidth: true + spacing: 10 + + Text { + text: "CONNECTION DETAILS" + color: root.t.netPopupDim + font.pixelSize: 10 + font.letterSpacing: 1.2 + Layout.fillWidth: true + } + + Rectangle { + Layout.fillWidth: true + height: 38; radius: 10 + color: root.wiredUp + ? root.t.netWiredOkBg + : root.t.netWiredErrBg + border.color: root.wiredUp + ? root.t.netWiredOkBorder + : root.t.netWiredErrBorder + border.width: 1 + + RowLayout { + anchors { fill: parent; leftMargin: 14; rightMargin: 14 } + spacing: 10 + + Text { + text: root.wiredUp ? "\uf0601" : "\uf0602" + color: root.wiredUp ? root.t.statusOk : root.t.statusErr + font.pixelSize: 18 + Layout.alignment: Qt.AlignVCenter + } + Text { + text: root.wiredUp ? "Connected" : "Cable unplugged" + color: root.wiredUp ? root.t.statusOk : root.t.statusErr + font.pixelSize: 13 + font.bold: true + Layout.fillWidth: true + } + } + } + + ColumnLayout { + Layout.fillWidth: true + spacing: 6 + + Repeater { + model: [ + { + label: "Interface", + value: (root.wiredDevice && root.wiredDevice.name) + ? root.wiredDevice.name : "—" + }, + { + label: "MAC", + value: (root.wiredDevice && root.wiredDevice.address) + ? root.wiredDevice.address : "—" + }, + { + label: "Link", + value: (root.wiredDevice) + ? (root.wiredDevice.hasLink ? "Up" : "Down") : "—" + }, + { + label: "Speed", + value: (root.wiredDevice && root.wiredUp && + root.wiredDevice.linkSpeed > 0) + ? (root.wiredDevice.linkSpeed + " Mbps") : "—" + } + ] + + delegate: RowLayout { + required property var modelData + Layout.fillWidth: true + + Text { + text: modelData.label + color: root.t.netPopupDim + font.pixelSize: 12 + Layout.fillWidth: true + } + Text { + text: modelData.value + color: root.t.netPopupText + font.pixelSize: 12 + font.bold: true + font.family: "monospace" + } + } + } + } + } + + Text { + visible: !root.hasWifi && !root.hasWired + text: "No network hardware detected." + color: root.t.netPopupDim + font.pixelSize: 12 + horizontalAlignment: Text.AlignHCenter + Layout.fillWidth: true + Layout.topMargin: 4 + } + + ColumnLayout { + visible: root.showPwdPrompt + Layout.fillWidth: true + spacing: 8 + + Rectangle { Layout.fillWidth: true; height: 1; color: root.t.netPopupSeparator } + + Text { + text: "Connect to \"" + root.pendingSSID + "\"" + color: root.t.netPopupHeader + font.pixelSize: 13 + font.bold: true + elide: Text.ElideRight + Layout.fillWidth: true + } + + Rectangle { + Layout.fillWidth: true + height: 36; radius: 8 + color: root.t.netFieldBg + border.color: pwdField.activeFocus ? root.t.accent : root.t.netPopupBorder + border.width: 1 + Behavior on border.color { ColorAnimation { duration: 120 } } + + RowLayout { + anchors { fill: parent; leftMargin: 12; rightMargin: 10 } + spacing: 6 + + TextInput { + id: pwdField + Layout.fillWidth: true + verticalAlignment: TextInput.AlignVCenter + echoMode: eyeToggle.showClear + ? TextInput.Normal : TextInput.Password + color: root.t.netPopupText + font.pixelSize: 13 + selectionColor: Qt.alpha(root.t.accent, 0.4) + clip: true + text: root.enteredPwd + onTextChanged: root.enteredPwd = text + + Keys.onReturnPressed: root.attemptConnect() + Keys.onEscapePressed: root.cancelConnect() + onVisibleChanged: if (visible) forceActiveFocus() + + Text { + anchors.fill: parent + verticalAlignment: Text.AlignVCenter + text: "Password" + color: root.t.netPopupDim + font.pixelSize: 13 + visible: pwdField.text === "" && !pwdField.activeFocus + } + } + + Text { + id: eyeToggle + property bool showClear: false + text: showClear ? "\uf0349" : "\uf0354" + color: eyeHover.hovered ? root.t.accent : root.t.netPopupDim + font.pixelSize: 15 + Layout.alignment: Qt.AlignVCenter + HoverHandler { id: eyeHover; cursorShape: Qt.PointingHandCursor } + TapHandler { onTapped: eyeToggle.showClear = !eyeToggle.showClear } + } + } + } + + RowLayout { + Layout.fillWidth: true + spacing: 8 + + Rectangle { + Layout.fillWidth: true + height: 34; radius: 8 + color: cancelHover.hovered ? root.t.netCancelBgHover : "transparent" + border.color: root.t.netPopupBorder + border.width: 1 + Behavior on color { ColorAnimation { duration: 120 } } + HoverHandler { id: cancelHover; cursorShape: Qt.PointingHandCursor } + TapHandler { onTapped: root.cancelConnect() } + Text { + anchors.centerIn: parent + text: "Cancel" + color: root.t.netPopupDim + font.pixelSize: 12 + } + } + + Rectangle { + Layout.fillWidth: true + height: 34; radius: 8 + color: connHover.hovered ? root.t.accent : Qt.alpha(root.t.accent, 0.75) + Behavior on color { ColorAnimation { duration: 120 } } + HoverHandler { id: connHover; cursorShape: Qt.PointingHandCursor } + TapHandler { onTapped: root.attemptConnect() } + Text { + anchors.centerIn: parent + text: "Connect" + color: root.t.netConnectText + font.pixelSize: 12 + font.bold: true + } + } + } + } + + Item { height: 4 } + } + } + } + } +} diff --git a/bar/modules/Stats.qml b/bar/modules/Stats.qml index b10a78f..46e3543 100644 --- a/bar/modules/Stats.qml +++ b/bar/modules/Stats.qml @@ -1,6 +1,5 @@ import QtQuick import QtQuick.Layouts -import Quickshell import Quickshell.Io import "../../config" as Cfg @@ -25,7 +24,19 @@ Rectangle { NumberAnimation { duration: 250; easing.type: Easing.OutCubic } } - HoverHandler { cursorShape: Qt.PointingHandCursor } + property bool expanded: Cfg.Config.statsStartExpanded + property bool hovered: false + + HoverHandler { + onHoveredChanged: root.hovered = hovered + } + + TapHandler { + onTapped: { + root.expanded = !root.expanded + if (!root.expanded) root.hovered = false + } + } property real cpuVal: 0 property real memVal: 0 @@ -36,166 +47,162 @@ Rectangle { running: true repeat: true triggeredOnStart: true - onTriggered: { - cpuProc.running = true - memProc.running = true - diskProc.running = true - } + onTriggered: { statsProc.running = true } } Process { - id: cpuProc - command: ["sh", "-c", "top -bn1 | awk '/^%Cpu/ {print 100-$8}'"] + id: statsProc + command: ["sh", "-c", "top -bn1 | awk '/^%Cpu/ {print 100-$8}'; free | awk '/Mem:/ {print ($2-$7)/$2 * 100}'; df / --output=pcent | tail -1 | tr -dc '0-9'"] + property string rawOutput: "" stdout: SplitParser { - onRead: data => { - let val = parseFloat(data.trim()) - root.cpuVal = isNaN(val) ? 0 : val + onRead: data => { statsProc.rawOutput += data + "\n" } + } + onRunningChanged: { + if (!statsProc.running && statsProc.rawOutput) { + const lines = statsProc.rawOutput.trim().split("\n") + if (lines.length >= 3) { + let v = parseFloat(lines[0]); root.cpuVal = isNaN(v) ? 0 : v + v = parseFloat(lines[1]); root.memVal = isNaN(v) ? 0 : v + v = parseFloat(lines[2]); root.diskVal = isNaN(v) ? 0 : v + } + statsProc.rawOutput = "" } } } - Process { - id: memProc - command: ["sh", "-c", "free | awk '/Mem:/ {print ($2-$7)/$2 * 100}'"] - stdout: SplitParser { - onRead: data => { - let val = parseFloat(data.trim()) - root.memVal = isNaN(val) ? 0 : val - } - } - } - - Process { - id: diskProc - command: ["sh", "-c", "df / --output=pcent | tail -1 | tr -dc '0-9'"] - stdout: SplitParser { - onRead: data => { - let val = parseFloat(data.trim()) - root.diskVal = isNaN(val) ? 0 : val - } - } - } - - component StatRing: Item { + component StatRing: RowLayout { id: ring + spacing: 5 property real value: 0.0 - property string label: "" + property string icon: "" + property real iconXOff: 0 property color ringColor: Cfg.Config.theme.accent property color trackColor: Cfg.Config.theme.statsTrackColor - property color labelColor: Cfg.Config.theme.textMain Behavior on value { - NumberAnimation { - duration: 800 - easing.type: Easing.OutCubic - } + NumberAnimation { duration: 800; easing.type: Easing.OutCubic } } readonly property real fraction: Math.max(0, Math.min(value, 100)) / 100 - width: Cfg.Config.statsRingSize - height: Cfg.Config.statsRingSize + Text { + id: leftPct + text: Math.round(ring.value) + "%" + color: ring.ringColor + font.pixelSize: Cfg.Config.statsRingFontSize + font.bold: true + font.family: "monospace" + Layout.alignment: Qt.AlignVCenter + clip: true - onFractionChanged: arc.requestPaint() - onRingColorChanged: arc.requestPaint() + readonly property bool active: Cfg.Config.statsLabelPosition === "left" && (root.expanded || root.hovered) + opacity: active ? 1 : 0 + Layout.preferredWidth: active ? implicitWidth : 0 - Canvas { - id: arc - anchors.fill: parent - antialiasing: true + Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } } + Behavior on Layout.preferredWidth { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } } + } - onPaint: { - var ctx = getContext("2d") - ctx.reset() + Item { + id: ringCore + Layout.preferredWidth: Cfg.Config.statsRingSize + Layout.preferredHeight: Cfg.Config.statsRingSize - var cx = width / 2 - var cy = height / 2 - var lw = 2.8 - var r = Math.min(cx, cy) - lw / 2 - 0.5 + onVisibleChanged: if (visible) arc.requestPaint() - ctx.beginPath() - ctx.arc(cx, cy, r, 0, Math.PI * 2) - ctx.strokeStyle = ring.trackColor - ctx.lineWidth = lw - ctx.stroke() + Canvas { + id: arc + anchors.fill: parent + antialiasing: true + + Connections { + target: ring + function onFractionChanged() { arc.requestPaint() } + function onRingColorChanged() { arc.requestPaint() } + } + + onPaint: { + var ctx = getContext("2d") + ctx.reset() + + var cx = width / 2 + var cy = height / 2 + var lw = 2.8 + var r = Math.min(cx, cy) - lw / 2 - 0.5 - if (ring.fraction > 0) { - var start = -Math.PI / 2 - var end = start + (ring.fraction * Math.PI * 2) ctx.beginPath() - ctx.arc(cx, cy, r, start, end) - ctx.strokeStyle = ring.ringColor + ctx.arc(cx, cy, r, 0, Math.PI * 2) + ctx.strokeStyle = ring.trackColor ctx.lineWidth = lw - ctx.lineCap = "round" ctx.stroke() + + if (ring.fraction > 0) { + var start = -Math.PI / 2 + var end = start + (ring.fraction * Math.PI * 2) + ctx.beginPath() + ctx.arc(cx, cy, r, start, end) + ctx.strokeStyle = ring.ringColor + ctx.lineWidth = lw + ctx.lineCap = "round" + ctx.stroke() + } } } - } - HoverHandler { - id: hov - onHoveredChanged: if (!hovered) ring.showAlt = false - } - - property bool showAlt: false - onShowAltChanged: arc.requestPaint() - - TapHandler { - enabled: !Cfg.Config.statsHideTextUntilHover || hov.hovered - onTapped: ring.showAlt = !ring.showAlt + Text { + anchors.centerIn: parent + anchors.horizontalCenterOffset: ring.iconXOff + text: ring.icon + color: ring.ringColor + font.pixelSize: Cfg.Config.statsIconSize + } } Text { - anchors.centerIn: parent + id: rightPct + text: Math.round(ring.value) + "%" + color: ring.ringColor + font.pixelSize: Cfg.Config.statsRingFontSize + font.bold: true + font.family: "monospace" + Layout.alignment: Qt.AlignVCenter + clip: true - text: { - const hide = Cfg.Config.statsHideTextUntilHover - const byDefault = Cfg.Config.statsShowPercentByDefault - if (hide && !hov.hovered) return "" - const showPct = ring.showAlt ? !byDefault : byDefault - return showPct ? Math.round(ring.value) + "%" : ring.label - } + readonly property bool active: Cfg.Config.statsLabelPosition === "right" && (root.expanded || root.hovered) + opacity: active ? 1 : 0 + Layout.preferredWidth: active ? implicitWidth : 0 - color: ring.labelColor - font.pixelSize: Cfg.Config.statsRingFontSize - font.bold: true - font.family: "monospace" - - opacity: (Cfg.Config.statsHideTextUntilHover && !hov.hovered) ? 0 : 1 - Behavior on opacity { - NumberAnimation { duration: 180; easing.type: Easing.OutCubic } - } + Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } } + Behavior on Layout.preferredWidth { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } } } } RowLayout { id: statsLayout anchors.centerIn: parent - spacing: 10 + spacing: 8 StatRing { value: root.cpuVal - label: "cpu" + icon: "󰍛" + iconXOff: 0.3 ringColor: root.t.statsCpuColor trackColor: root.t.statsTrackColor - labelColor: root.t.statsText } StatRing { value: root.memVal - label: "ram" + icon: "󰑭" ringColor: root.t.statsMemColor trackColor: root.t.statsTrackColor - labelColor: root.t.statsText } StatRing { value: root.diskVal - label: "disk" + icon: "󰒋" ringColor: root.t.statsDiskColor trackColor: root.t.statsTrackColor - labelColor: root.t.statsText } } } diff --git a/bar/modules/Tray.qml b/bar/modules/Tray.qml index 15dea88..4e187a4 100644 --- a/bar/modules/Tray.qml +++ b/bar/modules/Tray.qml @@ -13,9 +13,9 @@ Item { property real panelRadius: Cfg.Config.radius / 2 property int borderWidth: Cfg.Config.panelBorderWidth - property bool isTop: true property var targetScreen: null property string barSide: "right" + property bool isTop: true readonly property var t: Cfg.Config.theme @@ -23,14 +23,24 @@ Item { property bool manuallyExpanded: false property var menuWindow: null + readonly property bool effectiveExpanded: expanded || trayMenuPopup.popupOpen + implicitWidth: bgRect.width implicitHeight: bgRect.height + readonly property color tmBg: t.trayMenuBg + readonly property color tmBorder: t.trayMenuBorder + readonly property color tmText: t.trayMenuText + readonly property color tmDim: t.trayMenuDim + readonly property color tmHover: t.trayMenuHover + readonly property color tmCheck: t.trayMenuCheck + readonly property color tmSeparator: t.trayMenuSeparator + Rectangle { id: bgRect anchors.verticalCenter: parent.verticalCenter - height: Cfg.Config.trayHeight + height: parent.height width: mainRow.width radius: root.panelRadius @@ -41,8 +51,8 @@ Item { Behavior on color { ColorAnimation { duration: 150 } } Behavior on border.color { ColorAnimation { duration: 150 } } - HoverHandler { - id: bgHover + HoverHandler { + id: bgHover onHoveredChanged: { if (!root.manuallyExpanded) { root.expanded = hovered; @@ -54,21 +64,21 @@ Item { id: mainRow anchors.verticalCenter: parent.verticalCenter - leftPadding: root.expanded && root.barSide === "right" ? 8 : 0 - rightPadding: root.expanded && root.barSide === "left" ? 8 : 0 + leftPadding: root.effectiveExpanded && root.barSide === "right" ? 8 : 0 + rightPadding: root.effectiveExpanded && root.barSide === "left" ? 8 : 0 Behavior on leftPadding { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } } Behavior on rightPadding { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } } layoutDirection: root.barSide === "left" ? Qt.LeftToRight : Qt.RightToLeft - spacing: root.expanded ? 4 : 0 + spacing: root.effectiveExpanded ? 4 : 0 Behavior on spacing { NumberAnimation { duration: 180; easing.type: Easing.OutCubic } } Item { id: toggleArea - width: Cfg.Config.trayHeight - height: Cfg.Config.trayHeight + width: Cfg.Config.trayToggleSize + height: Cfg.Config.trayToggleSize z: 10 TapHandler { @@ -80,11 +90,11 @@ Item { Text { anchors.centerIn: parent - text: root.expanded + text: root.effectiveExpanded ? (root.barSide === "left" ? "󰅁" : "󰅂") : (root.barSide === "left" ? "󰅂" : "󰅁") color: root.t.trayIcon - font.pixelSize: Cfg.Config.trayHeight * 0.55 + font.pixelSize: Cfg.Config.trayToggleSize * 0.55 } } @@ -94,9 +104,9 @@ Item { clip: true anchors.verticalCenter: parent.verticalCenter - width: root.expanded ? implicitWidth : 0 - opacity: root.expanded ? 1 : 0 - + width: root.effectiveExpanded ? implicitWidth : 0 + opacity: root.effectiveExpanded ? 1 : 0 + Behavior on width { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } } Behavior on opacity { NumberAnimation { duration: 150 } } @@ -122,7 +132,9 @@ Item { } else if (mouse.button === Qt.MiddleButton) { item.secondaryActivate() } else if (mouse.button === Qt.RightButton) { - menuAnchor.open() + if (item.hasMenu) { + trayMenuPopup.open(item, iconDelegate) + } } } @@ -135,22 +147,462 @@ Item { anchors.fill: parent source: item.icon } + } + } + } + } + } - QsMenuAnchor { - id: menuAnchor - menu: item.menu - anchor.window: root.menuWindow - anchor.adjustment: PopupAdjustment.Flip - anchor.onAnchoring: { - if (!root.menuWindow) return - const global = iconDelegate.mapToGlobal(0, iconDelegate.height) - const local = root.menuWindow.contentItem.mapFromGlobal( - global.x, global.y - ) - menuAnchor.anchor.rect = Qt.rect( - local.x, local.y, - iconDelegate.width, 0 - ) + + + + PanelWindow { + id: trayMenuPopup + screen: root.targetScreen + visible: false + color: "transparent" + + property bool popupOpen: false + property bool _pendingOpen: false + property var activeTrayItem: null + property var activeDelegate: null + + function open(item, delegate) { + if (popupOpen) close() + hideTimer.stop() + _pendingOpen = false + activeTrayItem = item + activeDelegate = delegate + rootMenuOpener.menu = item.menu + visible = true + + + if (menuCol.height > 0) { + popupOpen = true + } else { + _pendingOpen = true + openFallbackTimer.restart() + } + } + + function close() { + _pendingOpen = false + openFallbackTimer.stop() + popupOpen = false + hideTimer.restart() + } + + + + + Connections { + target: menuCol + function onHeightChanged() { + if (trayMenuPopup._pendingOpen && menuCol.height > 0) { + trayMenuPopup._pendingOpen = false + openFallbackTimer.stop() + trayMenuPopup.popupOpen = true + } + } + } + + + Timer { + id: openFallbackTimer + interval: 400 + onTriggered: { + if (trayMenuPopup._pendingOpen) { + trayMenuPopup._pendingOpen = false + trayMenuPopup.popupOpen = true + } + } + } + + Timer { + id: hideTimer + interval: Cfg.Config.popupAnimDuration + 70 + onTriggered: { + trayMenuPopup.visible = false + rootMenuOpener.menu = null + trayMenuPopup.activeTrayItem = null + trayMenuPopup.activeDelegate = null + } + } + + anchors { top: true; bottom: true; left: true; right: true } + WlrLayershell.layer: WlrLayer.Top + WlrLayershell.namespace: "main-shell-tray-menu" + WlrLayershell.exclusiveZone: -1 + mask: popupOpen ? null : _trayMenuNoInput + Region { id: _trayMenuNoInput } + + TapHandler { onTapped: trayMenuPopup.close() } + + QsMenuOpener { id: rootMenuOpener } + + + Item { + id: menuCardWrapper + readonly property int cardW: 220 + readonly property int pad: 10 + + + x: { + if (!trayMenuPopup.activeDelegate) + return (parent.width - cardW) / 2 + var gp = trayMenuPopup.activeDelegate.mapToGlobal(0, 0) + var cx = gp.x + trayMenuPopup.activeDelegate.width / 2 + return Math.max(pad, + Math.min(cx - cardW / 2, parent.width - cardW - pad)) + } + + width: cardW + clip: true + + + anchors.top: root.isTop ? parent.top : undefined + anchors.bottom: root.isTop ? undefined : parent.bottom + anchors.topMargin: root.isTop ? (Cfg.Config.barHeight + 8) : 0 + anchors.bottomMargin: root.isTop ? 0 : (Cfg.Config.barHeight + 8) + height: parent.height - Cfg.Config.barHeight - 8 + + Rectangle { + id: menuCard + width: parent.width + height: menuCol.height + 12 + + + anchors.top: root.isTop ? parent.top : undefined + anchors.bottom: root.isTop ? undefined : parent.bottom + + transform: Translate { + y: trayMenuPopup.popupOpen ? 0 + : (root.isTop ? -menuCard.height : menuCard.height) + Behavior on y { + NumberAnimation { + duration: Cfg.Config.popupAnimDuration + easing.type: Easing.OutExpo + } + } + } + + opacity: trayMenuPopup.popupOpen ? 1 : 0 + Behavior on opacity { NumberAnimation { duration: 200 } } + + color: root.tmBg + radius: Cfg.Config.popupRadius + border.color: root.tmBorder + border.width: Cfg.Config.popupBorderWidth + + Column { + id: menuCol + anchors { left: parent.left; right: parent.right; top: parent.top } + anchors.margins: 6 + spacing: 0 + + Repeater { + model: rootMenuOpener.children + + delegate: Item { + id: menuDelegate + required property var modelData + property bool isSep: modelData.isSeparator + + width: menuCol.width + height: isSep ? 5 : 32 + + Rectangle { + visible: isSep + anchors { + left: parent.left; right: parent.right + verticalCenter: parent.verticalCenter + } + anchors.leftMargin: 8 + anchors.rightMargin: 8 + height: 1 + color: root.tmSeparator + } + + Rectangle { + id: itemBg + visible: !isSep + anchors.fill: parent + anchors.margins: 2 + radius: 10 + property bool active: itemMouse.containsMouse + || (submenuCard.visible + && trayMenuPopup._submenuParentRef === menuDelegate) + color: active ? root.tmHover : "transparent" + Behavior on color { ColorAnimation { duration: 120 } } + + RowLayout { + anchors.fill: parent + anchors.leftMargin: 8 + anchors.rightMargin: 8 + spacing: 8 + + Item { + Layout.preferredWidth: 16 + Layout.preferredHeight: 16 + visible: !isSep + + Text { + anchors.centerIn: parent + visible: modelData.buttonType === QsMenuButtonType.CheckBox + && modelData.checkState === Qt.Checked + text: "✓" + color: root.tmCheck + font.pixelSize: 14 + } + Text { + anchors.centerIn: parent + visible: modelData.buttonType === QsMenuButtonType.RadioButton + && modelData.checkState === Qt.Checked + text: "●" + color: root.tmCheck + font.pixelSize: 9 + } + Text { + anchors.centerIn: parent + visible: modelData.buttonType === QsMenuButtonType.RadioButton + && modelData.checkState !== Qt.Checked + text: "○" + color: root.tmDim + font.pixelSize: 11 + } + } + + Image { + visible: !isSep && modelData.icon !== "" + source: modelData.icon + width: 16; height: 16 + sourceSize: Qt.size(16, 16) + fillMode: Image.PreserveAspectFit + } + + Text { + text: modelData.text + color: modelData.enabled ? root.tmText : root.tmDim + font.pixelSize: 13 + Layout.fillWidth: true + elide: Text.ElideRight + } + + Text { + visible: !isSep && modelData.hasChildren + text: "▶" + color: root.tmDim + font.pixelSize: 9 + } + } + + MouseArea { + id: itemMouse + anchors.fill: parent + hoverEnabled: true + enabled: !isSep && modelData.enabled + + onClicked: { + if (modelData.hasChildren) { + closeSubmenuTimer.stop() + submenuDelayTimer.itemRef = menuDelegate + submenuDelayTimer.restart() + } else { + modelData.triggered() + trayMenuPopup.close() + } + } + onEntered: { + if (modelData.hasChildren) { + closeSubmenuTimer.stop() + submenuDelayTimer.itemRef = menuDelegate + submenuDelayTimer.restart() + } else { + closeSubmenuTimer.stop() + submenuCard.visible = false + } + } + onExited: { + if (modelData.hasChildren) + closeSubmenuTimer.restart() + } + } + } + } + } + } + } + } + + + property var _submenuParentRef: null + + QsMenuOpener { id: subMenuOpener } + + Timer { + id: submenuDelayTimer + interval: 250 + property var itemRef: null + onTriggered: { + if (!itemRef) return + var entry = itemRef.modelData + if (!entry || !entry.hasChildren) return + subMenuOpener.menu = entry + trayMenuPopup._submenuParentRef = itemRef + submenuCard.visible = true + } + } + + Timer { + id: closeSubmenuTimer + interval: 300 + onTriggered: { + if (!submenuCard.containsMouse) { + submenuCard.visible = false + subMenuOpener.menu = null + trayMenuPopup._submenuParentRef = null + } + } + } + + + Rectangle { + id: submenuCard + visible: false + property bool containsMouse: false + + property int subW: Math.max(submenuCol.width, 180) + + x: { + if (!trayMenuPopup._submenuParentRef) return 0 + var pItem = trayMenuPopup._submenuParentRef + var gp = pItem.mapToGlobal(0, 0) + var lp = trayMenuPopup.contentItem.mapFromGlobal(gp.x, gp.y) + var fromLeft = lp.x + pItem.width + 4 + if (fromLeft + subW + 10 > trayMenuPopup.contentItem.width) + return Math.max(2, lp.x - subW - 4) + return fromLeft + } + + y: { + if (!trayMenuPopup._submenuParentRef) return 0 + var pItem = trayMenuPopup._submenuParentRef + var gp = pItem.mapToGlobal(0, 0) + var lp = trayMenuPopup.contentItem.mapFromGlobal(gp.x, gp.y) + return Math.max(2, lp.y - 2) + } + + width: subW + height: submenuCol.height + 12 + + color: root.tmBg + radius: Cfg.Config.popupRadius + border.color: root.tmBorder + border.width: Cfg.Config.popupBorderWidth + + HoverHandler { + onHoveredChanged: { + submenuCard.containsMouse = hovered + if (!hovered) + closeSubmenuTimer.restart() + } + } + + Column { + id: submenuCol + anchors { left: parent.left; right: parent.right; top: parent.top } + anchors.margins: 6 + spacing: 0 + + Repeater { + model: subMenuOpener.children + + delegate: Item { + required property var modelData + property bool isSep: modelData.isSeparator + + width: submenuCol.width + height: isSep ? 5 : 32 + + Rectangle { + visible: isSep + anchors { + left: parent.left; right: parent.right + verticalCenter: parent.verticalCenter + } + anchors.leftMargin: 8; anchors.rightMargin: 8 + height: 1 + color: root.tmSeparator + } + + Rectangle { + id: subBg + visible: !isSep + anchors.fill: parent + anchors.margins: 2 + radius: 10 + color: subMouse.containsMouse ? root.tmHover : "transparent" + Behavior on color { ColorAnimation { duration: 120 } } + + RowLayout { + anchors.fill: parent + anchors.leftMargin: 8; anchors.rightMargin: 8 + spacing: 8 + + Item { + Layout.preferredWidth: 16 + Layout.preferredHeight: 16 + visible: !isSep + + Text { + anchors.centerIn: parent + visible: modelData.buttonType === QsMenuButtonType.CheckBox + && modelData.checkState === Qt.Checked + text: "✓"; color: root.tmCheck + font.pixelSize: 14 + } + Text { + anchors.centerIn: parent + visible: modelData.buttonType === QsMenuButtonType.RadioButton + && modelData.checkState === Qt.Checked + text: "●"; color: root.tmCheck + font.pixelSize: 9 + } + Text { + anchors.centerIn: parent + visible: modelData.buttonType === QsMenuButtonType.RadioButton + && modelData.checkState !== Qt.Checked + text: "○"; color: root.tmDim + font.pixelSize: 11 + } + } + + Image { + visible: !isSep && modelData.icon !== "" + source: modelData.icon + width: 16; height: 16 + sourceSize: Qt.size(16, 16) + fillMode: Image.PreserveAspectFit + } + + Text { + text: modelData.text + color: modelData.enabled ? root.tmText : root.tmDim + font.pixelSize: 13 + Layout.fillWidth: true + elide: Text.ElideRight + } + } + + MouseArea { + id: subMouse + anchors.fill: parent + hoverEnabled: true + enabled: !isSep && modelData.enabled + onClicked: { + modelData.triggered() + trayMenuPopup.close() + } } } } diff --git a/bar/modules/Volume.qml b/bar/modules/Volume.qml index 00839eb..cd59c6b 100644 --- a/bar/modules/Volume.qml +++ b/bar/modules/Volume.qml @@ -22,6 +22,42 @@ Rectangle { property int outputMaxVolume: Cfg.Config.volumeOutputMax property int inputMaxVolume: Cfg.Config.volumeInputMax + // Path to config.qml — using HOME to avoid Qt.resolvedUrl ambiguity + readonly property string configPath: + Quickshell.env("HOME") + "/.config/quickshell/config/config.qml" + + // Process that patches volumeOutputMax / volumeInputMax in config.qml + Process { + id: volConfigWriter + running: false + onExited: (code) => { + if (code !== 0) + console.warn("[Volume] Failed to patch config.qml (exit " + code + ")") + } + } + + // Debounce: write to disk 800 ms after the last limit change + Timer { + id: saveDebounce + interval: 800 + repeat: false + onTriggered: { + const outMax = Math.round(root.outputMaxVolume).toString() + const inMax = Math.round(root.inputMaxVolume).toString() + const cfgPath = root.configPath + const sh = + "sed -i" + + " -e 's/\\(property int[ ]\\+volumeOutputMax:[ ]\\+\\)[0-9.]\\+/\\1" + outMax + "/'" + + " -e 's/\\(property int[ ]\\+volumeInputMax:[ ]\\+\\)[0-9.]\\+/\\1" + inMax + "/'" + + " " + JSON.stringify(cfgPath) + volConfigWriter.command = ["/bin/sh", "-c", sh] + volConfigWriter.running = true + } + } + + onOutputMaxVolumeChanged: { if (root.outputMaxVolume >= 10) saveDebounce.restart() } + onInputMaxVolumeChanged: { if (root.inputMaxVolume >= 10) saveDebounce.restart() } + readonly property var sink: Pipewire.defaultAudioSink readonly property var source: Pipewire.defaultAudioSource diff --git a/config/config.qml b/config/config.qml index 140609a..4136ea7 100644 --- a/config/config.qml +++ b/config/config.qml @@ -6,12 +6,13 @@ import "themes" QtObject { id: root - readonly property var theme: Goldencity + readonly property var theme: Tropicalnight // ──────────────────────────────────────────────────────────────────────── // Widgets // ──────────────────────────────────────────────────────────────────────── - property bool enableBgDate: true + property bool enableBgDate: true + property bool enablePolkit: true // ──────────────────────────────────────────────────────────────────────── // BAR @@ -20,62 +21,64 @@ QtObject { // ── Position & Dimensions ───────────────────────────────────────────────── property string barPosition: "bottom" property int barHeight: 35 - property int margin: 15 + property int margin: 20 property int radius: 26 // ── Module Lists ────────────────────────────────────────────────────────── - property var leftModules: ["ArchLogo", "Stats"] + property var leftModules: ["Logo", "Stats", "Crypto"] property var centerModules: ["Workspace"] - property var rightModules: ["Tray", "Notifications", "Volume", "Clock"] + property var rightModules: ["Tray", "Notifications", "Volume", "Network", "Battery" ,"Clock"] - // ── Arch Logo ───────────────────────────────────────────────────────────── + // ── Logo ───────────────────────────────────────────────────────────── property int logoIconSize: 23 // icon font size; container width scales with it property string logoIcon: "󱄅" // Nerd Font glyph for the logo // ── Panel Styling ───────────────────────────────────────────────────────── property int panelBorderWidth: 1 - property int moduleSpacing: 2 // space between modules within a panel + property int moduleSpacing: 5 // space between modules within a panel property int panelSpacing: 12 // gap between left / center / right panels - property int moduleHeight: 32 // default chip height for bar modules + property int moduleHeight: 23 // default chip height for bar modules // ── Popup Styling ───────────────────────────────────────────────────────── property int popupRadius: 18 property int popupBorderWidth: 1 property int popupAnimDuration: 500 + + // ── Timezone (IANA name, e.g. "America/New_York", "Europe/London")g ────────── + // Leave empty to use the system timezone. + property string timezone: "America/New_York" - // ── Clock Popup ─────────────────────────────────────────────────────────── - // Date format shown in the clock popup. - // "MDY" → month/day/year (e.g. 4/28/2026) - // "DMY" → day/month/year (e.g. 28/4/2026) - property string clockDateFormat: "DMY" - + // Date format used in the clock popup and background date widget. + // "MDY" → month/day/year (e.g. May 14, 2026) + // "DMY" → day/month/year (e.g. 14 May 2026) + property string dateFormat: "DMY" + // ── Tray Styling ───────────────────────────────────────────────────────── - property int trayHeight: 26 - property int trayIconSize: 20 + property int trayToggleSize: 23 // size of the expand/collapse arrow button + property int trayIconSize: 20 // size of each system tray item icon // ── Stats Styling ───────────────────────────────────────────────────────── - property int statsRingSize: 23 - property int statsRingFontSize: 10 - // If true, the % value is shown by default; hovering shows the label, clicking swaps back. - // If false, the label is shown by default; hovering shows the %, clicking swaps back. - property bool statsShowPercentByDefault: true - // If true, text is hidden completely until you hover a ring. - // • Hover → shows the "default" value (% when statsShowPercentByDefault, label otherwise) - // • Click → swaps to the "other" value (label / %) - property bool statsHideTextUntilHover: false + property int statsRingSize: 22 + property int statsRingFontSize: 12 + property int statsIconSize: 10 + // Where the percentage label appears when stats are expanded. + // "right" → label to the right of the ring; "left" → label to the left. + property string statsLabelPosition: "right" + // If true, percentage labels are visible on startup without clicking. + property bool statsStartExpanded: false // ── Volume Module ───────────────────────────────────────────────────────── property int volumeIconSize: 20 property int volumeSliderHeight: 2 property int volumeHandleSize: 20 property int volumePopupWidth: 280 - property int volumeOutputMax: 100 + property int volumeOutputMax: 170 property int volumeInputMax: 200 // ──────────────────────────────────────────────────────────────────────── // FRAME // ──────────────────────────────────────────────────────────────────────── - property int frameBorderWidth: 2 + property int frameBorderWidth: 3 // ──────────────────────────────────────────────────────────────────────── // NOTIFICATIONS @@ -100,4 +103,45 @@ QtObject { property bool launcherCloseOnClickOutside: true property bool launcherSelectFirst: false property bool launcherMipmapIcons: false + + // ──────────────────────────────────────────────────────────────────────── + // CRYPTO + // ──────────────────────────────────────────────────────────────────────── + + // ── Coin/currency pairs to cycle ────────────────────────────────────────── + // Each entry is a { coin, currency } object. The bar chip rotates to the + // next pair after each successful CoinGecko fetch. + // + // coin – CoinGecko coin ID (find them at https://api.coingecko.com/api/v3/coins/list) + // currency – any quote currency CoinGecko supports: "usd", "eur", "brl", "btc", … + // + // Single pair → [{ coin: "bitcoin", currency: "usd" }] + // Three pairs → [{ coin: "bitcoin", currency: "usd" }, + // { coin: "monero", currency: "usd" }, + // { coin: "solana", currency: "brl" }] + // + // This list is the *default*; pairs can be added/removed live in the + // Settings popup and those changes are persisted across restarts. + property var cryptoPairs: [{ coin: "bitcoin", currency: "usd" }, { coin: "ethereum", currency: "eur" }, { coin: "monero", currency: "usd" }, { coin: "solana", currency: "brl" }] + + // Legacy fallbacks – used by Crypto.qml when cryptoPairs is absent. + // You can leave these as-is; they are derived from the first pair above. + property string cryptoCoinId: cryptoPairs[0].coin ?? "bitcoin" + property string cryptoVsCurrency: cryptoPairs[0].currency ?? "usd" + + // ── Compact label decimals ──────────────────────────────────────────────── + // 0 → "80k" + // 1 → "80.0k" (default) + // 3 → "80.000k" + property int cryptoDecimals: 2 + + // ── Poll interval ───────────────────────────────────────────────────────── + // Seconds between CoinGecko requests. Free tier allows ~30 req/min. + // With multiple pairs each refresh fetches one pair then advances the index, + // so the effective per-pair interval is: cryptoRefreshSec × cryptoPairs.length + property int cryptoRefreshSec: 120 + + // ── Bar chip icon ───────────────────────────────────────────────────────── + // Show the coin icon glyph ("󰿤") on the bar chip. + property bool cryptoShowIcon: true } diff --git a/config/themes/aero.qml b/config/themes/aero.qml index 2f61b66..1428760 100644 --- a/config/themes/aero.qml +++ b/config/themes/aero.qml @@ -29,13 +29,12 @@ QtObject { readonly property color borderToday: accent // ── ArchLogo Module ──────────────────────────────────────────────────────── - readonly property color archLogoBg: bgPanel - readonly property color archLogoBgHover: "#22ffffff" - readonly property color archLogoHover: accent - readonly property color archLogoBorder: border - readonly property color archLogoBorderActive: accent - readonly property color archLogoIcon: textMain - readonly property color archLogoIconActive: accent + readonly property color logoBg: bgPanel + readonly property color logoBgHover: "#22ffffff" + readonly property color logoBorder: border + readonly property color logoBorderActive: accent + readonly property color logoIcon: textMain + readonly property color logoIconActive: accent // ── Stats Module (Aurora Tones) ─────────────────────────────────────────── readonly property color statsBg: bgPanel @@ -45,7 +44,6 @@ QtObject { readonly property color statsCpuColor: "#cbdc3d" // Lime green readonly property color statsMemColor: "#4db6ac" // Teal readonly property color statsDiskColor: "#81d4fa" // Sky blue - readonly property color statsGpuColor: "#fff176" // Sun yellow readonly property color statsTrackColor: "#15ffffff" // ── Volume Module (Glass Water) ─────────────────────────────────────────── @@ -71,15 +69,55 @@ QtObject { readonly property color clockTime: textMain readonly property color clockSeconds: "#fff176" // Sun yellow readonly property color clockIcon: accent - readonly property color clockSeparator: separator - readonly property color clockText: textMain - readonly property color clockShadow: "#44000000" + + // ── Battery Module ──────────────────────────────────────────────────────── + readonly property color batteryFull: accent + readonly property color batteryCharging: accent + readonly property color batteryCritical: statusErr + readonly property color batteryLow: statusWarn + readonly property color batteryIcon: clockIcon + readonly property color batBg: bgPanel + readonly property color batBgHover: "#11ffffff" + readonly property color batBorder: border + readonly property color batBorderHover: accent + readonly property color batText: textMain + readonly property color batPopupBg: bgPopup + readonly property color batPopupBorder: borderPopup + readonly property color batPopupSeparator: separator + readonly property color batPopupHeader: "#cbdc3d" + readonly property color batPopupText: textMain + readonly property color batPopupDim: textDim + readonly property color batProgressTrack: separator + readonly property color batProfileActiveText: bg + + // ── Network Module ──────────────────────────────────────────────────────── + readonly property color netBg: bgPanel + readonly property color netBgHover: "#11ffffff" + readonly property color netBorder: border + readonly property color netBorderHover: accent + readonly property color netText: textMain + readonly property color netPopupBg: bgPopup + readonly property color netPopupBorder: borderPopup + readonly property color netPopupSeparator: separator + readonly property color netPopupHeader: "#cbdc3d" + readonly property color netPopupText: textMain + readonly property color netPopupDim: textDim + readonly property color netFieldBg: "#22ffffff" + readonly property color netCancelBgHover: "#22ffffff" + readonly property color netConnectText: "#ff0b3333" + readonly property color netApActiveBg: Qt.alpha(accent, 0.15) + readonly property color netApHoverBg: calArrowBgHover + readonly property color netDisconnectBg: Qt.alpha(statusErr, 0.18) + readonly property color netDisconnectBorder: Qt.alpha(statusErr, 0.45) + readonly property color netWiredOkBg: Qt.alpha(statusOk, 0.12) + readonly property color netWiredErrBg: Qt.alpha(statusErr, 0.12) + readonly property color netWiredOkBorder: Qt.alpha(statusOk, 0.4) + readonly property color netWiredErrBorder: Qt.alpha(statusErr, 0.4) // ── Calendar Popup ───────────────────────────────────────────────────────── readonly property color calCardBg: bgPopup readonly property color calCardBorder: borderPopup readonly property color calSeparator: separator - readonly property color calShadow: "#66000000" readonly property color clockPopupHeader: "#cbdc3d" readonly property color clockPopupText: textMain readonly property color clockPopupDim: textDim @@ -137,7 +175,6 @@ QtObject { readonly property color notifToastBg: bgPopup readonly property color notifToastBgHover: "#22ffffff" readonly property color notifToastBorder: borderPopup - readonly property color notifToastShadow: "#44000000" readonly property color notifToastText: textMain readonly property color notifToastDim: textDim readonly property color notifToastAppName: "#ff81d4fa" @@ -154,11 +191,10 @@ QtObject { // ── History popup ────────────────────────────────────────────────────────── readonly property color notifHistoryBg: bgPopup readonly property color notifHistoryBorder: "#33cbdc3d" - readonly property color notifHistoryShadow: "#55000000" readonly property color notifHistoryHover: "#11cbdc3d" readonly property color notifHistoryEmpty: textDim - // new variables adaptated + // new variables adapted readonly property color frameBorder: "transparent" readonly property color clockPopupDate: textMain readonly property color clockPopupUtc: textDim @@ -168,8 +204,96 @@ QtObject { readonly property color calArrowBgHover: "#22cbdc3d" readonly property color calArrowBgPress: accent + // ── Crypto Module ────────────────────────────────────────────────────────── + readonly property color cryptoBg: bgPanel + readonly property color cryptoBgHover: "#15ffffff" + readonly property color cryptoBorder: border + readonly property color cryptoBorderHover: accent + readonly property color cryptoIcon: accent + readonly property color cryptoLabel: textDim + readonly property color cryptoValue: textMain + readonly property color cryptoUp: statusOk + readonly property color cryptoDown: statusErr + readonly property color cryptoPopupBg: bgPopup + readonly property color cryptoPopupBorder: borderPopup + readonly property color cryptoPopupHeader: accent + readonly property color cryptoPopupText: textMain + readonly property color cryptoPopupDim: textDim + + // Tinted surfaces (derived from main colours) + readonly property color cryptoUpBg: Qt.rgba(cryptoUp.r, cryptoUp.g, cryptoUp.b, 0.13) + readonly property color cryptoDownBg: Qt.rgba(cryptoDown.r, cryptoDown.g, cryptoDown.b, 0.13) + readonly property color cryptoChipRemoveBg: Qt.rgba(cryptoDown.r, cryptoDown.g, cryptoDown.b, 0.27) + readonly property color cryptoApplyIdleBg: Qt.rgba(accent.r, accent.g, accent.b, 0.20) + // ── BgDate Module ────────────────────────────────────────────────────────── readonly property color bgDateText: textMain readonly property color bgDateShadow: "#66000000" readonly property color bgDateSecondsText: textMain + + // ── Polkit Dialog ────────────────────────────────────────────────────────── + // Overlay + readonly property color polkitOverlayBg: "#bb000000" + + // Card chrome + readonly property color polkitCardBg: bgPopup + readonly property color polkitCardBorder: Qt.rgba(accent.r, accent.g, accent.b, 0.16) + + // Lock icon + readonly property color polkitIconBg: Qt.rgba(accent.r, accent.g, accent.b, 0.12) + readonly property color polkitIconBorder: Qt.rgba(accent.r, accent.g, accent.b, 0.27) + readonly property color polkitIconColor: accent + + // Text hierarchy + readonly property color polkitTitle: textMain + readonly property color polkitMessage: textMain + + // Action-ID pill + readonly property color polkitPillBg: Qt.rgba(textDim.r, textDim.g, textDim.b, 0.10) + readonly property color polkitPillText: textDim + + // Divider + readonly property color polkitDivider: separator + + // Supplementary message — error state + readonly property color polkitSuppErrBg: Qt.rgba(statusErr.r, statusErr.g, statusErr.b, 0.10) + readonly property color polkitSuppErrBorder: Qt.rgba(statusErr.r, statusErr.g, statusErr.b, 0.27) + readonly property color polkitSuppErrText: statusErr + + // Supplementary message — info / ok state + readonly property color polkitSuppOkBg: Qt.rgba(statusOk.r, statusOk.g, statusOk.b, 0.10) + readonly property color polkitSuppOkBorder: Qt.rgba(statusOk.r, statusOk.g, statusOk.b, 0.27) + readonly property color polkitSuppOkText: statusOk + + // Fields (password input & identity selector) + readonly property color polkitFieldBg: launcherInput + readonly property color polkitFieldBorder: "#22ffffff" + readonly property color polkitFieldBorderFocus: accent + + // Identity selector text + readonly property color polkitUserLabel: textDim + readonly property color polkitUserValue: accent + + // Input prompt label above password field + readonly property color polkitInputPrompt: textDim + + // TextInput inside password field + readonly property color polkitInputText: textMain + readonly property color polkitInputSelection: Qt.rgba(accent.r, accent.g, accent.b, 0.33) + readonly property color polkitInputSelectedText: bg + + // Eye / show-password toggle + readonly property color polkitEyeIcon: textDim + readonly property color polkitEyeIconHover: accent + + // Authenticate button + readonly property color polkitAuthBg: accent + readonly property color polkitAuthBgHover: Qt.lighter(accent, 1.15) + readonly property color polkitAuthText: bg + + // Cancel button + readonly property color polkitCancelBg: "transparent" + readonly property color polkitCancelBgHover: "#22ffffff" + readonly property color polkitCancelBorder: "#22ffffff" + readonly property color polkitCancelText: textDim } diff --git a/config/themes/aurora.qml b/config/themes/aurora.qml index d119970..71bbffd 100644 --- a/config/themes/aurora.qml +++ b/config/themes/aurora.qml @@ -29,13 +29,12 @@ QtObject { readonly property color borderToday: accent // ── ArchLogo Module ──────────────────────────────────────────────────────── - readonly property color archLogoBg: bgPanel - readonly property color archLogoBgHover: "#22ffffff" - readonly property color archLogoHover: accent - readonly property color archLogoBorder: border - readonly property color archLogoBorderActive: accent - readonly property color archLogoIcon: textMain - readonly property color archLogoIconActive: accent + readonly property color logoBg: bgPanel + readonly property color logoBgHover: "#22ffffff" + readonly property color logoBorder: border + readonly property color logoBorderActive: accent + readonly property color logoIcon: textMain + readonly property color logoIconActive: accent // ── Stats Module (Variation: Cosmic Cyan) ────────────────────────────────── readonly property color statsBg: bgPanel @@ -45,7 +44,6 @@ QtObject { readonly property color statsCpuColor: "#ff00f2ff" readonly property color statsMemColor: "#ffbc7fff" // Trail violet readonly property color statsDiskColor: "#ff7fbaff" // Sky blue - readonly property color statsGpuColor: "#ff3df5ff" // Bright teal readonly property color statsTrackColor: "#15ffffff" // ── Volume Module (Variation: Comet Purple) ──────────────────────────────── @@ -71,15 +69,55 @@ QtObject { readonly property color clockTime: textMain readonly property color clockSeconds: "#ffbc7fff" // Purple seconds readonly property color clockIcon: accent - readonly property color clockSeparator: separator - readonly property color clockText: textMain - readonly property color clockShadow: "#66000000" + + // ── Battery Module ──────────────────────────────────────────────────────── + readonly property color batteryFull: accent + readonly property color batteryCharging: accent + readonly property color batteryCritical: statusErr + readonly property color batteryLow: statusWarn + readonly property color batteryIcon: clockIcon + readonly property color batBg: bgPanel + readonly property color batBgHover: "#11ffffff" + readonly property color batBorder: border + readonly property color batBorderHover: accent + readonly property color batText: textMain + readonly property color batPopupBg: bgPopup + readonly property color batPopupBorder: borderPopup + readonly property color batPopupSeparator: separator + readonly property color batPopupHeader: "#ff82e6ff" + readonly property color batPopupText: textMain + readonly property color batPopupDim: textDim + readonly property color batProgressTrack: separator + readonly property color batProfileActiveText: bg + + // ── Network Module ──────────────────────────────────────────────────────── + readonly property color netBg: bgPanel + readonly property color netBgHover: "#11ffffff" + readonly property color netBorder: border + readonly property color netBorderHover: accent + readonly property color netText: textMain + readonly property color netPopupBg: bgPopup + readonly property color netPopupBorder: borderPopup + readonly property color netPopupSeparator: separator + readonly property color netPopupHeader: "#ff82e6ff" + readonly property color netPopupText: textMain + readonly property color netPopupDim: textDim + readonly property color netFieldBg: "#22ffffff" + readonly property color netCancelBgHover: "#22ffffff" + readonly property color netConnectText: "#ff050a14" + readonly property color netApActiveBg: Qt.alpha(accent, 0.15) + readonly property color netApHoverBg: calArrowBgHover + readonly property color netDisconnectBg: Qt.alpha(statusErr, 0.18) + readonly property color netDisconnectBorder: Qt.alpha(statusErr, 0.45) + readonly property color netWiredOkBg: Qt.alpha(statusOk, 0.12) + readonly property color netWiredErrBg: Qt.alpha(statusErr, 0.12) + readonly property color netWiredOkBorder: Qt.alpha(statusOk, 0.4) + readonly property color netWiredErrBorder: Qt.alpha(statusErr, 0.4) // ── Calendar Popup ───────────────────────────────────────────────────────── readonly property color calCardBg: bgPopup readonly property color calCardBorder: borderPopup readonly property color calSeparator: separator - readonly property color calShadow: "#88000000" readonly property color clockPopupHeader: "#ff82e6ff" readonly property color clockPopupText: textMain readonly property color clockPopupDim: textDim @@ -131,14 +169,12 @@ QtObject { readonly property color notifBadgeBg: statusErr readonly property color notifBadgeText: "#ffffffff" readonly property color notifSeparator: separator - readonly property color notifDnd: separator - + readonly property color notifDnd: separator // ── Toast cards (Variation: Nebula) ──────────────────────────────────────── readonly property color notifToastBg: bgPopup readonly property color notifToastBgHover: "#22ffffff" readonly property color notifToastBorder: borderPopup - readonly property color notifToastShadow: "#44000000" readonly property color notifToastText: textMain readonly property color notifToastDim: textDim readonly property color notifToastAppName: "#ffbc7fff" @@ -155,11 +191,10 @@ QtObject { // ── History popup ────────────────────────────────────────────────────────── readonly property color notifHistoryBg: bgPopup readonly property color notifHistoryBorder: "#3300d9ff" - readonly property color notifHistoryShadow: "#55000000" readonly property color notifHistoryHover: "#1100d9ff" readonly property color notifHistoryEmpty: textDim - // new variables adaptated + // new variables adapted readonly property color frameBorder: "transparent" readonly property color clockPopupDate: textMain readonly property color clockPopupUtc: textDim @@ -169,8 +204,96 @@ QtObject { readonly property color calArrowBgHover: "#2200d9ff" readonly property color calArrowBgPress: accent + // ── Crypto Module ────────────────────────────────────────────────────────── + readonly property color cryptoBg: bgPanel + readonly property color cryptoBgHover: "#15ffffff" + readonly property color cryptoBorder: border + readonly property color cryptoBorderHover: accent + readonly property color cryptoIcon: accent + readonly property color cryptoLabel: textDim + readonly property color cryptoValue: textMain + readonly property color cryptoUp: statusOk + readonly property color cryptoDown: statusErr + readonly property color cryptoPopupBg: bgPopup + readonly property color cryptoPopupBorder: borderPopup + readonly property color cryptoPopupHeader: accent + readonly property color cryptoPopupText: textMain + readonly property color cryptoPopupDim: textDim + + // Tinted surfaces (derived from main colours) + readonly property color cryptoUpBg: Qt.rgba(cryptoUp.r, cryptoUp.g, cryptoUp.b, 0.13) + readonly property color cryptoDownBg: Qt.rgba(cryptoDown.r, cryptoDown.g, cryptoDown.b, 0.13) + readonly property color cryptoChipRemoveBg: Qt.rgba(cryptoDown.r, cryptoDown.g, cryptoDown.b, 0.27) + readonly property color cryptoApplyIdleBg: Qt.rgba(accent.r, accent.g, accent.b, 0.20) + // ── BgDate Module ────────────────────────────────────────────────────────── readonly property color bgDateText: textMain readonly property color bgDateShadow: "#88000000" readonly property color bgDateSecondsText: textMain + + // ── Polkit Dialog ────────────────────────────────────────────────────────── + // Overlay + readonly property color polkitOverlayBg: "#bb000000" + + // Card chrome + readonly property color polkitCardBg: bgPopup + readonly property color polkitCardBorder: Qt.rgba(accent.r, accent.g, accent.b, 0.16) + + // Lock icon + readonly property color polkitIconBg: Qt.rgba(accent.r, accent.g, accent.b, 0.12) + readonly property color polkitIconBorder: Qt.rgba(accent.r, accent.g, accent.b, 0.27) + readonly property color polkitIconColor: accent + + // Text hierarchy + readonly property color polkitTitle: textMain + readonly property color polkitMessage: textMain + + // Action-ID pill + readonly property color polkitPillBg: Qt.rgba(textDim.r, textDim.g, textDim.b, 0.10) + readonly property color polkitPillText: textDim + + // Divider + readonly property color polkitDivider: separator + + // Supplementary message — error state + readonly property color polkitSuppErrBg: Qt.rgba(statusErr.r, statusErr.g, statusErr.b, 0.10) + readonly property color polkitSuppErrBorder: Qt.rgba(statusErr.r, statusErr.g, statusErr.b, 0.27) + readonly property color polkitSuppErrText: statusErr + + // Supplementary message — info / ok state + readonly property color polkitSuppOkBg: Qt.rgba(statusOk.r, statusOk.g, statusOk.b, 0.10) + readonly property color polkitSuppOkBorder: Qt.rgba(statusOk.r, statusOk.g, statusOk.b, 0.27) + readonly property color polkitSuppOkText: statusOk + + // Fields (password input & identity selector) + readonly property color polkitFieldBg: launcherInput + readonly property color polkitFieldBorder: "#22ffffff" + readonly property color polkitFieldBorderFocus: accent + + // Identity selector text + readonly property color polkitUserLabel: textDim + readonly property color polkitUserValue: accent + + // Input prompt label above password field + readonly property color polkitInputPrompt: textDim + + // TextInput inside password field + readonly property color polkitInputText: textMain + readonly property color polkitInputSelection: Qt.rgba(accent.r, accent.g, accent.b, 0.33) + readonly property color polkitInputSelectedText: bg + + // Eye / show-password toggle + readonly property color polkitEyeIcon: textDim + readonly property color polkitEyeIconHover: accent + + // Authenticate button + readonly property color polkitAuthBg: accent + readonly property color polkitAuthBgHover: Qt.lighter(accent, 1.15) + readonly property color polkitAuthText: bg + + // Cancel button + readonly property color polkitCancelBg: "transparent" + readonly property color polkitCancelBgHover: "#22ffffff" + readonly property color polkitCancelBorder: "#22ffffff" + readonly property color polkitCancelText: textDim } diff --git a/config/themes/beach.qml b/config/themes/beach.qml index 5f52732..49789c2 100644 --- a/config/themes/beach.qml +++ b/config/themes/beach.qml @@ -29,13 +29,12 @@ QtObject { readonly property color borderToday: accent // ── ArchLogo Module ──────────────────────────────────────────────────────── - readonly property color archLogoBg: bgPanel - readonly property color archLogoBgHover: "#22ffffff" - readonly property color archLogoHover: accent - readonly property color archLogoBorder: border - readonly property color archLogoBorderActive: accent - readonly property color archLogoIcon: textMain - readonly property color archLogoIconActive: accent + readonly property color logoBg: bgPanel + readonly property color logoBgHover: "#22ffffff" + readonly property color logoBorder: border + readonly property color logoBorderActive: accent + readonly property color logoIcon: textMain + readonly property color logoIconActive: accent // ── Stats Module (Variation: Shoreline Tones) ────────────────────────────── readonly property color statsBg: bgPanel @@ -45,7 +44,6 @@ QtObject { readonly property color statsCpuColor: "#ffebc6a0" // Sunset gold readonly property color statsMemColor: "#ff8fa3b0" // Ocean mist readonly property color statsDiskColor: "#ffc2d1d9" // Bright sky blue - readonly property color statsGpuColor: "#ffd9a385" // Sand tan readonly property color statsTrackColor: "#15ffffff" // ── Volume Module (Variation: Deep Water) ────────────────────────────────── @@ -71,15 +69,55 @@ QtObject { readonly property color clockTime: textMain readonly property color clockSeconds: "#ffd9a385" // Sun-glow orange readonly property color clockIcon: accent - readonly property color clockSeparator: separator - readonly property color clockText: textMain - readonly property color clockShadow: "#44000000" + + // ── Battery Module ──────────────────────────────────────────────────────── + readonly property color batteryFull: accent + readonly property color batteryCharging: accent + readonly property color batteryCritical: statusErr + readonly property color batteryLow: statusWarn + readonly property color batteryIcon: clockIcon + readonly property color batBg: bgPanel + readonly property color batBgHover: "#11ffffff" + readonly property color batBorder: border + readonly property color batBorderHover: accent + readonly property color batText: textMain + readonly property color batPopupBg: bgPopup + readonly property color batPopupBorder: borderPopup + readonly property color batPopupSeparator: separator + readonly property color batPopupHeader: "#ffebc6a0" + readonly property color batPopupText: textMain + readonly property color batPopupDim: textDim + readonly property color batProgressTrack: separator + readonly property color batProfileActiveText: bg + + // ── Network Module ──────────────────────────────────────────────────────── + readonly property color netBg: bgPanel + readonly property color netBgHover: "#11ffffff" + readonly property color netBorder: border + readonly property color netBorderHover: accent + readonly property color netText: textMain + readonly property color netPopupBg: bgPopup + readonly property color netPopupBorder: borderPopup + readonly property color netPopupSeparator: separator + readonly property color netPopupHeader: "#ffebc6a0" + readonly property color netPopupText: textMain + readonly property color netPopupDim: textDim + readonly property color netFieldBg: "#22ffffff" + readonly property color netCancelBgHover: "#22ffffff" + readonly property color netConnectText: "#ff1a212e" + readonly property color netApActiveBg: Qt.alpha(accent, 0.15) + readonly property color netApHoverBg: calArrowBgHover + readonly property color netDisconnectBg: Qt.alpha(statusErr, 0.18) + readonly property color netDisconnectBorder: Qt.alpha(statusErr, 0.45) + readonly property color netWiredOkBg: Qt.alpha(statusOk, 0.12) + readonly property color netWiredErrBg: Qt.alpha(statusErr, 0.12) + readonly property color netWiredOkBorder: Qt.alpha(statusOk, 0.4) + readonly property color netWiredErrBorder: Qt.alpha(statusErr, 0.4) // ── Calendar Popup ───────────────────────────────────────────────────────── readonly property color calCardBg: bgPopup readonly property color calCardBorder: borderPopup readonly property color calSeparator: separator - readonly property color calShadow: "#66000000" readonly property color clockPopupHeader: "#ffebc6a0" readonly property color clockPopupText: textMain readonly property color clockPopupDim: textDim @@ -131,13 +169,12 @@ QtObject { readonly property color notifBadgeBg: statusErr readonly property color notifBadgeText: "#ffffffff" readonly property color notifSeparator: separator - readonly property color notifDnd: separator + readonly property color notifDnd: separator // ── Toast cards (Variation: Dusk) ────────────────────────────────────────── readonly property color notifToastBg: bgPopup readonly property color notifToastBgHover: "#22ffffff" readonly property color notifToastBorder: borderPopup - readonly property color notifToastShadow: "#44000000" readonly property color notifToastText: textMain readonly property color notifToastDim: textDim readonly property color notifToastAppName: "#ff8fa3b0" @@ -154,11 +191,10 @@ QtObject { // ── History popup ────────────────────────────────────────────────────────── readonly property color notifHistoryBg: bgPopup readonly property color notifHistoryBorder: "#33ebc6a0" - readonly property color notifHistoryShadow: "#55000000" readonly property color notifHistoryHover: "#11ebc6a0" readonly property color notifHistoryEmpty: textDim - // new variables adaptated + // new variables adapted readonly property color frameBorder: "transparent" readonly property color clockPopupDate: textMain readonly property color clockPopupUtc: textDim @@ -168,8 +204,96 @@ QtObject { readonly property color calArrowBgHover: "#22ebc6a0" readonly property color calArrowBgPress: accent + // ── Crypto Module ────────────────────────────────────────────────────────── + readonly property color cryptoBg: bgPanel + readonly property color cryptoBgHover: "#15ffffff" + readonly property color cryptoBorder: border + readonly property color cryptoBorderHover: accent + readonly property color cryptoIcon: accent + readonly property color cryptoLabel: textDim + readonly property color cryptoValue: textMain + readonly property color cryptoUp: statusOk + readonly property color cryptoDown: statusErr + readonly property color cryptoPopupBg: bgPopup + readonly property color cryptoPopupBorder: borderPopup + readonly property color cryptoPopupHeader: accent + readonly property color cryptoPopupText: textMain + readonly property color cryptoPopupDim: textDim + + // Tinted surfaces (derived from main colours) + readonly property color cryptoUpBg: Qt.rgba(cryptoUp.r, cryptoUp.g, cryptoUp.b, 0.13) + readonly property color cryptoDownBg: Qt.rgba(cryptoDown.r, cryptoDown.g, cryptoDown.b, 0.13) + readonly property color cryptoChipRemoveBg: Qt.rgba(cryptoDown.r, cryptoDown.g, cryptoDown.b, 0.27) + readonly property color cryptoApplyIdleBg: Qt.rgba(accent.r, accent.g, accent.b, 0.20) + // ── BgDate Module ────────────────────────────────────────────────────────── readonly property color bgDateText: textMain readonly property color bgDateShadow: "#66000000" readonly property color bgDateSecondsText: textMain + + // ── Polkit Dialog ────────────────────────────────────────────────────────── + // Overlay + readonly property color polkitOverlayBg: "#bb000000" + + // Card chrome + readonly property color polkitCardBg: bgPopup + readonly property color polkitCardBorder: Qt.rgba(accent.r, accent.g, accent.b, 0.16) + + // Lock icon + readonly property color polkitIconBg: Qt.rgba(accent.r, accent.g, accent.b, 0.12) + readonly property color polkitIconBorder: Qt.rgba(accent.r, accent.g, accent.b, 0.27) + readonly property color polkitIconColor: accent + + // Text hierarchy + readonly property color polkitTitle: textMain + readonly property color polkitMessage: textMain + + // Action-ID pill + readonly property color polkitPillBg: Qt.rgba(textDim.r, textDim.g, textDim.b, 0.10) + readonly property color polkitPillText: textDim + + // Divider + readonly property color polkitDivider: separator + + // Supplementary message — error state + readonly property color polkitSuppErrBg: Qt.rgba(statusErr.r, statusErr.g, statusErr.b, 0.10) + readonly property color polkitSuppErrBorder: Qt.rgba(statusErr.r, statusErr.g, statusErr.b, 0.27) + readonly property color polkitSuppErrText: statusErr + + // Supplementary message — info / ok state + readonly property color polkitSuppOkBg: Qt.rgba(statusOk.r, statusOk.g, statusOk.b, 0.10) + readonly property color polkitSuppOkBorder: Qt.rgba(statusOk.r, statusOk.g, statusOk.b, 0.27) + readonly property color polkitSuppOkText: statusOk + + // Fields (password input & identity selector) + readonly property color polkitFieldBg: launcherInput + readonly property color polkitFieldBorder: "#22ffffff" + readonly property color polkitFieldBorderFocus: accent + + // Identity selector text + readonly property color polkitUserLabel: textDim + readonly property color polkitUserValue: accent + + // Input prompt label above password field + readonly property color polkitInputPrompt: textDim + + // TextInput inside password field + readonly property color polkitInputText: textMain + readonly property color polkitInputSelection: Qt.rgba(accent.r, accent.g, accent.b, 0.33) + readonly property color polkitInputSelectedText: bg + + // Eye / show-password toggle + readonly property color polkitEyeIcon: textDim + readonly property color polkitEyeIconHover: accent + + // Authenticate button + readonly property color polkitAuthBg: accent + readonly property color polkitAuthBgHover: Qt.lighter(accent, 1.15) + readonly property color polkitAuthText: bg + + // Cancel button + readonly property color polkitCancelBg: "transparent" + readonly property color polkitCancelBgHover: "#22ffffff" + readonly property color polkitCancelBorder: "#22ffffff" + readonly property color polkitCancelText: textDim } diff --git a/config/themes/colorful.qml b/config/themes/colorful.qml index 963685c..ee783fc 100644 --- a/config/themes/colorful.qml +++ b/config/themes/colorful.qml @@ -29,13 +29,12 @@ QtObject { readonly property color borderToday: accent // ── ArchLogo Module ──────────────────────────────────────────────────────── - readonly property color archLogoBg: bgPanel - readonly property color archLogoBgHover: "#22ffffff" - readonly property color archLogoHover: accent - readonly property color archLogoBorder: border - readonly property color archLogoBorderActive: accent - readonly property color archLogoIcon: textMain - readonly property color archLogoIconActive: accent + readonly property color logoBg: bgPanel + readonly property color logoBgHover: "#22ffffff" + readonly property color logoBorder: border + readonly property color logoBorderActive: accent + readonly property color logoIcon: textMain + readonly property color logoIconActive: accent // ── Stats Module (Variation: Alpenglow) ──────────────────────────────────── readonly property color statsBg: bgPanel @@ -45,7 +44,6 @@ QtObject { readonly property color statsCpuColor: "#fff0a1ba" // Pink readonly property color statsMemColor: "#ffffcc99" // Horizon orange readonly property color statsDiskColor: "#ff8da0c4" // Water blue - readonly property color statsGpuColor: "#ffb08dc4" // Purple mist readonly property color statsTrackColor: "#15ffffff" // ── Volume Module (Variation: Frozen Lake) ───────────────────────────────── @@ -71,15 +69,55 @@ QtObject { readonly property color clockTime: textMain readonly property color clockSeconds: "#ffffcc99" // Horizon orange readonly property color clockIcon: accent - readonly property color clockSeparator: separator - readonly property color clockText: textMain - readonly property color clockShadow: "#44000000" + + // ── Battery Module ──────────────────────────────────────────────────────── + readonly property color batteryFull: accent + readonly property color batteryCharging: accent + readonly property color batteryCritical: statusErr + readonly property color batteryLow: statusWarn + readonly property color batteryIcon: clockIcon + readonly property color batBg: bgPanel + readonly property color batBgHover: "#11ffffff" + readonly property color batBorder: border + readonly property color batBorderHover: accent + readonly property color batText: textMain + readonly property color batPopupBg: bgPopup + readonly property color batPopupBorder: borderPopup + readonly property color batPopupSeparator: separator + readonly property color batPopupHeader: "#fff0a1ba" + readonly property color batPopupText: textMain + readonly property color batPopupDim: textDim + readonly property color batProgressTrack: separator + readonly property color batProfileActiveText: bg + + // ── Network Module ──────────────────────────────────────────────────────── + readonly property color netBg: bgPanel + readonly property color netBgHover: "#11ffffff" + readonly property color netBorder: border + readonly property color netBorderHover: accent + readonly property color netText: textMain + readonly property color netPopupBg: bgPopup + readonly property color netPopupBorder: borderPopup + readonly property color netPopupSeparator: separator + readonly property color netPopupHeader: "#fff0a1ba" + readonly property color netPopupText: textMain + readonly property color netPopupDim: textDim + readonly property color netFieldBg: "#22ffffff" + readonly property color netCancelBgHover: "#22ffffff" + readonly property color netConnectText: "#ff1c2433" + readonly property color netApActiveBg: Qt.alpha(accent, 0.15) + readonly property color netApHoverBg: calArrowBgHover + readonly property color netDisconnectBg: Qt.alpha(statusErr, 0.18) + readonly property color netDisconnectBorder: Qt.alpha(statusErr, 0.45) + readonly property color netWiredOkBg: Qt.alpha(statusOk, 0.12) + readonly property color netWiredErrBg: Qt.alpha(statusErr, 0.12) + readonly property color netWiredOkBorder: Qt.alpha(statusOk, 0.4) + readonly property color netWiredErrBorder: Qt.alpha(statusErr, 0.4) // ── Calendar Popup ───────────────────────────────────────────────────────── readonly property color calCardBg: bgPopup readonly property color calCardBorder: borderPopup readonly property color calSeparator: separator - readonly property color calShadow: "#66000000" readonly property color clockPopupHeader: "#fff0a1ba" readonly property color clockPopupText: textMain readonly property color clockPopupDim: textDim @@ -131,14 +169,12 @@ QtObject { readonly property color notifBadgeBg: statusErr readonly property color notifBadgeText: "#ffffffff" readonly property color notifSeparator: separator - readonly property color notifDnd: separator - + readonly property color notifDnd: separator // ── Toast cards (Variation: Twilight) ────────────────────────────────────── readonly property color notifToastBg: bgPopup readonly property color notifToastBgHover: "#22ffffff" readonly property color notifToastBorder: borderPopup - readonly property color notifToastShadow: "#44000000" readonly property color notifToastText: textMain readonly property color notifToastDim: textDim readonly property color notifToastAppName: "#ffb08dc4" @@ -155,11 +191,10 @@ QtObject { // ── History popup ────────────────────────────────────────────────────────── readonly property color notifHistoryBg: bgPopup readonly property color notifHistoryBorder: "#33f0a1ba" - readonly property color notifHistoryShadow: "#55000000" readonly property color notifHistoryHover: "#11f0a1ba" readonly property color notifHistoryEmpty: textDim - // new variables adaptated + // new variables adapted readonly property color frameBorder: "transparent" readonly property color clockPopupDate: textMain readonly property color clockPopupUtc: textDim @@ -169,8 +204,96 @@ QtObject { readonly property color calArrowBgHover: "#22f0a1ba" readonly property color calArrowBgPress: accent + // ── Crypto Module ────────────────────────────────────────────────────────── + readonly property color cryptoBg: bgPanel + readonly property color cryptoBgHover: "#15ffffff" + readonly property color cryptoBorder: border + readonly property color cryptoBorderHover: accent + readonly property color cryptoIcon: accent + readonly property color cryptoLabel: textDim + readonly property color cryptoValue: textMain + readonly property color cryptoUp: statusOk + readonly property color cryptoDown: statusErr + readonly property color cryptoPopupBg: bgPopup + readonly property color cryptoPopupBorder: borderPopup + readonly property color cryptoPopupHeader: accent + readonly property color cryptoPopupText: textMain + readonly property color cryptoPopupDim: textDim + + // Tinted surfaces (derived from main colours) + readonly property color cryptoUpBg: Qt.rgba(cryptoUp.r, cryptoUp.g, cryptoUp.b, 0.13) + readonly property color cryptoDownBg: Qt.rgba(cryptoDown.r, cryptoDown.g, cryptoDown.b, 0.13) + readonly property color cryptoChipRemoveBg: Qt.rgba(cryptoDown.r, cryptoDown.g, cryptoDown.b, 0.27) + readonly property color cryptoApplyIdleBg: Qt.rgba(accent.r, accent.g, accent.b, 0.20) + // ── BgDate Module ────────────────────────────────────────────────────────── readonly property color bgDateText: textMain readonly property color bgDateShadow: "#66000000" readonly property color bgDateSecondsText: textMain + + // ── Polkit Dialog ────────────────────────────────────────────────────────── + // Overlay + readonly property color polkitOverlayBg: "#bb000000" + + // Card chrome + readonly property color polkitCardBg: bgPopup + readonly property color polkitCardBorder: Qt.rgba(accent.r, accent.g, accent.b, 0.16) + + // Lock icon + readonly property color polkitIconBg: Qt.rgba(accent.r, accent.g, accent.b, 0.12) + readonly property color polkitIconBorder: Qt.rgba(accent.r, accent.g, accent.b, 0.27) + readonly property color polkitIconColor: accent + + // Text hierarchy + readonly property color polkitTitle: textMain + readonly property color polkitMessage: textMain + + // Action-ID pill + readonly property color polkitPillBg: Qt.rgba(textDim.r, textDim.g, textDim.b, 0.10) + readonly property color polkitPillText: textDim + + // Divider + readonly property color polkitDivider: separator + + // Supplementary message — error state + readonly property color polkitSuppErrBg: Qt.rgba(statusErr.r, statusErr.g, statusErr.b, 0.10) + readonly property color polkitSuppErrBorder: Qt.rgba(statusErr.r, statusErr.g, statusErr.b, 0.27) + readonly property color polkitSuppErrText: statusErr + + // Supplementary message — info / ok state + readonly property color polkitSuppOkBg: Qt.rgba(statusOk.r, statusOk.g, statusOk.b, 0.10) + readonly property color polkitSuppOkBorder: Qt.rgba(statusOk.r, statusOk.g, statusOk.b, 0.27) + readonly property color polkitSuppOkText: statusOk + + // Fields (password input & identity selector) + readonly property color polkitFieldBg: launcherInput + readonly property color polkitFieldBorder: "#22ffffff" + readonly property color polkitFieldBorderFocus: accent + + // Identity selector text + readonly property color polkitUserLabel: textDim + readonly property color polkitUserValue: accent + + // Input prompt label above password field + readonly property color polkitInputPrompt: textDim + + // TextInput inside password field + readonly property color polkitInputText: textMain + readonly property color polkitInputSelection: Qt.rgba(accent.r, accent.g, accent.b, 0.33) + readonly property color polkitInputSelectedText: bg + + // Eye / show-password toggle + readonly property color polkitEyeIcon: textDim + readonly property color polkitEyeIconHover: accent + + // Authenticate button + readonly property color polkitAuthBg: accent + readonly property color polkitAuthBgHover: Qt.lighter(accent, 1.15) + readonly property color polkitAuthText: bg + + // Cancel button + readonly property color polkitCancelBg: "transparent" + readonly property color polkitCancelBgHover: "#22ffffff" + readonly property color polkitCancelBorder: "#22ffffff" + readonly property color polkitCancelText: textDim } diff --git a/config/themes/core b/config/themes/core deleted file mode 100644 index 3bf8a34..0000000 Binary files a/config/themes/core and /dev/null differ diff --git a/config/themes/cosmic.qml b/config/themes/cosmic.qml index f3d4f96..8c8fdd4 100644 --- a/config/themes/cosmic.qml +++ b/config/themes/cosmic.qml @@ -29,13 +29,12 @@ QtObject { readonly property color borderToday: accent // ── ArchLogo Module ──────────────────────────────────────────────────────── - readonly property color archLogoBg: bgPanel - readonly property color archLogoBgHover: "#22ffffff" - readonly property color archLogoHover: accent - readonly property color archLogoBorder: border - readonly property color archLogoBorderActive: accent - readonly property color archLogoIcon: textMain - readonly property color archLogoIconActive: accent + readonly property color logoBg: bgPanel + readonly property color logoBgHover: "#22ffffff" + readonly property color logoBorder: border + readonly property color logoBorderActive: accent + readonly property color logoIcon: textMain + readonly property color logoIconActive: accent // ── Stats Module (Variation: Comet Trail) ────────────────────────────────── readonly property color statsBg: bgPanel @@ -45,7 +44,6 @@ QtObject { readonly property color statsCpuColor: "#ffbdf0ff" readonly property color statsMemColor: "#ffcfb3ff" // Nebula purple readonly property color statsDiskColor: "#ffffc2e0" // Horizon pink - readonly property color statsGpuColor: accent readonly property color statsTrackColor: "#15ffffff" // ── Volume Module (Variation: Nebula Purple) ─────────────────────────────── @@ -71,15 +69,55 @@ QtObject { readonly property color clockTime: textMain readonly property color clockSeconds: "#ffffc2e0" // Horizon pink readonly property color clockIcon: accent - readonly property color clockSeparator: separator - readonly property color clockText: textMain - readonly property color clockShadow: "#44000000" + + // ── Battery Module ──────────────────────────────────────────────────────── + readonly property color batteryFull: accent + readonly property color batteryCharging: accent + readonly property color batteryCritical: statusErr + readonly property color batteryLow: statusWarn + readonly property color batteryIcon: clockIcon + readonly property color batBg: bgPanel + readonly property color batBgHover: "#11ffffff" + readonly property color batBorder: border + readonly property color batBorderHover: accent + readonly property color batText: textMain + readonly property color batPopupBg: bgPopup + readonly property color batPopupBorder: borderPopup + readonly property color batPopupSeparator: separator + readonly property color batPopupHeader: "#ffcfb3ff" + readonly property color batPopupText: textMain + readonly property color batPopupDim: textDim + readonly property color batProgressTrack: separator + readonly property color batProfileActiveText: bg + + // ── Network Module ──────────────────────────────────────────────────────── + readonly property color netBg: bgPanel + readonly property color netBgHover: "#11ffffff" + readonly property color netBorder: border + readonly property color netBorderHover: accent + readonly property color netText: textMain + readonly property color netPopupBg: bgPopup + readonly property color netPopupBorder: borderPopup + readonly property color netPopupSeparator: separator + readonly property color netPopupHeader: "#ffcfb3ff" + readonly property color netPopupText: textMain + readonly property color netPopupDim: textDim + readonly property color netFieldBg: "#22ffffff" + readonly property color netCancelBgHover: "#22ffffff" + readonly property color netConnectText: "#ff0d1326" + readonly property color netApActiveBg: Qt.alpha(accent, 0.15) + readonly property color netApHoverBg: calArrowBgHover + readonly property color netDisconnectBg: Qt.alpha(statusErr, 0.18) + readonly property color netDisconnectBorder: Qt.alpha(statusErr, 0.45) + readonly property color netWiredOkBg: Qt.alpha(statusOk, 0.12) + readonly property color netWiredErrBg: Qt.alpha(statusErr, 0.12) + readonly property color netWiredOkBorder: Qt.alpha(statusOk, 0.4) + readonly property color netWiredErrBorder: Qt.alpha(statusErr, 0.4) // ── Calendar Popup ───────────────────────────────────────────────────────── readonly property color calCardBg: bgPopup readonly property color calCardBorder: borderPopup readonly property color calSeparator: separator - readonly property color calShadow: "#66000000" readonly property color clockPopupHeader: "#ffcfb3ff" readonly property color clockPopupText: textMain readonly property color clockPopupDim: textDim @@ -131,13 +169,12 @@ QtObject { readonly property color notifBadgeBg: statusErr readonly property color notifBadgeText: "#ffffffff" readonly property color notifSeparator: separator - readonly property color notifDnd: separator + readonly property color notifDnd: separator // ── Toast cards (Variation: Horizon Dusk) ────────────────────────────────── readonly property color notifToastBg: bgPopup readonly property color notifToastBgHover: "#22ffffff" readonly property color notifToastBorder: borderPopup - readonly property color notifToastShadow: "#44000000" readonly property color notifToastText: textMain readonly property color notifToastDim: textDim readonly property color notifToastAppName: "#ffcfb3ff" @@ -154,11 +191,10 @@ QtObject { // ── History popup ────────────────────────────────────────────────────────── readonly property color notifHistoryBg: bgPopup readonly property color notifHistoryBorder: "#33bdf0ff" - readonly property color notifHistoryShadow: "#55000000" readonly property color notifHistoryHover: "#11bdf0ff" readonly property color notifHistoryEmpty: textDim - // new variables adaptated + // new variables adapted readonly property color frameBorder: "transparent" readonly property color clockPopupDate: textMain readonly property color clockPopupUtc: textDim @@ -168,8 +204,96 @@ QtObject { readonly property color calArrowBgHover: "#22bdf0ff" readonly property color calArrowBgPress: accent + // ── Crypto Module ────────────────────────────────────────────────────────── + readonly property color cryptoBg: bgPanel + readonly property color cryptoBgHover: "#15ffffff" + readonly property color cryptoBorder: border + readonly property color cryptoBorderHover: accent + readonly property color cryptoIcon: accent + readonly property color cryptoLabel: textDim + readonly property color cryptoValue: textMain + readonly property color cryptoUp: statusOk + readonly property color cryptoDown: statusErr + readonly property color cryptoPopupBg: bgPopup + readonly property color cryptoPopupBorder: borderPopup + readonly property color cryptoPopupHeader: accent + readonly property color cryptoPopupText: textMain + readonly property color cryptoPopupDim: textDim + + // Tinted surfaces (derived from main colours) + readonly property color cryptoUpBg: Qt.rgba(cryptoUp.r, cryptoUp.g, cryptoUp.b, 0.13) + readonly property color cryptoDownBg: Qt.rgba(cryptoDown.r, cryptoDown.g, cryptoDown.b, 0.13) + readonly property color cryptoChipRemoveBg: Qt.rgba(cryptoDown.r, cryptoDown.g, cryptoDown.b, 0.27) + readonly property color cryptoApplyIdleBg: Qt.rgba(accent.r, accent.g, accent.b, 0.20) + // ── BgDate Module ────────────────────────────────────────────────────────── readonly property color bgDateText: textMain readonly property color bgDateShadow: "#66000000" readonly property color bgDateSecondsText: textMain + + // ── Polkit Dialog ────────────────────────────────────────────────────────── + // Overlay + readonly property color polkitOverlayBg: "#bb000000" + + // Card chrome + readonly property color polkitCardBg: bgPopup + readonly property color polkitCardBorder: Qt.rgba(accent.r, accent.g, accent.b, 0.16) + + // Lock icon + readonly property color polkitIconBg: Qt.rgba(accent.r, accent.g, accent.b, 0.12) + readonly property color polkitIconBorder: Qt.rgba(accent.r, accent.g, accent.b, 0.27) + readonly property color polkitIconColor: accent + + // Text hierarchy + readonly property color polkitTitle: textMain + readonly property color polkitMessage: textMain + + // Action-ID pill + readonly property color polkitPillBg: Qt.rgba(textDim.r, textDim.g, textDim.b, 0.10) + readonly property color polkitPillText: textDim + + // Divider + readonly property color polkitDivider: separator + + // Supplementary message — error state + readonly property color polkitSuppErrBg: Qt.rgba(statusErr.r, statusErr.g, statusErr.b, 0.10) + readonly property color polkitSuppErrBorder: Qt.rgba(statusErr.r, statusErr.g, statusErr.b, 0.27) + readonly property color polkitSuppErrText: statusErr + + // Supplementary message — info / ok state + readonly property color polkitSuppOkBg: Qt.rgba(statusOk.r, statusOk.g, statusOk.b, 0.10) + readonly property color polkitSuppOkBorder: Qt.rgba(statusOk.r, statusOk.g, statusOk.b, 0.27) + readonly property color polkitSuppOkText: statusOk + + // Fields (password input & identity selector) + readonly property color polkitFieldBg: launcherInput + readonly property color polkitFieldBorder: "#22ffffff" + readonly property color polkitFieldBorderFocus: accent + + // Identity selector text + readonly property color polkitUserLabel: textDim + readonly property color polkitUserValue: accent + + // Input prompt label above password field + readonly property color polkitInputPrompt: textDim + + // TextInput inside password field + readonly property color polkitInputText: textMain + readonly property color polkitInputSelection: Qt.rgba(accent.r, accent.g, accent.b, 0.33) + readonly property color polkitInputSelectedText: bg + + // Eye / show-password toggle + readonly property color polkitEyeIcon: textDim + readonly property color polkitEyeIconHover: accent + + // Authenticate button + readonly property color polkitAuthBg: accent + readonly property color polkitAuthBgHover: Qt.lighter(accent, 1.15) + readonly property color polkitAuthText: bg + + // Cancel button + readonly property color polkitCancelBg: "transparent" + readonly property color polkitCancelBgHover: "#22ffffff" + readonly property color polkitCancelBorder: "#22ffffff" + readonly property color polkitCancelText: textDim } diff --git a/config/themes/everforest.qml b/config/themes/everforest.qml index f410c28..3abd462 100644 --- a/config/themes/everforest.qml +++ b/config/themes/everforest.qml @@ -29,13 +29,12 @@ QtObject { readonly property color borderToday: accent // ── ArchLogo Module ──────────────────────────────────────────────────────── - readonly property color archLogoBg: bgPanel - readonly property color archLogoBgHover: "#22ffffff" - readonly property color archLogoHover: accent - readonly property color archLogoBorder: border - readonly property color archLogoBorderActive: accent - readonly property color archLogoIcon: textMain - readonly property color archLogoIconActive: accent + readonly property color logoBg: bgPanel + readonly property color logoBgHover: "#22ffffff" + readonly property color logoBorder: border + readonly property color logoBorderActive: accent + readonly property color logoIcon: textMain + readonly property color logoIconActive: accent // ── Stats Module ─────────────────────────────────────────────────────────── readonly property color statsBg: bgPanel @@ -45,7 +44,6 @@ QtObject { readonly property color statsCpuColor: statsRingColor readonly property color statsMemColor: statsRingColor readonly property color statsDiskColor: statsRingColor - readonly property color statsGpuColor: statsRingColor readonly property color statsTrackColor: "#28ffffff" // ── Volume Module ────────────────────────────────────────────────────────── @@ -71,15 +69,55 @@ QtObject { readonly property color clockTime: textMain readonly property color clockSeconds: textMain readonly property color clockIcon: textMain - readonly property color clockSeparator: separator - readonly property color clockText: textMain - readonly property color clockShadow: "#22000000" + + // ── Battery Module ──────────────────────────────────────────────────────── + readonly property color batteryFull: accent + readonly property color batteryCharging: accent + readonly property color batteryCritical: statusErr + readonly property color batteryLow: statusWarn + readonly property color batteryIcon: clockIcon + readonly property color batBg: bgPanel + readonly property color batBgHover: "#22ffffff" + readonly property color batBorder: border + readonly property color batBorderHover: accent + readonly property color batText: textMain + readonly property color batPopupBg: bgPopup + readonly property color batPopupBorder: borderPopup + readonly property color batPopupSeparator: separator + readonly property color batPopupHeader: accent + readonly property color batPopupText: textMain + readonly property color batPopupDim: textDim + readonly property color batProgressTrack: separator + readonly property color batProfileActiveText: bg + + // ── Network Module ──────────────────────────────────────────────────────── + readonly property color netBg: bgPanel + readonly property color netBgHover: "#22ffffff" + readonly property color netBorder: border + readonly property color netBorderHover: accent + readonly property color netText: textMain + readonly property color netPopupBg: bgPopup + readonly property color netPopupBorder: borderPopup + readonly property color netPopupSeparator: separator + readonly property color netPopupHeader: accent + readonly property color netPopupText: textMain + readonly property color netPopupDim: textDim + readonly property color netFieldBg: "#22ffffff" + readonly property color netCancelBgHover: "#22ffffff" + readonly property color netConnectText: "#ff0e1410" + readonly property color netApActiveBg: Qt.alpha(accent, 0.15) + readonly property color netApHoverBg: calArrowBgHover + readonly property color netDisconnectBg: Qt.alpha(statusErr, 0.18) + readonly property color netDisconnectBorder: Qt.alpha(statusErr, 0.45) + readonly property color netWiredOkBg: Qt.alpha(statusOk, 0.12) + readonly property color netWiredErrBg: Qt.alpha(statusErr, 0.12) + readonly property color netWiredOkBorder: Qt.alpha(statusOk, 0.4) + readonly property color netWiredErrBorder: Qt.alpha(statusErr, 0.4) // ── Calendar Popup ───────────────────────────────────────────────────────── readonly property color calCardBg: bgPopup readonly property color calCardBorder: borderPopup readonly property color calSeparator: separator - readonly property color calShadow: "#44000000" readonly property color clockPopupHeader: accent readonly property color clockPopupText: textMain readonly property color clockPopupDim: textDim @@ -131,14 +169,12 @@ QtObject { readonly property color notifBadgeBg: statusErr readonly property color notifBadgeText: "#ff0e1410" readonly property color notifSeparator: separator - readonly property color notifDnd: separator - + readonly property color notifDnd: separator // ── Toast cards ──────────────────────────────────────────────────────────── readonly property color notifToastBg: bgPopup readonly property color notifToastBgHover: borderPopup readonly property color notifToastBorder: borderPopup - readonly property color notifToastShadow: "#55000000" readonly property color notifToastText: textMain readonly property color notifToastDim: textDim readonly property color notifToastAppName: accent @@ -155,11 +191,10 @@ QtObject { // ── History popup ────────────────────────────────────────────────────────── readonly property color notifHistoryBg: bgPopup readonly property color notifHistoryBorder: borderPopup - readonly property color notifHistoryShadow: "#55000000" readonly property color notifHistoryHover: "#22ffffff" readonly property color notifHistoryEmpty: textDim - // new variables adaptated + // new variables adapted readonly property color frameBorder: "transparent" readonly property color clockPopupDate: textMain readonly property color clockPopupUtc: textDim @@ -169,10 +204,97 @@ QtObject { readonly property color calArrowBgHover: "#224a8c5c" readonly property color calArrowBgPress: accent + // ── Crypto Module ────────────────────────────────────────────────────────── + readonly property color cryptoBg: bgPanel + readonly property color cryptoBgHover: "#15ffffff" + readonly property color cryptoBorder: border + readonly property color cryptoBorderHover: accent + readonly property color cryptoIcon: accent + readonly property color cryptoLabel: textDim + readonly property color cryptoValue: textMain + readonly property color cryptoUp: statusOk + readonly property color cryptoDown: statusErr + readonly property color cryptoPopupBg: bgPopup + readonly property color cryptoPopupBorder: borderPopup + readonly property color cryptoPopupHeader: accent + readonly property color cryptoPopupText: textMain + readonly property color cryptoPopupDim: textDim + + // Tinted surfaces (derived from main colours) + readonly property color cryptoUpBg: Qt.rgba(cryptoUp.r, cryptoUp.g, cryptoUp.b, 0.13) + readonly property color cryptoDownBg: Qt.rgba(cryptoDown.r, cryptoDown.g, cryptoDown.b, 0.13) + readonly property color cryptoChipRemoveBg: Qt.rgba(cryptoDown.r, cryptoDown.g, cryptoDown.b, 0.27) + readonly property color cryptoApplyIdleBg: Qt.rgba(accent.r, accent.g, accent.b, 0.20) + // ── BgDate Module ────────────────────────────────────────────────────────── readonly property color bgDateText: textMain readonly property color bgDateShadow: "#44000000" readonly property color bgDateSecondsText: textMain + + // ── Polkit Dialog ────────────────────────────────────────────────────────── + // Overlay + readonly property color polkitOverlayBg: "#bb000000" + + // Card chrome + readonly property color polkitCardBg: bgPopup + readonly property color polkitCardBorder: Qt.rgba(accent.r, accent.g, accent.b, 0.16) + + // Lock icon + readonly property color polkitIconBg: Qt.rgba(accent.r, accent.g, accent.b, 0.12) + readonly property color polkitIconBorder: Qt.rgba(accent.r, accent.g, accent.b, 0.27) + readonly property color polkitIconColor: accent + + // Text hierarchy + readonly property color polkitTitle: textMain + readonly property color polkitMessage: textMain + + // Action-ID pill + readonly property color polkitPillBg: Qt.rgba(textDim.r, textDim.g, textDim.b, 0.10) + readonly property color polkitPillText: textDim + + // Divider + readonly property color polkitDivider: separator + + // Supplementary message — error state + readonly property color polkitSuppErrBg: Qt.rgba(statusErr.r, statusErr.g, statusErr.b, 0.10) + readonly property color polkitSuppErrBorder: Qt.rgba(statusErr.r, statusErr.g, statusErr.b, 0.27) + readonly property color polkitSuppErrText: statusErr + + // Supplementary message — info / ok state + readonly property color polkitSuppOkBg: Qt.rgba(statusOk.r, statusOk.g, statusOk.b, 0.10) + readonly property color polkitSuppOkBorder: Qt.rgba(statusOk.r, statusOk.g, statusOk.b, 0.27) + readonly property color polkitSuppOkText: statusOk + + // Fields (password input & identity selector) + readonly property color polkitFieldBg: launcherInput + readonly property color polkitFieldBorder: "#22ffffff" + readonly property color polkitFieldBorderFocus: accent + + // Identity selector text + readonly property color polkitUserLabel: textDim + readonly property color polkitUserValue: accent + + // Input prompt label above password field + readonly property color polkitInputPrompt: textDim + + // TextInput inside password field + readonly property color polkitInputText: textMain + readonly property color polkitInputSelection: Qt.rgba(accent.r, accent.g, accent.b, 0.33) + readonly property color polkitInputSelectedText: bg + + // Eye / show-password toggle + readonly property color polkitEyeIcon: textDim + readonly property color polkitEyeIconHover: accent + + // Authenticate button + readonly property color polkitAuthBg: accent + readonly property color polkitAuthBgHover: Qt.lighter(accent, 1.15) + readonly property color polkitAuthText: bg + + // Cancel button + readonly property color polkitCancelBg: "transparent" + readonly property color polkitCancelBgHover: "#22ffffff" + readonly property color polkitCancelBorder: "#22ffffff" + readonly property color polkitCancelText: textDim } - diff --git a/config/themes/goldencity.qml b/config/themes/goldencity.qml index 83ee675..5cf3598 100644 --- a/config/themes/goldencity.qml +++ b/config/themes/goldencity.qml @@ -25,18 +25,17 @@ QtObject { // ── Accent & Borders ────────────────────────────────────────────────────── readonly property color accent: "#ffc19375" // Warm hazy horizon gold readonly property color border: "transparent" - readonly property color frameBorder: "transparent" + readonly property color frameBorder: "transparent" readonly property color borderPopup: "transparent" readonly property color borderToday: accent - // ── ArchLogo Module ──────────────────────────────────────────────────────── - readonly property color archLogoBg: bgPanel - readonly property color archLogoBgHover: "#22ffffff" - readonly property color archLogoHover: accent - readonly property color archLogoBorder: border - readonly property color archLogoBorderActive: accent - readonly property color archLogoIcon: textMain - readonly property color archLogoIconActive: accent + // ── Logo Module ──────────────────────────────────────────────────────── + readonly property color logoBg: bgPanel + readonly property color logoBgHover: "#22ffffff" + readonly property color logoBorder: border + readonly property color logoBorderActive: accent + readonly property color logoIcon: textMain + readonly property color logoIconActive: accent // ── Stats Module (Variation: Skyline Pulse) ─────────────────────────────── readonly property color statsBg: bgPanel @@ -46,7 +45,6 @@ QtObject { readonly property color statsCpuColor: "#ffc19375" // Horizon gold readonly property color statsMemColor: "#ff89b4fa" // Building window blue readonly property color statsDiskColor: "#ff94e2d5" // Teal office light - readonly property color statsGpuColor: "#fff38ba8" // Aviation warning red readonly property color statsTrackColor: "#15ffffff" // ── Volume Module (Variation: Hazy Dusk) ────────────────────────────────── @@ -72,15 +70,55 @@ QtObject { readonly property color clockTime: textMain readonly property color clockSeconds: "#ffc19375" // Sun-glow gold readonly property color clockIcon: accent - readonly property color clockSeparator: separator - readonly property color clockText: textMain - readonly property color clockShadow: "#66000000" + + // ── Battery Module ──────────────────────────────────────────────────────── + readonly property color batteryFull: accent + readonly property color batteryCharging: accent + readonly property color batteryCritical: statusErr + readonly property color batteryLow: statusWarn + readonly property color batteryIcon: clockIcon + readonly property color batBg: bgPanel + readonly property color batBgHover: "#11ffffff" + readonly property color batBorder: border + readonly property color batBorderHover: accent + readonly property color batText: textMain + readonly property color batPopupBg: bgPopup + readonly property color batPopupBorder: borderPopup + readonly property color batPopupSeparator: separator + readonly property color batPopupHeader: "#ffc19375" + readonly property color batPopupText: textMain + readonly property color batPopupDim: textDim + readonly property color batProgressTrack: separator + readonly property color batProfileActiveText: bg + + // ── Network Module ──────────────────────────────────────────────────────── + readonly property color netBg: bgPanel + readonly property color netBgHover: "#11ffffff" + readonly property color netBorder: border + readonly property color netBorderHover: accent + readonly property color netText: textMain + readonly property color netPopupBg: bgPopup + readonly property color netPopupBorder: borderPopup + readonly property color netPopupSeparator: separator + readonly property color netPopupHeader: "#ffc19375" + readonly property color netPopupText: textMain + readonly property color netPopupDim: textDim + readonly property color netFieldBg: "#22ffffff" + readonly property color netCancelBgHover: "#22ffffff" + readonly property color netConnectText: "#ff0c0e12" + readonly property color netApActiveBg: Qt.alpha(accent, 0.15) + readonly property color netApHoverBg: calArrowBgHover + readonly property color netDisconnectBg: Qt.alpha(statusErr, 0.18) + readonly property color netDisconnectBorder: Qt.alpha(statusErr, 0.45) + readonly property color netWiredOkBg: Qt.alpha(statusOk, 0.12) + readonly property color netWiredErrBg: Qt.alpha(statusErr, 0.12) + readonly property color netWiredOkBorder: Qt.alpha(statusOk, 0.4) + readonly property color netWiredErrBorder: Qt.alpha(statusErr, 0.4) // ── Calendar Popup ───────────────────────────────────────────────────────── readonly property color calCardBg: bgPopup readonly property color calCardBorder: borderPopup readonly property color calSeparator: separator - readonly property color calShadow: "#88000000" readonly property color clockPopupHeader: "#ffc19375" readonly property color clockPopupText: textMain readonly property color clockPopupDim: textDim @@ -109,6 +147,15 @@ QtObject { readonly property color trayBorderHover: "#22ffffff" readonly property color trayIcon: textMain + // ── Tray Context Menu ────────────────────────────────────────────────────── + readonly property color trayMenuBg: bgPopup + readonly property color trayMenuBorder: borderPopup + readonly property color trayMenuText: textMain + readonly property color trayMenuDim: textDim + readonly property color trayMenuHover: "#22c19375" // soft gold tint + readonly property color trayMenuCheck: accent + readonly property color trayMenuSeparator: separator + // ── Launcher (Variation: Shanghai Mist) ─────────────────────────────────── readonly property color launcherBg: bgPopup readonly property color launcherBorder: borderPopup @@ -145,7 +192,6 @@ QtObject { readonly property color notifToastBg: bgPopup readonly property color notifToastBgHover: "#22ffffff" readonly property color notifToastBorder: borderPopup - readonly property color notifToastShadow: "#88000000" readonly property color notifToastText: textMain readonly property color notifToastDim: textDim readonly property color notifToastAppName: "#ff89b4fa" @@ -162,7 +208,6 @@ QtObject { // ── History popup ────────────────────────────────────────────────────────── readonly property color notifHistoryBg: bgPopup readonly property color notifHistoryBorder: borderPopup - readonly property color notifHistoryShadow: "#aa000000" readonly property color notifHistoryHover: "#11c19375" readonly property color notifHistoryEmpty: textDim @@ -170,4 +215,96 @@ QtObject { readonly property color bgDateText: "#ffffffff" // Pure white (brightest possible) readonly property color bgDateShadow: "#88000000" // Slightly softer shadow to let the white glow readonly property color bgDateSecondsText: bgDateText // Brighter, more "sunlight" gold/yellow + + // ── Crypto Module ────────────────────────────────────────────────────────── + readonly property color cryptoBg: bgPanel + readonly property color cryptoBgHover: "#15ffffff" // subtle hover wash (matches other modules) + readonly property color cryptoBorder: border + readonly property color cryptoBorderHover: accent // horizon gold on hover / when popup is open + + readonly property color cryptoIcon: accent // coin glyph colour + readonly property color cryptoLabel: textDim // "BTC/USD: " prefix + readonly property color cryptoValue: textMain // price number on chip + + readonly property color cryptoUp: "#ffb4c9a1" // ▲ positive change — soft green + readonly property color cryptoDown: "#fff38ba8" // ▼ negative change — soft red + + // Popup card + readonly property color cryptoPopupBg: bgPopup + readonly property color cryptoPopupBorder: borderPopup + readonly property color cryptoPopupHeader: "#ffc19375" // horizon gold (matches clock popup) + readonly property color cryptoPopupText: textMain + readonly property color cryptoPopupDim: textDim + + // Tinted surfaces (derived from main colours) + readonly property color cryptoUpBg: Qt.rgba(cryptoUp.r, cryptoUp.g, cryptoUp.b, 0.13) + readonly property color cryptoDownBg: Qt.rgba(cryptoDown.r, cryptoDown.g, cryptoDown.b, 0.13) + readonly property color cryptoChipRemoveBg: Qt.rgba(cryptoDown.r, cryptoDown.g, cryptoDown.b, 0.27) + readonly property color cryptoApplyIdleBg: Qt.rgba(accent.r, accent.g, accent.b, 0.20) + + // ── Polkit Dialog ────────────────────────────────────────────────────────── + // Overlay + readonly property color polkitOverlayBg: "#bb000000" // dim backdrop behind the card + + // Card chrome + readonly property color polkitCardBg: bgPopup // near-opaque deep shadow (matches bgPopup family) + readonly property color polkitCardBorder: border // faint horizon-gold ring + + // Lock icon + readonly property color polkitIconBg: "#1ec19375" // translucent gold fill + readonly property color polkitIconBorder: "#44c19375" // stronger gold ring + readonly property color polkitIconColor: accent // glyph — horizon gold + + // Text hierarchy + readonly property color polkitTitle: textMain // "Authentication Required" + readonly property color polkitMessage: "#cce8eef2" // main message (slightly dimmed) + + // Action-ID pill + readonly property color polkitPillBg: "#1a4a5d6e" // muted sky-blue tint + readonly property color polkitPillText: "#667a8996" // monospace action-id label + + // Divider between pill and supplementary area + readonly property color polkitDivider: "#334a5d6e" // separator line + + // Supplementary message — error state + readonly property color polkitSuppErrBg: "#1af38ba8" // translucent rose fill + readonly property color polkitSuppErrBorder: "#44f38ba8" // rose ring + readonly property color polkitSuppErrText: statusErr // rose icon + text + + // Supplementary message — info / ok state + readonly property color polkitSuppOkBg: "#1ab4c9a1" // translucent sage fill + readonly property color polkitSuppOkBorder: "#44b4c9a1" // sage ring + readonly property color polkitSuppOkText: statusOk // sage icon + text + + // Fields (password input & identity selector share the same palette) + readonly property color polkitFieldBg: "#44080a0d" // deep dark fill + readonly property color polkitFieldBorder: "#22ffffff" // inactive border + readonly property color polkitFieldBorderFocus: accent // focused border — horizon gold + + // Identity selector text + readonly property color polkitUserLabel: "#997a8996" // "User:" prefix + readonly property color polkitUserValue: accent // selected identity name + + // Input prompt label above the password field + readonly property color polkitInputPrompt: "#887a8996" // dim label + + // TextInput inside the password field + readonly property color polkitInputText: textMain // typed text + readonly property color polkitInputSelection: "#55c19375" // selection highlight + readonly property color polkitInputSelectedText: bg // text over selection + + // Eye / show-password toggle + readonly property color polkitEyeIcon: "#667a8996" // default glyph + readonly property color polkitEyeIconHover: accent // hovered glyph + + // Authenticate button + readonly property color polkitAuthBg: "#ddc19375" // slightly-muted gold fill + readonly property color polkitAuthBgHover: accent // full gold on hover + readonly property color polkitAuthText: bg // dark label on gold + + // Cancel button + readonly property color polkitCancelBg: "#0dffffff" // ghost fill + readonly property color polkitCancelBgHover: "#22ffffff" // brighter ghost on hover + readonly property color polkitCancelBorder: "#22ffffff" // outline + readonly property color polkitCancelText: "#cce8eef2" // slightly-dimmed white label } diff --git a/config/themes/hummingbird.qml b/config/themes/hummingbird.qml new file mode 100644 index 0000000..c67f858 --- /dev/null +++ b/config/themes/hummingbird.qml @@ -0,0 +1,277 @@ +pragma Singleton +import QtQuick +import Quickshell + +QtObject { + id: theme + + // ── Wallpaper ────────────────────────────────────────────────────────────── + readonly property string wallpaper: Quickshell.env("HOME") + "/.config/quickshell/wallpapers/hummingbird.jpg" + + // ── Base Backgrounds ─────────────────────────────────────────────────────── + readonly property color bg: "#cc0d140d" // Deep mossy shadow + readonly property color bgPanel: "transparent" + readonly property color bgPopup: "#cc0d140d" // Dark forest floor + readonly property color bgFrame: bg + + // ── Separators ──────────────────────────────────────────────────────────── + readonly property color separator: "#338bc34a" // Muted leaf green + + // ── Text ────────────────────────────────────────────────────────────────── + readonly property color textMain: "#fff1f8e9" // Creamy mint highlight + readonly property color textDim: "#99a5ad94" // Sage horizon gray + readonly property color todayText: "#ffffffff" + + // ── Accent & Borders ────────────────────────────────────────────────────── + readonly property color accent: "#ffff5722" // Fiery vermilion (throat) + readonly property color border: "transparent" + readonly property color frameBorder: "transparent" + readonly property color borderPopup: "transparent" + readonly property color borderToday: accent + + // ── Logo Module ──────────────────────────────────────────────────────── + readonly property color logoBg: bgPanel + readonly property color logoBgHover: "#22ffffff" + readonly property color logoBorder: border + readonly property color logoBorderActive: accent + readonly property color logoIcon: textMain + readonly property color logoIconActive: accent + + // ── Stats Module (Variation: Canopy Pulse) ─────────────────────────────── + readonly property color statsBg: bgPanel + readonly property color statsBorder: border + readonly property color statsText: "#ff8bc34a" // Leaf green + readonly property color statsRingColor: accent + readonly property color statsCpuColor: "#ffff5722" // Vermilion + readonly property color statsMemColor: "#ff8bc34a" // Bright green + readonly property color statsDiskColor: "#ff90caf9" // Bokeh sky blue + readonly property color statsTrackColor: "#15ffffff" + + // ── Volume Module (Variation: Forest Echo) ────────────────────────────────── + readonly property color volBg: bgPanel + readonly property color volBgHover: "#222e7d32" + readonly property color volBorder: border + readonly property color volBorderHover: "#ff8bc34a" + readonly property color volPopupBg: bgPopup + readonly property color volPopupBorder: borderPopup + readonly property color volIcon: "#ff8bc34a" + readonly property color volIconMuted: "#ff5d665d" + readonly property color volTrack: "#22ffffff" + readonly property color volHandle: accent + readonly property color volHandleBorder: "#33ffffff" + readonly property color volMuteBg: "#33f44336" + readonly property color volPickerHover: "#15ff5722" + + // ── Clock Module ─────────────────────────────────────────────────────────── + readonly property color clockBg: bgPanel + readonly property color clockBgHover: "#11ffffff" + readonly property color clockBorder: border + readonly property color clockBorderHover: accent + readonly property color clockTime: textMain + readonly property color clockSeconds: "#ffff5722" // Fiery glow + readonly property color clockIcon: accent + + // ── Battery Module ──────────────────────────────────────────────────────── + readonly property color batteryFull: accent + readonly property color batteryCharging: "#ff8bc34a" + readonly property color batteryCritical: statusErr + readonly property color batteryLow: statusWarn + readonly property color batteryIcon: clockIcon + readonly property color batBg: bgPanel + readonly property color batBgHover: "#11ffffff" + readonly property color batBorder: border + readonly property color batBorderHover: accent + readonly property color batText: textMain + readonly property color batPopupBg: bgPopup + readonly property color batPopupBorder: borderPopup + readonly property color batPopupSeparator: separator + readonly property color batPopupHeader: "#ffff5722" + readonly property color batPopupText: textMain + readonly property color batPopupDim: textDim + readonly property color batProgressTrack: separator + readonly property color batProfileActiveText: bg + + // ── Network Module ──────────────────────────────────────────────────────── + readonly property color netBg: bgPanel + readonly property color netBgHover: "#11ffffff" + readonly property color netBorder: border + readonly property color netBorderHover: accent + readonly property color netText: textMain + readonly property color netPopupBg: bgPopup + readonly property color netPopupBorder: borderPopup + readonly property color netPopupSeparator: separator + readonly property color netPopupHeader: "#ffff5722" + readonly property color netPopupText: textMain + readonly property color netPopupDim: textDim + readonly property color netFieldBg: "#22ffffff" + readonly property color netCancelBgHover: "#22ffffff" + readonly property color netConnectText: "#ff0d140d" + readonly property color netApActiveBg: Qt.alpha(accent, 0.15) + readonly property color netApHoverBg: "#22ff5722" + readonly property color netDisconnectBg: Qt.alpha(statusErr, 0.18) + readonly property color netDisconnectBorder: Qt.alpha(statusErr, 0.45) + readonly property color netWiredOkBg: Qt.alpha(statusOk, 0.12) + readonly property color netWiredErrBg: Qt.alpha(statusErr, 0.12) + readonly property color netWiredOkBorder: Qt.alpha(statusOk, 0.4) + readonly property color netWiredErrBorder: Qt.alpha(statusErr, 0.4) + + // ── Calendar Popup ───────────────────────────────────────────────────────── + readonly property color calCardBg: bgPopup + readonly property color calCardBorder: borderPopup + readonly property color calSeparator: separator + readonly property color clockPopupHeader: "#ffff5722" + readonly property color clockPopupText: textMain + readonly property color clockPopupDim: textDim + readonly property color clockPopupDate: textMain + readonly property color clockPopupUtc: textDim + readonly property color calArrowIcon: textDim + readonly property color calArrowIconHover: "#ffff5722" + readonly property color calArrowBg: "transparent" + readonly property color calArrowBgHover: "#22ff5722" + readonly property color calArrowBgPress: "#ffff5722" + + // ── Workspace Module ─────────────────────────────────────────────────────── + readonly property color wsPanelBg: bgPanel + readonly property color wsPanelBorder: "transparent" + readonly property color wsActiveBg: accent + readonly property color wsInactiveBg: "#15ffffff" + readonly property color wsHoverBg: "#332e7d32" + readonly property color wsActiveText: "#ff0d140d" + readonly property color wsInactiveText: textMain + readonly property color wsHoverText: "#ffffffff" + + // ── Tray Module ──────────────────────────────────────────────────────────── + readonly property color trayBg: bgPanel + readonly property color trayBgHover: "#11ffffff" + readonly property color trayBorder: border + readonly property color trayBorderHover: "#22ffffff" + readonly property color trayIcon: textMain + + // ── Tray Context Menu ────────────────────────────────────────────────────── + readonly property color trayMenuBg: bgPopup + readonly property color trayMenuBorder: borderPopup + readonly property color trayMenuText: textMain + readonly property color trayMenuDim: textDim + readonly property color trayMenuHover: "#22ff5722" + readonly property color trayMenuCheck: accent + readonly property color trayMenuSeparator: separator + + // ── Launcher (Variation: Forest Shade) ─────────────────────────────────── + readonly property color launcherBg: bgPopup + readonly property color launcherBorder: borderPopup + readonly property color launcherInput: "#440d140d" + readonly property color launcherInputBorderFocus: accent + readonly property color launcherPill: "#442e7d32" + readonly property color launcherText: textMain + readonly property color launcherDim: textDim + readonly property color launcherAccent: accent + readonly property color launcherHover: "#22ff5722" + readonly property color launcherSelected: "#44ff5722" + readonly property color launcherModeActiveText: "#ffffffff" + readonly property color launcherModeActiveBg: accent + + // ── Status Colors ────────────────────────────────────────────────────────── + readonly property color statusOk: "#ff8bc34a" // Leaf green + readonly property color statusWarn: "#ffffa726" // Amber + readonly property color statusErr: "#fff44336" // Red bird feather + readonly property color statusInfo: "#ff90caf9" // Bokeh sky blue + + // ── Notifications Module ─────────────────────────────────────────────────── + readonly property color notifBg: bgPanel + readonly property color notifBgHover: "#11ffffff" + readonly property color notifBorder: border + readonly property color notifBorderActive: accent + readonly property color notifIcon: textMain + readonly property color notifIconActive: accent + readonly property color notifBadgeBg: statusErr + readonly property color notifBadgeText: "#ffffffff" + readonly property color notifSeparator: separator + readonly property color notifDnd: separator + + // ── Toast cards (Variation: Canopy Glow) ───────────────────────────────────── + readonly property color notifToastBg: bgPopup + readonly property color notifToastBgHover: "#22ffffff" + readonly property color notifToastBorder: borderPopup + readonly property color notifToastText: textMain + readonly property color notifToastDim: textDim + readonly property color notifToastAppName: "#ff90caf9" + + // ── Urgency colours ──────────────────────────────────────────────────────── + readonly property color notifUrgencyCritical: statusErr + readonly property color notifUrgencyNormal: statusInfo + readonly property color notifUrgencyLow: textDim + + // ── Progress bar ─────────────────────────────────────────────────────────── + readonly property color notifProgressBg: "#22ffffff" + readonly property color notifProgressFill: accent + + // ── History popup ────────────────────────────────────────────────────────── + readonly property color notifHistoryBg: bgPopup + readonly property color notifHistoryBorder: borderPopup + readonly property color notifHistoryHover: "#11ff5722" + readonly property color notifHistoryEmpty: textDim + + // ── BgDate Module ────────────────────────────────────────────────────────── + readonly property color bgDateText: "#ffffffff" + readonly property color bgDateShadow: "#aa000000" + readonly property color bgDateSecondsText: "#ffff5722" + + // ── Crypto Module ────────────────────────────────────────────────────────── + readonly property color cryptoBg: bgPanel + readonly property color cryptoBgHover: "#15ffffff" + readonly property color cryptoBorder: border + readonly property color cryptoBorderHover: accent + readonly property color cryptoIcon: accent + readonly property color cryptoLabel: textDim + readonly property color cryptoValue: textMain + readonly property color cryptoUp: "#ff8bc34a" + readonly property color cryptoDown: "#fff44336" + + readonly property color cryptoPopupBg: bgPopup + readonly property color cryptoPopupBorder: borderPopup + readonly property color cryptoPopupHeader: "#ffff5722" + readonly property color cryptoPopupText: textMain + readonly property color cryptoPopupDim: textDim + + readonly property color cryptoUpBg: Qt.rgba(cryptoUp.r, cryptoUp.g, cryptoUp.b, 0.13) + readonly property color cryptoDownBg: Qt.rgba(cryptoDown.r, cryptoDown.g, cryptoDown.b, 0.13) + readonly property color cryptoChipRemoveBg: Qt.rgba(cryptoDown.r, cryptoDown.g, cryptoDown.b, 0.27) + readonly property color cryptoApplyIdleBg: Qt.rgba(accent.r, accent.g, accent.b, 0.20) + + // ── Polkit Dialog ────────────────────────────────────────────────────────── + readonly property color polkitOverlayBg: "#bb000000" + readonly property color polkitCardBg: bgPopup + readonly property color polkitCardBorder: border + readonly property color polkitIconBg: "#1eff5722" + readonly property color polkitIconBorder: "#44ff5722" + readonly property color polkitIconColor: accent + readonly property color polkitTitle: textMain + readonly property color polkitMessage: "#cce1f8e9" + readonly property color polkitPillBg: "#1a2e7d32" + readonly property color polkitPillText: "#66a5ad94" + readonly property color polkitDivider: "#332e7d32" + readonly property color polkitSuppErrBg: "#1af44336" + readonly property color polkitSuppErrBorder: "#44f44336" + readonly property color polkitSuppErrText: statusErr + readonly property color polkitSuppOkBg: "#1a8bc34a" + readonly property color polkitSuppOkBorder: "#448bc34a" + readonly property color polkitSuppOkText: statusOk + readonly property color polkitFieldBg: "#440d140d" + readonly property color polkitFieldBorder: "#22ffffff" + readonly property color polkitFieldBorderFocus: accent + readonly property color polkitUserLabel: "#99a5ad94" + readonly property color polkitUserValue: accent + readonly property color polkitInputPrompt: "#88a5ad94" + readonly property color polkitInputText: textMain + readonly property color polkitInputSelection: "#55ff5722" + readonly property color polkitInputSelectedText: bg + readonly property color polkitEyeIcon: "#66a5ad94" + readonly property color polkitEyeIconHover: accent + readonly property color polkitAuthBg: "#ddff5722" + readonly property color polkitAuthBgHover: accent + readonly property color polkitAuthText: bg + readonly property color polkitCancelBg: "#0dffffff" + readonly property color polkitCancelBgHover: "#22ffffff" + readonly property color polkitCancelBorder: "#22ffffff" + readonly property color polkitCancelText: "#cce1f8e9" +} diff --git a/config/themes/metropolitan.qml b/config/themes/metropolitan.qml index 87008c3..edc89ef 100644 --- a/config/themes/metropolitan.qml +++ b/config/themes/metropolitan.qml @@ -30,13 +30,12 @@ QtObject { readonly property color borderToday: accent // ── ArchLogo Module ──────────────────────────────────────────────────────── - readonly property color archLogoBg: bgPanel - readonly property color archLogoBgHover: "#22ffffff" - readonly property color archLogoHover: accent - readonly property color archLogoBorder: border - readonly property color archLogoBorderActive: accent - readonly property color archLogoIcon: textMain - readonly property color archLogoIconActive: accent + readonly property color logoBg: bgPanel + readonly property color logoBgHover: "#22ffffff" + readonly property color logoBorder: border + readonly property color logoBorderActive: accent + readonly property color logoIcon: textMain + readonly property color logoIconActive: accent // ── Stats Module (Variation: Night City Lights) ─────────────────────────── readonly property color statsBg: bgPanel @@ -46,7 +45,6 @@ QtObject { readonly property color statsCpuColor: "#ffffb86c" // Streetlight gold readonly property color statsMemColor: "#ff9ece6a" // Train stripe green readonly property color statsDiskColor: "#ff7dcfff" // Cyan window light - readonly property color statsGpuColor: "#fff7768e" // Traffic taillight red readonly property color statsTrackColor: "#15ffffff" // ── Volume Module (Variation: Urban Sound) ──────────────────────────────── @@ -72,15 +70,55 @@ QtObject { readonly property color clockTime: textMain readonly property color clockSeconds: "#ff9ece6a" // Subtle green pulse readonly property color clockIcon: accent - readonly property color clockSeparator: separator - readonly property color clockText: textMain - readonly property color clockShadow: "#66000000" + + // ── Battery Module ──────────────────────────────────────────────────────── + readonly property color batteryFull: accent + readonly property color batteryCharging: accent + readonly property color batteryCritical: statusErr + readonly property color batteryLow: statusWarn + readonly property color batteryIcon: clockIcon + readonly property color batBg: bgPanel + readonly property color batBgHover: "#11ffffff" + readonly property color batBorder: border + readonly property color batBorderHover: accent + readonly property color batText: textMain + readonly property color batPopupBg: bgPopup + readonly property color batPopupBorder: borderPopup + readonly property color batPopupSeparator: separator + readonly property color batPopupHeader: "#ffffb86c" + readonly property color batPopupText: textMain + readonly property color batPopupDim: textDim + readonly property color batProgressTrack: separator + readonly property color batProfileActiveText: bg + + // ── Network Module ──────────────────────────────────────────────────────── + readonly property color netBg: bgPanel + readonly property color netBgHover: "#11ffffff" + readonly property color netBorder: border + readonly property color netBorderHover: accent + readonly property color netText: textMain + readonly property color netPopupBg: bgPopup + readonly property color netPopupBorder: borderPopup + readonly property color netPopupSeparator: separator + readonly property color netPopupHeader: "#ffffb86c" + readonly property color netPopupText: textMain + readonly property color netPopupDim: textDim + readonly property color netFieldBg: "#22ffffff" + readonly property color netCancelBgHover: "#22ffffff" + readonly property color netConnectText: "#ff0d1117" + readonly property color netApActiveBg: Qt.alpha(accent, 0.15) + readonly property color netApHoverBg: calArrowBgHover + readonly property color netDisconnectBg: Qt.alpha(statusErr, 0.18) + readonly property color netDisconnectBorder: Qt.alpha(statusErr, 0.45) + readonly property color netWiredOkBg: Qt.alpha(statusOk, 0.12) + readonly property color netWiredErrBg: Qt.alpha(statusErr, 0.12) + readonly property color netWiredOkBorder: Qt.alpha(statusOk, 0.4) + readonly property color netWiredErrBorder: Qt.alpha(statusErr, 0.4) // ── Calendar Popup ───────────────────────────────────────────────────────── readonly property color calCardBg: bgPopup readonly property color calCardBorder: borderPopup readonly property color calSeparator: separator - readonly property color calShadow: "#aa000000" readonly property color clockPopupHeader: "#ffffb86c" readonly property color clockPopupText: textMain readonly property color clockPopupDim: textDim @@ -138,7 +176,6 @@ QtObject { readonly property color notifToastBg: bgPopup readonly property color notifToastBgHover: "#22ffffff" readonly property color notifToastBorder: borderPopup - readonly property color notifToastShadow: "#88000000" readonly property color notifToastText: textMain readonly property color notifToastDim: textDim readonly property color notifToastAppName: "#ff7dcfff" @@ -155,11 +192,10 @@ QtObject { // ── History popup ────────────────────────────────────────────────────────── readonly property color notifHistoryBg: bgPopup readonly property color notifHistoryBorder: "#44ffb86c" - readonly property color notifHistoryShadow: "#aa000000" readonly property color notifHistoryHover: "#11ffb86c" readonly property color notifHistoryEmpty: textDim - // new variables adaptated + // new variables adapted readonly property color frameBorder: "transparent" readonly property color clockPopupDate: textMain readonly property color clockPopupUtc: textDim @@ -169,8 +205,96 @@ QtObject { readonly property color calArrowBgHover: "#22ffb86c" readonly property color calArrowBgPress: accent + // ── Crypto Module ────────────────────────────────────────────────────────── + readonly property color cryptoBg: bgPanel + readonly property color cryptoBgHover: "#15ffffff" + readonly property color cryptoBorder: border + readonly property color cryptoBorderHover: accent + readonly property color cryptoIcon: accent + readonly property color cryptoLabel: textDim + readonly property color cryptoValue: textMain + readonly property color cryptoUp: statusOk + readonly property color cryptoDown: statusErr + readonly property color cryptoPopupBg: bgPopup + readonly property color cryptoPopupBorder: borderPopup + readonly property color cryptoPopupHeader: accent + readonly property color cryptoPopupText: textMain + readonly property color cryptoPopupDim: textDim + + // Tinted surfaces (derived from main colours) + readonly property color cryptoUpBg: Qt.rgba(cryptoUp.r, cryptoUp.g, cryptoUp.b, 0.13) + readonly property color cryptoDownBg: Qt.rgba(cryptoDown.r, cryptoDown.g, cryptoDown.b, 0.13) + readonly property color cryptoChipRemoveBg: Qt.rgba(cryptoDown.r, cryptoDown.g, cryptoDown.b, 0.27) + readonly property color cryptoApplyIdleBg: Qt.rgba(accent.r, accent.g, accent.b, 0.20) + // ── BgDate Module ────────────────────────────────────────────────────────── readonly property color bgDateText: textMain readonly property color bgDateShadow: "#aa000000" readonly property color bgDateSecondsText: textMain + + // ── Polkit Dialog ────────────────────────────────────────────────────────── + // Overlay + readonly property color polkitOverlayBg: "#bb000000" + + // Card chrome + readonly property color polkitCardBg: bgPopup + readonly property color polkitCardBorder: Qt.rgba(accent.r, accent.g, accent.b, 0.16) + + // Lock icon + readonly property color polkitIconBg: Qt.rgba(accent.r, accent.g, accent.b, 0.12) + readonly property color polkitIconBorder: Qt.rgba(accent.r, accent.g, accent.b, 0.27) + readonly property color polkitIconColor: accent + + // Text hierarchy + readonly property color polkitTitle: textMain + readonly property color polkitMessage: textMain + + // Action-ID pill + readonly property color polkitPillBg: Qt.rgba(textDim.r, textDim.g, textDim.b, 0.10) + readonly property color polkitPillText: textDim + + // Divider + readonly property color polkitDivider: separator + + // Supplementary message — error state + readonly property color polkitSuppErrBg: Qt.rgba(statusErr.r, statusErr.g, statusErr.b, 0.10) + readonly property color polkitSuppErrBorder: Qt.rgba(statusErr.r, statusErr.g, statusErr.b, 0.27) + readonly property color polkitSuppErrText: statusErr + + // Supplementary message — info / ok state + readonly property color polkitSuppOkBg: Qt.rgba(statusOk.r, statusOk.g, statusOk.b, 0.10) + readonly property color polkitSuppOkBorder: Qt.rgba(statusOk.r, statusOk.g, statusOk.b, 0.27) + readonly property color polkitSuppOkText: statusOk + + // Fields (password input & identity selector) + readonly property color polkitFieldBg: launcherInput + readonly property color polkitFieldBorder: "#22ffffff" + readonly property color polkitFieldBorderFocus: accent + + // Identity selector text + readonly property color polkitUserLabel: textDim + readonly property color polkitUserValue: accent + + // Input prompt label above password field + readonly property color polkitInputPrompt: textDim + + // TextInput inside password field + readonly property color polkitInputText: textMain + readonly property color polkitInputSelection: Qt.rgba(accent.r, accent.g, accent.b, 0.33) + readonly property color polkitInputSelectedText: bg + + // Eye / show-password toggle + readonly property color polkitEyeIcon: textDim + readonly property color polkitEyeIconHover: accent + + // Authenticate button + readonly property color polkitAuthBg: accent + readonly property color polkitAuthBgHover: Qt.lighter(accent, 1.15) + readonly property color polkitAuthText: bg + + // Cancel button + readonly property color polkitCancelBg: "transparent" + readonly property color polkitCancelBgHover: "#22ffffff" + readonly property color polkitCancelBorder: "#22ffffff" + readonly property color polkitCancelText: textDim } diff --git a/config/themes/neonica.qml b/config/themes/neonica.qml index 27060b4..1ff3178 100644 --- a/config/themes/neonica.qml +++ b/config/themes/neonica.qml @@ -29,13 +29,12 @@ QtObject { readonly property color borderToday: accent // ── ArchLogo Module ──────────────────────────────────────────────────────── - readonly property color archLogoBg: bgPanel - readonly property color archLogoBgHover: "#22ffffff" - readonly property color archLogoHover: accent - readonly property color archLogoBorder: border - readonly property color archLogoBorderActive: accent - readonly property color archLogoIcon: textMain - readonly property color archLogoIconActive: accent + readonly property color logoBg: bgPanel + readonly property color logoBgHover: "#22ffffff" + readonly property color logoBorder: border + readonly property color logoBorderActive: accent + readonly property color logoIcon: textMain + readonly property color logoIconActive: accent // ── Stats Module (Variation: Cyan/Blue) ──────────────────────────────────── readonly property color statsBg: bgPanel @@ -45,7 +44,6 @@ QtObject { readonly property color statsCpuColor: "#ff9ccfd8" // Light Blue readonly property color statsMemColor: "#ffc4a7e7" // Soft Purple readonly property color statsDiskColor: "#ffebbcba" // Muted Rose - readonly property color statsGpuColor: accent readonly property color statsTrackColor: "#15ffffff" // ── Volume Module (Variation: Violet) ────────────────────────────────────── @@ -71,15 +69,55 @@ QtObject { readonly property color clockTime: textMain readonly property color clockSeconds: "#ffeb6f92" // Pink seconds readonly property color clockIcon: accent - readonly property color clockSeparator: separator - readonly property color clockText: textMain - readonly property color clockShadow: "#22000000" + + // ── Battery Module ──────────────────────────────────────────────────────── + readonly property color batteryFull: accent + readonly property color batteryCharging: accent + readonly property color batteryCritical: statusErr + readonly property color batteryLow: statusWarn + readonly property color batteryIcon: clockIcon + readonly property color batBg: bgPanel + readonly property color batBgHover: "#22ffffff" + readonly property color batBorder: border + readonly property color batBorderHover: accent + readonly property color batText: textMain + readonly property color batPopupBg: bgPopup + readonly property color batPopupBorder: borderPopup + readonly property color batPopupSeparator: separator + readonly property color batPopupHeader: "#ff9ccfd8" + readonly property color batPopupText: textMain + readonly property color batPopupDim: textDim + readonly property color batProgressTrack: separator + readonly property color batProfileActiveText: bg + + // ── Network Module ──────────────────────────────────────────────────────── + readonly property color netBg: bgPanel + readonly property color netBgHover: "#22ffffff" + readonly property color netBorder: border + readonly property color netBorderHover: accent + readonly property color netText: textMain + readonly property color netPopupBg: bgPopup + readonly property color netPopupBorder: borderPopup + readonly property color netPopupSeparator: separator + readonly property color netPopupHeader: "#ff9ccfd8" + readonly property color netPopupText: textMain + readonly property color netPopupDim: textDim + readonly property color netFieldBg: "#22ffffff" + readonly property color netCancelBgHover: "#22ffffff" + readonly property color netConnectText: "#ff0d0b1c" + readonly property color netApActiveBg: Qt.alpha(accent, 0.15) + readonly property color netApHoverBg: calArrowBgHover + readonly property color netDisconnectBg: Qt.alpha(statusErr, 0.18) + readonly property color netDisconnectBorder: Qt.alpha(statusErr, 0.45) + readonly property color netWiredOkBg: Qt.alpha(statusOk, 0.12) + readonly property color netWiredErrBg: Qt.alpha(statusErr, 0.12) + readonly property color netWiredOkBorder: Qt.alpha(statusOk, 0.4) + readonly property color netWiredErrBorder: Qt.alpha(statusErr, 0.4) // ── Calendar Popup ───────────────────────────────────────────────────────── readonly property color calCardBg: bgPopup readonly property color calCardBorder: borderPopup readonly property color calSeparator: separator - readonly property color calShadow: "#44000000" readonly property color clockPopupHeader: "#ff9ccfd8" // Blue header readonly property color clockPopupText: textMain readonly property color clockPopupDim: "#ff6e6a86" @@ -131,12 +169,12 @@ QtObject { readonly property color notifBadgeBg: "#ffeb6f92" readonly property color notifBadgeText: "#ff0d0b1c" readonly property color notifSeparator: separator + readonly property color notifDnd: separator // ── Toast cards ──────────────────────────────────────────────────────────── readonly property color notifToastBg: bgPopup readonly property color notifToastBgHover: "#443b224c" readonly property color notifToastBorder: borderPopup - readonly property color notifToastShadow: "#33000000" readonly property color notifToastText: textMain readonly property color notifToastDim: "#ff908caa" readonly property color notifToastAppName: "#ffc4a7e7" // Violet App name @@ -153,11 +191,10 @@ QtObject { // ── History popup ────────────────────────────────────────────────────────── readonly property color notifHistoryBg: bgPopup readonly property color notifHistoryBorder: "#33c4a7e7" - readonly property color notifHistoryShadow: "#33000000" readonly property color notifHistoryHover: "#22c4a7e7" readonly property color notifHistoryEmpty: "#ff6e6a86" - // new variables adaptated + // new variables adapted readonly property color frameBorder: "transparent" readonly property color clockPopupDate: textMain readonly property color clockPopupUtc: textDim @@ -167,8 +204,96 @@ QtObject { readonly property color calArrowBgHover: "#22ff2e97" readonly property color calArrowBgPress: accent + // ── Crypto Module ────────────────────────────────────────────────────────── + readonly property color cryptoBg: bgPanel + readonly property color cryptoBgHover: "#15ffffff" + readonly property color cryptoBorder: border + readonly property color cryptoBorderHover: accent + readonly property color cryptoIcon: accent + readonly property color cryptoLabel: textDim + readonly property color cryptoValue: textMain + readonly property color cryptoUp: statusOk + readonly property color cryptoDown: statusErr + readonly property color cryptoPopupBg: bgPopup + readonly property color cryptoPopupBorder: borderPopup + readonly property color cryptoPopupHeader: accent + readonly property color cryptoPopupText: textMain + readonly property color cryptoPopupDim: textDim + + // Tinted surfaces (derived from main colours) + readonly property color cryptoUpBg: Qt.rgba(cryptoUp.r, cryptoUp.g, cryptoUp.b, 0.13) + readonly property color cryptoDownBg: Qt.rgba(cryptoDown.r, cryptoDown.g, cryptoDown.b, 0.13) + readonly property color cryptoChipRemoveBg: Qt.rgba(cryptoDown.r, cryptoDown.g, cryptoDown.b, 0.27) + readonly property color cryptoApplyIdleBg: Qt.rgba(accent.r, accent.g, accent.b, 0.20) + // ── BgDate Module ────────────────────────────────────────────────────────── readonly property color bgDateText: textMain readonly property color bgDateShadow: "#44000000" readonly property color bgDateSecondsText: textMain + + // ── Polkit Dialog ────────────────────────────────────────────────────────── + // Overlay + readonly property color polkitOverlayBg: "#bb000000" + + // Card chrome + readonly property color polkitCardBg: bgPopup + readonly property color polkitCardBorder: Qt.rgba(accent.r, accent.g, accent.b, 0.16) + + // Lock icon + readonly property color polkitIconBg: Qt.rgba(accent.r, accent.g, accent.b, 0.12) + readonly property color polkitIconBorder: Qt.rgba(accent.r, accent.g, accent.b, 0.27) + readonly property color polkitIconColor: accent + + // Text hierarchy + readonly property color polkitTitle: textMain + readonly property color polkitMessage: textMain + + // Action-ID pill + readonly property color polkitPillBg: Qt.rgba(textDim.r, textDim.g, textDim.b, 0.10) + readonly property color polkitPillText: textDim + + // Divider + readonly property color polkitDivider: separator + + // Supplementary message — error state + readonly property color polkitSuppErrBg: Qt.rgba(statusErr.r, statusErr.g, statusErr.b, 0.10) + readonly property color polkitSuppErrBorder: Qt.rgba(statusErr.r, statusErr.g, statusErr.b, 0.27) + readonly property color polkitSuppErrText: statusErr + + // Supplementary message — info / ok state + readonly property color polkitSuppOkBg: Qt.rgba(statusOk.r, statusOk.g, statusOk.b, 0.10) + readonly property color polkitSuppOkBorder: Qt.rgba(statusOk.r, statusOk.g, statusOk.b, 0.27) + readonly property color polkitSuppOkText: statusOk + + // Fields (password input & identity selector) + readonly property color polkitFieldBg: launcherInput + readonly property color polkitFieldBorder: "#22ffffff" + readonly property color polkitFieldBorderFocus: accent + + // Identity selector text + readonly property color polkitUserLabel: textDim + readonly property color polkitUserValue: accent + + // Input prompt label above password field + readonly property color polkitInputPrompt: textDim + + // TextInput inside password field + readonly property color polkitInputText: textMain + readonly property color polkitInputSelection: Qt.rgba(accent.r, accent.g, accent.b, 0.33) + readonly property color polkitInputSelectedText: bg + + // Eye / show-password toggle + readonly property color polkitEyeIcon: textDim + readonly property color polkitEyeIconHover: accent + + // Authenticate button + readonly property color polkitAuthBg: accent + readonly property color polkitAuthBgHover: Qt.lighter(accent, 1.15) + readonly property color polkitAuthText: bg + + // Cancel button + readonly property color polkitCancelBg: "transparent" + readonly property color polkitCancelBgHover: "#22ffffff" + readonly property color polkitCancelBorder: "#22ffffff" + readonly property color polkitCancelText: textDim } diff --git a/config/themes/qmldir b/config/themes/qmldir index 7cea0ad..8eec4ac 100644 --- a/config/themes/qmldir +++ b/config/themes/qmldir @@ -10,9 +10,6 @@ singleton Aero 1.0 aero.qml singleton Supreme 1.0 supreme.qml singleton Metropolitan 1.0 metropolitan.qml singleton Goldencity 1.0 goldencity.qml - - - - - +singleton Hummingbird 1.0 hummingbird.qml +singleton Tropicalnight 1.0 tropicalnight.qml diff --git a/config/themes/supreme.qml b/config/themes/supreme.qml index dd87248..b25fda9 100644 --- a/config/themes/supreme.qml +++ b/config/themes/supreme.qml @@ -29,13 +29,12 @@ QtObject { readonly property color borderToday: accent // ── ArchLogo Module ──────────────────────────────────────────────────────── - readonly property color archLogoBg: bgPanel - readonly property color archLogoBgHover: "#22ffffff" - readonly property color archLogoHover: accent - readonly property color archLogoBorder: border - readonly property color archLogoBorderActive: accent - readonly property color archLogoIcon: textMain - readonly property color archLogoIconActive: accent + readonly property color logoBg: bgPanel + readonly property color logoBgHover: "#22ffffff" + readonly property color logoBorder: border + readonly property color logoBorderActive: accent + readonly property color logoIcon: textMain + readonly property color logoIconActive: accent // ── Stats Module (Candy Tones) ───────────────────────────────────────────── readonly property color statsBg: bgPanel @@ -45,7 +44,6 @@ QtObject { readonly property color statsCpuColor: "#ffff7eb9" // Hot pink readonly property color statsMemColor: "#ffee99ff" // Soft purple readonly property color statsDiskColor: "#ff70c5ce" // Cyan pop - readonly property color statsGpuColor: "#ffff9a9e" // Pastel coral readonly property color statsTrackColor: "#15ffffff" // ── Volume Module (Berry Glass) ─────────────────────────────────────────── @@ -71,15 +69,55 @@ QtObject { readonly property color clockTime: textMain readonly property color clockSeconds: "#ffff9a9e" // Pastel peach readonly property color clockIcon: accent - readonly property color clockSeparator: separator - readonly property color clockText: textMain - readonly property color clockShadow: "#442d1621" + + // ── Battery Module ──────────────────────────────────────────────────────── + readonly property color batteryFull: accent + readonly property color batteryCharging: accent + readonly property color batteryCritical: statusErr + readonly property color batteryLow: statusWarn + readonly property color batteryIcon: clockIcon + readonly property color batBg: bgPanel + readonly property color batBgHover: "#11ffffff" + readonly property color batBorder: border + readonly property color batBorderHover: accent + readonly property color batText: textMain + readonly property color batPopupBg: bgPopup + readonly property color batPopupBorder: borderPopup + readonly property color batPopupSeparator: separator + readonly property color batPopupHeader: "#ffff7eb9" + readonly property color batPopupText: textMain + readonly property color batPopupDim: textDim + readonly property color batProgressTrack: separator + readonly property color batProfileActiveText: bg + + // ── Network Module ──────────────────────────────────────────────────────── + readonly property color netBg: bgPanel + readonly property color netBgHover: "#11ffffff" + readonly property color netBorder: border + readonly property color netBorderHover: accent + readonly property color netText: textMain + readonly property color netPopupBg: bgPopup + readonly property color netPopupBorder: borderPopup + readonly property color netPopupSeparator: separator + readonly property color netPopupHeader: "#ffff7eb9" + readonly property color netPopupText: textMain + readonly property color netPopupDim: textDim + readonly property color netFieldBg: "#22ffffff" + readonly property color netCancelBgHover: "#22ffffff" + readonly property color netConnectText: "#ff2d1621" + readonly property color netApActiveBg: Qt.alpha(accent, 0.15) + readonly property color netApHoverBg: calArrowBgHover + readonly property color netDisconnectBg: Qt.alpha(statusErr, 0.18) + readonly property color netDisconnectBorder: Qt.alpha(statusErr, 0.45) + readonly property color netWiredOkBg: Qt.alpha(statusOk, 0.12) + readonly property color netWiredErrBg: Qt.alpha(statusErr, 0.12) + readonly property color netWiredOkBorder: Qt.alpha(statusOk, 0.4) + readonly property color netWiredErrBorder: Qt.alpha(statusErr, 0.4) // ── Calendar Popup ───────────────────────────────────────────────────────── readonly property color calCardBg: bgPopup readonly property color calCardBorder: borderPopup readonly property color calSeparator: separator - readonly property color calShadow: "#662d1621" readonly property color clockPopupHeader: "#ffff7eb9" readonly property color clockPopupText: textMain readonly property color clockPopupDim: textDim @@ -137,7 +175,6 @@ QtObject { readonly property color notifToastBg: bgPopup readonly property color notifToastBgHover: "#22ffffff" readonly property color notifToastBorder: borderPopup - readonly property color notifToastShadow: "#442d1621" readonly property color notifToastText: textMain readonly property color notifToastDim: textDim readonly property color notifToastAppName: "#ffff7eb9" @@ -154,11 +191,10 @@ QtObject { // ── History popup ────────────────────────────────────────────────────────── readonly property color notifHistoryBg: bgPopup readonly property color notifHistoryBorder: "#33ff7eb9" - readonly property color notifHistoryShadow: "#552d1621" readonly property color notifHistoryHover: "#11ff7eb9" readonly property color notifHistoryEmpty: textDim - // new variables adaptated + // new variables adapted readonly property color frameBorder: "transparent" readonly property color clockPopupDate: textMain readonly property color clockPopupUtc: textDim @@ -168,8 +204,96 @@ QtObject { readonly property color calArrowBgHover: "#22ff7eb9" readonly property color calArrowBgPress: accent + // ── Crypto Module ────────────────────────────────────────────────────────── + readonly property color cryptoBg: bgPanel + readonly property color cryptoBgHover: "#15ffffff" + readonly property color cryptoBorder: border + readonly property color cryptoBorderHover: accent + readonly property color cryptoIcon: accent + readonly property color cryptoLabel: textDim + readonly property color cryptoValue: textMain + readonly property color cryptoUp: statusOk + readonly property color cryptoDown: statusErr + readonly property color cryptoPopupBg: bgPopup + readonly property color cryptoPopupBorder: borderPopup + readonly property color cryptoPopupHeader: accent + readonly property color cryptoPopupText: textMain + readonly property color cryptoPopupDim: textDim + + // Tinted surfaces (derived from main colours) + readonly property color cryptoUpBg: Qt.rgba(cryptoUp.r, cryptoUp.g, cryptoUp.b, 0.13) + readonly property color cryptoDownBg: Qt.rgba(cryptoDown.r, cryptoDown.g, cryptoDown.b, 0.13) + readonly property color cryptoChipRemoveBg: Qt.rgba(cryptoDown.r, cryptoDown.g, cryptoDown.b, 0.27) + readonly property color cryptoApplyIdleBg: Qt.rgba(accent.r, accent.g, accent.b, 0.20) + // ── BgDate Module ────────────────────────────────────────────────────────── readonly property color bgDateText: textMain readonly property color bgDateShadow: "#662d1621" readonly property color bgDateSecondsText: textMain + + // ── Polkit Dialog ────────────────────────────────────────────────────────── + // Overlay + readonly property color polkitOverlayBg: "#bb000000" + + // Card chrome + readonly property color polkitCardBg: bgPopup + readonly property color polkitCardBorder: Qt.rgba(accent.r, accent.g, accent.b, 0.16) + + // Lock icon + readonly property color polkitIconBg: Qt.rgba(accent.r, accent.g, accent.b, 0.12) + readonly property color polkitIconBorder: Qt.rgba(accent.r, accent.g, accent.b, 0.27) + readonly property color polkitIconColor: accent + + // Text hierarchy + readonly property color polkitTitle: textMain + readonly property color polkitMessage: textMain + + // Action-ID pill + readonly property color polkitPillBg: Qt.rgba(textDim.r, textDim.g, textDim.b, 0.10) + readonly property color polkitPillText: textDim + + // Divider + readonly property color polkitDivider: separator + + // Supplementary message — error state + readonly property color polkitSuppErrBg: Qt.rgba(statusErr.r, statusErr.g, statusErr.b, 0.10) + readonly property color polkitSuppErrBorder: Qt.rgba(statusErr.r, statusErr.g, statusErr.b, 0.27) + readonly property color polkitSuppErrText: statusErr + + // Supplementary message — info / ok state + readonly property color polkitSuppOkBg: Qt.rgba(statusOk.r, statusOk.g, statusOk.b, 0.10) + readonly property color polkitSuppOkBorder: Qt.rgba(statusOk.r, statusOk.g, statusOk.b, 0.27) + readonly property color polkitSuppOkText: statusOk + + // Fields (password input & identity selector) + readonly property color polkitFieldBg: launcherInput + readonly property color polkitFieldBorder: "#22ffffff" + readonly property color polkitFieldBorderFocus: accent + + // Identity selector text + readonly property color polkitUserLabel: textDim + readonly property color polkitUserValue: accent + + // Input prompt label above password field + readonly property color polkitInputPrompt: textDim + + // TextInput inside password field + readonly property color polkitInputText: textMain + readonly property color polkitInputSelection: Qt.rgba(accent.r, accent.g, accent.b, 0.33) + readonly property color polkitInputSelectedText: bg + + // Eye / show-password toggle + readonly property color polkitEyeIcon: textDim + readonly property color polkitEyeIconHover: accent + + // Authenticate button + readonly property color polkitAuthBg: accent + readonly property color polkitAuthBgHover: Qt.lighter(accent, 1.15) + readonly property color polkitAuthText: bg + + // Cancel button + readonly property color polkitCancelBg: "transparent" + readonly property color polkitCancelBgHover: "#22ffffff" + readonly property color polkitCancelBorder: "#22ffffff" + readonly property color polkitCancelText: textDim } diff --git a/config/themes/tropicalnight.qml b/config/themes/tropicalnight.qml new file mode 100644 index 0000000..6eb0933 --- /dev/null +++ b/config/themes/tropicalnight.qml @@ -0,0 +1,278 @@ +pragma Singleton +import QtQuick +import Quickshell + +QtObject { + id: theme + + // ── Wallpaper ────────────────────────────────────────────────────────────── + readonly property string wallpaper: Quickshell.env("HOME") + "/.config/quickshell/wallpapers/tropicalnight.png" + + // ── Base Backgrounds ─────────────────────────────────────────────────────── + readonly property color bg: "#cc161a22" // Deep navy-black from silhouettes + readonly property color bgPanel: "transparent" + readonly property color bgPopup: bg + readonly property color bgFrame: bg + + // ── Separators ──────────────────────────────────────────────────────────── + readonly property color separator: "#334a5d6e" // Muted sky blue-gray + + // ── Text ────────────────────────────────────────────────────────────────── + readonly property color textMain: "#ffe8eef2" // Bright moonlit white + readonly property color textDim: "#997a8996" // Hazy horizon gray + readonly property color todayText: "#ffffffff" + + // ── Accent & Borders ────────────────────────────────────────────────────── + readonly property color accent: "#ff89b4fa" // Summer sky blue (Main Accent) + readonly property color border: "transparent" + readonly property color frameBorder: "transparent" + readonly property color borderPopup: "transparent" + readonly property color borderToday: accent + + // ── Logo Module ──────────────────────────────────────────────────────── + readonly property color logoBg: bgPanel + readonly property color logoBgHover: "#22ffffff" + readonly property color logoBorder: border + readonly property color logoBorderActive: accent + readonly property color logoIcon: textMain + readonly property color logoIconActive: accent + + // ── Stats Module ────────────────────────────────────────────────────────── + readonly property color statsBg: bgPanel + readonly property color statsBorder: border + readonly property color statsText: "#ff7a8996" + readonly property color statsRingColor: accent + readonly property color statsCpuColor: "#ff89b4fa" + readonly property color statsMemColor: "#ffb4c9a1" // Soft moonlit green + readonly property color statsDiskColor: "#ff94e2d5" + readonly property color statsTrackColor: "#15ffffff" + + // ── Volume Module ───────────────────────────────────────────────────────── + readonly property color volBg: bgPanel + readonly property color volBgHover: "#224a5d6e" + readonly property color volBorder: border + readonly property color volBorderHover: accent + readonly property color volPopupBg: bgPopup + readonly property color volPopupBorder: borderPopup + readonly property color volIcon: accent + readonly property color volIconMuted: "#ff5a6b78" + readonly property color volTrack: "#22ffffff" + readonly property color volHandle: accent + readonly property color volHandleBorder: "#33ffffff" + readonly property color volMuteBg: "#33f38ba8" + readonly property color volPickerHover: "#1589b4fa" + + // ── Clock Module ─────────────────────────────────────────────────────────── + readonly property color clockBg: bgPanel + readonly property color clockBgHover: "#11ffffff" + readonly property color clockBorder: border + readonly property color clockBorderHover: accent + readonly property color clockTime: textMain + readonly property color clockSeconds: "#ff89b4fa" + readonly property color clockIcon: accent + + // ── Battery Module ──────────────────────────────────────────────────────── + readonly property color batteryFull: accent + readonly property color batteryCharging: accent + readonly property color batteryCritical: statusErr + readonly property color batteryLow: statusWarn + readonly property color batteryIcon: clockIcon + readonly property color batBg: bgPanel + readonly property color batBgHover: "#11ffffff" + readonly property color batBorder: border + readonly property color batBorderHover: accent + readonly property color batText: textMain + readonly property color batPopupBg: bgPopup + readonly property color batPopupBorder: borderPopup + readonly property color batPopupSeparator: separator + readonly property color batPopupHeader: accent + readonly property color batPopupText: textMain + readonly property color batPopupDim: textDim + readonly property color batProgressTrack: separator + readonly property color batProfileActiveText: bg + + // ── Network Module ──────────────────────────────────────────────────────── + readonly property color netBg: bgPanel + readonly property color netBgHover: "#11ffffff" + readonly property color netBorder: border + readonly property color netBorderHover: accent + readonly property color netText: textMain + readonly property color netPopupBg: bgPopup + readonly property color netPopupBorder: borderPopup + readonly property color netPopupSeparator: separator + readonly property color netPopupHeader: accent + readonly property color netPopupText: textMain + readonly property color netPopupDim: textDim + readonly property color netFieldBg: "#22ffffff" + readonly property color netCancelBgHover: "#22ffffff" + readonly property color netConnectText: "#ff0c0e12" + readonly property color netApActiveBg: Qt.alpha(accent, 0.15) + readonly property color netApHoverBg: "#2289b4fa" + readonly property color netDisconnectBg: Qt.alpha(statusErr, 0.18) + readonly property color netDisconnectBorder: Qt.alpha(statusErr, 0.45) + readonly property color netWiredOkBg: Qt.alpha(statusOk, 0.12) + readonly property color netWiredErrBg: Qt.alpha(statusErr, 0.12) + readonly property color netWiredOkBorder: Qt.alpha(statusOk, 0.4) + readonly property color netWiredErrBorder: Qt.alpha(statusErr, 0.4) + + // ── Calendar Popup ───────────────────────────────────────────────────────── + readonly property color calCardBg: bgPopup + readonly property color calCardBorder: borderPopup + readonly property color calSeparator: separator + readonly property color clockPopupHeader: accent + readonly property color clockPopupText: textMain + readonly property color clockPopupDim: textDim + readonly property color clockPopupDate: textMain + readonly property color clockPopupUtc: textDim + readonly property color calArrowIcon: textDim + readonly property color calArrowIconHover: accent + readonly property color calArrowBg: "transparent" + readonly property color calArrowBgHover: "#2289b4fa" + readonly property color calArrowBgPress: accent + + // ── Workspace Module ─────────────────────────────────────────────────────── + readonly property color wsPanelBg: bgPanel + readonly property color wsPanelBorder: "transparent" + readonly property color wsActiveBg: accent + readonly property color wsInactiveBg: "#15ffffff" + readonly property color wsHoverBg: "#334a5d6e" + readonly property color wsActiveText: "#ff0c0e12" + readonly property color wsInactiveText: textMain + readonly property color wsHoverText: "#ffffffff" + + // ── Tray Module ──────────────────────────────────────────────────────────── + readonly property color trayBg: bgPanel + readonly property color trayBgHover: "#11ffffff" + readonly property color trayBorder: border + readonly property color trayBorderHover: "#22ffffff" + readonly property color trayIcon: textMain + + // ── Tray Context Menu ────────────────────────────────────────────────────── + readonly property color trayMenuBg: bgPopup + readonly property color trayMenuBorder: borderPopup + readonly property color trayMenuText: textMain + readonly property color trayMenuDim: textDim + readonly property color trayMenuHover: "#2289b4fa" + readonly property color trayMenuCheck: accent + readonly property color trayMenuSeparator: separator + + // ── Launcher ────────────────────────────────────────────────────────────── + readonly property color launcherBg: bgPopup + readonly property color launcherBorder: borderPopup + readonly property color launcherInput: "#44080a0d" + readonly property color launcherInputBorderFocus: accent + readonly property color launcherPill: "#444a5d6e" + readonly property color launcherText: textMain + readonly property color launcherDim: textDim + readonly property color launcherAccent: accent + readonly property color launcherHover: "#2289b4fa" + readonly property color launcherSelected: "#4489b4fa" + readonly property color launcherModeActiveText: "#ffffffff" + readonly property color launcherModeActiveBg: accent + + // ── Status Colors ────────────────────────────────────────────────────────── + readonly property color statusOk: "#ffb4c9a1" + readonly property color statusWarn: "#ff89b4fa" + readonly property color statusErr: "#fff38ba8" + readonly property color statusInfo: "#ff89b4fa" + + // ── Notifications Module ─────────────────────────────────────────────────── + readonly property color notifBg: bgPanel + readonly property color notifBgHover: "#11ffffff" + readonly property color notifBorder: border + readonly property color notifBorderActive: accent + readonly property color notifIcon: textMain + readonly property color notifIconActive: accent + readonly property color notifBadgeBg: statusErr + readonly property color notifBadgeText: "#ffffffff" + readonly property color notifSeparator: separator + readonly property color notifDnd: separator + + // ── Toast cards ──────────────────────────────────────────────────────────── + readonly property color notifToastBg: bgPopup + readonly property color notifToastBgHover: "#22ffffff" + readonly property color notifToastBorder: borderPopup + readonly property color notifToastText: textMain + readonly property color notifToastDim: textDim + readonly property color notifToastAppName: "#ff89b4fa" + + // ── Urgency colours ──────────────────────────────────────────────────────── + readonly property color notifUrgencyCritical: statusErr + readonly property color notifUrgencyNormal: statusInfo + readonly property color notifUrgencyLow: textDim + + // ── Progress bar ─────────────────────────────────────────────────────────── + readonly property color notifProgressBg: "#22ffffff" + readonly property color notifProgressFill: accent + + // ── History popup ────────────────────────────────────────────────────────── + readonly property color notifHistoryBg: bgPopup + readonly property color notifHistoryBorder: borderPopup + readonly property color notifHistoryHover: "#1189b4fa" + readonly property color notifHistoryEmpty: textDim + + // ── BgDate Module ────────────────────────────────────────────────────────── + readonly property color bgDateText: accent + readonly property color bgDateShadow: "#88000000" + readonly property color bgDateSecondsText: accent + + // ── Crypto Module ────────────────────────────────────────────────────────── + readonly property color cryptoBg: bgPanel + readonly property color cryptoBgHover: "#15ffffff" + readonly property color cryptoBorder: border + readonly property color cryptoBorderHover: accent + readonly property color cryptoIcon: accent + readonly property color cryptoLabel: textDim + readonly property color cryptoValue: textMain + readonly property color cryptoUp: "#ffb4c9a1" + readonly property color cryptoDown: "#fff38ba8" + readonly property color cryptoPopupBg: bgPopup + readonly property color cryptoPopupBorder: borderPopup + readonly property color cryptoPopupHeader: accent + readonly property color cryptoPopupText: textMain + readonly property color cryptoPopupDim: textDim + + readonly property color cryptoUpBg: Qt.rgba(cryptoUp.r, cryptoUp.g, cryptoUp.b, 0.13) + readonly property color cryptoDownBg: Qt.rgba(cryptoDown.r, cryptoDown.g, cryptoDown.b, 0.13) + readonly property color cryptoChipRemoveBg: Qt.rgba(cryptoDown.r, cryptoDown.g, cryptoDown.b, 0.27) + readonly property color cryptoApplyIdleBg: Qt.rgba(accent.r, accent.g, accent.b, 0.20) + + // ── Polkit Dialog ────────────────────────────────────────────────────────── + readonly property color polkitOverlayBg: "#bb000000" + readonly property color polkitCardBg: bgPopup + readonly property color polkitCardBorder: border + readonly property color polkitIconBg: "#1e89b4fa" + readonly property color polkitIconBorder: "#4489b4fa" + readonly property color polkitIconColor: accent + readonly property color polkitTitle: textMain + readonly property color polkitMessage: "#cce8eef2" + readonly property color polkitPillBg: "#1a4a5d6e" + readonly property color polkitPillText: "#667a8996" + readonly property color polkitDivider: "#334a5d6e" + readonly property color polkitSuppErrBg: "#1af38ba8" + readonly property color polkitSuppErrBorder: "#44f38ba8" + readonly property color polkitSuppErrText: statusErr + readonly property color polkitSuppOkBg: "#1ab4c9a1" + readonly property color polkitSuppOkBorder: "#44b4c9a1" + readonly property color polkitSuppOkText: statusOk + readonly property color polkitFieldBg: "#44080a0d" + readonly property color polkitFieldBorder: "#22ffffff" + readonly property color polkitFieldBorderFocus: accent + readonly property color polkitUserLabel: "#997a8996" + readonly property color polkitUserValue: accent + readonly property color polkitInputPrompt: "#887a8996" + readonly property color polkitInputText: textMain + readonly property color polkitInputSelection: "#5589b4fa" + readonly property color polkitInputSelectedText: bg + readonly property color polkitEyeIcon: "#667a8996" + readonly property color polkitEyeIconHover: accent + readonly property color polkitAuthBg: "#dd89b4fa" + readonly property color polkitAuthBgHover: accent + readonly property color polkitAuthText: bg + readonly property color polkitCancelBg: "#0dffffff" + readonly property color polkitCancelBgHover: "#22ffffff" + readonly property color polkitCancelBorder: "#22ffffff" + readonly property color polkitCancelText: "#cce8eef2" +} + + diff --git a/launcher/Launcher.qml b/launcher/Launcher.qml index 7236e2e..70e6074 100644 --- a/launcher/Launcher.qml +++ b/launcher/Launcher.qml @@ -80,7 +80,6 @@ PanelWindow { } function shellQuote(str) { - // POSIX single-quote escaping: wrap in ' and replace every ' with '\'' return "'" + str.replace(/'/g, "'\\''") + "'" } diff --git a/polkit/Polkit.qml b/polkit/Polkit.qml new file mode 100644 index 0000000..c442a6e --- /dev/null +++ b/polkit/Polkit.qml @@ -0,0 +1,544 @@ +import QtQuick +import QtQuick.Controls +import Quickshell +import Quickshell.Wayland +import Quickshell.Services.Polkit + +import "../config" as Cfg + + +Item { + id: root + + readonly property var t: Cfg.Config.theme + readonly property var cfg: Cfg.Config + + property bool interactionAvailable: false + property bool windowShouldExist: false + + PolkitAgent { + id: agent + + onAuthenticationRequestStarted: { + root.windowShouldExist = true + root.interactionAvailable = true + resetInput() + } + } + + readonly property var flow: agent.flow ?? null + + Connections { + target: flow + function onAuthenticationFailed() { + root.interactionAvailable = true + resetInput() + } + } + + function submitAuth() { + if (!root.interactionAvailable) return + if (flow?.isResponseRequired) { + root.interactionAvailable = false + flow.submit(windowLoader.item?.passwordText ?? "") + windowLoader.item?.clearPassword() + } + } + + function resetInput() { + windowLoader.item?.resetInput() + } + + Loader { + id: windowLoader + active: root.windowShouldExist + + sourceComponent: Component { + PanelWindow { + id: overlay + + property bool exiting: false + property bool authFailed: false + property var displayFlow: null + + function captureFlowSnapshot(flow) { + if (!flow) return null + return { + message: flow.message, + actionId: flow.actionId, + supplementaryMessage: flow.supplementaryMessage, + supplementaryIsError: flow.supplementaryIsError, + identities: flow.identities?.slice() ?? [], + selectedIdentity: flow.selectedIdentity, + isResponseRequired: flow.isResponseRequired, + inputPrompt: flow.inputPrompt, + responseVisible: flow.responseVisible + } + } + + readonly property string passwordText: passwordInput.text + function clearPassword() { passwordInput.clear() } + function resetInput() { + passwordInput.clear() + passwordInput.forceActiveFocus() + } + + visible: true + + anchors { top: true; bottom: true; left: true; right: true } + color: "transparent" + + WlrLayershell.layer: WlrLayer.Overlay + WlrLayershell.exclusiveZone: -1 + WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive + WlrLayershell.namespace: "main-shell-polkit" + + Timer { + id: exitDebounce + interval: 250 + onTriggered: { + if (!agent.isActive && !overlay.exiting) { + overlay.exiting = true + exitAnim.start() + } + } + } + + function cancelAndClose() { + if (overlay.exiting) return + overlay.exiting = true + exitAnim.start() + root.flow?.cancelAuthenticationRequest() + } + + Rectangle { + anchors.fill: parent + color: t.polkitOverlayBg + } + + component DialogButton: Rectangle { + property string label + property int labelWeight: Font.Normal + property color bgColor + property color bgHoverColor + property color borderColor + property color borderHoverColor + property color labelColor + signal tapped() + + width: (parent.width - 12) / 2 + height: 44 + radius: 10 + + HoverHandler { id: hover } + + color: hover.hovered ? bgHoverColor : bgColor + border.color: hover.hovered ? borderHoverColor : borderColor + border.width: 1 + + Behavior on color { ColorAnimation { duration: 200 } } + Behavior on border.color { ColorAnimation { duration: 200 } } + + scale: pressHandler.pressed ? 0.97 : 1.0 + Behavior on scale { NumberAnimation { duration: 100; easing.type: Easing.OutQuad } } + + TapHandler { + id: pressHandler + cursorShape: Qt.PointingHandCursor + onTapped: parent.tapped() + } + + Text { + anchors.centerIn: parent + text: label + color: labelColor + font.pixelSize: 13 + font.weight: labelWeight + font.letterSpacing: 0.2 + } + } + + Rectangle { + id: card + + property real shakeX: 0 + + x: Math.round((parent.width - width) / 2) + shakeX + y: Math.round((parent.height - height) / 2) + + width: 440 + height: content.implicitHeight + 64 + + radius: cfg.popupRadius + color: t.polkitCardBg + border.color: t.polkitCardBorder + border.width: 1 + + opacity: 0 + scale: 0.86 + + Component.onCompleted: { + overlay.displayFlow = agent.flow + enterAnim.start() + } + + ParallelAnimation { + id: enterAnim + NumberAnimation { + target: card; property: "opacity" + from: 0; to: 1 + duration: 260; easing.type: Easing.OutCubic + } + NumberAnimation { + target: card; property: "scale" + from: 0.86; to: 1.0 + duration: 360; easing.type: Easing.OutBack; easing.overshoot: 0.6 + } + } + + ParallelAnimation { + id: exitAnim + NumberAnimation { + target: card; property: "opacity" + from: 1; to: 0 + duration: 200; easing.type: Easing.InCubic + } + NumberAnimation { + target: card; property: "scale" + from: 1.0; to: 0.88 + duration: 260; easing.type: Easing.InBack + } + onFinished: { + overlay.exiting = false + root.windowShouldExist = false + } + } + + Connections { + target: agent + function onIsActiveChanged() { + if (agent.isActive) { + overlay.authFailed = false + overlay.displayFlow = agent.flow + exitDebounce.stop() + if (overlay.exiting) { + exitAnim.stop() + overlay.exiting = false + } + card.opacity = 0 + card.scale = 0.86 + enterAnim.restart() + } else { + if (overlay.displayFlow) + overlay.displayFlow = captureFlowSnapshot(overlay.displayFlow) + exitDebounce.restart() + } + } + } + + Connections { + target: flow + function onAuthenticationFailed() { + overlay.authFailed = true + shakeAnim.restart() + } + } + + SequentialAnimation { + id: shakeAnim + NumberAnimation { target: card; property: "shakeX"; to: 12; duration: 45; easing.type: Easing.InOutQuad } + NumberAnimation { target: card; property: "shakeX"; to: -10; duration: 45 } + NumberAnimation { target: card; property: "shakeX"; to: 8; duration: 45 } + NumberAnimation { target: card; property: "shakeX"; to: -5; duration: 45 } + NumberAnimation { target: card; property: "shakeX"; to: 3; duration: 45 } + NumberAnimation { target: card; property: "shakeX"; to: 0; duration: 45 } + } + + Column { + id: content + + anchors { + top: parent.top + left: parent.left + right: parent.right + topMargin: 32 + leftMargin: 32 + rightMargin: 32 + } + spacing: 16 + + Item { + width: parent.width + height: 72 + + Rectangle { + anchors.centerIn: parent + width: 66 + height: 66 + radius: 33 + color: t.polkitIconBg + border.color: t.polkitIconBorder + border.width: 1 + + Text { + anchors.centerIn: parent + text: "󰌆" + font.pixelSize: 28 + color: t.polkitIconColor + Component.onCompleted: { + if (implicitWidth < 4) text = "🔒" + } + } + } + } + + Text { + width: parent.width + horizontalAlignment: Text.AlignHCenter + text: "Authentication Required" + color: t.polkitTitle + font.pixelSize: 17 + font.weight: Font.DemiBold + font.letterSpacing: 0.3 + } + + Text { + visible: !!displayFlow?.message + width: parent.width + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + text: displayFlow?.message ?? "" + color: t.polkitMessage + font.pixelSize: 13 + lineHeight: 1.50 + } + + Item { + visible: !!displayFlow?.actionId + width: parent.width + height: pillBg.height + + Rectangle { + id: pillBg + anchors.horizontalCenter: parent.horizontalCenter + width: Math.min(pillText.implicitWidth + 22, parent.width) + height: pillText.implicitHeight + 10 + radius: 6 + color: t.polkitPillBg + + Text { + id: pillText + anchors.centerIn: parent + width: parent.width - 22 + elide: Text.ElideMiddle + text: displayFlow?.actionId ?? "" + color: t.polkitPillText + font.pixelSize: 11 + font.family: "monospace" + } + } + } + + Rectangle { + width: parent.width + height: 1 + color: t.polkitDivider + } + + Rectangle { + visible: overlay.authFailed || !!displayFlow?.supplementaryMessage + width: parent.width + height: suppRow.implicitHeight + 20 + radius: 10 + color: overlay.authFailed || displayFlow?.supplementaryIsError ? t.polkitSuppErrBg : t.polkitSuppOkBg + border.color: overlay.authFailed || displayFlow?.supplementaryIsError ? t.polkitSuppErrBorder : t.polkitSuppOkBorder + border.width: 1 + + Row { + id: suppRow + anchors { + top: parent.top; topMargin: 10 + bottom: parent.bottom; bottomMargin: 10 + left: parent.left; leftMargin: 14 + right: parent.right; rightMargin: 14 + } + spacing: 9 + + Text { + anchors.top: parent.top + text: "✕" + font.pixelSize: 12 + font.weight: Font.Medium + lineHeight: 1.8 + color: t.polkitSuppErrText + } + + Text { + width: parent.width - 22 + wrapMode: Text.WordWrap + text: overlay.authFailed ? "Authentication failed, please try again." : (displayFlow?.supplementaryMessage ?? "") + font.pixelSize: 12 + lineHeight: 1.45 + color: overlay.authFailed || displayFlow?.supplementaryIsError ? t.polkitSuppErrText : t.polkitSuppOkText + } + } + } + + Text { + visible: true + width: parent.width + text: displayFlow?.inputPrompt ?? "Password" + color: t.polkitInputPrompt + font.pixelSize: 12 + } + + Rectangle { + visible: true + width: parent.width + height: 46 + radius: 10 + color: t.polkitFieldBg + border.color: passwordInput.activeFocus ? t.polkitFieldBorderFocus : t.polkitFieldBorder + border.width: passwordInput.activeFocus ? 1.5 : 1 + + Behavior on border.color { ColorAnimation { duration: 120 } } + Behavior on border.width { NumberAnimation { duration: 120 } } + + TextInput { + id: passwordInput + + enabled: root.interactionAvailable + + anchors { + left: parent.left; leftMargin: 16 + right: eyeBtn.left; rightMargin: 8 + verticalCenter: parent.verticalCenter + } + + property bool showPassword: false + + echoMode: (displayFlow?.responseVisible || showPassword) + ? TextInput.Normal : TextInput.Password + + color: t.polkitInputText + selectionColor: t.polkitInputSelection + selectedTextColor: t.polkitInputSelectedText + font.pixelSize: 14 + passwordCharacter: "•" + clip: true + + Keys.onReturnPressed: root.submitAuth() + Keys.onEnterPressed: root.submitAuth() + Keys.onEscapePressed: overlay.cancelAndClose() + } + + Text { + id: eyeBtn + anchors { + right: parent.right; rightMargin: 14 + verticalCenter: parent.verticalCenter + } + text: passwordInput.showPassword ? "🙈" : "👁" + font.pixelSize: 20 + color: eyeHover.hovered ? t.polkitEyeIconHover : t.polkitEyeIcon + + Behavior on color { ColorAnimation { duration: 120 } } + + HoverHandler { id: eyeHover } + MouseArea { + anchors.fill: parent + anchors.margins: -8 + cursorShape: Qt.PointingHandCursor + onClicked: passwordInput.showPassword = !passwordInput.showPassword + } + } + } + + Item { width: 1; height: 2 } + + Row { + width: parent.width + spacing: 12 + layoutDirection: Qt.RightToLeft + + DialogButton { + label: "Authenticate" + labelWeight: Font.Medium + labelColor: t.polkitAuthText + bgColor: t.polkitAuthBg + bgHoverColor: t.polkitAuthBgHover + borderColor: t.polkitAuthBg + borderHoverColor: t.polkitAuthBgHover + onTapped: root.submitAuth() + } + + DialogButton { + label: "Cancel" + labelColor: t.polkitCancelText + bgColor: t.polkitCancelBg + bgHoverColor: t.polkitCancelBgHover + borderColor: t.polkitCancelBorder + borderHoverColor: t.polkitCancelBgHover + onTapped: overlay.cancelAndClose() + } + } + } + + Rectangle { + anchors.fill: parent + radius: cfg.popupRadius + color: Qt.rgba( + t.polkitCardBg.r, + t.polkitCardBg.g, + t.polkitCardBg.b, 0.78) + opacity: root.interactionAvailable ? 0 : 1 + visible: opacity > 0 + + Behavior on opacity { NumberAnimation { duration: 180; easing.type: Easing.InOutSine } } + + Canvas { + id: spinnerCanvas + anchors.centerIn: parent + width: 40 + height: 40 + + property real angle: 0 + + NumberAnimation on angle { + running: !root.interactionAvailable + from: 0 + to: 360 + duration: 700 + loops: Animation.Infinite + } + + onAngleChanged: requestPaint() + + onPaint: { + const ctx = getContext("2d") + ctx.clearRect(0, 0, width, height) + const cx = width / 2, cy = height / 2 + const r = width / 2 - 3 + + ctx.strokeStyle = Qt.rgba( + t.polkitTitle.r, t.polkitTitle.g, t.polkitTitle.b, 0.12) + ctx.lineWidth = 3.5 + ctx.beginPath() + ctx.arc(cx, cy, r, 0, 2 * Math.PI) + ctx.stroke() + + const start = (angle - 90) * Math.PI / 180 + ctx.strokeStyle = Qt.rgba( + t.polkitTitle.r, t.polkitTitle.g, t.polkitTitle.b, 0.88) + ctx.lineCap = "round" + ctx.beginPath() + ctx.arc(cx, cy, r, start, start + 1.3 * Math.PI) + ctx.stroke() + } + } + } + } + } + } + } +} diff --git a/shell.qml b/shell.qml index ffd1c27..f70f6d8 100644 --- a/shell.qml +++ b/shell.qml @@ -7,16 +7,33 @@ import Quickshell.Io import "bar" import "launcher" import "widgets/bgDate" +import "polkit" import "config" as Cfg ShellRoot { id: root + + Component.onCompleted: { + Qt.application.organization = "quickshell" + Qt.application.name = "quickshell" + } readonly property bool isTop: Cfg.Config.barPosition === "top" property color frameBackground: Cfg.Config.theme.bgFrame property color frameBorderColor: Cfg.Config.theme.frameBorder ?? Cfg.Config.theme.borderPopup + // ── Polkit Agent ───────────────────────────────────────────────────────── + Loader { + active: Cfg.Config.enablePolkit + source: Qt.resolvedUrl("polkit/Polkit.qml") + } + + Component { + id: polkitComp + Polkit {} + } + // ── Spacing Gap: Top / Bottom ──────────────────────────────────────── Variants { model: Quickshell.screens @@ -117,7 +134,6 @@ ShellRoot { id: frameCanvas anchors.fill: parent - // Animation Properties opacity: 0 scale: 0.98 @@ -212,9 +228,6 @@ ShellRoot { ctx.stroke() } - onWidthChanged: requestPaint() - onHeightChanged: requestPaint() - Connections { target: root function onFrameBackgroundChanged() { frameCanvas.requestPaint() } @@ -245,4 +258,3 @@ ShellRoot { } } } - diff --git a/wallpapers/hummingbird.jpg b/wallpapers/hummingbird.jpg new file mode 100644 index 0000000..66ec326 Binary files /dev/null and b/wallpapers/hummingbird.jpg differ diff --git a/wallpapers/tropicalnight.png b/wallpapers/tropicalnight.png new file mode 100644 index 0000000..8d69647 Binary files /dev/null and b/wallpapers/tropicalnight.png differ diff --git a/wallpapers/wallhaven-4dr56m.jpg b/wallpapers/wallhaven-4dr56m.jpg new file mode 100644 index 0000000..df4148b Binary files /dev/null and b/wallpapers/wallhaven-4dr56m.jpg differ diff --git a/widgets/bgDate/BgDate.qml b/widgets/bgDate/BgDate.qml index 4f8c290..ae59527 100644 --- a/widgets/bgDate/BgDate.qml +++ b/widgets/bgDate/BgDate.qml @@ -1,9 +1,12 @@ import QtQuick import Quickshell import Quickshell.Wayland +import Quickshell.Io import "../../config" as Cfg PanelWindow { + id: root + required property var modelData screen: modelData color: "transparent" @@ -14,10 +17,46 @@ PanelWindow { WlrLayershell.exclusiveZone: -1 mask: Region {} + readonly property string tz: Cfg.Config.timezone || "" + + property int tzOffsetMin: 0 + property string fullDateString: "" property int typedChars: 0 property string secondsString: "" + onTzOffsetMinChanged: { + updateDate(new Date()) + } + + Process { + id: tzProc + command: ["bash", "-c", "TZ=" + (root.tz || "UTC") + " date +%z"] + running: root.tz !== "" + stdout: SplitParser { + onRead: data => { + var s = data.trim() + if (s.length < 5) return + var sign = (s[0] === "-") ? -1 : 1 + var h = parseInt(s.substring(1, 3), 10) + var m = parseInt(s.substring(3, 5), 10) + root.tzOffsetMin = sign * (h * 60 + m) + } + } + } + + Timer { + interval: 1800000; running: root.tz !== ""; repeat: true + onTriggered: tzProc.running = true + } + + function toTz(date) { + if (!tz) return date + return new Date(date.getTime() + + date.getTimezoneOffset() * 60000 + + root.tzOffsetMin * 60000) + } + function startTyping() { typedChars = 0 secondsText.opacity = 0 @@ -26,13 +65,14 @@ PanelWindow { function updateDate(d) { let now = d || new Date() - fullDateString = now.toLocaleDateString(Qt.locale(), "dddd, d MMMM yyyy") + fullDateString = toTz(now).toLocaleDateString(Qt.locale(), + Cfg.Config.dateFormat === "DMY" ? "dddd, d MMMM yyyy" : "dddd, MMMM d, yyyy") startTyping() } Component.onCompleted: { let d = new Date() - secondsString = Qt.formatTime(d, "hh:mm:ss") + secondsString = Qt.formatTime(toTz(d), "hh:mm:ss") updateDate(d) } @@ -50,6 +90,9 @@ PanelWindow { font.letterSpacing: 4 opacity: 0.55 style: Text.Raised + renderType: Text.NativeRendering + layer.enabled: true + layer.smooth: true text: fullDateString.substring(0, typedChars) } @@ -63,6 +106,9 @@ PanelWindow { font.letterSpacing: 6 opacity: 0 style: Text.Raised + renderType: Text.NativeRendering + layer.enabled: true + layer.smooth: true text: secondsString Behavior on opacity { @@ -94,8 +140,9 @@ PanelWindow { repeat: true onTriggered: { let d = new Date() - secondsString = Qt.formatTime(d, "hh:mm:ss") - if (d.getSeconds() === 0 && d.getMinutes() === 0 && d.getHours() === 0) + let tzd = toTz(d) + secondsString = Qt.formatTime(tzd, "hh:mm:ss") + if (tzd.getHours() === 0 && tzd.getMinutes() === 0 && tzd.getSeconds() === 0) updateDate(d) } }