From 43af16801fcbb7056a51ed5fd6539c74ff5c0379 Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Fri, 12 Sep 2025 14:21:21 +0200 Subject: ... --- accounts/gkleen@sif/niri/default.nix | 44 ++- accounts/gkleen@sif/niri/waybar.nix | 358 --------------------- accounts/gkleen@sif/shell/default.nix | 1 + .../shell/quickshell-plugins/Systemd.cpp | 27 +- .../shell/quickshell-plugins/Systemd.hpp | 3 + .../shell/quickshell/ActiveWindowDisplay.qml | 20 +- accounts/gkleen@sif/shell/quickshell/Bar.qml | 7 + .../gkleen@sif/shell/quickshell/BatteryWidget.qml | 72 +++-- .../shell/quickshell/BrightnessWidget.qml | 20 +- accounts/gkleen@sif/shell/quickshell/Clock.qml | 17 +- .../gkleen@sif/shell/quickshell/KeyboardLayout.qml | 24 +- .../shell/quickshell/LidSwitchInhibitorWidget.qml | 1 + .../gkleen@sif/shell/quickshell/Lockscreen.qml | 7 +- .../gkleen@sif/shell/quickshell/PipewireWidget.qml | 40 ++- .../shell/quickshell/Services/MprisProxy.qml | 8 + .../gkleen@sif/shell/quickshell/SystemTray.qml | 43 +-- accounts/gkleen@sif/shell/quickshell/UnixIPC.qml | 18 ++ .../shell/quickshell/WaylandInhibitorWidget.qml | 1 + .../shell/quickshell/WorkspaceSwitcher.qml | 11 +- .../gkleen@sif/shell/quickshell/WorktimeWidget.qml | 120 +++++++ accounts/gkleen@sif/shell/quickshell/shell.qml | 3 +- flake.lock | 8 +- flake.nix | 2 +- overlays/worktime/worktime/__main__.py | 2 +- 24 files changed, 354 insertions(+), 503 deletions(-) delete mode 100644 accounts/gkleen@sif/niri/waybar.nix create mode 100644 accounts/gkleen@sif/shell/quickshell/Services/MprisProxy.qml create mode 100644 accounts/gkleen@sif/shell/quickshell/WorktimeWidget.qml diff --git a/accounts/gkleen@sif/niri/default.nix b/accounts/gkleen@sif/niri/default.nix index 32d55f97..3c29b83c 100644 --- a/accounts/gkleen@sif/niri/default.nix +++ b/accounts/gkleen@sif/niri/default.nix @@ -165,7 +165,6 @@ let with-focused-window-action = config.lib.niri.actions.spawn (lib.getExe (with_predicate_window ".is_focused")); in { imports = [ - ./waybar.nix ./mako.nix ]; @@ -716,7 +715,7 @@ in { (plain "layer-rule" [ (sleaf "match" { namespace = "^notifications$"; }) - (sleaf "match" { namespace = "^waybar$"; }) + (sleaf "match" { namespace = "^bar$"; }) (sleaf "match" { namespace = "^launcher$"; }) (sleaf "block-out-from" "screencast") ]) @@ -908,23 +907,23 @@ in { "Mod+Right".action = set-column-width "+10%"; "Mod+Shift+Z" = { - action = spawn (lib.getExe niri) "msg" "action" "power-off-monitors"; + action = power-off-monitors; allow-when-locked = true; }; - "Mod+Shift+L".action = spawn loginctl "lock-session"; + # "Mod+Shift+L".action = spawn loginctl "lock-session"; "Mod+Shift+E".action = quit; - "Mod+Shift+Minus" = { - action = spawn systemctl "suspend"; - allow-when-locked = true; - }; - "Mod+Shift+Control+Minus" = { - action = spawn systemctl "hibernate"; - allow-when-locked = true; - }; - "Mod+Shift+P" = { - action = spawn (lib.getExe pkgs.playerctl) "-a" "pause"; - allow-when-locked = true; - }; + # "Mod+Shift+Minus" = { + # action = spawn systemctl "suspend"; + # allow-when-locked = true; + # }; + # "Mod+Shift+Control+Minus" = { + # action = spawn systemctl "hibernate"; + # allow-when-locked = true; + # }; + # "Mod+Shift+P" = { + # action = spawn (lib.getExe pkgs.playerctl) "-a" "pause"; + # allow-when-locked = true; + # }; "Mod+Semicolon".action = spawn makoctl "dismiss" "--group"; "Mod+Shift+Semicolon".action = spawn makoctl "dismiss" "--all"; @@ -974,6 +973,19 @@ in { action = shell { Brightness = "down"; }; allow-when-locked = true; }; + "Mod+Shift+L".action = shell { LockSession = {}; }; + "Mod+Shift+Minus" = { + action = shell { Suspend = {}; }; + allow-when-locked = true; + }; + "Mod+Shift+Control+Minus" = { + action = shell { Hibernate = {}; }; + allow-when-locked = true; + }; + "Mod+Shift+P" = { + action = shell { Mpris = { PauseAll = {}; }; }; + allow-when-locked = true; + }; })) (map ({ name, selector, spawn, key, ...}: if key != null && selector != null && spawn != null then bind key { action = focus-or-spawn-action selector name spawn; } else null) cfg.scratchspaces) (map ({ name, moveKey, ...}: if moveKey != null then bind moveKey { action = kdl.magic-leaf "move-column-to-workspace" name; } else null) cfg.scratchspaces) diff --git a/accounts/gkleen@sif/niri/waybar.nix b/accounts/gkleen@sif/niri/waybar.nix deleted file mode 100644 index 04e255da..00000000 --- a/accounts/gkleen@sif/niri/waybar.nix +++ /dev/null @@ -1,358 +0,0 @@ -{ lib, config, pkgs, ... }: -let - swayosd-client = lib.getExe' config.services.swayosd.package "swayosd-client"; -in { - config = { - programs.waybar = { - enable = false; # true; - systemd = { - enable = true; - target = "graphical-session.target"; - }; - settings = let - windowRewrites = { - "(.*) — Mozilla Firefox" = "$1"; - "(.*) - Mozilla Thunderbird" = "$1"; - "(.*) - mpv" = "$1"; - }; - iconSize = 11; - in [ - { - layer = "top"; - position = "top"; - height = 21; - output = [ "eDP-1" "DP-2" "DP-3" ]; - modules-left = [ "niri/workspaces" ]; - modules-center = [ "niri/window" ]; - modules-right = [ "custom/worktime" "custom/worktime-today" - "custom/weather" - "custom/keymap" - "privacy" "tray" "wireplumber" "backlight" "battery" "idle_inhibitor" "custom/mako" "custom/lid_inhibitor" "clock" ]; - - "custom/lid_inhibitor" = { - format = "{}"; - return-type = "json"; - exec = lib.getExe pkgs.waybar-systemd-inhibit; - on-click = lib.getExe' pkgs.waybar-systemd-inhibit "waybar-systemd-inhibit-toggle"; - }; - "custom/mako" = { - format = "{}"; - return-type = "json"; - exec = pkgs.writers.writePython3 "mako-silent" { libraries = [ pkgs.python3Packages.dbus-next ]; } '' - from dbus_next.aio import MessageBus - - import asyncio - - import json - - - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - - - async def main(): - bus = await MessageBus().connect() - # the introspection xml would normally be included in your project, but - # this is convenient for development - introspection = await bus.introspect('org.freedesktop.Notifications', '/fr/emersion/Mako') # noqa: E501 - - obj = bus.get_proxy_object('org.freedesktop.Notifications', '/fr/emersion/Mako', introspection) # noqa: E501 - mako = obj.get_interface('fr.emersion.Mako') - properties = obj.get_interface('org.freedesktop.DBus.Properties') - - async def print_mode(): - modes = await mako.get_modes() - is_silent = "silent" in modes - icon = "󰂛" if is_silent else "󰂚" - text = f"{icon}" # noqa: E501 - if is_silent: - text = f"{text}" - print(json.dumps({'text': text, 'tooltip': ', '.join(modes)}, separators=(',', ':')), flush=True) # noqa: E501 - - async def on_properties_changed(interface_name, changed_properties, invalidated_properties): # noqa: E501 - if "Modes" not in invalidated_properties: - return - - await print_mode() - - properties.on_properties_changed(on_properties_changed) - await print_mode() - - await loop.create_future() - - - loop.run_until_complete(main()) - ''; - on-click = "makoctl mode -t silent"; - }; - "custom/weather" = { - format = "{}"; - tooltip = true; - interval = 3600; - exec = "${lib.getExe pkgs.wttrbar} --hide-conditions --nerd --custom-indicator \"{ICON} {FeelsLikeC}°\""; - return-type = "json"; - }; - "custom/keymap" = { - format = "{}"; - tooltip = true; - return-type = "json"; - exec = pkgs.writers.writePython3 "keymap" {} '' - import os - import socket - import json - - - def output(keymap): - short = keymap - if keymap == "English (programmer Dvorak)": - short = "dvp" - elif keymap == "English (US)": - short = "us" - print(json.dumps({'text': short, 'tooltip': keymap}, separators=(',', ':')), flush=True) # noqa: E501 - - - keyboard_layouts = [] - - sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - sock.connect(os.environ["NIRI_SOCKET"]) - sock.send(b"\"EventStream\"\n") - for line in sock.makefile(buffering=1, encoding='utf-8'): - if line_json := json.loads(line): - if "KeyboardLayoutsChanged" in line_json: - keyboard_layouts = line_json["KeyboardLayoutsChanged"]["keyboard_layouts"]["names"] # noqa: E501 - output(keyboard_layouts[line_json["KeyboardLayoutsChanged"]["keyboard_layouts"]["current_idx"]]) # noqa: E501 - if "KeyboardLayoutSwitched" in line_json: - output(keyboard_layouts[line_json["KeyboardLayoutSwitched"]["idx"]]) # noqa: E501 - ''; - on-click = "niri msg action switch-layout next"; - }; - "custom/worktime" = { - interval = 60; - exec = "${lib.getExe pkgs.worktime} time --waybar"; - return-type = "json"; - }; - "custom/worktime-today" = { - interval = 60; - exec = "${lib.getExe pkgs.worktime} today --waybar"; - return-type = "json"; - }; - "niri/workspaces" = { - ignore = map ({ name, ... }: name) config.programs.niri.scratchspaces; - }; - "niri/window" = { - separate-outputs = true; - icon = true; - icon-size = 14; - rewrite = windowRewrites; - }; - clock = { - interval = 1; - # timezone = "Europe/Berlin"; - format = "W{:%V-%u %F %H:%M:%S%Ez}"; - tooltip-format = "{calendar}"; - calendar = { - mode = "year"; - mode-mon-col = 3; - weeks-pos = "left"; - on-scroll = 1; - format = { - months = "{}"; - days = "{}"; - weeks = "{}"; - weekdays = "{}"; - today = "{}"; - }; - }; - }; - battery = { - format = "{icon}"; - icon-size = iconSize - 2; - states = { warning = 30; critical = 15; }; - format-icons = ["󰂎" "󰁺" "󰁻" "󰁼" "󰁽" "󰁾" "󰁿" "󰂀" "󰂁" "󰂂" "󰁹" ]; - format-charging = "󰂄"; - format-plugged = "󰚥"; - tooltip-format = "{capacity}% {timeTo}"; - interval = 20; - }; - tray = { - icon-size = 16; - # show-passive-items = true; - spacing = 1; - }; - privacy = { - icon-spacing = 7; - icon-size = iconSize; - modules = [ - { type = "screenshare"; } - { type = "audio-in"; } - ]; - }; - idle_inhibitor = { - format = "{icon}"; - icon-size = iconSize; - format-icons = { activated = "󰈈"; deactivated = "󰈉"; }; - timeout = 120; - }; - backlight = { - format = "{icon}"; - icon-size = iconSize; - tooltip-format = "{percent}%"; - format-icons = ["󰃚" "󰃛" "󰃜" "󰃝" "󰃞" "󰃟" "󰃠"]; - on-scroll-up = "${swayosd-client} --brightness raise"; - on-scroll-down = "${swayosd-client} --brightness lower"; - }; - wireplumber = { - format = "{icon}"; - icon-size = iconSize; - tooltip-format = "{volume}% {node_name}"; - format-icons = ["󰕿" "󰖀" "󰕾"]; - format-muted = "󰝟"; - # ignored-sinks = ["Easy Effects Sink"]; - on-scroll-up = "${swayosd-client} --output-volume raise"; - on-scroll-down = "${swayosd-client} --output-volume lower"; - on-click = "${swayosd-client} --output-volume mute-toggle"; - }; - } - { - layer = "top"; - position = "top"; - height = 14; - output = [ "!eDP-1" "!DP-2" "!DP-3" "*" ]; - modules-left = [ "niri/workspaces" ]; - modules-center = [ "niri/window" ]; - modules-right = [ "clock" ]; - - "niri/workspaces" = { - ignore = map ({ name, ... }: name) config.programs.niri.scratchspaces; - }; - "niri/window" = { - separate-outputs = true; - icon = true; - icon-size = 14; - rewrite = windowRewrites; - }; - clock = { - interval = 1; - # timezone = "Europe/Berlin"; - format = "{:%H:%M}"; - tooltip-format = "W{:%V-%u %F %H:%M:%S%Ez}"; - }; - } - ]; - style = '' - @define-color white #ffffff; - @define-color grey #555555; - @define-color blue #1a8fff; - @define-color green #23fd00; - @define-color orange #f28a21; - @define-color red #f2201f; - - * { - border: none; - font-family: "Fira Sans"; - font-size: 10pt; - min-height: 0; - } - - window#waybar { - background-color: rgba(0, 0, 0, 0.66); - color: @white; - } - - .modules-left { - margin-left: 38px; - } - .modules-right { - margin-right: 38px; - } - - .module { - margin: 0 5px; - } - - #workspaces button { - color: @white; - padding: 2px 5px; - } - #workspaces button.empty { - color: @grey; - } - #workspaces button.active { - color: @green; - } - #workspaces button.urgent { - color: @red; - } - - #custom-weather, #custom-keymap, #custom-worktime, #custom-worktime-today { - color: @grey; - margin: 0 5px; - } - #custom-weather { - margin-right: 3px; - } - #custom-keymap { - margin-left: 3px; - margin-right: 3px; - } - - #tray { - margin: 0; - } - #battery, #idle_inhibitor, #backlight, #wireplumber, #custom-mako, #custom-lid_inhibitor { - color: @grey; - margin: 0 5px 0 2px; - } - #idle_inhibitor { - margin-right: 4px; - margin-left: 6px; - } - #custom-mako { - margin-right: 4px; - margin-left: 3px; - } - #custom-lid_inhibitor { - margin-right: 3px; - margin-left: 3px; - } - #battery { - margin-right: 3px; - } - #battery.discharging { - color: @white; - } - #battery.warning { - color: @orange; - } - #battery.critical { - color: @red; - } - #battery.charging { - color: @white; - } - #idle_inhibitor.activated { - color: @white; - } - #custom-worktime.running, #custom-worktime-today.running { - color: @white; - } - #custom-worktime.over, #custom-worktime-today.over { - color: @orange; - } - - #idle_inhibitor, #custom-lid_inhibitor { - padding-top: 1px; - } - - #privacy { - color: @red; - margin: -1px 4px 0px 3px; - } - #clock { - /* margin-right: 5px; */ - font-feature-settings: "tnum"; - } - ''; - }; - }; -} diff --git a/accounts/gkleen@sif/shell/default.nix b/accounts/gkleen@sif/shell/default.nix index 5025dd90..44462865 100644 --- a/accounts/gkleen@sif/shell/default.nix +++ b/accounts/gkleen@sif/shell/default.nix @@ -102,6 +102,7 @@ rev = "2424e748e0cc63ab7b9c095a099b9fe239b737c0"; hash = "sha256-QMGl7soAhErrrnY3aKOZpt49yebkSNzy10p/v5OaqQ0="; }); + worktime = builtins.toJSON (lib.getExe pkgs.worktime); }; }; }; diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.cpp b/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.cpp index 884ea17f..308659e9 100644 --- a/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.cpp +++ b/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.cpp @@ -51,26 +51,17 @@ void Systemd::onLogindSessionPropertiesChanged(const QString& interface_name, co emit this->lockedHintChanged(); } -void Systemd::stopUserUnit(const QString& unit, const QString& mode) { - this->systemdManager->StopUnit(unit, mode); -} +void Systemd::stopUserUnit(const QString& unit, const QString& mode) { this->systemdManager->StopUnit(unit, mode); } -void Systemd::setBrightness(const QString& subsystem, const QString& name, quint32 brightness) { - this->logindSession->SetBrightness(subsystem, name, brightness); -} +void Systemd::setBrightness(const QString& subsystem, const QString& name, quint32 brightness) { this->logindSession->SetBrightness(subsystem, name, brightness); } -bool Systemd::idleHint() { - return this->logindSession->idleHint(); -} -void Systemd::setIdleHint(bool idle) { - this->logindSession->SetIdleHint(idle); -} -bool Systemd::lockedHint() { - return this->logindSession->lockedHint(); -} -void Systemd::setLockedHint(bool locked) { - this->logindSession->SetLockedHint(locked); -} +bool Systemd::idleHint() { return this->logindSession->idleHint(); } +void Systemd::setIdleHint(bool idle) { this->logindSession->SetIdleHint(idle); } +bool Systemd::lockedHint() { return this->logindSession->lockedHint(); } +void Systemd::setLockedHint(bool locked) { this->logindSession->SetLockedHint(locked); } +void Systemd::lockSession() { this->logindSession->call("Lock"); } +void Systemd::suspend() { this->logindManager->Suspend(true); } +void Systemd::hibernate() { this->logindManager->Hibernate(true); } std::string SystemdInhibitorParams::toString(SystemdInhibitorParams::WhatItem what) { if (what == SystemdInhibitorParams::Shutdown) diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.hpp b/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.hpp index 84752d76..615024d2 100644 --- a/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.hpp +++ b/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.hpp @@ -28,6 +28,9 @@ public: Q_INVOKABLE void setBrightness(const QString& subsystem, const QString& name, quint32 brightness); Q_INVOKABLE void setIdleHint(bool idle); Q_INVOKABLE void setLockedHint(bool locked); + Q_INVOKABLE void lockSession(); + Q_INVOKABLE void suspend(); + Q_INVOKABLE void hibernate(); bool idleHint(); bool lockedHint(); diff --git a/accounts/gkleen@sif/shell/quickshell/ActiveWindowDisplay.qml b/accounts/gkleen@sif/shell/quickshell/ActiveWindowDisplay.qml index 883f9001..dcc23279 100644 --- a/accounts/gkleen@sif/shell/quickshell/ActiveWindowDisplay.qml +++ b/accounts/gkleen@sif/shell/quickshell/ActiveWindowDisplay.qml @@ -149,16 +149,22 @@ Item { hoverEnabled: true enabled: true - anchors.centerIn: parent + anchors.fill: parent - Text { - id: widgetTooltipText + Item { + anchors.fill: parent - font.pointSize: 10 - font.family: "Fira Mono" - color: "white" + Text { + id: widgetTooltipText + + anchors.centerIn: parent - text: JSON.stringify(Object.assign({}, activeWindowDisplay.activeWindow), null, 2) + font.pointSize: 10 + font.family: "Fira Mono" + color: "white" + + text: JSON.stringify(Object.assign({}, activeWindowDisplay.activeWindow), null, 2) + } } } } diff --git a/accounts/gkleen@sif/shell/quickshell/Bar.qml b/accounts/gkleen@sif/shell/quickshell/Bar.qml index f8092604..7f97bd75 100644 --- a/accounts/gkleen@sif/shell/quickshell/Bar.qml +++ b/accounts/gkleen@sif/shell/quickshell/Bar.qml @@ -1,4 +1,5 @@ import Quickshell +import Quickshell.Wayland import QtQuick PanelWindow { @@ -6,6 +7,8 @@ PanelWindow { required property var screen + WlrLayershell.namespace: "bar" + anchors { top: true left: true @@ -63,6 +66,10 @@ PanelWindow { anchors.verticalCenter: parent.verticalCenter spacing: 0 + WorktimeWidget { command: "time"; } + + WorktimeWidget { command: "today"; } + KeyboardLayout {} Item { diff --git a/accounts/gkleen@sif/shell/quickshell/BatteryWidget.qml b/accounts/gkleen@sif/shell/quickshell/BatteryWidget.qml index fd031627..da17df2a 100644 --- a/accounts/gkleen@sif/shell/quickshell/BatteryWidget.qml +++ b/accounts/gkleen@sif/shell/quickshell/BatteryWidget.qml @@ -87,42 +87,48 @@ Item { hoverEnabled: true enabled: true - anchors.centerIn: parent - - Text { - id: widgetTooltipText - - font.pointSize: 10 - font.family: "Fira Sans" - color: "white" + anchors.fill: parent - text: { - const stateStr = UPowerDeviceState.toString(root.batteryDevice.state); - var outStr = stateStr; - if (root.batteryDevice.state != UPowerDeviceState.FullyCharged) - outStr += ` ${Math.round(root.batteryDevice.percentage * 100)}%`; - - function formatTime(t) { - var res = ""; - for (const unit of [{ "s": "h", "v": 3600 }, { "s": "m", "v": 60 }, { "s": "s", "v": 1 }]) { - if (t < unit.v) - continue; - res += Math.floor(t / unit.v) + unit.s; - t %= unit.v; + Item { + anchors.fill: parent + + Text { + id: widgetTooltipText + + anchors.centerIn: parent + + font.pointSize: 10 + font.family: "Fira Sans" + color: "white" + + text: { + const stateStr = UPowerDeviceState.toString(root.batteryDevice.state); + var outStr = stateStr; + if (root.batteryDevice.state != UPowerDeviceState.FullyCharged) + outStr += ` ${Math.round(root.batteryDevice.percentage * 100)}%`; + + function formatTime(t) { + var res = ""; + for (const unit of [{ "s": "h", "v": 3600 }, { "s": "m", "v": 60 }, { "s": "s", "v": 1 }]) { + if (t < unit.v) + continue; + res += Math.floor(t / unit.v) + unit.s; + t %= unit.v; + } + return res; + } + if (root.batteryDevice.timeToEmpty != 0) { + const tStr = formatTime(Math.floor(root.batteryDevice.timeToEmpty / 60) * 60); + if (tStr) + outStr += " " + tStr; + } else if (root.batteryDevice.timeToFull != 0) { + const tStr = formatTime(Math.ceil(root.batteryDevice.timeToFull / 60) * 60); + if (tStr) + outStr += " " + tStr; } - return res; - } - if (root.batteryDevice.timeToEmpty != 0) { - const tStr = formatTime(Math.floor(root.batteryDevice.timeToEmpty / 60) * 60); - if (tStr) - outStr += " " + tStr; - } else if (root.batteryDevice.timeToFull != 0) { - const tStr = formatTime(Math.ceil(root.batteryDevice.timeToFull / 60) * 60); - if (tStr) - outStr += " " + tStr; - } - return outStr; + return outStr; + } } } } diff --git a/accounts/gkleen@sif/shell/quickshell/BrightnessWidget.qml b/accounts/gkleen@sif/shell/quickshell/BrightnessWidget.qml index 7f9c1ad0..3bb5a80e 100644 --- a/accounts/gkleen@sif/shell/quickshell/BrightnessWidget.qml +++ b/accounts/gkleen@sif/shell/quickshell/BrightnessWidget.qml @@ -62,16 +62,22 @@ Item { hoverEnabled: true enabled: true - anchors.centerIn: parent + anchors.fill: parent + + Item { + anchors.fill: parent + + Text { + id: widgetTooltipText - Text { - id: widgetTooltipText + anchors.centerIn: parent - font.pointSize: 10 - font.family: "Fira Sans" - color: "white" + font.pointSize: 10 + font.family: "Fira Sans" + color: "white" - text: `${Math.round(Brightness.currBrightness * 100)}%` + text: `${Math.round(Brightness.currBrightness * 100)}%` + } } } } diff --git a/accounts/gkleen@sif/shell/quickshell/Clock.qml b/accounts/gkleen@sif/shell/quickshell/Clock.qml index bb618f6a..b7004528 100644 --- a/accounts/gkleen@sif/shell/quickshell/Clock.qml +++ b/accounts/gkleen@sif/shell/quickshell/Clock.qml @@ -80,8 +80,8 @@ Item { } } - implicitWidth: clockTooltipContent.width - implicitHeight: clockTooltipContent.height + implicitWidth: tooltipLayout.childrenRect.width + 16 + implicitHeight: tooltipLayout.childrenRect.height + 16 color: "black" onVisibleChanged: { @@ -99,13 +99,20 @@ Item { anchors.fill: parent - WrapperItem { + Item { id: clockTooltipContent - margin: 8 + anchors.fill: parent ColumnLayout { - anchors.centerIn: parent + id: tooltipLayout + + anchors { + left: parent.left + top: parent.top + leftMargin: 8 + topMargin: 8 + } Text { id: yearLabel diff --git a/accounts/gkleen@sif/shell/quickshell/KeyboardLayout.qml b/accounts/gkleen@sif/shell/quickshell/KeyboardLayout.qml index bc3750f9..46302e54 100644 --- a/accounts/gkleen@sif/shell/quickshell/KeyboardLayout.qml +++ b/accounts/gkleen@sif/shell/quickshell/KeyboardLayout.qml @@ -87,18 +87,24 @@ Item { hoverEnabled: true enabled: true - anchors.centerIn: parent + anchors.fill: parent - Text { - id: kbdTooltipText + Item { + anchors.fill: parent - font.pointSize: 10 - font.family: "Fira Sans" - color: "white" + Text { + id: kbdTooltipText - text: { - const currentLayout = NiriService.keyboardLayouts?.names?.[NiriService.keyboardLayouts.current_idx]; - return currentLayout || ""; + anchors.centerIn: parent + + font.pointSize: 10 + font.family: "Fira Sans" + color: "white" + + text: { + const currentLayout = NiriService.keyboardLayouts?.names?.[NiriService.keyboardLayouts.current_idx]; + return currentLayout || ""; + } } } } diff --git a/accounts/gkleen@sif/shell/quickshell/LidSwitchInhibitorWidget.qml b/accounts/gkleen@sif/shell/quickshell/LidSwitchInhibitorWidget.qml index 2be0692a..8410dcda 100644 --- a/accounts/gkleen@sif/shell/quickshell/LidSwitchInhibitorWidget.qml +++ b/accounts/gkleen@sif/shell/quickshell/LidSwitchInhibitorWidget.qml @@ -16,6 +16,7 @@ Item { anchors.fill: parent hoverEnabled: true + cursorShape: Qt.PointingHandCursor onClicked: InhibitorState.lidSwitchInhibited = !InhibitorState.lidSwitchInhibited diff --git a/accounts/gkleen@sif/shell/quickshell/Lockscreen.qml b/accounts/gkleen@sif/shell/quickshell/Lockscreen.qml index 456baa98..f983388c 100644 --- a/accounts/gkleen@sif/shell/quickshell/Lockscreen.qml +++ b/accounts/gkleen@sif/shell/quickshell/Lockscreen.qml @@ -70,11 +70,6 @@ Scope { mode: Custom.SystemdInhibitorParams.Delay } - Scope { - id: mprisProxy - property list players: Mpris.players.values - } - WlSessionLock { id: lock @@ -85,7 +80,7 @@ Scope { if (locked) { NiriService.sendCommand({ "Action": { "PowerOffMonitors": {} } }); Custom.KeePassXC.lockAllDatabases(); - Array.from(mprisProxy.players).forEach(player => { + Array.from(MprisProxy.players).forEach(player => { if (player.canPause && player.isPlaying) player.pause(); }); diff --git a/accounts/gkleen@sif/shell/quickshell/PipewireWidget.qml b/accounts/gkleen@sif/shell/quickshell/PipewireWidget.qml index 3e0b8fd9..9c6b65a4 100644 --- a/accounts/gkleen@sif/shell/quickshell/PipewireWidget.qml +++ b/accounts/gkleen@sif/shell/quickshell/PipewireWidget.qml @@ -272,16 +272,22 @@ Item { onWheel: event => defaultSinkMouseArea.scrollVolume(event); - anchors.centerIn: parent + anchors.fill: parent + + Item { + anchors.fill: parent - Text { - id: volumeTooltipText + Text { + id: volumeTooltipText - font.pointSize: 10 - font.family: "Fira Sans" - color: "white" + anchors.centerIn: parent - text: `${Math.round(defaultSinkItem.modelData?.audio?.volume * 100)}%` + font.pointSize: 10 + font.family: "Fira Sans" + color: "white" + + text: `${Math.round(defaultSinkItem.modelData?.audio?.volume * 100)}%` + } } } } @@ -391,16 +397,22 @@ Item { onWheel: event => defaultSourceMouseArea.scrollVolume(event); - anchors.centerIn: parent + anchors.fill: parent + + Item { + anchors.fill: parent - Text { - id: volumeTooltipText + Text { + id: volumeTooltipText - font.pointSize: 10 - font.family: "Fira Sans" - color: "white" + anchors.centerIn: parent - text: `${Math.round(defaultSourceItem.modelData?.audio?.volume * 100)}%` + font.pointSize: 10 + font.family: "Fira Sans" + color: "white" + + text: `${Math.round(defaultSourceItem.modelData?.audio?.volume * 100)}%` + } } } } diff --git a/accounts/gkleen@sif/shell/quickshell/Services/MprisProxy.qml b/accounts/gkleen@sif/shell/quickshell/Services/MprisProxy.qml new file mode 100644 index 00000000..e3ab9755 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/MprisProxy.qml @@ -0,0 +1,8 @@ +pragma Singleton + +import Quickshell +import Quickshell.Services.Mpris + +Scope { + property list players: Mpris.players.values +} diff --git a/accounts/gkleen@sif/shell/quickshell/SystemTray.qml b/accounts/gkleen@sif/shell/quickshell/SystemTray.qml index 351e74ee..f7b4ed96 100644 --- a/accounts/gkleen@sif/shell/quickshell/SystemTray.qml +++ b/accounts/gkleen@sif/shell/quickshell/SystemTray.qml @@ -155,32 +155,39 @@ Item { hoverEnabled: true enabled: true - margin: 8 + margin: 4 - Column { - Text { - id: tooltipTitle + anchors.fill: parent - enabled: trayItem.tooltipTitle + Item { + anchors.fill: parent - font.pointSize: 10 - font.family: "Fira Sans" - font.bold: true - color: "white" + Column { + anchors.centerIn: parent + Text { + id: tooltipTitle - text: trayItem.tooltipTitle - } + enabled: trayItem.tooltipTitle - Text { - id: tooltipDescription + font.pointSize: 10 + font.family: "Fira Sans" + font.bold: true + color: "white" - enabled: trayItem.tooltipDescription + text: trayItem.tooltipTitle + } + + Text { + id: tooltipDescription - font.pointSize: 10 - font.family: "Fira Sans" - color: "white" + enabled: trayItem.tooltipDescription - text: trayItem.tooltipDescription + font.pointSize: 10 + font.family: "Fira Sans" + color: "white" + + text: trayItem.tooltipDescription + } } } } diff --git a/accounts/gkleen@sif/shell/quickshell/UnixIPC.qml b/accounts/gkleen@sif/shell/quickshell/UnixIPC.qml index 742ef4f5..e7b7b673 100644 --- a/accounts/gkleen@sif/shell/quickshell/UnixIPC.qml +++ b/accounts/gkleen@sif/shell/quickshell/UnixIPC.qml @@ -1,7 +1,9 @@ import Quickshell import Quickshell.Io import Quickshell.Services.Pipewire +import Quickshell.Services.Mpris import qs.Services +import Custom as Custom Scope { id: root @@ -19,6 +21,14 @@ Scope { root.onCommandVolume(command.Volume); else if (command.Brightness) root.onCommandBrightness(command.Brightness); + else if (command.LockSession) + Custom.Systemd.lockSession(); + else if (command.Suspend) + Custom.Systemd.suspend(); + else if (command.Hibernate) + Custom.Systemd.hibernate(); + else if (command.Mpris) + root.onCommandMpris(command.Mpris); else console.warn("UnixIPC: Command not handled:", JSON.stringify(command)); } catch (e) { @@ -56,4 +66,12 @@ Scope { if (command === "down") Brightness.currBrightness -= 0.02 } + + function onCommandMpris(command) { + if (command.PauseAll) + Array.from(MprisProxy.players).forEach(player => { + if (player.canPause && player.isPlaying) + player.pause(); + }); + } } diff --git a/accounts/gkleen@sif/shell/quickshell/WaylandInhibitorWidget.qml b/accounts/gkleen@sif/shell/quickshell/WaylandInhibitorWidget.qml index 0633f350..0512ff51 100644 --- a/accounts/gkleen@sif/shell/quickshell/WaylandInhibitorWidget.qml +++ b/accounts/gkleen@sif/shell/quickshell/WaylandInhibitorWidget.qml @@ -25,6 +25,7 @@ Item { anchors.fill: parent hoverEnabled: true + cursorShape: Qt.PointingHandCursor onClicked: InhibitorState.waylandIdleInhibited = !InhibitorState.waylandIdleInhibited diff --git a/accounts/gkleen@sif/shell/quickshell/WorkspaceSwitcher.qml b/accounts/gkleen@sif/shell/quickshell/WorkspaceSwitcher.qml index 4cbebcc9..2d3aca1b 100644 --- a/accounts/gkleen@sif/shell/quickshell/WorkspaceSwitcher.qml +++ b/accounts/gkleen@sif/shell/quickshell/WorkspaceSwitcher.qml @@ -149,6 +149,8 @@ Row { WrapperMouseArea { id: windowMouseArea + required property int index + required property var modelData property var windowData: modelData hoverEnabled: true @@ -164,12 +166,11 @@ Row { WrapperRectangle { color: windowMouseArea.containsMouse ? "#33808080" : "transparent"; - anchors.fill: parent - WrapperItem { - anchors.fill: parent - - margin: 4 + rightMargin: 8 + leftMargin: 8 + topMargin: windowMouseArea.index == 0 ? 8 : 4 + bottomMargin: windowMouseArea.index == windowsModel.values.length - 1 ? 8 : 4 Text { id: windowLabel diff --git a/accounts/gkleen@sif/shell/quickshell/WorktimeWidget.qml b/accounts/gkleen@sif/shell/quickshell/WorktimeWidget.qml new file mode 100644 index 00000000..04bcc581 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/WorktimeWidget.qml @@ -0,0 +1,120 @@ +import QtQml +import Quickshell +import Quickshell.Io +import QtQuick +import Quickshell.Widgets + +Item { + id: root + + required property string command + property var state: null + + height: parent.height + width: label.contentWidth + 8 + anchors.verticalCenter: parent.verticalCenter + + Process { + id: process + running: true + command: [ @worktime@, root.command, "--waybar" ] + stdout: StdioCollector { + id: processCollector + onStreamFinished: { + try { + root.state = JSON.parse(processCollector.text); + } catch (e) { + console.warn("Worktime: Failed to parse output:", processCollector.text, e); + } + } + } + } + + Timer { + running: true + interval: 60 + repeat: true + onTriggered: process.running = true + } + + WrapperMouseArea { + id: mouseArea + + anchors.fill: parent + + enabled: true + hoverEnabled: true + + Item { + anchors.fill: parent + + Text { + id: label + + anchors.centerIn: parent + + visible: root.state?.text ?? false + text: root.state?.text ?? "" + + font.pointSize: 10 + font.family: "Fira Sans" + color: { + if (root.state?.class == "running") + return "white"; + if (root.state?.class == "over") + return "#f28a21"; + return "#555"; + } + } + } + } + + PopupWindow { + id: tooltip + + property bool nextVisible: Boolean(root.state?.tooltip ?? false) && (mouseArea.containsMouse || tooltipMouseArea.containsMouse) + + anchor { + item: mouseArea + edges: Edges.Bottom | Edges.Left + } + visible: false + + onNextVisibleChanged: hangTimer.restart() + + Timer { + id: hangTimer + interval: 100 + onTriggered: tooltip.visible = tooltip.nextVisible + } + + implicitWidth: tooltipText.contentWidth + 16 + implicitHeight: tooltipText.contentHeight + 16 + color: "black" + + WrapperMouseArea { + id: tooltipMouseArea + + enabled: true + hoverEnabled: true + + anchors.fill: parent + + Item { + anchors.fill: parent + + Text { + id: tooltipText + + anchors.centerIn: parent + + font.pointSize: 10 + font.family: "Fira Sans" + color: "white" + + text: root.state?.tooltip ?? "" + } + } + } + } +} diff --git a/accounts/gkleen@sif/shell/quickshell/shell.qml b/accounts/gkleen@sif/shell/quickshell/shell.qml index 693d741f..10c2eff6 100644 --- a/accounts/gkleen@sif/shell/quickshell/shell.qml +++ b/accounts/gkleen@sif/shell/quickshell/shell.qml @@ -20,7 +20,8 @@ ShellRoot { screen: screenScope.modelData WlrLayershell.layer: WlrLayer.Background - WlrLayershell.exclusionMode: ExclusionMode.Ignore + WlrLayershell.namespace: "background" + exclusionMode: ExclusionMode.Ignore anchors.top: true anchors.bottom: true diff --git a/flake.lock b/flake.lock index 05385ecd..b99d27a9 100644 --- a/flake.lock +++ b/flake.lock @@ -541,16 +541,16 @@ "niri-unstable": { "flake": false, "locked": { - "lastModified": 1757438446, - "narHash": "sha256-4NN+weI4isQCFB+A36J+CU8tb251maVlBO9usuzgMQ8=", + "lastModified": 1757671534, + "narHash": "sha256-7tfypHWNtR+wZS9K9XrvcUwyvZ3h8CxInQ2mVsjUU9A=", "owner": "gkleen", "repo": "niri", - "rev": "2c7f7053ce360279160a9e2366d54980def848ad", + "rev": "5e3611a3c5f8c819e5517d0b3f795f161579a0db", "type": "github" }, "original": { "owner": "gkleen", - "ref": "feat/unix-sockets", + "ref": "fix/locked-monitor-control", "repo": "niri", "type": "github" } diff --git a/flake.nix b/flake.nix index 0b2ce0a8..b93a1f2e 100644 --- a/flake.nix +++ b/flake.nix @@ -213,7 +213,7 @@ type = "github"; owner = "gkleen"; repo = "niri"; - ref = "feat/unix-sockets"; + ref = "fix/locked-monitor-control"; }; }; }; diff --git a/overlays/worktime/worktime/__main__.py b/overlays/worktime/worktime/__main__.py index 016690f0..ffeb1b84 100755 --- a/overlays/worktime/worktime/__main__.py +++ b/overlays/worktime/worktime/__main__.py @@ -491,7 +491,7 @@ class Worktime(object): for day in sorted(days_forward): day_time = timedelta() if day in self.extra_days_to_work: - day_time += self.extra_days_to_work + day_time += self.extra_days_to_work[day] if day in holidays and not day in self.extra_days_to_work: day_time -= holidays[day] if day.isoweekday() in self.workdays: -- cgit v1.2.3