small component patches + code cleanup

This commit is contained in:
SomeElse
2026-05-17 22:30:27 +00:00
parent 262fd8afa0
commit 6db876fc8a
21 changed files with 469 additions and 465 deletions

View File

@@ -73,7 +73,6 @@ PanelWindow {
item.height = Qt.binding(() => barContent.height - 12) item.height = Qt.binding(() => barContent.height - 12)
const props = { const props = {
"panelRadius": cfg.radius / 2,
"borderWidth": cfg.panelBorderWidth, "borderWidth": cfg.panelBorderWidth,
"isTop": barWindow.isTop, "isTop": barWindow.isTop,
"targetScreen": barWindow.modelData, "targetScreen": barWindow.modelData,
@@ -95,7 +94,7 @@ PanelWindow {
anchors.left: parent.left anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
height: parent.height height: parent.height
width: Math.max(0, Math.min(leftRow.implicitWidth, barContent.sideMaxWidth)) width: Math.min(leftRow.implicitWidth, barContent.sideMaxWidth)
clip: true clip: true
Row { Row {
@@ -123,7 +122,7 @@ PanelWindow {
anchors.right: parent.right anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
height: parent.height height: parent.height
width: Math.max(0, Math.min(rightRow.implicitWidth, barContent.sideMaxWidth)) width: Math.min(rightRow.implicitWidth, barContent.sideMaxWidth)
clip: true clip: true
Row { Row {

View File

@@ -51,11 +51,11 @@ ModuleChip {
readonly property bool isLow: batReady && !charging && pct <= 30 readonly property bool isLow: batReady && !charging && pct <= 30
readonly property color iconColor: { readonly property color iconColor: {
if (fullCharge) return t.batteryFull ?? t.accent if (fullCharge) return t.batteryFull
if (charging) return t.batteryCharging ?? t.accent if (charging) return t.batteryCharging
if (isCritical) return t.batteryCritical ?? "#ff5555" if (isCritical) return t.batteryCritical
if (isLow) return t.batteryLow ?? "#ffb86c" if (isLow) return t.batteryLow
return t.batteryIcon ?? t.clockIcon return t.batteryIcon
} }
readonly property var up: UPower readonly property var up: UPower
@@ -92,21 +92,17 @@ ModuleChip {
return names return names
} }
chipColor: t.batBg
chipColorHovered: t.batBgHover
chipBorderColor: t.batBorder
chipBorderColorHovered: t.batBorderHover
chipHoverEnabled: true
chipTapAction: () => batteryPopup.toggle()
chipAnimWidth: true
width: batLayout.implicitWidth + 24 width: batLayout.implicitWidth + 24
height: Cfg.Config.moduleHeight
radius: panelRadius 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 { RowLayout {
id: batLayout id: batLayout
anchors.centerIn: parent anchors.centerIn: parent
@@ -144,8 +140,8 @@ ModuleChip {
font.bold: true font.bold: true
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
opacity: (hoverHandler.hovered || batteryPopup.popupOpen) ? 1.0 : 0.0 opacity: (root.hovered || batteryPopup.popupOpen) ? 1.0 : 0.0
Layout.preferredWidth: (hoverHandler.hovered || batteryPopup.popupOpen) Layout.preferredWidth: (root.hovered || batteryPopup.popupOpen)
? implicitWidth : 0 ? implicitWidth : 0
clip: true clip: true
@@ -154,37 +150,16 @@ ModuleChip {
} }
} }
PopupPanel { StandardPopup {
id: batteryPopup id: batteryPopup
ns: "battery" ns: "battery"
chipRoot: root chipRoot: root
cardWidth: 260 cardWidth: 260
cardColor: t.batPopupBg
Rectangle { cardBorderColor: t.batPopupBorder
id: cardShadow cardHeight: cardCol.implicitHeight + 28
anchors.fill: card showShadow: true
anchors.margins: -3 layerEnabled: true
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 } }
}
PopupCard {
id: card
popupOpen: batteryPopup.popupOpen
isTop: root.isTop
animEnabled: batteryPopup.visible
height: cardCol.implicitHeight + 28
color: root.t.batPopupBg
radius: Cfg.Config.popupRadius
border.color: root.t.batPopupBorder
border.width: Cfg.Config.popupBorderWidth
layer.enabled: true
ColumnLayout { ColumnLayout {
id: cardCol id: cardCol
@@ -515,5 +490,4 @@ ModuleChip {
Item { height: 2 } Item { height: 2 }
} }
} }
}
} }

View File

@@ -9,29 +9,17 @@ import "../../components"
ModuleChip { ModuleChip {
id: root id: root
chipColor: t.clockBg
chipColorHovered: t.clockBgHover
chipBorderColor: t.clockBorder
chipBorderColorHovered: t.clockBorderHover
chipHoverEnabled: true
chipTapAction: () => calendarPopup.toggle()
chipAnimWidth: true
width: clockLayout.implicitWidth + 24 width: clockLayout.implicitWidth + 24
height: Cfg.Config.moduleHeight
radius: panelRadius radius: panelRadius
color: hoverHandler.hovered ? t.clockBgHover : t.clockBg
border.color: hoverHandler.hovered ? t.clockBorderHover : t.clockBorder
border.width: borderWidth
anchors.right: parent.right
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: calendarPopup.toggle()
}
Item { Item {
id: timeManager id: timeManager
property date now: new Date() property date now: new Date()
@@ -70,9 +58,9 @@ ModuleChip {
font.bold: true font.bold: true
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
opacity: (hoverHandler.hovered || calendarPopup.popupOpen) && Cfg.Config.clockExtendEnabled ? 1.0 : 0.0 opacity: (root.hovered || calendarPopup.popupOpen) && Cfg.Config.clockExtendEnabled ? 1.0 : 0.0
visible: opacity > 0 visible: opacity > 0
Layout.preferredWidth: (hoverHandler.hovered || calendarPopup.popupOpen) && Cfg.Config.clockExtendEnabled ? implicitWidth : 0 Layout.preferredWidth: (root.hovered || calendarPopup.popupOpen) && Cfg.Config.clockExtendEnabled ? implicitWidth : 0
clip: true clip: true
Behavior on opacity { NumberAnimation { duration: 200 } } Behavior on opacity { NumberAnimation { duration: 200 } }
@@ -80,24 +68,14 @@ ModuleChip {
} }
} }
PopupPanel { StandardPopup {
id: calendarPopup id: calendarPopup
ns: "calendar" ns: "calendar"
chipRoot: root chipRoot: root
cardWidth: 284 cardWidth: 284
cardColor: t.calCardBg
PopupCard { cardBorderColor: t.calCardBorder
id: card cardHeight: cardCol.implicitHeight + 28
popupOpen: calendarPopup.popupOpen
isTop: root.isTop
animEnabled: calendarPopup.visible
height: cardCol.implicitHeight + 24
color: root.t.calCardBg
radius: Cfg.Config.popupRadius
border.color: root.t.calCardBorder
border.width: Cfg.Config.popupBorderWidth
layer.enabled: true
component MonthNavButton: Rectangle { component MonthNavButton: Rectangle {
id: navBtn id: navBtn
@@ -252,7 +230,7 @@ ModuleChip {
Repeater { Repeater {
model: ["Mo","Tu","We","Th","Fr","Sa","Su"] model: ["Mo","Tu","We","Th","Fr","Sa","Su"]
Text { Text {
width: (card.width - 32) / 7 width: (calendarPopup.card.width - 32) / 7
text: modelData; color: root.t.clockPopupDim text: modelData; color: root.t.clockPopupDim
font.pixelSize: 11; horizontalAlignment: Text.AlignHCenter font.pixelSize: 11; horizontalAlignment: Text.AlignHCenter
} }
@@ -267,7 +245,7 @@ ModuleChip {
Repeater { Repeater {
model: 7 model: 7
delegate: Item { delegate: Item {
width: (card.width - 32) / 7; height: 26 width: (calendarPopup.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 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 inMonth: dayNum >= 1 && dayNum <= new Date(cal.displayYear, cal.displayMonth + 1, 0).getDate()
readonly property bool isToday: { readonly property bool isToday: {
@@ -338,5 +316,4 @@ ModuleChip {
Item { height: 4 } Item { height: 4 }
} }
} }
}
} }

View File

@@ -12,29 +12,21 @@ ModuleChip {
readonly property var cfg: Cfg.Config readonly property var cfg: Cfg.Config
readonly property string configPath: ConfigWriter {
Quickshell.env("HOME") + "/.config/quickshell/config/config.qml" id: configWriter
Process {
id: cryptoConfigWriter
running: false
onExited: (code) => {
if (code !== 0)
console.warn("[Crypto] Failed to patch config.qml (exit " + code + ")")
}
} }
width: chipRow.implicitWidth + 24 width: chipRow.implicitWidth + 24
height: cfg.moduleHeight
radius: panelRadius radius: panelRadius
color: hoverHandler.hovered ? (t.cryptoBgHover ?? "#15ffffff") : (t.cryptoBg ?? "transparent") chipColor: t.cryptoBg
border.color: (hoverHandler.hovered || cryptoPopup.popupOpen) ? (t.cryptoBorderHover ?? t.accent ?? "#ffc19375") : (t.cryptoBorder ?? "transparent") chipColorHovered: t.cryptoBgHover
border.width: borderWidth chipBorderColor: t.cryptoBorder
chipBorderColorHovered: t.cryptoBorderHover
Behavior on width { NumberAnimation { duration: 250; easing.type: Easing.OutCubic } } chipHoverEnabled: true
Behavior on color { ColorAnimation { duration: 150 } } chipTapAction: () => cryptoPopup.toggle()
Behavior on border.color { ColorAnimation { duration: 150 } } popupOpen: cryptoPopup.popupOpen
chipAnimWidth: true
property var localPairs: [{ coin: "bitcoin", currency: "usd" }] property var localPairs: [{ coin: "bitcoin", currency: "usd" }]
property int activeCoinIndex: 0 property int activeCoinIndex: 0
@@ -46,18 +38,18 @@ ModuleChip {
: "usd" : "usd"
property string localVsCur: "usd" property string localVsCur: "usd"
property int localDecimals: cfg.cryptoDecimals ?? 1 property int localDecimals: cfg.cryptoDecimals
property int localRefreshSec: cfg.cryptoRefreshSec ?? 60 property int localRefreshSec: cfg.cryptoRefreshSec
property bool localShowIcon: cfg.cryptoShowIcon ?? true property bool localShowIcon: cfg.cryptoShowIcon
function resolveWithPersistence() { function resolveWithPersistence() {
root.localPairs = Array.isArray(cfg.cryptoPairs) && cfg.cryptoPairs.length > 0 ? cfg.cryptoPairs : [{ coin: "bitcoin", currency: "usd" }] root.localPairs = Array.isArray(cfg.cryptoPairs) && cfg.cryptoPairs.length > 0 ? cfg.cryptoPairs : [{ coin: "bitcoin", currency: "usd" }]
root.localVsCur = root.localPairs[0].currency root.localVsCur = root.localPairs[0].currency
root.displayCurrency = root.localPairs[0].currency root.displayCurrency = root.localPairs[0].currency
root.activeCoinIndex = 0 root.activeCoinIndex = 0
root.localDecimals = cfg.cryptoDecimals ?? 1 root.localDecimals = cfg.cryptoDecimals
root.localRefreshSec = cfg.cryptoRefreshSec ?? 60 root.localRefreshSec = cfg.cryptoRefreshSec
root.localShowIcon = cfg.cryptoShowIcon ?? true root.localShowIcon = cfg.cryptoShowIcon
} }
function persistSettings() { function persistSettings() {
@@ -65,30 +57,17 @@ ModuleChip {
const decimals = Math.round(root.localDecimals) const decimals = Math.round(root.localDecimals)
const refreshSec = Math.round(root.localRefreshSec) const refreshSec = Math.round(root.localRefreshSec)
const showIcon = root.localShowIcon const showIcon = root.localShowIcon
const cfgPath = root.configPath
const pairsQml = "[" + pairs.map(p => const pairsQml = "[" + pairs.map(p =>
'{ coin: "' + p.coin + '", currency: "' + p.currency + '" }' '{ coin: "' + p.coin + '", currency: "' + p.currency + '" }'
).join(", ") + "]" ).join(", ") + "]"
configWriter.writeInts([
const sh = { key: "cryptoDecimals", value: decimals },
"CFG=" + JSON.stringify(cfgPath) + "\n" + { key: "cryptoRefreshSec", value: refreshSec }
"sed -i" + ])
" -e 's/\\(property int[ ]\\+cryptoDecimals:[ ]\\+\\)[0-9.]\\+/\\1" + decimals.toString() + "/'" + configWriter.writeBool("cryptoShowIcon", showIcon)
" -e 's/\\(property int[ ]\\+cryptoRefreshSec:[ ]\\+\\)[0-9.]\\+/\\1" + refreshSec.toString() + "/'" + configWriter.writeArray("cryptoPairs", pairsQml)
" -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 price: -1
@@ -222,12 +201,6 @@ ModuleChip {
onLocalRefreshSecChanged: refreshTimer.restart() onLocalRefreshSecChanged: refreshTimer.restart()
HoverHandler {
id: hoverHandler
cursorShape: Qt.PointingHandCursor
}
TapHandler { onTapped: cryptoPopup.toggle() }
RowLayout { RowLayout {
id: chipRow id: chipRow
anchors.centerIn: parent anchors.centerIn: parent
@@ -250,7 +223,7 @@ ModuleChip {
Text { Text {
visible: root.localShowIcon visible: root.localShowIcon
text: "󰿤" text: "󰿤"
color: t.cryptoIcon ?? t.accent ?? "#ffc19375" color: t.cryptoIcon
font.pixelSize: 14 font.pixelSize: 14
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
Layout.rightMargin: 7 Layout.rightMargin: 7
@@ -258,14 +231,14 @@ ModuleChip {
Text { Text {
text: root.coinSymbol + "/" + root.displayCurrency.toUpperCase() + ": " text: root.coinSymbol + "/" + root.displayCurrency.toUpperCase() + ": "
color: t.cryptoLabel ?? t.textDim ?? "#997a8996" color: t.cryptoLabel
font.pixelSize: 12 font.pixelSize: 12
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
} }
Text { Text {
text: root.hasError ? "err" : root.price < 0 ? "…" : root.compactPrice(root.price) text: root.hasError ? "err" : root.price < 0 ? "…" : root.compactPrice(root.price)
color: t.cryptoValue ?? t.textMain ?? "#ffe8eef2" color: t.cryptoValue
font.pixelSize: 13 font.pixelSize: 13
font.bold: true font.bold: true
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
@@ -274,7 +247,7 @@ ModuleChip {
Text { Text {
visible: root.price >= 0 visible: root.price >= 0
text: root.change24h >= 0 ? "▲" : "▼" text: root.change24h >= 0 ? "▲" : "▼"
color: root.change24h >= 0 ? (t.cryptoUp ?? "#ffb4c9a1") : (t.cryptoDown ?? "#fff38ba8") color: root.change24h >= 0 ? (t.cryptoUp) : (t.cryptoDown)
font.pixelSize: 9 font.pixelSize: 9
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
@@ -284,34 +257,16 @@ ModuleChip {
} }
PopupPanel { StandardPopup {
id: cryptoPopup id: cryptoPopup
ns: "crypto" ns: "crypto"
chipRoot: root chipRoot: root
cardWidth: 300 cardWidth: 300
hasKeyboardFocus: true hasKeyboardFocus: true
cardColor: t.cryptoPopupBg
function open() { cardBorderColor: t.cryptoPopupBorder
popupCenterX = root.mapToItem(null, 0, 0).x + root.width / 2 cardHeight: cardCol.implicitHeight + 24
_doOpen() layerEnabled: true
}
PopupCard {
id: card
popupOpen: cryptoPopup.popupOpen
isTop: root.isTop
animEnabled: cryptoPopup.visible
height: cardCol.implicitHeight + 24
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
property int activeTab: 0
ColumnLayout { ColumnLayout {
id: cardCol id: cardCol
@@ -324,7 +279,7 @@ ModuleChip {
Layout.fillWidth: true Layout.fillWidth: true
height: 30 height: 30
radius: 10 radius: 10
color: t.cryptoFieldBg ?? "#15ffffff" color: t.cryptoFieldBg
RowLayout { RowLayout {
anchors.fill: parent anchors.fill: parent
@@ -338,16 +293,16 @@ ModuleChip {
height: 24 height: 24
radius: 8 radius: 8
readonly property bool active: card.activeTab === tabIndex readonly property bool active: cryptoPopup.activeTab === tabIndex
color: active color: active
? (t.accent ?? "#ffc19375") ? (t.accent)
: (tm.containsMouse ? (t.cryptoFieldBgFocus ?? "#22ffffff") : "transparent") : (tm.containsMouse ? (t.cryptoFieldBgFocus) : "transparent")
Behavior on color { ColorAnimation { duration: 130 } } Behavior on color { ColorAnimation { duration: 130 } }
Text { Text {
anchors.centerIn: parent anchors.centerIn: parent
text: parent.label text: parent.label
color: parent.active ? (t.cryptoTextOnAccent ?? "#ff0c0e12") : (t.cryptoPopupDim ?? t.textDim ?? "#997a8996") color: parent.active ? (t.cryptoTextOnAccent) : (t.cryptoPopupDim)
font.pixelSize: 11 font.pixelSize: 11
font.bold: parent.active font.bold: parent.active
Behavior on color { ColorAnimation { duration: 130 } } Behavior on color { ColorAnimation { duration: 130 } }
@@ -356,7 +311,7 @@ ModuleChip {
id: tm id: tm
anchors.fill: parent; hoverEnabled: true anchors.fill: parent; hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: card.activeTab = tabIndex onClicked: cryptoPopup.activeTab = tabIndex
} }
} }
@@ -376,8 +331,8 @@ ModuleChip {
ColumnLayout { ColumnLayout {
id: priceContent id: priceContent
width: tabContainer.width width: tabContainer.width
x: card.activeTab === 0 ? 0 : -tabContainer.width x: cryptoPopup.activeTab === 0 ? 0 : -tabContainer.width
opacity: card.activeTab === 0 ? 1.0 : 0.0 opacity: cryptoPopup.activeTab === 0 ? 1.0 : 0.0
Behavior on x { NumberAnimation { duration: 260; easing.type: Easing.OutCubic } } Behavior on x { NumberAnimation { duration: 260; easing.type: Easing.OutCubic } }
Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } } Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } }
spacing: 10 spacing: 10
@@ -387,7 +342,7 @@ ModuleChip {
Text { Text {
text: "󰿤" text: "󰿤"
color: t.cryptoIcon ?? t.accent ?? "#ffc19375" color: t.cryptoIcon
font.pixelSize: 22 font.pixelSize: 22
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
} }
@@ -399,13 +354,13 @@ ModuleChip {
Text { Text {
text: root.coinSymbol + " / " + root.displayCurrency.toUpperCase() text: root.coinSymbol + " / " + root.displayCurrency.toUpperCase()
color: t.cryptoPopupHeader ?? t.clockPopupHeader ?? "#ffc19375" color: t.cryptoPopupHeader
font.pixelSize: 15 font.pixelSize: 15
font.bold: true font.bold: true
} }
Text { Text {
text: root.coinName !== "" ? root.coinName : (root.localCoinId.charAt(0).toUpperCase() + root.localCoinId.slice(1)) text: root.coinName !== "" ? root.coinName : (root.localCoinId.charAt(0).toUpperCase() + root.localCoinId.slice(1))
color: t.cryptoPopupDim ?? t.textDim ?? "#997a8996" color: t.cryptoPopupDim
font.pixelSize: 11 font.pixelSize: 11
} }
} }
@@ -413,13 +368,13 @@ ModuleChip {
Rectangle { Rectangle {
width: badge.implicitWidth + 12 width: badge.implicitWidth + 12
height: 22; radius: 11 height: 22; radius: 11
color: root.change24h >= 0 ? (t.cryptoUpBg ?? "#22b4c9a1") : (t.cryptoDownBg ?? "#22f38ba8") color: root.change24h >= 0 ? (t.cryptoUpBg) : (t.cryptoDownBg)
Text { Text {
id: badge id: badge
anchors.centerIn: parent anchors.centerIn: parent
text: (root.change24h >= 0 ? "▲ +" : "▼ ") + root.change24h.toFixed(2) + "%" text: (root.change24h >= 0 ? "▲ +" : "▼ ") + root.change24h.toFixed(2) + "%"
color: root.change24h >= 0 ? (t.cryptoUp ?? "#ffb4c9a1") : (t.cryptoDown ?? "#fff38ba8") color: root.change24h >= 0 ? (t.cryptoUp) : (t.cryptoDown)
font.pixelSize: 11 font.pixelSize: 11
font.bold: true font.bold: true
} }
@@ -430,20 +385,20 @@ ModuleChip {
Layout.fillWidth: true Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
text: root.price < 0 ? "Loading…" : root.displayCurrency.toUpperCase() + " " + root.fullPrice(root.price) text: root.price < 0 ? "Loading…" : root.displayCurrency.toUpperCase() + " " + root.fullPrice(root.price)
color: t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2" color: t.cryptoPopupText
font.pixelSize: 24 font.pixelSize: 24
font.bold: true font.bold: true
font.family: "monospace" font.family: "monospace"
} }
Rectangle { Layout.fillWidth: true; height: 1; color: t.calSeparator ?? "#334a5d6e" } Rectangle { Layout.fillWidth: true; height: 1; color: t.calSeparator }
component StatRow: RowLayout { component StatRow: RowLayout {
property string label: "" property string label: ""
property string value: "" property string value: ""
Layout.fillWidth: true Layout.fillWidth: true
Text { text: label; color: t.cryptoPopupDim ?? t.textDim ?? "#997a8996"; font.pixelSize: 12; Layout.fillWidth: true } Text { text: label; color: t.cryptoPopupDim; font.pixelSize: 12; Layout.fillWidth: true }
Text { text: value; color: t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2"; font.pixelSize: 12; font.bold: true; font.family: "monospace" } Text { text: value; color: t.cryptoPopupText; font.pixelSize: 12; font.bold: true; font.family: "monospace" }
} }
StatRow { label: "Rank"; value: root.rank > 0 ? "#" + root.rank : "—" } StatRow { label: "Rank"; value: root.rank > 0 ? "#" + root.rank : "—" }
@@ -455,16 +410,16 @@ ModuleChip {
RowLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
Text { text: "ATH"; color: t.cryptoPopupDim ?? t.textDim ?? "#997a8996"; font.pixelSize: 12; Layout.fillWidth: true } Text { text: "ATH"; color: t.cryptoPopupDim; font.pixelSize: 12; Layout.fillWidth: true }
Text { Text {
text: root.ath > 0 ? root.fullPrice(root.ath) : "—" text: root.ath > 0 ? root.fullPrice(root.ath) : "—"
color: t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2" color: t.cryptoPopupText
font.pixelSize: 12; font.bold: true; font.family: "monospace" font.pixelSize: 12; font.bold: true; font.family: "monospace"
} }
Text { Text {
visible: root.ath > 0 visible: root.ath > 0
text: "(" + root.athChange.toFixed(2) + "%)" text: "(" + root.athChange.toFixed(2) + "%)"
color: t.cryptoDown ?? "#fff38ba8" color: t.cryptoDown
font.pixelSize: 11; font.bold: true; font.family: "monospace" font.pixelSize: 11; font.bold: true; font.family: "monospace"
} }
} }
@@ -477,14 +432,14 @@ ModuleChip {
Layout.fillWidth: true Layout.fillWidth: true
Text { Text {
text: "24h Range" text: "24h Range"
color: t.cryptoPopupDim ?? t.textDim ?? "#997a8996" color: t.cryptoPopupDim
font.pixelSize: 12 font.pixelSize: 12
Layout.fillWidth: true Layout.fillWidth: true
} }
Text { Text {
visible: root.high24h > root.low24h && root.price >= 0 visible: root.high24h > root.low24h && root.price >= 0
text: ((root.price - root.low24h) / (root.high24h - root.low24h) * 100).toFixed(1) + "% of range" text: ((root.price - root.low24h) / (root.high24h - root.low24h) * 100).toFixed(1) + "% of range"
color: t.cryptoPopupDim ?? t.textDim ?? "#997a8996" color: t.cryptoPopupDim
font.pixelSize: 11 font.pixelSize: 11
} }
} }
@@ -496,15 +451,15 @@ ModuleChip {
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
radius: 3 radius: 3
color: t.cryptoRangeTrack ?? "#1affffff" color: t.cryptoRangeTrack
} }
Rectangle { Rectangle {
height: parent.height height: parent.height
radius: 3 radius: 3
color: root.change24h >= 0 color: root.change24h >= 0
? (t.cryptoUp ?? "#ffb4c9a1") ? (t.cryptoUp)
: (t.cryptoDown ?? "#fff38ba8") : (t.cryptoDown)
width: (root.high24h > root.low24h && root.price >= 0) width: (root.high24h > root.low24h && root.price >= 0)
? Math.max(radius * 2, ? Math.max(radius * 2,
Math.min(parent.width, Math.min(parent.width,
@@ -519,28 +474,28 @@ ModuleChip {
Layout.fillWidth: true Layout.fillWidth: true
Text { Text {
text: root.low24h > 0 ? root.fullPrice(root.low24h) : "—" text: root.low24h > 0 ? root.fullPrice(root.low24h) : "—"
color: t.cryptoDown ?? "#fff38ba8" color: t.cryptoDown
font.pixelSize: 10 font.pixelSize: 10
font.family: "monospace" font.family: "monospace"
} }
Item { Layout.fillWidth: true } Item { Layout.fillWidth: true }
Text { Text {
text: root.high24h > 0 ? root.fullPrice(root.high24h) : "—" text: root.high24h > 0 ? root.fullPrice(root.high24h) : "—"
color: t.cryptoUp ?? "#ffb4c9a1" color: t.cryptoUp
font.pixelSize: 10 font.pixelSize: 10
font.family: "monospace" font.family: "monospace"
} }
} }
} }
Rectangle { Layout.fillWidth: true; height: 1; color: t.calSeparator ?? "#334a5d6e" } Rectangle { Layout.fillWidth: true; height: 1; color: t.calSeparator }
RowLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
Text { Text {
text: root.hasError ? "⚠ fetch error" : "Updated " + (root.lastUpdated || "—") text: root.hasError ? "⚠ fetch error" : "Updated " + (root.lastUpdated || "—")
color: t.cryptoPopupDim ?? t.textDim ?? "#997a8996" color: t.cryptoPopupDim
font.pixelSize: 11 font.pixelSize: 11
Layout.fillWidth: true Layout.fillWidth: true
} }
@@ -549,7 +504,7 @@ ModuleChip {
id: refreshBtn id: refreshBtn
width: 26; height: 26; radius: 8 width: 26; height: 26; radius: 8
color: rMouse.containsMouse color: rMouse.containsMouse
? (rMouse.pressed ? (t.accent ?? "#ffc19375") : (t.cryptoFieldBgFocus ?? "#22ffffff")) ? (rMouse.pressed ? (t.accent) : (t.cryptoFieldBgFocus))
: "transparent" : "transparent"
Behavior on color { ColorAnimation { duration: 120 } } Behavior on color { ColorAnimation { duration: 120 } }
@@ -557,7 +512,7 @@ ModuleChip {
id: refreshIcon id: refreshIcon
anchors.centerIn: parent anchors.centerIn: parent
text: "󰑐" text: "󰑐"
color: t.cryptoIcon ?? t.accent ?? "#ffc19375" color: t.cryptoIcon
font.pixelSize: 14 font.pixelSize: 14
RotationAnimator { RotationAnimator {
@@ -585,8 +540,8 @@ ModuleChip {
ColumnLayout { ColumnLayout {
id: settingsContent id: settingsContent
width: tabContainer.width width: tabContainer.width
x: card.activeTab === 1 ? 0 : tabContainer.width x: cryptoPopup.activeTab === 1 ? 0 : tabContainer.width
opacity: card.activeTab === 1 ? 1.0 : 0.0 opacity: cryptoPopup.activeTab === 1 ? 1.0 : 0.0
Behavior on x { NumberAnimation { duration: 260; easing.type: Easing.OutCubic } } Behavior on x { NumberAnimation { duration: 260; easing.type: Easing.OutCubic } }
Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } } Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } }
spacing: 12 spacing: 12
@@ -600,7 +555,7 @@ ModuleChip {
Text { Text {
text: label text: label
color: t.cryptoPopupDim ?? t.textDim ?? "#997a8996" color: t.cryptoPopupDim
font.pixelSize: 11 font.pixelSize: 11
font.capitalization: Font.AllUppercase font.capitalization: Font.AllUppercase
} }
@@ -609,8 +564,8 @@ ModuleChip {
Layout.fillWidth: true Layout.fillWidth: true
height: 30 height: 30
radius: 8 radius: 8
color: fi.activeFocus ? (t.cryptoFieldBgFocus ?? "#22ffffff") : (t.cryptoFieldBg ?? "#15ffffff") color: fi.activeFocus ? (t.cryptoFieldBgFocus) : (t.cryptoFieldBg)
border.color: fi.activeFocus ? (t.accent ?? "#ffc19375") : "transparent" border.color: fi.activeFocus ? (t.accent) : "transparent"
border.width: 1 border.width: 1
Behavior on color { ColorAnimation { duration: 120 } } Behavior on color { ColorAnimation { duration: 120 } }
Behavior on border.color { ColorAnimation { duration: 120 } } Behavior on border.color { ColorAnimation { duration: 120 } }
@@ -618,16 +573,16 @@ ModuleChip {
TextInput { TextInput {
id: fi id: fi
anchors { fill: parent; leftMargin: 10; rightMargin: 10; topMargin: 7; bottomMargin: 7 } anchors { fill: parent; leftMargin: 10; rightMargin: 10; topMargin: 7; bottomMargin: 7 }
color: t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2" color: t.cryptoPopupText
font.pixelSize: 12 font.pixelSize: 12
font.family: "monospace" font.family: "monospace"
selectionColor: t.accent ?? "#ffc19375" selectionColor: t.accent
clip: true clip: true
Text { Text {
anchors.fill: parent anchors.fill: parent
text: parent.parent.parent.placeholder text: parent.parent.parent.placeholder
color: t.cryptoPlaceholder ?? "#557a8996" color: t.cryptoPlaceholder
font.pixelSize: 12 font.pixelSize: 12
font.family: "monospace" font.family: "monospace"
visible: fi.text === "" && !fi.activeFocus visible: fi.text === "" && !fi.activeFocus
@@ -645,26 +600,26 @@ ModuleChip {
signal stepped(int v) signal stepped(int v)
Layout.fillWidth: true Layout.fillWidth: true
Text { text: label; color: t.cryptoPopupDim ?? t.textDim ?? "#997a8996"; font.pixelSize: 12; Layout.fillWidth: true } Text { text: label; color: t.cryptoPopupDim; font.pixelSize: 12; Layout.fillWidth: true }
Rectangle { Rectangle {
width: 24; height: 24; radius: 7 width: 24; height: 24; radius: 7
color: mm.containsMouse ? (mm.pressed ? (t.accent ?? "#ffc19375") : (t.cryptoFieldBgFocus ?? "#22ffffff")) : (t.cryptoFieldBg ?? "#15ffffff") color: mm.containsMouse ? (mm.pressed ? (t.accent) : (t.cryptoFieldBgFocus)) : (t.cryptoFieldBg)
Behavior on color { ColorAnimation { duration: 100 } } Behavior on color { ColorAnimation { duration: 100 } }
Text { anchors.centerIn: parent; text: ""; font.pixelSize: 14; font.bold: true; color: t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2" } Text { anchors.centerIn: parent; text: ""; font.pixelSize: 14; font.bold: true; color: t.cryptoPopupText }
MouseArea { id: mm; anchors.fill: parent; hoverEnabled: true; cursorShape: Qt.PointingHandCursor; onClicked: if (value > minVal) stepped(value - 1) } MouseArea { id: mm; anchors.fill: parent; hoverEnabled: true; cursorShape: Qt.PointingHandCursor; onClicked: if (value > minVal) stepped(value - 1) }
} }
Rectangle { Rectangle {
width: 54; height: 24; radius: 6; color: t.cryptoFieldBg ?? "#15ffffff" width: 54; height: 24; radius: 6; color: t.cryptoFieldBg
Text { anchors.centerIn: parent; text: value; color: t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2"; font.pixelSize: 12; font.bold: true; font.family: "monospace" } Text { anchors.centerIn: parent; text: value; color: t.cryptoPopupText; font.pixelSize: 12; font.bold: true; font.family: "monospace" }
} }
Rectangle { Rectangle {
width: 24; height: 24; radius: 7 width: 24; height: 24; radius: 7
color: pm.containsMouse ? (pm.pressed ? (t.accent ?? "#ffc19375") : (t.cryptoFieldBgFocus ?? "#22ffffff")) : (t.cryptoFieldBg ?? "#15ffffff") color: pm.containsMouse ? (pm.pressed ? (t.accent) : (t.cryptoFieldBgFocus)) : (t.cryptoFieldBg)
Behavior on color { ColorAnimation { duration: 100 } } Behavior on color { ColorAnimation { duration: 100 } }
Text { anchors.centerIn: parent; text: "+"; font.pixelSize: 14; font.bold: true; color: t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2" } Text { anchors.centerIn: parent; text: "+"; font.pixelSize: 14; font.bold: true; color: t.cryptoPopupText }
MouseArea { id: pm; anchors.fill: parent; hoverEnabled: true; cursorShape: Qt.PointingHandCursor; onClicked: if (value < maxVal) stepped(value + 1) } MouseArea { id: pm; anchors.fill: parent; hoverEnabled: true; cursorShape: Qt.PointingHandCursor; onClicked: if (value < maxVal) stepped(value + 1) }
} }
} }
@@ -675,11 +630,11 @@ ModuleChip {
signal toggled(bool v) signal toggled(bool v)
Layout.fillWidth: true Layout.fillWidth: true
Text { text: label; color: t.cryptoPopupDim ?? t.textDim ?? "#997a8996"; font.pixelSize: 12; Layout.fillWidth: true } Text { text: label; color: t.cryptoPopupDim; font.pixelSize: 12; Layout.fillWidth: true }
Rectangle { Rectangle {
width: 36; height: 20; radius: 10 width: 36; height: 20; radius: 10
color: checked ? (t.accent ?? "#ffc19375") : (t.cryptoFieldBgFocus ?? "#22ffffff") color: checked ? (t.accent) : (t.cryptoFieldBgFocus)
Behavior on color { ColorAnimation { duration: 150 } } Behavior on color { ColorAnimation { duration: 150 } }
Rectangle { Rectangle {
@@ -699,7 +654,7 @@ ModuleChip {
Text { Text {
text: "Pairs to cycle" text: "Pairs to cycle"
color: t.cryptoPopupDim ?? t.textDim ?? "#997a8996" color: t.cryptoPopupDim
font.pixelSize: 11 font.pixelSize: 11
font.capitalization: Font.AllUppercase font.capitalization: Font.AllUppercase
} }
@@ -718,7 +673,7 @@ ModuleChip {
width: 12; z: 2 width: 12; z: 2
gradient: Gradient { gradient: Gradient {
orientation: Gradient.Horizontal orientation: Gradient.Horizontal
GradientStop { position: 0.0; color: t.cryptoPopupBg ?? "#cc0c0e12" } GradientStop { position: 0.0; color: t.cryptoPopupBg }
GradientStop { position: 1.0; color: "transparent" } GradientStop { position: 1.0; color: "transparent" }
} }
visible: chipScroll.contentX > 0 visible: chipScroll.contentX > 0
@@ -729,7 +684,7 @@ ModuleChip {
gradient: Gradient { gradient: Gradient {
orientation: Gradient.Horizontal orientation: Gradient.Horizontal
GradientStop { position: 0.0; color: "transparent" } GradientStop { position: 0.0; color: "transparent" }
GradientStop { position: 1.0; color: t.cryptoPopupBg ?? "#cc0c0e12" } GradientStop { position: 1.0; color: t.cryptoPopupBg }
} }
visible: chipScroll.contentX < chipScroll.contentWidth - chipScroll.width - 1 visible: chipScroll.contentX < chipScroll.contentWidth - chipScroll.width - 1
} }
@@ -761,7 +716,7 @@ ModuleChip {
id: dotSpace id: dotSpace
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
x: 0; width: 5; height: 5; radius: 3 x: 0; width: 5; height: 5; radius: 3
color: isActive ? (t.accent ?? "#ffc19375") : "#33ffffff" color: isActive ? (t.accent) : "#33ffffff"
Behavior on color { ColorAnimation { duration: 150 } } Behavior on color { ColorAnimation { duration: 150 } }
} }
@@ -771,8 +726,8 @@ ModuleChip {
x: dotSpace.width + 5 x: dotSpace.width + 5
text: (modelData.coin ?? "?") + "/" + (modelData.currency ?? "?").toUpperCase() text: (modelData.coin ?? "?") + "/" + (modelData.currency ?? "?").toUpperCase()
color: isActive color: isActive
? (t.accent ?? "#ffc19375") ? (t.accent)
: (t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2") : (t.cryptoPopupText)
font.pixelSize: 12 font.pixelSize: 12
font.bold: isActive font.bold: isActive
font.family: "monospace" font.family: "monospace"
@@ -784,12 +739,12 @@ ModuleChip {
anchors { right: parent.right anchors { right: parent.right
verticalCenter: parent.verticalCenter } verticalCenter: parent.verticalCenter }
width: 14; height: 14; radius: 4 width: 14; height: 14; radius: 4
color: rmChipMouse.containsMouse ? (t.cryptoChipRemoveBg ?? "#44f38ba8") : "transparent" color: rmChipMouse.containsMouse ? (t.cryptoChipRemoveBg) : "transparent"
Behavior on color { ColorAnimation { duration: 100 } } Behavior on color { ColorAnimation { duration: 100 } }
Text { Text {
anchors.centerIn: parent anchors.centerIn: parent
text: "×"; font.pixelSize: 11; font.bold: true text: "×"; font.pixelSize: 11; font.bold: true
color: t.cryptoDown ?? "#fff38ba8" color: t.cryptoDown
} }
MouseArea { MouseArea {
id: rmChipMouse id: rmChipMouse
@@ -818,13 +773,13 @@ ModuleChip {
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
radius: 2 radius: 2
color: t.cryptoFieldBg ?? "#15ffffff" color: t.cryptoFieldBg
} }
Rectangle { Rectangle {
height: parent.height height: parent.height
radius: 2 radius: 2
color: t.accent ?? "#ffc19375" color: t.accent
opacity: 0.7 opacity: 0.7
readonly property real ratio: chipScroll.width / Math.max(1, chipScroll.contentWidth) readonly property real ratio: chipScroll.width / Math.max(1, chipScroll.contentWidth)
@@ -846,7 +801,7 @@ ModuleChip {
Text { Text {
text: "Coin ID" text: "Coin ID"
color: t.cryptoPopupDim ?? t.textDim ?? "#997a8996" color: t.cryptoPopupDim
font.pixelSize: 11 font.pixelSize: 11
font.capitalization: Font.AllUppercase font.capitalization: Font.AllUppercase
} }
@@ -854,8 +809,8 @@ ModuleChip {
Rectangle { Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
height: 30; radius: 8 height: 30; radius: 8
color: addCoinInput.activeFocus ? (t.cryptoFieldBgFocus ?? "#22ffffff") : (t.cryptoFieldBg ?? "#15ffffff") color: addCoinInput.activeFocus ? (t.cryptoFieldBgFocus) : (t.cryptoFieldBg)
border.color: addCoinInput.activeFocus ? (t.accent ?? "#ffc19375") : "transparent" border.color: addCoinInput.activeFocus ? (t.accent) : "transparent"
border.width: 1 border.width: 1
Behavior on color { ColorAnimation { duration: 120 } } Behavior on color { ColorAnimation { duration: 120 } }
Behavior on border.color { ColorAnimation { duration: 120 } } Behavior on border.color { ColorAnimation { duration: 120 } }
@@ -863,15 +818,15 @@ ModuleChip {
TextInput { TextInput {
id: addCoinInput id: addCoinInput
anchors { fill: parent; leftMargin: 10; rightMargin: 10; topMargin: 7; bottomMargin: 7 } anchors { fill: parent; leftMargin: 10; rightMargin: 10; topMargin: 7; bottomMargin: 7 }
color: t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2" color: t.cryptoPopupText
font.pixelSize: 12; font.family: "monospace" font.pixelSize: 12; font.family: "monospace"
selectionColor: t.accent ?? "#ffc19375" selectionColor: t.accent
clip: true clip: true
Text { Text {
anchors.fill: parent anchors.fill: parent
text: "e.g. bitcoin, monero, solana" text: "e.g. bitcoin, monero, solana"
color: t.cryptoPlaceholder ?? "#557a8996" color: t.cryptoPlaceholder
font.pixelSize: 12; font.family: "monospace" font.pixelSize: 12; font.family: "monospace"
visible: parent.text === "" && !parent.activeFocus visible: parent.text === "" && !parent.activeFocus
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
@@ -888,7 +843,7 @@ ModuleChip {
Text { Text {
text: "Currency" text: "Currency"
color: t.cryptoPopupDim ?? t.textDim ?? "#997a8996" color: t.cryptoPopupDim
font.pixelSize: 11 font.pixelSize: 11
font.capitalization: Font.AllUppercase font.capitalization: Font.AllUppercase
} }
@@ -900,8 +855,8 @@ ModuleChip {
Rectangle { Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
height: 30; radius: 8 height: 30; radius: 8
color: addCurrencyInput.activeFocus ? (t.cryptoFieldBgFocus ?? "#22ffffff") : (t.cryptoFieldBg ?? "#15ffffff") color: addCurrencyInput.activeFocus ? (t.cryptoFieldBgFocus) : (t.cryptoFieldBg)
border.color: addCurrencyInput.activeFocus ? (t.accent ?? "#ffc19375") : "transparent" border.color: addCurrencyInput.activeFocus ? (t.accent) : "transparent"
border.width: 1 border.width: 1
Behavior on color { ColorAnimation { duration: 120 } } Behavior on color { ColorAnimation { duration: 120 } }
Behavior on border.color { ColorAnimation { duration: 120 } } Behavior on border.color { ColorAnimation { duration: 120 } }
@@ -909,15 +864,15 @@ ModuleChip {
TextInput { TextInput {
id: addCurrencyInput id: addCurrencyInput
anchors { fill: parent; leftMargin: 10; rightMargin: 10; topMargin: 7; bottomMargin: 7 } anchors { fill: parent; leftMargin: 10; rightMargin: 10; topMargin: 7; bottomMargin: 7 }
color: t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2" color: t.cryptoPopupText
font.pixelSize: 12; font.family: "monospace" font.pixelSize: 12; font.family: "monospace"
selectionColor: t.accent ?? "#ffc19375" selectionColor: t.accent
clip: true clip: true
Text { Text {
anchors.fill: parent anchors.fill: parent
text: "e.g. usd, eur, brl" text: "e.g. usd, eur, brl"
color: t.cryptoPlaceholder ?? "#557a8996" color: t.cryptoPlaceholder
font.pixelSize: 12; font.family: "monospace" font.pixelSize: 12; font.family: "monospace"
visible: parent.text === "" && !parent.activeFocus visible: parent.text === "" && !parent.activeFocus
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
@@ -938,13 +893,13 @@ ModuleChip {
Rectangle { Rectangle {
width: 30; height: 30; radius: 8 width: 30; height: 30; radius: 8
color: addPairMouse.containsMouse color: addPairMouse.containsMouse
? (addPairMouse.pressed ? (t.accent ?? "#ffc19375") : (t.cryptoFieldBgFocus ?? "#22ffffff")) ? (addPairMouse.pressed ? (t.accent) : (t.cryptoFieldBgFocus))
: (t.cryptoFieldBg ?? "#15ffffff") : (t.cryptoFieldBg)
Behavior on color { ColorAnimation { duration: 100 } } Behavior on color { ColorAnimation { duration: 100 } }
Text { Text {
anchors.centerIn: parent; text: "+" anchors.centerIn: parent; text: "+"
font.pixelSize: 14; font.bold: true font.pixelSize: 14; font.bold: true
color: t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2" color: t.cryptoPopupText
} }
MouseArea { MouseArea {
id: addPairMouse id: addPairMouse
@@ -978,7 +933,7 @@ ModuleChip {
Text { Text {
text: "Refresh (seconds)" text: "Refresh (seconds)"
color: t.cryptoPopupDim ?? t.textDim ?? "#997a8996" color: t.cryptoPopupDim
font.pixelSize: 12 font.pixelSize: 12
Layout.fillWidth: true Layout.fillWidth: true
} }
@@ -986,10 +941,10 @@ ModuleChip {
Rectangle { Rectangle {
width: 24; height: 24; radius: 7 width: 24; height: 24; radius: 7
color: decMouse.containsMouse color: decMouse.containsMouse
? (decMouse.pressed ? (t.accent ?? "#ffc19375") : (t.cryptoFieldBgFocus ?? "#22ffffff")) ? (decMouse.pressed ? (t.accent) : (t.cryptoFieldBgFocus))
: (t.cryptoFieldBg ?? "#15ffffff") : (t.cryptoFieldBg)
Behavior on color { ColorAnimation { duration: 100 } } Behavior on color { ColorAnimation { duration: 100 } }
Text { anchors.centerIn: parent; text: ""; font.pixelSize: 14; font.bold: true; color: t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2" } Text { anchors.centerIn: parent; text: ""; font.pixelSize: 14; font.bold: true; color: t.cryptoPopupText }
MouseArea { MouseArea {
id: decMouse id: decMouse
anchors.fill: parent; hoverEnabled: true; cursorShape: Qt.PointingHandCursor anchors.fill: parent; hoverEnabled: true; cursorShape: Qt.PointingHandCursor
@@ -999,8 +954,8 @@ ModuleChip {
Rectangle { Rectangle {
width: 54; height: 24; radius: 6 width: 54; height: 24; radius: 6
color: refreshInput.activeFocus ? (t.cryptoFieldBgFocus ?? "#22ffffff") : (t.cryptoFieldBg ?? "#15ffffff") color: refreshInput.activeFocus ? (t.cryptoFieldBgFocus) : (t.cryptoFieldBg)
border.color: refreshInput.activeFocus ? (t.accent ?? "#ffc19375") : "transparent" border.color: refreshInput.activeFocus ? (t.accent) : "transparent"
border.width: 1 border.width: 1
Behavior on color { ColorAnimation { duration: 120 } } Behavior on color { ColorAnimation { duration: 120 } }
Behavior on border.color { ColorAnimation { duration: 120 } } Behavior on border.color { ColorAnimation { duration: 120 } }
@@ -1009,11 +964,11 @@ ModuleChip {
id: refreshInput id: refreshInput
anchors { fill: parent; leftMargin: 6; rightMargin: 6; topMargin: 4; bottomMargin: 4 } anchors { fill: parent; leftMargin: 6; rightMargin: 6; topMargin: 4; bottomMargin: 4 }
text: root.localRefreshSec.toString() text: root.localRefreshSec.toString()
color: t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2" color: t.cryptoPopupText
font.pixelSize: 12 font.pixelSize: 12
font.bold: true font.bold: true
font.family: "monospace" font.family: "monospace"
selectionColor: t.accent ?? "#ffc19375" selectionColor: t.accent
horizontalAlignment: TextInput.AlignHCenter horizontalAlignment: TextInput.AlignHCenter
inputMethodHints: Qt.ImhDigitsOnly inputMethodHints: Qt.ImhDigitsOnly
validator: IntValidator { bottom: 10; top: 3600 } validator: IntValidator { bottom: 10; top: 3600 }
@@ -1048,10 +1003,10 @@ ModuleChip {
Rectangle { Rectangle {
width: 24; height: 24; radius: 7 width: 24; height: 24; radius: 7
color: incMouse.containsMouse color: incMouse.containsMouse
? (incMouse.pressed ? (t.accent ?? "#ffc19375") : (t.cryptoFieldBgFocus ?? "#22ffffff")) ? (incMouse.pressed ? (t.accent) : (t.cryptoFieldBgFocus))
: (t.cryptoFieldBg ?? "#15ffffff") : (t.cryptoFieldBg)
Behavior on color { ColorAnimation { duration: 100 } } Behavior on color { ColorAnimation { duration: 100 } }
Text { anchors.centerIn: parent; text: "+"; font.pixelSize: 14; font.bold: true; color: t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2" } Text { anchors.centerIn: parent; text: "+"; font.pixelSize: 14; font.bold: true; color: t.cryptoPopupText }
MouseArea { MouseArea {
id: incMouse id: incMouse
anchors.fill: parent; hoverEnabled: true; cursorShape: Qt.PointingHandCursor anchors.fill: parent; hoverEnabled: true; cursorShape: Qt.PointingHandCursor
@@ -1066,7 +1021,7 @@ ModuleChip {
onToggled: (v) => root.localShowIcon = v onToggled: (v) => root.localShowIcon = v
} }
Rectangle { Layout.fillWidth: true; height: 1; color: t.calSeparator ?? "#334a5d6e" } Rectangle { Layout.fillWidth: true; height: 1; color: t.calSeparator }
Rectangle { Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
@@ -1074,15 +1029,15 @@ ModuleChip {
radius: 9 radius: 9
color: applyMouse.containsMouse color: applyMouse.containsMouse
? (applyMouse.pressed ? (applyMouse.pressed
? Qt.darker(t.accent ?? "#ffc19375", 1.15) ? Qt.darker(t.accent, 1.15)
: (t.accent ?? "#ffc19375")) : (t.accent))
: (t.cryptoApplyIdleBg ?? "#33c19375") : (t.cryptoApplyIdleBg)
Behavior on color { ColorAnimation { duration: 130 } } Behavior on color { ColorAnimation { duration: 130 } }
Text { Text {
anchors.centerIn: parent anchors.centerIn: parent
text: "Apply & Refresh" text: "Apply & Refresh"
color: applyMouse.containsMouse ? (t.cryptoTextOnAccent ?? "#ff0c0e12") : (t.cryptoPopupText ?? t.textMain ?? "#ffe8eef2") color: applyMouse.containsMouse ? (t.cryptoTextOnAccent) : (t.cryptoPopupText)
font.pixelSize: 12 font.pixelSize: 12
font.bold: true font.bold: true
Behavior on color { ColorAnimation { duration: 130 } } Behavior on color { ColorAnimation { duration: 130 } }
@@ -1097,7 +1052,7 @@ ModuleChip {
root.coinSymbol = root.localCoinId.toUpperCase().slice(0, 4) root.coinSymbol = root.localCoinId.toUpperCase().slice(0, 4)
root.persistSettings() root.persistSettings()
root.fetchPrice() root.fetchPrice()
card.activeTab = 0 cryptoPopup.activeTab = 0
} }
} }
} }
@@ -1105,7 +1060,7 @@ ModuleChip {
Text { Text {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
text: "Reset to defaults" text: "Reset to defaults"
color: t.cryptoPopupDim ?? t.textDim ?? "#997a8996" color: t.cryptoPopupDim
font.pixelSize: 11 font.pixelSize: 11
font.underline: true font.underline: true
@@ -1116,12 +1071,12 @@ ModuleChip {
root.localVsCur = "usd" root.localVsCur = "usd"
root.displayCurrency = root.localPairs.length > 0 ? root.localPairs[0].currency : "usd" root.displayCurrency = root.localPairs.length > 0 ? root.localPairs[0].currency : "usd"
root.activeCoinIndex = 0 root.activeCoinIndex = 0
root.localDecimals = cfg.cryptoDecimals ?? 1 root.localDecimals = cfg.cryptoDecimals
root.localRefreshSec = cfg.cryptoRefreshSec ?? 60 root.localRefreshSec = cfg.cryptoRefreshSec
root.localShowIcon = cfg.cryptoShowIcon ?? true root.localShowIcon = cfg.cryptoShowIcon
root.persistSettings() root.persistSettings()
root.price = -1 root.price = -1
card.activeTab = 0 cryptoPopup.activeTab = 0
} }
} }
} }
@@ -1131,5 +1086,4 @@ ModuleChip {
} }
} }
} }
}
} }

View File

@@ -9,7 +9,7 @@ ModuleChip {
property var launcher: null property var launcher: null
readonly property bool launcherActive: !!launcher && launcher.isOpen readonly property bool launcherActive: !!launcher && launcher.isOpen
readonly property bool isHovered: hoverHandler.hovered readonly property bool isHovered: hovered
property real spinTarget: 0 property real spinTarget: 0
@@ -26,16 +26,12 @@ ModuleChip {
width: Cfg.Config.logoIconSize * 2 + 4 width: Cfg.Config.logoIconSize * 2 + 4
height: parent.height height: parent.height
color: isHovered ? t.logoBgHover : t.logoBg chipColor: t.logoBg
radius: panelRadius chipColorHovered: t.logoBgHover
chipBorderColor: t.logoBorder
border.color: launcherActive ? t.logoBorderActive : t.logoBorder chipBorderColorHovered: t.logoBorderActive
border.width: borderWidth popupOpen: launcherActive
chipAnimDuration: 250
Behavior on color { ColorAnimation { duration: 250 } }
Behavior on border.color { ColorAnimation { duration: 250 } }
HoverHandler { id: hoverHandler }
TapHandler { TapHandler {
onTapped: { onTapped: {

View File

@@ -107,16 +107,14 @@ ModuleChip {
implicitWidth: chipRow.implicitWidth + 24 implicitWidth: chipRow.implicitWidth + 24
height: Cfg.Config.moduleHeight height: Cfg.Config.moduleHeight
radius: panelRadius 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 } } chipColor: t.netBg
Behavior on color { ColorAnimation { duration: 150 } } chipColorHovered: t.netBgHover
Behavior on border.color { ColorAnimation { duration: 150 } } chipBorderColor: t.netBorder
chipBorderColorHovered: t.netBorderHover
HoverHandler { id: chipHover; cursorShape: Qt.PointingHandCursor } chipHoverEnabled: true
TapHandler { onTapped: netPopup.toggle() } chipTapAction: () => netPopup.toggle()
chipAnimImplicitWidth: true
RowLayout { RowLayout {
id: chipRow id: chipRow
@@ -150,11 +148,15 @@ ModuleChip {
} }
} }
PopupPanel { StandardPopup {
id: netPopup id: netPopup
ns: "network" ns: "network"
chipRoot: root chipRoot: root
cardWidth: 310 cardWidth: 310
cardColor: t.netPopupBg
cardBorderColor: t.netPopupBorder
cardHeight: cardBody.implicitHeight + 28
layerEnabled: true
function open() { function open() {
root.cancelConnect() root.cancelConnect()
@@ -163,18 +165,6 @@ ModuleChip {
root.wifiDevice.scannerEnabled = true root.wifiDevice.scannerEnabled = true
} }
PopupCard {
popupOpen: netPopup.popupOpen
isTop: root.isTop
animEnabled: netPopup.visible
height: cardBody.implicitHeight + 24
color: root.t.netPopupBg
radius: Cfg.Config.popupRadius
border.color: root.t.netPopupBorder
border.width: Cfg.Config.popupBorderWidth
layer.enabled: true
ColumnLayout { ColumnLayout {
id: cardBody id: cardBody
anchors { anchors {
@@ -656,5 +646,4 @@ ModuleChip {
Item { height: 4 } Item { height: 4 }
} }
} }
}
} }

View File

@@ -16,17 +16,17 @@ ModuleChip {
property bool historyOpen: historyPopup.popupOpen property bool historyOpen: historyPopup.popupOpen
width: 35 width: 35
height: 40 height: Cfg.Config.moduleHeight
radius: panelRadius radius: panelRadius
color: hoverHandler.hovered ? t.notifBgHover : t.notifBg chipColor: t.notifBg
chipColorHovered: t.notifBgHover
popupOpen: historyOpen
border.color: root.doNotDisturb border.color: root.doNotDisturb
? t.notifDnd ? t.notifDnd
: ((hoverHandler.hovered || historyOpen) ? t.notifBorderActive : t.notifBorder) : ((hovered || historyOpen) ? t.notifBorderActive : t.notifBorder)
border.width: borderWidth
Behavior on color { ColorAnimation { duration: 150 } }
Behavior on border.color { ColorAnimation { duration: 150 } }
Behavior on width { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } } Behavior on width { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } }
component NotificationCard: Item { component NotificationCard: Item {
@@ -41,8 +41,8 @@ ModuleChip {
property int timeout: 5000 property int timeout: 5000
property var t property var t
property var urgencyColor: function(u) { return "#ffffff" } property var urgencyColor
property var urgencyIcon: function(u) { return "" } property var urgencyIcon
signal dismissed(string toastId) signal dismissed(string toastId)
@@ -250,8 +250,8 @@ ModuleChip {
property string timeStr: "" property string timeStr: ""
property var t property var t
property var urgencyColor: function(u) { return "#ffffff" } property var urgencyColor
property var urgencyIcon: function(u) { return "" } property var urgencyIcon
signal dismissed(int itemIndex) signal dismissed(int itemIndex)
@@ -334,7 +334,7 @@ ModuleChip {
} }
} }
HoverHandler { id: hoverHandler; cursorShape: Qt.PointingHandCursor; onHoveredChanged: if (hovered && Cfg.Config.notificationShakeEnabled) bellRingAnim.start() } HoverHandler { cursorShape: Qt.PointingHandCursor; onHoveredChanged: if (hovered && Cfg.Config.notificationShakeEnabled) bellRingAnim.start() }
TapHandler { TapHandler {
onTapped: { onTapped: {
if (historyPopup.popupOpen) { if (historyPopup.popupOpen) {
@@ -458,7 +458,7 @@ ModuleChip {
var finalTimeout = (notif.expireTimeout > 0) ? notif.expireTimeout : cfgTimeout var finalTimeout = (notif.expireTimeout > 0) ? notif.expireTimeout : cfgTimeout
if (!root.doNotDisturb) { if (!root.doNotDisturb) {
var toastEntry = { toastId, nid: notif.id, appName, summary, body, urgency, timeStr, timeout: finalTimeout } var toastEntry = { toastId, appName, summary, body, urgency, timeStr, timeout: finalTimeout }
if (toastLayer.atBottom) activeToasts.append(toastEntry) if (toastLayer.atBottom) activeToasts.append(toastEntry)
else activeToasts.insert(0, toastEntry) else activeToasts.insert(0, toastEntry)
if (!root.historyOpen) root.unreadCount++ if (!root.historyOpen) root.unreadCount++

View File

@@ -9,22 +9,14 @@ ModuleChip {
id: root id: root
width: statsLayout.implicitWidth + 20 width: statsLayout.implicitWidth + 20
height: Cfg.Config.moduleHeight chipColor: t.statsBg
color: t.statsBg chipBorderColor: t.statsBorder
radius: panelRadius
border.color: t.statsBorder
border.width: borderWidth
Behavior on width { Behavior on width {
NumberAnimation { duration: 250; easing.type: Easing.OutCubic } NumberAnimation { duration: 250; easing.type: Easing.OutCubic }
} }
property bool expanded: Cfg.Config.statsStartExpanded property bool expanded: Cfg.Config.statsStartExpanded
property bool hovered: false
HoverHandler {
onHoveredChanged: root.hovered = hovered
}
TapHandler { TapHandler {
onTapped: { onTapped: {

View File

@@ -8,25 +8,17 @@ import Quickshell.Widgets
import "../../config" as Cfg import "../../config" as Cfg
import "../../components" import "../../components"
Item { ModuleChip {
id: root id: root
property real panelRadius: Cfg.Config.radius / 2
property int borderWidth: Cfg.Config.panelBorderWidth
property var targetScreen: null
property string barSide: "right" property string barSide: "right"
property bool isTop: true
readonly property var t: Cfg.Config.theme
property bool expanded: false property bool expanded: false
property bool manuallyExpanded: false property bool manuallyExpanded: false
readonly property bool effectiveExpanded: expanded || trayMenuPopup.popupOpen readonly property bool effectiveExpanded: expanded || trayMenuPopup.popupOpen
implicitWidth: bgRect.width implicitWidth: mainRow.width
implicitHeight: bgRect.height implicitHeight: Cfg.Config.barHeight
readonly property color tmBg: t.trayMenuBg readonly property color tmBg: t.trayMenuBg
readonly property color tmBorder: t.trayMenuBorder readonly property color tmBorder: t.trayMenuBorder
@@ -36,23 +28,13 @@ Item {
readonly property color tmCheck: t.trayMenuCheck readonly property color tmCheck: t.trayMenuCheck
readonly property color tmSeparator: t.trayMenuSeparator readonly property color tmSeparator: t.trayMenuSeparator
Rectangle { chipColor: t.trayBg
id: bgRect chipColorHovered: t.trayBgHover
anchors.verticalCenter: parent.verticalCenter chipBorderColor: t.trayBorder
chipBorderColorHovered: t.trayBorderHover
height: parent.height popupOpen: trayMenuPopup.popupOpen
width: mainRow.width
radius: root.panelRadius
color: bgHover.hovered ? root.t.trayBgHover : root.t.trayBg
border.color: bgHover.hovered ? root.t.trayBorderHover : root.t.trayBorder
border.width: root.borderWidth
Behavior on color { ColorAnimation { duration: 150 } }
Behavior on border.color { ColorAnimation { duration: 150 } }
HoverHandler { HoverHandler {
id: bgHover
onHoveredChanged: { onHoveredChanged: {
if (!root.manuallyExpanded) { if (!root.manuallyExpanded) {
root.expanded = hovered; root.expanded = hovered;
@@ -150,7 +132,7 @@ Item {
} }
} }
} }
}
} }

View File

@@ -14,41 +14,24 @@ ModuleChip {
property int outputMaxVolume: Cfg.Config.volumeOutputMax property int outputMaxVolume: Cfg.Config.volumeOutputMax
property int inputMaxVolume: Cfg.Config.volumeInputMax property int inputMaxVolume: Cfg.Config.volumeInputMax
// Path to config.qml — using HOME to avoid Qt.resolvedUrl ambiguity ConfigWriter {
readonly property string configPath: id: configWriter
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 { Timer {
id: saveDebounce id: saveDebounce
interval: 800 interval: 800
repeat: false repeat: false
onTriggered: { onTriggered: {
const outMax = Math.round(root.outputMaxVolume).toString() configWriter.writeInts([
const inMax = Math.round(root.inputMaxVolume).toString() { key: "volumeOutputMax", value: root.outputMaxVolume },
const cfgPath = root.configPath { key: "volumeInputMax", value: root.inputMaxVolume }
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() } onOutputMaxVolumeChanged: saveDebounce.restart()
onInputMaxVolumeChanged: { if (root.inputMaxVolume >= 10) saveDebounce.restart() } onInputMaxVolumeChanged: saveDebounce.restart()
readonly property var sink: Pipewire.defaultAudioSink readonly property var sink: Pipewire.defaultAudioSink
readonly property var source: Pipewire.defaultAudioSource readonly property var source: Pipewire.defaultAudioSource
@@ -115,24 +98,22 @@ ModuleChip {
} }
width: chipLayout.implicitWidth + 15 width: chipLayout.implicitWidth + 15
height: Cfg.Config.moduleHeight
radius: panelRadius radius: panelRadius
color: hoverHandler.hovered ? t.volBgHover : t.volBg chipColor: t.volBg
border.color: (hoverHandler.hovered || volumePopup.popupOpen) ? t.volBorderHover : t.volBorder chipColorHovered: t.volBgHover
border.width: borderWidth chipBorderColor: t.volBorder
chipBorderColorHovered: t.volBorderHover
Behavior on color { ColorAnimation { duration: 150 } } chipHoverEnabled: true
Behavior on border.color { ColorAnimation { duration: 150 } } chipTapAction: () => volumePopup.toggle()
popupOpen: volumePopup.popupOpen
HoverHandler { HoverHandler {
id: hoverHandler
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onHoveredChanged: { onHoveredChanged: {
if (hovered && Cfg.Config.volumeShakeEnabled) shakeAnim.restart() if (hovered && Cfg.Config.volumeShakeEnabled) shakeAnim.restart()
} }
} }
TapHandler { onTapped: volumePopup.toggle() }
SequentialAnimation { SequentialAnimation {
id: shakeAnim id: shakeAnim
@@ -159,22 +140,14 @@ ModuleChip {
} }
} }
PopupPanel { StandardPopup {
id: volumePopup id: volumePopup
ns: "volume" ns: "volume"
chipRoot: root chipRoot: root
cardWidth: Cfg.Config.volumePopupWidth cardWidth: Cfg.Config.volumePopupWidth
cardColor: t.volPopupBg
PopupCard { cardBorderColor: t.volPopupBorder
popupOpen: volumePopup.popupOpen cardHeight: popupCol.implicitHeight + 34
isTop: root.isTop
animEnabled: volumePopup.visible
height: popupCol.implicitHeight + 32
radius: Cfg.Config.popupRadius
color: root.t.volPopupBg
border.color: root.t.volPopupBorder
border.width: Cfg.Config.popupBorderWidth
ColumnLayout { ColumnLayout {
id: popupCol id: popupCol
@@ -273,7 +246,6 @@ ModuleChip {
Item { height: 2 } Item { height: 2 }
} }
} }
}
component VolumeSection: ColumnLayout { component VolumeSection: ColumnLayout {
id: vs id: vs

View File

@@ -10,10 +10,8 @@ ModuleChip {
width: workspaceLayout.implicitWidth + 24 width: workspaceLayout.implicitWidth + 24
height: Cfg.Config.moduleHeight height: Cfg.Config.moduleHeight
color: t.wsPanelBg chipColor: t.wsPanelBg
radius: panelRadius chipBorderColor: t.wsPanelBorder
border.color: t.wsPanelBorder
border.width: borderWidth
RowLayout { RowLayout {
id: workspaceLayout id: workspaceLayout

View File

@@ -0,0 +1,68 @@
import QtQuick
import Quickshell
import Quickshell.Io
Item {
id: root
property string configPath:
Quickshell.env("HOME") + "/.config/quickshell/config/config.qml"
Process {
id: writerProc
running: false
onExited: (code) => {
if (code !== 0)
console.warn("[ConfigWriter] Patch failed (exit " + code + ")")
}
property string _name: "ConfigWriter"
}
function writeInt(key, value) {
const n = Math.round(value).toString()
const sh = "sed -i" +
" -e 's/\\(property int[[:space:]]\\+\\b" + key + "\\b:[ ]*\\)[0-9.]\\+/\\1" + n + "/'" +
" " + JSON.stringify(root.configPath)
writerProc.command = ["/bin/sh", "-c", sh]
writerProc.running = true
}
function writeBool(key, value) {
const v = value ? "true" : "false"
const sh = "sed -i" +
" -e 's/\\(property bool[[:space:]]\\+\\b" + key + "\\b:[ ]*\\)\\(true\\|false\\)/\\1" + v + "/'" +
" " + JSON.stringify(root.configPath)
writerProc.command = ["/bin/sh", "-c", sh]
writerProc.running = true
}
function writeInts(pairs) {
let args = ""
for (let i = 0; i < pairs.length; i++) {
const n = Math.round(pairs[i].value).toString()
args += " -e 's/\\(property int[[:space:]]\\+\\b" + pairs[i].key + "\\b:[ ]*\\)[0-9.]\\+/\\1" + n + "/'"
}
const sh = "sed -i" + args + " " + JSON.stringify(root.configPath)
writerProc.command = ["/bin/sh", "-c", sh]
writerProc.running = true
}
function writeArray(key, pairsQml) {
const sh =
"CFG=" + JSON.stringify(root.configPath) + "\n" +
"QS_PAIRS='" + pairsQml + "'\n" +
"awk -v pairs=\"$QS_PAIRS\" '" +
"/^[[:space:]]*property var[[:space:]]+" + key + ":[[:space:]]/{print \" property var " + key + ": \"pairs;sk=/\\]/?0:1;next}" +
"sk&&/\\]/{sk=0;next}" +
"sk{next}" +
"{print}'" +
" \"$CFG\" > \"${CFG}.tmp\" && mv \"${CFG}.tmp\" \"$CFG\"\n"
writerProc.command = ["/bin/sh", "-c", sh]
writerProc.running = true
}
function writeScript(sh) {
writerProc.command = ["/bin/sh", "-c", sh]
writerProc.running = true
}
}

View File

@@ -0,0 +1,34 @@
import QtQuick
Rectangle {
id: root
property color idleColor: "transparent"
property color hoverColor: "transparent"
property color pressColor: "transparent"
property color borderIdleColor: "transparent"
property color borderHoverColor: "transparent"
property real pressScale: 0.97
property int animDuration: 150
readonly property bool hovered: _hover.hovered
readonly property bool pressed: _tap.pressed
signal clicked()
color: pressed ? pressColor : (hovered ? hoverColor : idleColor)
border.color: hovered ? borderHoverColor : borderIdleColor
scale: pressed ? pressScale : 1.0
Behavior on color { ColorAnimation { duration: root.animDuration } }
Behavior on border.color { ColorAnimation { duration: root.animDuration } }
Behavior on scale { NumberAnimation { duration: 100; easing.type: Easing.OutQuad } }
HoverHandler { id: _hover }
TapHandler {
id: _tap
cursorShape: Qt.PointingHandCursor
onTapped: root.clicked()
}
}

View File

@@ -13,4 +13,44 @@ Rectangle {
readonly property var t: Cfg.Config.theme readonly property var t: Cfg.Config.theme
property bool moduleEnabled: true property bool moduleEnabled: true
property bool hovered: false
property bool popupOpen: false
property color chipColor: "transparent"
property color chipColorHovered: "transparent"
property color chipBorderColor: "transparent"
property color chipBorderColorHovered: "transparent"
property bool chipHoverEnabled: false
property var chipTapAction: null
property int chipAnimDuration: 150
property bool chipAnimWidth: false
property bool chipAnimImplicitWidth: false
radius: panelRadius
color: hovered ? chipColorHovered : chipColor
border.color: (hovered || popupOpen) ? chipBorderColorHovered : chipBorderColor
border.width: borderWidth
Behavior on color { ColorAnimation { duration: chip.chipAnimDuration } }
Behavior on border.color { ColorAnimation { duration: chip.chipAnimDuration } }
Behavior on width {
NumberAnimation { duration: 250; easing.type: Easing.OutCubic }
enabled: chipAnimWidth
}
Behavior on implicitWidth {
NumberAnimation { duration: 250; easing.type: Easing.OutCubic }
enabled: chipAnimImplicitWidth
}
HoverHandler {
id: hoverHandler
cursorShape: chipHoverEnabled ? Qt.PointingHandCursor : Qt.ArrowCursor
onHoveredChanged: chip.hovered = hovered
}
TapHandler {
onTapped: if (chip.chipTapAction) chip.chipTapAction()
}
} }

View File

@@ -7,6 +7,7 @@ Rectangle {
required property bool popupOpen required property bool popupOpen
required property bool isTop required property bool isTop
property bool animEnabled: true property bool animEnabled: true
property bool showShadow: false
width: parent.width width: parent.width
@@ -27,6 +28,18 @@ Rectangle {
opacity: popupOpen ? 1.0 : 0.0 opacity: popupOpen ? 1.0 : 0.0
Behavior on opacity { NumberAnimation { duration: 200 } } Behavior on opacity { NumberAnimation { duration: 200 } }
Rectangle {
visible: card.showShadow && card.popupOpen
anchors.fill: parent
anchors.margins: -3
radius: card.radius + 3
color: "transparent"
border.color: Qt.rgba(0, 0, 0, 0.35)
border.width: 6
opacity: card.showShadow && card.popupOpen ? 0.6 : 0
Behavior on opacity { NumberAnimation { duration: 200 } }
}
default property alias _popupCardContent: _content.data default property alias _popupCardContent: _content.data
Item { Item {

View File

@@ -0,0 +1,31 @@
import QtQuick
import "../config" as Cfg
PopupPanel {
id: popup
property color cardColor: "transparent"
property color cardBorderColor: "transparent"
property alias cardHeight: card.height
property alias showShadow: card.showShadow
property bool layerEnabled: false
property alias hasKeyboardFocus: popup.hasKeyboardFocus
property alias card: card
property int activeTab: 0
default property alias popupContent: card._popupCardContent
PopupCard {
id: card
popupOpen: popup.popupOpen
isTop: popup.chipRoot ? popup.chipRoot.isTop : true
animEnabled: popup.visible
color: popup.cardColor
radius: Cfg.Config.popupRadius
border.color: popup.cardBorderColor
border.width: Cfg.Config.popupBorderWidth
layer.enabled: popup.layerEnabled
Behavior on height { NumberAnimation { duration: 220; easing.type: Easing.OutCubic } }
}
}

View File

@@ -29,7 +29,7 @@ Item {
} }
Timer { Timer {
interval: 1800000 interval: 300000
running: root.tz !== "" running: root.tz !== ""
repeat: true repeat: true
onTriggered: tzProc.running = true onTriggered: tzProc.running = true

View File

@@ -19,7 +19,7 @@ QtObject {
// ──────────────────────────────────────────────────────────────────────── // ────────────────────────────────────────────────────────────────────────
// ── Position & Dimensions ───────────────────────────────────────────────── // ── Position & Dimensions ─────────────────────────────────────────────────
property string barPosition: "bottom" property string barPosition: "top"
property int barHeight: 35 property int barHeight: 35
property int margin: 20 property int margin: 20
property int radius: 26 property int radius: 26
@@ -49,7 +49,7 @@ QtObject {
// ── Timezone (IANA name, e.g. "America/New_York", "Europe/London") ────────── // ── Timezone (IANA name, e.g. "America/New_York", "Europe/London") ──────────
// Leave empty to use the system timezone. // Leave empty to use the system timezone.
property string timezone: none property string timezone: "America/New_York"
// Date format used in the clock popup and background date widget. // Date format used in the clock popup and background date widget.
// "MDY" → month/day/year (e.g. May 14, 2026) // "MDY" → month/day/year (e.g. May 14, 2026)
@@ -76,7 +76,7 @@ QtObject {
property int volumeSliderHeight: 2 property int volumeSliderHeight: 2
property int volumeHandleSize: 20 property int volumeHandleSize: 20
property int volumePopupWidth: 280 property int volumePopupWidth: 280
property int volumeOutputMax: 140 property int volumeOutputMax: 190
property int volumeInputMax: 200 property int volumeInputMax: 200
@@ -127,7 +127,7 @@ QtObject {
// //
// This list is the *default*; pairs can be added/removed live in the // This list is the *default*; pairs can be added/removed live in the
// Settings popup and those changes are persisted across restarts. // 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" }] property var cryptoPairs: [{ coin: "bitcoin", currency: "usd" }, { coin: "ethereum", currency: "eur" }, { coin: "monero", currency: "usd" }]
// ── Compact label decimals ──────────────────────────────────────────────── // ── Compact label decimals ────────────────────────────────────────────────
// 0 → "80k" // 0 → "80k"

View File

@@ -4,6 +4,7 @@ import Quickshell.Wayland
import Quickshell.Services.Polkit import Quickshell.Services.Polkit
import "../config" as Cfg import "../config" as Cfg
import "../components"
Item { Item {
@@ -115,37 +116,18 @@ Item {
color: t.polkitOverlayBg color: t.polkitOverlayBg
} }
component DialogButton: Rectangle { component DialogButton: HoverButton {
property string label property string label
property int labelWeight: Font.Normal property int labelWeight: Font.Normal
property color bgColor
property color bgHoverColor
property color borderColor
property color borderHoverColor
property color labelColor property color labelColor
signal tapped() signal tapped()
width: (parent.width - 12) / 2 width: (parent.width - 12) / 2
height: 44 height: 44
radius: 10 radius: 10
HoverHandler { id: hover }
color: hover.hovered ? bgHoverColor : bgColor
border.color: hover.hovered ? borderHoverColor : borderColor
border.width: 1 border.width: 1
Behavior on color { ColorAnimation { duration: 200 } } onClicked: tapped()
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 { Text {
anchors.centerIn: parent anchors.centerIn: parent
@@ -464,20 +446,22 @@ Item {
label: "Authenticate" label: "Authenticate"
labelWeight: Font.Medium labelWeight: Font.Medium
labelColor: t.polkitAuthText labelColor: t.polkitAuthText
bgColor: t.polkitAuthBg idleColor: t.polkitAuthBg
bgHoverColor: t.polkitAuthBgHover hoverColor: t.polkitAuthBgHover
borderColor: t.polkitAuthBg borderIdleColor: t.polkitAuthBg
borderHoverColor: t.polkitAuthBgHover borderHoverColor: t.polkitAuthBgHover
animDuration: 200
onTapped: root.submitAuth() onTapped: root.submitAuth()
} }
DialogButton { DialogButton {
label: "Cancel" label: "Cancel"
labelColor: t.polkitCancelText labelColor: t.polkitCancelText
bgColor: t.polkitCancelBg idleColor: t.polkitCancelBg
bgHoverColor: t.polkitCancelBgHover hoverColor: t.polkitCancelBgHover
borderColor: t.polkitCancelBorder borderIdleColor: t.polkitCancelBorder
borderHoverColor: t.polkitCancelBgHover borderHoverColor: t.polkitCancelBgHover
animDuration: 200
onTapped: overlay.cancelAndClose() onTapped: overlay.cancelAndClose()
} }
} }

View File

@@ -21,7 +21,7 @@ ShellRoot {
readonly property bool isTop: Cfg.Config.barPosition === "top" readonly property bool isTop: Cfg.Config.barPosition === "top"
property color frameBackground: Cfg.Config.theme.bgFrame property color frameBackground: Cfg.Config.theme.bgFrame
property color frameBorderColor: Cfg.Config.theme.frameBorder ?? Cfg.Config.theme.borderPopup property color frameBorderColor: Cfg.Config.theme.frameBorder
// ── Polkit Agent ───────────────────────────────────────────────────────── // ── Polkit Agent ─────────────────────────────────────────────────────────
Loader { Loader {

View File

@@ -14,6 +14,7 @@ PanelWindow {
anchors { top: true; bottom: true; left: true; right: true } anchors { top: true; bottom: true; left: true; right: true }
WlrLayershell.layer: WlrLayer.Background WlrLayershell.layer: WlrLayer.Background
WlrLayershell.namespace: "main-shell-bgDate"
WlrLayershell.exclusiveZone: -1 WlrLayershell.exclusiveZone: -1
mask: Region {} mask: Region {}