This commit is contained in:
Damocles 2026-04-12 18:44:27 +02:00
parent 21f96dc68e
commit b06e3582ff
23 changed files with 597 additions and 197 deletions

View file

@ -16,7 +16,8 @@ M.BarSection {
property bool _osdActive: false
readonly property bool _showPanel: root._hovered || _panelHovered || _osdActive
onPercentChanged: if (percent > 0) _flashPanel()
onPercentChanged: if (percent > 0)
_flashPanel()
function _flashPanel() {
_osdActive = true;
@ -56,7 +57,8 @@ M.BarSection {
stdout: StdioCollector {
onStreamFinished: {
const dev = text.trim();
if (dev) root._blDev = "/sys/class/backlight/" + dev;
if (dev)
root._blDev = "/sys/class/backlight/" + dev;
}
}
}
@ -114,10 +116,7 @@ M.BarSection {
anchors.left: true
margins.top: 0
margins.left: Math.max(0, Math.min(
Math.round(root.mapToGlobal(root.width / 2, 0).x - (QsWindow.window?.screen?.x ?? 0) - implicitWidth / 2),
(panel.screen?.width ?? 1920) - implicitWidth
))
margins.left: Math.max(0, Math.min(Math.round(root.mapToGlobal(root.width / 2, 0).x - (QsWindow.window?.screen?.x ?? 0) - implicitWidth / 2), (panel.screen?.width ?? 1920) - implicitWidth))
implicitWidth: panelContent.width
implicitHeight: panelContent.height
@ -138,14 +137,38 @@ M.BarSection {
ParallelAnimation {
id: showAnim
NumberAnimation { target: panelContent; property: "opacity"; to: 1; duration: 120; easing.type: Easing.OutCubic }
NumberAnimation { target: panelContent; property: "y"; to: 0; duration: 150; easing.type: Easing.OutCubic }
NumberAnimation {
target: panelContent
property: "opacity"
to: 1
duration: 120
easing.type: Easing.OutCubic
}
NumberAnimation {
target: panelContent
property: "y"
to: 0
duration: 150
easing.type: Easing.OutCubic
}
}
ParallelAnimation {
id: hideAnim
NumberAnimation { target: panelContent; property: "opacity"; to: 0; duration: 150; easing.type: Easing.InCubic }
NumberAnimation { target: panelContent; property: "y"; to: -panelContent.height; duration: 150; easing.type: Easing.InCubic }
NumberAnimation {
target: panelContent
property: "opacity"
to: 0
duration: 150
easing.type: Easing.InCubic
}
NumberAnimation {
target: panelContent
property: "y"
to: -panelContent.height
duration: 150
easing.type: Easing.InCubic
}
onFinished: panel._winVisible = false
}
@ -210,7 +233,11 @@ M.BarSection {
color: M.Theme.base0A
radius: 3
Behavior on width { NumberAnimation { duration: 80 } }
Behavior on width {
NumberAnimation {
duration: 80
}
}
}
MouseArea {
@ -218,7 +245,10 @@ M.BarSection {
anchors.margins: -6
cursorShape: Qt.PointingHandCursor
onPressed: mouse => _set(mouse)
onPositionChanged: mouse => { if (pressed) _set(mouse); }
onPositionChanged: mouse => {
if (pressed)
_set(mouse);
}
function _set(mouse) {
root.setPercent(mouse.x / slider.width * 100);
}

View file

@ -34,8 +34,14 @@ PanelWindow {
height: 3
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop { position: 0; color: M.Theme.base0C }
GradientStop { position: 1; color: M.Theme.base09 }
GradientStop {
position: 0
color: M.Theme.base0C
}
GradientStop {
position: 1
color: M.Theme.base09
}
}
}
@ -53,8 +59,12 @@ PanelWindow {
M.BarGroup {
borderColor: M.Theme.base0D
M.Clock { visible: M.Modules.clock.enable }
M.Notifications { visible: M.Modules.notifications.enable }
M.Clock {
visible: M.Modules.clock.enable
}
M.Notifications {
visible: M.Modules.notifications.enable
}
}
}
@ -67,11 +77,17 @@ PanelWindow {
M.BarGroup {
borderColor: M.Theme.base0D
M.Workspaces { bar: bar; visible: M.Modules.workspaces.enable }
M.Workspaces {
bar: bar
visible: M.Modules.workspaces.enable
}
}
M.BarGroup {
borderColor: M.Theme.base0D
M.Tray { bar: bar; visible: M.Modules.tray.enable }
M.Tray {
bar: bar
visible: M.Modules.tray.enable
}
}
M.BarGroup {
borderColor: M.Theme.base0D
@ -80,7 +96,9 @@ PanelWindow {
visible: M.Modules.windowTitle.enable
}
}
Item { Layout.fillWidth: true }
Item {
Layout.fillWidth: true
}
}
// ---- right ----
@ -90,45 +108,73 @@ PanelWindow {
anchors.verticalCenter: parent.verticalCenter
spacing: M.Theme.barSpacing
Item { Layout.fillWidth: true }
Item {
Layout.fillWidth: true
}
// Media
M.BarGroup {
borderColor: M.Theme.base0E
M.Mpris { bar: bar }
M.Volume { visible: M.Modules.volume.enable }
M.Mpris {
bar: bar
}
M.Volume {
visible: M.Modules.volume.enable
}
}
// Connectivity
M.BarGroup {
borderColor: M.Theme.base0D
M.Network { bar: bar; visible: M.Modules.network.enable }
M.Bluetooth { bar: bar }
M.Network {
bar: bar
visible: M.Modules.network.enable
}
M.Bluetooth {
bar: bar
}
}
// Controls
M.BarGroup {
borderColor: M.Theme.base0A
M.Backlight {}
M.PowerProfile { visible: M.Modules.powerProfile.enable }
M.IdleInhibitor { visible: M.Modules.idleInhibitor.enable }
M.PowerProfile {
visible: M.Modules.powerProfile.enable
}
M.IdleInhibitor {
visible: M.Modules.idleInhibitor.enable
}
}
// Stats
M.BarGroup {
borderColor: M.Theme.base08
M.Cpu { visible: M.Modules.cpu.enable }
M.Memory { visible: M.Modules.memory.enable }
M.Temperature { visible: M.Modules.temperature.enable }
M.Weather { visible: M.Modules.weather.enable }
M.Disk { visible: M.Modules.disk.enable }
M.Cpu {
visible: M.Modules.cpu.enable
}
M.Memory {
visible: M.Modules.memory.enable
}
M.Temperature {
visible: M.Modules.temperature.enable
}
M.Weather {
visible: M.Modules.weather.enable
}
M.Disk {
visible: M.Modules.disk.enable
}
}
// Power
M.BarGroup {
borderColor: M.Theme.base08
M.Battery {}
M.Power { bar: bar; visible: M.Modules.power.enable }
M.Power {
bar: bar
visible: M.Modules.power.enable
}
}
}
}

View file

@ -41,8 +41,14 @@ Item {
anchors.fill: parent
radius: M.Theme.radius
gradient: Gradient {
GradientStop { position: 0; color: Qt.rgba(root.borderColor.r, root.borderColor.g, root.borderColor.b, 0.15) }
GradientStop { position: 1; color: "transparent" }
GradientStop {
position: 0
color: Qt.rgba(root.borderColor.r, root.borderColor.g, root.borderColor.b, 0.15)
}
GradientStop {
position: 1
color: "transparent"
}
}
}

View file

@ -21,9 +21,23 @@ Text {
SequentialAnimation {
id: _crossfade
NumberAnimation { target: root; property: "opacity"; to: 0; duration: 60; easing.type: Easing.InQuad }
ScriptAction { script: root._displayIcon = root._pendingIcon }
NumberAnimation { target: root; property: "opacity"; to: 1; duration: 100; easing.type: Easing.OutQuad }
NumberAnimation {
target: root
property: "opacity"
to: 0
duration: 60
easing.type: Easing.InQuad
}
ScriptAction {
script: root._displayIcon = root._pendingIcon
}
NumberAnimation {
target: root
property: "opacity"
to: 1
duration: 100
easing.type: Easing.OutQuad
}
}
color: M.Theme.base05
font.pixelSize: M.Theme.fontSize + 1

View file

@ -8,7 +8,11 @@ Row {
property string tooltip: ""
property bool _hovered: false
Behavior on opacity { NumberAnimation { duration: 150 } }
Behavior on opacity {
NumberAnimation {
duration: 150
}
}
layer.enabled: _hovered
layer.effect: MultiEffect {

View file

@ -28,19 +28,37 @@ M.BarSection {
SequentialAnimation {
running: root._critical
loops: Animation.Infinite
NumberAnimation { target: root; property: "_blinkOpacity"; to: 0.2; duration: 400; easing.type: Easing.InOutQuad }
NumberAnimation { target: root; property: "_blinkOpacity"; to: 1; duration: 400; easing.type: Easing.InOutQuad }
onRunningChanged: if (!running) root._blinkOpacity = 1
NumberAnimation {
target: root
property: "_blinkOpacity"
to: 0.2
duration: 400
easing.type: Easing.InOutQuad
}
NumberAnimation {
target: root
property: "_blinkOpacity"
to: 1
duration: 400
easing.type: Easing.InOutQuad
}
onRunningChanged: if (!running)
root._blinkOpacity = 1
}
property bool _warnSent: false
property bool _critSent: false
onChargingChanged: { _warnSent = false; _critSent = false; }
onChargingChanged: {
_warnSent = false;
_critSent = false;
}
onPctChanged: {
if (charging) return;
if (charging)
return;
if (pct < _critThresh && !_critSent) {
_critSent = true; _warnSent = true;
_critSent = true;
_warnSent = true;
_notif.command = ["notify-send", "--urgency=critical", "--icon=battery-low", "--category=device", "Very Low Battery", "Connect to power now!"];
_notif.running = true;
} else if (pct < _warnThresh && !_warnSent) {
@ -50,7 +68,9 @@ M.BarSection {
}
}
Process { id: _notif }
Process {
id: _notif
}
M.BarIcon {
icon: {

View file

@ -72,7 +72,10 @@ M.BarSection {
TapHandler {
acceptedButtons: Qt.LeftButton
cursorShape: Qt.PointingHandCursor
onTapped: { menuLoader.active = !menuLoader.active; M.FlyoutState.visible = false; }
onTapped: {
menuLoader.active = !menuLoader.active;
M.FlyoutState.visible = false;
}
}
TapHandler {

View file

@ -13,23 +13,18 @@ M.PopupPanel {
property Process _scanner: Process {
id: scanner
running: true
command: ["sh", "-c",
"bluetoothctl devices Paired 2>/dev/null | while read -r _ mac name; do " +
"info=$(bluetoothctl info \"$mac\" 2>/dev/null); " +
"conn=$(echo \"$info\" | grep -c 'Connected: yes'); " +
"bat=$(echo \"$info\" | awk -F'[(): ]' '/Battery Percentage/{for(i=1;i<=NF;i++) if($i+0==$i && $i!=\"\") print $i}'); " +
"echo \"$mac:$conn:${bat:-}:$name\"; " +
"done"
]
command: ["sh", "-c", "bluetoothctl devices Paired 2>/dev/null | while read -r _ mac name; do " + "info=$(bluetoothctl info \"$mac\" 2>/dev/null); " + "conn=$(echo \"$info\" | grep -c 'Connected: yes'); " + "bat=$(echo \"$info\" | awk -F'[(): ]' '/Battery Percentage/{for(i=1;i<=NF;i++) if($i+0==$i && $i!=\"\") print $i}'); " + "echo \"$mac:$conn:${bat:-}:$name\"; " + "done"]
stdout: StdioCollector {
onStreamFinished: {
const devs = [];
for (const line of text.trim().split("\n")) {
if (!line) continue;
if (!line)
continue;
const i1 = line.indexOf(":");
const i2 = line.indexOf(":", i1 + 1);
const i3 = line.indexOf(":", i2 + 1);
if (i3 < 0) continue;
if (i3 < 0)
continue;
devs.push({
mac: line.slice(0, i1),
connected: line.slice(i1 + 1, i2) === "1",
@ -38,7 +33,8 @@ M.PopupPanel {
});
}
devs.sort((a, b) => {
if (a.connected !== b.connected) return a.connected ? -1 : 1;
if (a.connected !== b.connected)
return a.connected ? -1 : 1;
return a.name.localeCompare(b.name);
});
menuWindow._devices = devs;
@ -51,7 +47,8 @@ M.PopupPanel {
property string action: ""
property string mac: ""
command: ["bluetoothctl", action, mac]
onRunningChanged: if (!running) scanner.running = true
onRunningChanged: if (!running)
scanner.running = true
}
Repeater {

View file

@ -33,24 +33,45 @@ PanelWindow {
anchors.left: true
margins.top: 0
margins.left: Math.max(0, Math.min(
Math.round(M.FlyoutState.itemX - implicitWidth / 2),
screen.width - implicitWidth
))
margins.left: Math.max(0, Math.min(Math.round(M.FlyoutState.itemX - implicitWidth / 2), screen.width - implicitWidth))
implicitWidth: label.implicitWidth + M.Theme.barPadding * 2
implicitHeight: label.implicitHeight + M.Theme.barPadding * 2
ParallelAnimation {
id: showAnim
NumberAnimation { target: content; property: "opacity"; to: 1; duration: 120; easing.type: Easing.OutCubic }
NumberAnimation { target: content; property: "y"; to: 0; duration: 150; easing.type: Easing.OutCubic }
NumberAnimation {
target: content
property: "opacity"
to: 1
duration: 120
easing.type: Easing.OutCubic
}
NumberAnimation {
target: content
property: "y"
to: 0
duration: 150
easing.type: Easing.OutCubic
}
}
ParallelAnimation {
id: hideAnim
NumberAnimation { target: content; property: "opacity"; to: 0; duration: 150; easing.type: Easing.InCubic }
NumberAnimation { target: content; property: "y"; to: -content.height; duration: 150; easing.type: Easing.InCubic }
NumberAnimation {
target: content
property: "opacity"
to: 0
duration: 150
easing.type: Easing.InCubic
}
NumberAnimation {
target: content
property: "y"
to: -content.height
duration: 150
easing.type: Easing.InCubic
}
onFinished: root._winVisible = false
}

View file

@ -7,25 +7,77 @@ import Quickshell.Io
QtObject {
id: root
property var workspaces: ({ enable: true })
property var tray: ({ enable: true })
property var windowTitle: ({ enable: true })
property var clock: ({ enable: true })
property var notifications: ({ enable: true })
property var mpris: ({ enable: true })
property var volume: ({ enable: true })
property var bluetooth: ({ enable: true, interval: 5000 })
property var backlight: ({ enable: true, step: 5 })
property var network: ({ enable: true, interval: 5000 })
property var powerProfile: ({ enable: true, interval: 5000 })
property var idleInhibitor: ({ enable: true })
property var weather: ({ enable: true, args: ["--nerd"], interval: 3600000 })
property var temperature: ({ enable: true, interval: 2000, warm: 60, hot: 80 })
property var cpu: ({ enable: true, interval: 1000 })
property var memory: ({ enable: true, interval: 2000 })
property var disk: ({ enable: true, interval: 30000 })
property var battery: ({ enable: true, warning: 25, critical: 15 })
property var power: ({ enable: true })
property var workspaces: ({
enable: true
})
property var tray: ({
enable: true
})
property var windowTitle: ({
enable: true
})
property var clock: ({
enable: true
})
property var notifications: ({
enable: true
})
property var mpris: ({
enable: true
})
property var volume: ({
enable: true
})
property var bluetooth: ({
enable: true,
interval: 5000
})
property var backlight: ({
enable: true,
step: 5
})
property var network: ({
enable: true,
interval: 5000
})
property var powerProfile: ({
enable: true,
interval: 5000
})
property var idleInhibitor: ({
enable: true
})
property var weather: ({
enable: true,
args: ["--nerd"],
interval: 3600000
})
property var temperature: ({
enable: true,
interval: 2000,
warm: 60,
hot: 80
})
property var cpu: ({
enable: true,
interval: 1000
})
property var memory: ({
enable: true,
interval: 2000
})
property var disk: ({
enable: true,
interval: 30000
})
property var battery: ({
enable: true,
warning: 25,
critical: 15
})
property var power: ({
enable: true
})
property FileView _file: FileView {
path: (Quickshell.env("XDG_CONFIG_HOME") || (Quickshell.env("HOME") + "/.config")) + "/nova-shell/modules.json"
@ -42,12 +94,15 @@ QtObject {
return;
}
for (const k of Object.keys(data)) {
if (!(k in root)) continue;
if (!(k in root))
continue;
const v = data[k];
if (typeof v === "object" && v !== null)
root[k] = Object.assign({}, root[k], v);
else if (typeof v === "boolean")
root[k] = Object.assign({}, root[k], { enable: v });
root[k] = Object.assign({}, root[k], {
enable: v
});
}
}
}

View file

@ -19,8 +19,12 @@ M.BarSection {
// Cache art URL at root level so it's captured even when panel is hidden
readonly property string _artUrl: player?.trackArtUrl ?? ""
readonly property string _currentTrack: player?.trackTitle ?? ""
on_ArtUrlChanged: if (_artUrl) _cachedArt = _artUrl
on_CurrentTrackChanged: if (_currentTrack !== _artTrack) { _artTrack = _currentTrack; _cachedArt = _artUrl || "" }
on_ArtUrlChanged: if (_artUrl)
_cachedArt = _artUrl
on_CurrentTrackChanged: if (_currentTrack !== _artTrack) {
_artTrack = _currentTrack;
_cachedArt = _artUrl || "";
}
// Preload art while panel is hidden ensures QML image cache has the pixels
Image {
@ -89,10 +93,7 @@ M.BarSection {
anchors.left: true
margins.top: 0
margins.left: Math.max(0, Math.min(
Math.round(root.mapToGlobal(root.width / 2, 0).x - (QsWindow.window?.screen?.x ?? 0) - implicitWidth / 2),
(panel.screen?.width ?? 1920) - implicitWidth
))
margins.left: Math.max(0, Math.min(Math.round(root.mapToGlobal(root.width / 2, 0).x - (QsWindow.window?.screen?.x ?? 0) - implicitWidth / 2), (panel.screen?.width ?? 1920) - implicitWidth))
implicitWidth: panelContent.width
implicitHeight: panelContent.height
@ -113,14 +114,38 @@ M.BarSection {
ParallelAnimation {
id: showAnim
NumberAnimation { target: panelContent; property: "opacity"; to: 1; duration: 120; easing.type: Easing.OutCubic }
NumberAnimation { target: panelContent; property: "y"; to: 0; duration: 150; easing.type: Easing.OutCubic }
NumberAnimation {
target: panelContent
property: "opacity"
to: 1
duration: 120
easing.type: Easing.OutCubic
}
NumberAnimation {
target: panelContent
property: "y"
to: 0
duration: 150
easing.type: Easing.OutCubic
}
}
ParallelAnimation {
id: hideAnim
NumberAnimation { target: panelContent; property: "opacity"; to: 0; duration: 150; easing.type: Easing.InCubic }
NumberAnimation { target: panelContent; property: "y"; to: -panelContent.height; duration: 150; easing.type: Easing.InCubic }
NumberAnimation {
target: panelContent
property: "opacity"
to: 0
duration: 150
easing.type: Easing.InCubic
}
NumberAnimation {
target: panelContent
property: "y"
to: -panelContent.height
duration: 150
easing.type: Easing.InCubic
}
onFinished: panel._winVisible = false
}
@ -172,10 +197,14 @@ M.BarSection {
source: root._cachedArt
property bool _hasArt: false
onStatusChanged: if (status === Image.Ready) _hasArt = true
onStatusChanged: if (status === Image.Ready)
_hasArt = true
Connections {
target: root
function on_CachedArtChanged() { if (!root._cachedArt) _artImg._hasArt = false }
function on_CachedArtChanged() {
if (!root._cachedArt)
_artImg._hasArt = false;
}
}
}
@ -186,8 +215,14 @@ M.BarSection {
height: 40
visible: _artImg.visible
gradient: Gradient {
GradientStop { position: 0; color: "transparent" }
GradientStop { position: 1; color: M.Theme.base01 }
GradientStop {
position: 0
color: "transparent"
}
GradientStop {
position: 1
color: M.Theme.base01
}
}
}
@ -229,7 +264,8 @@ M.BarSection {
width: parent.width
text: {
const p = root.player;
if (!p) return "";
if (!p)
return "";
const artist = Array.isArray(p.trackArtists) ? p.trackArtists.join(", ") : (p.trackArtists || "");
return [artist, p.trackAlbum].filter(s => s).join(" \u2014 ");
}

View file

@ -10,7 +10,8 @@ M.BarSection {
const parts = [];
if (root.state === "wifi") {
parts.push("WiFi: " + root.essid);
if (root.signal) parts.push("Signal: " + root.signal + "%");
if (root.signal)
parts.push("Signal: " + root.signal + "%");
} else if (root.state === "eth") {
parts.push("Ethernet");
} else if (root.state === "linked") {
@ -18,8 +19,10 @@ M.BarSection {
} else {
return "Disconnected";
}
if (root.ipAddr) parts.push("IP: " + root.ipAddr);
if (root.ifname) parts.push("Interface: " + root.ifname);
if (root.ipAddr)
parts.push("IP: " + root.ipAddr);
if (root.ifname)
parts.push("Interface: " + root.ifname);
return parts.join("\n");
}
@ -96,7 +99,10 @@ M.BarSection {
TapHandler {
cursorShape: Qt.PointingHandCursor
onTapped: { menuLoader.active = !menuLoader.active; M.FlyoutState.visible = false; }
onTapped: {
menuLoader.active = !menuLoader.active;
M.FlyoutState.visible = false;
}
}
Loader {

View file

@ -13,12 +13,7 @@ M.PopupPanel {
property Process _scanner: Process {
id: scanner
running: true
command: ["sh", "-c",
"echo '---CONNS---';" +
"nmcli -t -f NAME,UUID,TYPE,ACTIVE connection show 2>/dev/null;" +
"echo '---WIFI---';" +
"nmcli -t -f SSID,SIGNAL device wifi list --rescan no 2>/dev/null"
]
command: ["sh", "-c", "echo '---CONNS---';" + "nmcli -t -f NAME,UUID,TYPE,ACTIVE connection show 2>/dev/null;" + "echo '---WIFI---';" + "nmcli -t -f SSID,SIGNAL device wifi list --rescan no 2>/dev/null"]
stdout: StdioCollector {
onStreamFinished: {
const sections = text.split("---WIFI---");
@ -28,16 +23,19 @@ M.PopupPanel {
// Visible SSIDs with signal
const visible = {};
for (const l of wifiLines.trim().split("\n")) {
if (!l) continue;
if (!l)
continue;
const parts = l.split(":");
const ssid = parts[0];
if (ssid) visible[ssid] = parseInt(parts[1]) || 0;
if (ssid)
visible[ssid] = parseInt(parts[1]) || 0;
}
// Saved connections filter: show wired always, wifi only if visible
const nets = [];
for (const l of connLines.trim().split("\n")) {
if (!l) continue;
if (!l)
continue;
const parts = l.split(":");
const name = parts[0];
const uuid = parts[1];
@ -45,7 +43,8 @@ M.PopupPanel {
const active = parts[3] === "yes";
const isWifi = type.includes("wireless");
if (isWifi && !(name in visible)) continue;
if (isWifi && !(name in visible))
continue;
nets.push({
name: name,
@ -58,8 +57,10 @@ M.PopupPanel {
// Active first, then by signal (wifi) or name
nets.sort((a, b) => {
if (a.active !== b.active) return a.active ? -1 : 1;
if (a.signal >= 0 && b.signal >= 0) return b.signal - a.signal;
if (a.active !== b.active)
return a.active ? -1 : 1;
if (a.signal >= 0 && b.signal >= 0)
return b.signal - a.signal;
return a.name.localeCompare(b.name);
});
@ -72,14 +73,16 @@ M.PopupPanel {
id: connectProc
property string uuid: ""
command: ["nmcli", "connection", "up", uuid]
onRunningChanged: if (!running) scanner.running = true
onRunningChanged: if (!running)
scanner.running = true
}
property Process _disconnectProc: Process {
id: disconnectProc
property string uuid: ""
command: ["nmcli", "connection", "down", uuid]
onRunningChanged: if (!running) scanner.running = true
onRunningChanged: if (!running)
scanner.running = true
}
Repeater {

View file

@ -57,17 +57,31 @@ M.BarSection {
id: countScale
origin.x: countLabel.width / 2
origin.y: countLabel.height / 2
xScale: 1; yScale: 1
xScale: 1
yScale: 1
}
SequentialAnimation {
id: popAnim
NumberAnimation { target: countScale; properties: "xScale,yScale"; to: 1.4; duration: 100; easing.type: Easing.OutQuad }
NumberAnimation { target: countScale; properties: "xScale,yScale"; to: 1.0; duration: 200; easing.type: Easing.OutElastic }
NumberAnimation {
target: countScale
properties: "xScale,yScale"
to: 1.4
duration: 100
easing.type: Easing.OutQuad
}
NumberAnimation {
target: countScale
properties: "xScale,yScale"
to: 1.0
duration: 200
easing.type: Easing.OutElastic
}
}
}
onCountChanged: if (count > 0) popAnim.start()
onCountChanged: if (count > 0)
popAnim.start()
TapHandler {
acceptedButtons: Qt.LeftButton

View file

@ -35,24 +35,45 @@ PanelWindow {
anchors.left: true
margins.top: 0
margins.left: Math.max(0, Math.min(
Math.round(M.OsdState.itemX - implicitWidth / 2),
screen.width - implicitWidth
))
margins.left: Math.max(0, Math.min(Math.round(M.OsdState.itemX - implicitWidth / 2), screen.width - implicitWidth))
implicitWidth: 200
implicitHeight: 48
ParallelAnimation {
id: showAnim
NumberAnimation { target: content; property: "opacity"; to: 1; duration: 150; easing.type: Easing.OutCubic }
NumberAnimation { target: content; property: "y"; to: 0; duration: 200; easing.type: Easing.OutCubic }
NumberAnimation {
target: content
property: "opacity"
to: 1
duration: 150
easing.type: Easing.OutCubic
}
NumberAnimation {
target: content
property: "y"
to: 0
duration: 200
easing.type: Easing.OutCubic
}
}
ParallelAnimation {
id: hideAnim
NumberAnimation { target: content; property: "opacity"; to: 0; duration: 250; easing.type: Easing.InCubic }
NumberAnimation { target: content; property: "y"; to: -content.height; duration: 250; easing.type: Easing.InCubic }
NumberAnimation {
target: content
property: "opacity"
to: 0
duration: 250
easing.type: Easing.InCubic
}
NumberAnimation {
target: content
property: "y"
to: -content.height
duration: 250
easing.type: Easing.InCubic
}
onFinished: root._winVisible = false
}
@ -104,7 +125,10 @@ PanelWindow {
radius: 3
Behavior on width {
NumberAnimation { duration: 120; easing.type: Easing.OutCubic }
NumberAnimation {
duration: 120
easing.type: Easing.OutCubic
}
}
}
}

View file

@ -14,7 +14,7 @@ PanelWindow {
required property real anchorX
property real panelWidth: 220
signal dismissed()
signal dismissed
visible: true
color: "transparent"
@ -44,10 +44,7 @@ PanelWindow {
Item {
id: panel
x: Math.max(0, Math.min(
Math.round(root.anchorX - contentCol.width / 2),
root.width - contentCol.width
))
x: Math.max(0, Math.min(Math.round(root.anchorX - contentCol.width / 2), root.width - contentCol.width))
y: 0
width: contentCol.width
height: contentCol.height
@ -79,14 +76,40 @@ PanelWindow {
ParallelAnimation {
id: showAnim
NumberAnimation { target: panel; property: "opacity"; from: 0; to: 1; duration: 150; easing.type: Easing.OutCubic }
NumberAnimation { target: panel; property: "y"; from: -panel.height; to: 0; duration: 200; easing.type: Easing.OutCubic }
NumberAnimation {
target: panel
property: "opacity"
from: 0
to: 1
duration: 150
easing.type: Easing.OutCubic
}
NumberAnimation {
target: panel
property: "y"
from: -panel.height
to: 0
duration: 200
easing.type: Easing.OutCubic
}
}
ParallelAnimation {
id: hideAnim
NumberAnimation { target: panel; property: "opacity"; to: 0; duration: 150; easing.type: Easing.InCubic }
NumberAnimation { target: panel; property: "y"; to: -panel.height; duration: 150; easing.type: Easing.InCubic }
NumberAnimation {
target: panel
property: "opacity"
to: 0
duration: 150
easing.type: Easing.InCubic
}
NumberAnimation {
target: panel
property: "y"
to: -panel.height
duration: 150
easing.type: Easing.InCubic
}
onFinished: root.dismissed()
}
}

View file

@ -18,7 +18,10 @@ M.BarIcon {
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: { menuLoader.active = !menuLoader.active; M.FlyoutState.visible = false; }
onClicked: {
menuLoader.active = !menuLoader.active;
M.FlyoutState.visible = false;
}
}
Loader {

View file

@ -18,11 +18,36 @@ M.PopupPanel {
Repeater {
model: [
{ label: "Lock", icon: "\uF023", cmd: ["loginctl", "lock-session"], color: M.Theme.base0D },
{ label: "Suspend", icon: "\uF186", cmd: ["systemctl", "suspend"], color: M.Theme.base0E },
{ label: "Logout", icon: "\uF2F5", cmd: menuWindow._isNiri ? ["niri", "msg", "action", "quit"] : ["loginctl", "terminate-user", ""], color: M.Theme.base0A },
{ label: "Reboot", icon: "\uF021", cmd: ["systemctl", "reboot"], color: M.Theme.base09 },
{ label: "Shutdown", icon: "\uF011", cmd: ["systemctl", "poweroff"], color: M.Theme.base08 }
{
label: "Lock",
icon: "\uF023",
cmd: ["loginctl", "lock-session"],
color: M.Theme.base0D
},
{
label: "Suspend",
icon: "\uF186",
cmd: ["systemctl", "suspend"],
color: M.Theme.base0E
},
{
label: "Logout",
icon: "\uF2F5",
cmd: menuWindow._isNiri ? ["niri", "msg", "action", "quit"] : ["loginctl", "terminate-user", ""],
color: M.Theme.base0A
},
{
label: "Reboot",
icon: "\uF021",
cmd: ["systemctl", "reboot"],
color: M.Theme.base09
},
{
label: "Shutdown",
icon: "\uF011",
cmd: ["systemctl", "poweroff"],
color: M.Theme.base08
}
]
delegate: Item {

View file

@ -8,9 +8,12 @@ M.BarSection {
tooltip: "Temperature: " + root.celsius + "\u00B0C"
property int celsius: 0
property color _stateColor: celsius > (M.Modules.temperature.hot || 80) ? M.Theme.base09
: celsius > (M.Modules.temperature.warm || 60) ? M.Theme.base0A : M.Theme.base08
Behavior on _stateColor { ColorAnimation { duration: 300 } }
property color _stateColor: celsius > (M.Modules.temperature.hot || 80) ? M.Theme.base09 : celsius > (M.Modules.temperature.warm || 60) ? M.Theme.base0A : M.Theme.base08
Behavior on _stateColor {
ColorAnimation {
duration: 300
}
}
FileView {
id: thermal

View file

@ -30,9 +30,22 @@ RowLayout {
SequentialAnimation {
running: iconItem._needsAttention
loops: Animation.Infinite
NumberAnimation { target: iconItem; property: "_pulseOpacity"; to: 0.3; duration: 400; easing.type: Easing.InOutQuad }
NumberAnimation { target: iconItem; property: "_pulseOpacity"; to: 1; duration: 400; easing.type: Easing.InOutQuad }
onRunningChanged: if (!running) iconItem._pulseOpacity = 1
NumberAnimation {
target: iconItem
property: "_pulseOpacity"
to: 0.3
duration: 400
easing.type: Easing.InOutQuad
}
NumberAnimation {
target: iconItem
property: "_pulseOpacity"
to: 1
duration: 400
easing.type: Easing.InOutQuad
}
onRunningChanged: if (!running)
iconItem._pulseOpacity = 1
}
Item {

View file

@ -120,10 +120,7 @@ M.BarSection {
anchors.left: true
margins.top: 0
margins.left: Math.max(0, Math.min(
Math.round(root.mapToGlobal(root.width / 2, 0).x - (QsWindow.window?.screen?.x ?? 0) - implicitWidth / 2),
(panel.screen?.width ?? 1920) - implicitWidth
))
margins.left: Math.max(0, Math.min(Math.round(root.mapToGlobal(root.width / 2, 0).x - (QsWindow.window?.screen?.x ?? 0) - implicitWidth / 2), (panel.screen?.width ?? 1920) - implicitWidth))
implicitWidth: panelContent.width
implicitHeight: panelContent.height
@ -146,14 +143,38 @@ M.BarSection {
ParallelAnimation {
id: showAnim
NumberAnimation { target: panelContent; property: "opacity"; to: 1; duration: 120; easing.type: Easing.OutCubic }
NumberAnimation { target: panelContent; property: "y"; to: 0; duration: 150; easing.type: Easing.OutCubic }
NumberAnimation {
target: panelContent
property: "opacity"
to: 1
duration: 120
easing.type: Easing.OutCubic
}
NumberAnimation {
target: panelContent
property: "y"
to: 0
duration: 150
easing.type: Easing.OutCubic
}
}
ParallelAnimation {
id: hideAnim
NumberAnimation { target: panelContent; property: "opacity"; to: 0; duration: 150; easing.type: Easing.InCubic }
NumberAnimation { target: panelContent; property: "y"; to: -panelContent.height; duration: 150; easing.type: Easing.InCubic }
NumberAnimation {
target: panelContent
property: "opacity"
to: 0
duration: 150
easing.type: Easing.InCubic
}
NumberAnimation {
target: panelContent
property: "y"
to: -panelContent.height
duration: 150
easing.type: Easing.InCubic
}
onFinished: panel._winVisible = false
}
@ -203,7 +224,8 @@ M.BarSection {
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: if (root.sink?.audio) root.sink.audio.muted = !root.sink.audio.muted
onClicked: if (root.sink?.audio)
root.sink.audio.muted = !root.sink.audio.muted
}
}
@ -229,7 +251,11 @@ M.BarSection {
color: root._volumeColor
radius: 3
Behavior on width { NumberAnimation { duration: 80 } }
Behavior on width {
NumberAnimation {
duration: 80
}
}
}
MouseArea {
@ -237,9 +263,13 @@ M.BarSection {
anchors.margins: -6
cursorShape: Qt.PointingHandCursor
onPressed: mouse => _setVol(mouse)
onPositionChanged: mouse => { if (pressed) _setVol(mouse); }
onPositionChanged: mouse => {
if (pressed)
_setVol(mouse);
}
function _setVol(mouse) {
if (!root.sink?.audio) return;
if (!root.sink?.audio)
return;
root.sink.audio.volume = Math.max(0, Math.min(1, mouse.x / slider.width));
}
}
@ -281,7 +311,12 @@ M.BarSection {
property real _targetHeight: root._expanded ? implicitHeight : 0
height: _targetHeight
Behavior on height { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } }
Behavior on height {
NumberAnimation {
duration: 200
easing.type: Easing.OutCubic
}
}
// Separator
Rectangle {
@ -445,9 +480,13 @@ M.BarSection {
anchors.margins: -6
cursorShape: Qt.PointingHandCursor
onPressed: mouse => _set(mouse)
onPositionChanged: mouse => { if (pressed) _set(mouse); }
onPositionChanged: mouse => {
if (pressed)
_set(mouse);
}
function _set(mouse) {
if (!streamEntry.modelData.audio) return;
if (!streamEntry.modelData.audio)
return;
streamEntry.modelData.audio.volume = Math.max(0, Math.min(1, mouse.x / streamSlider.width));
}
}
@ -468,7 +507,10 @@ M.BarSection {
}
// Bottom padding
Item { width: 1; height: 4 }
Item {
width: 1
height: 4
}
}
}
}

View file

@ -90,7 +90,11 @@ Row {
height: 20
radius: M.Theme.radius
color: pill.active ? M.Theme.base0D : (pill._hovered ? M.Theme.base03 : M.Theme.base02)
Behavior on color { ColorAnimation { duration: 150 } }
Behavior on color {
ColorAnimation {
duration: 150
}
}
Text {
anchors.centerIn: parent

View file

@ -66,7 +66,8 @@ in
default = true;
description = "Enable the ${name} module.";
};
} // extra;
}
// extra;
};
};
intervalOpt = default: {
@ -103,7 +104,9 @@ in
description = "Brightness adjustment step (%).";
};
};
temperature = moduleOpt "temperature" ((intervalOpt 2000) // {
temperature = moduleOpt "temperature" (
(intervalOpt 2000)
// {
warm = lib.mkOption {
type = lib.types.int;
default = 60;
@ -114,7 +117,8 @@ in
default = 80;
description = "Temperature threshold for hot state (°C).";
};
});
}
);
battery = moduleOpt "battery" {
warning = lib.mkOption {
type = lib.types.int;
@ -127,7 +131,9 @@ in
description = "Battery percentage for critical notification and blink.";
};
};
weather = moduleOpt "weather" ((intervalOpt 3600000) // {
weather = moduleOpt "weather" (
(intervalOpt 3600000)
// {
args = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "--nerd" ];
@ -138,7 +144,8 @@ in
"Berlin"
];
};
});
}
);
};
theme = lib.mkOption {
@ -174,7 +181,8 @@ in
++ lib.optional cfg.modules.weather.enable pkgs.wttrbar;
xdg.configFile."nova-shell/modules.json".source =
(pkgs.formats.json { }).generate "nova-shell-modules.json" cfg.modules;
(pkgs.formats.json { }).generate "nova-shell-modules.json"
cfg.modules;
xdg.configFile."nova-shell/theme.json".source =
let