diff options
| -rw-r--r-- | app.tsx | 7 | ||||
| -rw-r--r-- | config.ts | 8 | ||||
| -rw-r--r-- | modules/osds.tsx | 79 | ||||
| -rw-r--r-- | scss/osds.scss | 24 |
4 files changed, 103 insertions, 15 deletions
@@ -22,15 +22,14 @@ App.start({ <Launcher />; <NotifPopups />; - Monitors.get_default().forEach(m => { - <Osds monitor={m} />; - <Bar monitor={m} />; - }); + <Osds />; + Monitors.get_default().forEach(m => <Bar monitor={m} />); console.log("Caelestia started"); }, requestHandler(request, res) { if (request === "reload css") loadStyleAsync().catch(console.error); + else if (request.startsWith("show")) App.get_window(request.split(" ")[1])?.show(); else if (request === "media play pause") Players.get_default().lastPlayer?.play_pause(); else if (request === "media next") Players.get_default().lastPlayer?.next(); else if (request === "media previous") Players.get_default().lastPlayer?.previous(); @@ -36,6 +36,14 @@ export const osds = { hideDelay: 1500, showValue: true, }, + lock: { + caps: { + hideDelay: 1000, + }, + num: { + hideDelay: 1000, + }, + }, }; // Services diff --git a/modules/osds.tsx b/modules/osds.tsx index e8299b2..afd71db 100644 --- a/modules/osds.tsx +++ b/modules/osds.tsx @@ -1,11 +1,12 @@ -import { timeout, Variable, type Time } from "astal"; -import { Astal, Gtk, type Widget } from "astal/gtk3"; +import { execAsync, register, timeout, Variable, type Time } from "astal"; +import { App, Astal, Gtk, Widget } from "astal/gtk3"; import cairo from "cairo"; import AstalWp from "gi://AstalWp"; +import Cairo from "gi://cairo"; import Pango from "gi://Pango"; import PangoCairo from "gi://PangoCairo"; import { osds as config } from "../config"; -import { type Monitor } from "../services/monitors"; +import Monitors, { type Monitor } from "../services/monitors"; import { PopupWindow } from "../utils/widgets"; const getStyle = (context: Gtk.StyleContext, prop: string) => context.get_property(prop, Gtk.StateFlags.NORMAL); @@ -40,8 +41,8 @@ const SliderOsd = ({ drawAreaSetup, }: { fillIcons?: boolean; - monitor: Monitor; - type: keyof typeof config; + monitor?: Monitor; + type: "volume" | "brightness"; windowSetup: (self: Widget.Window, show: () => void) => void; className?: string; initValue: number; @@ -49,7 +50,7 @@ const SliderOsd = ({ }) => ( <PopupWindow name={type} - monitor={monitor.id} + monitor={monitor?.id} keymode={Astal.Keymode.NONE} anchor={config[type].position} margin={config[type].margin} @@ -206,10 +207,9 @@ const SliderOsd = ({ </PopupWindow> ); -const Volume = ({ monitor, audio }: { monitor: Monitor; audio: AstalWp.Audio }) => ( +const Volume = ({ audio }: { audio: AstalWp.Audio }) => ( <SliderOsd fillIcons - monitor={monitor} type="volume" windowSetup={(self, show) => { self.hook(audio.defaultSpeaker, "notify::volume", show); @@ -259,9 +259,66 @@ const Brightness = ({ monitor }: { monitor: Monitor }) => ( /> ); -export default ({ monitor }: { monitor: Monitor }) => { - if (AstalWp.get_default()) <Volume monitor={monitor} audio={AstalWp.get_default()!.audio} />; - <Brightness monitor={monitor} />; +@register() +class LockOsd extends Widget.Window { + readonly lockType: "caps" | "num"; + + #timeout: Time | null = null; + + constructor({ type, icon, right }: { type: "caps" | "num"; icon: string; right?: boolean }) { + super({ + visible: false, + name: `lock-${type}`, + application: App, + namespace: `caelestia-lock-${type}`, + anchor: + Astal.WindowAnchor.TOP | Astal.WindowAnchor.LEFT | Astal.WindowAnchor.BOTTOM | Astal.WindowAnchor.RIGHT, + exclusivity: Astal.Exclusivity.IGNORE, + }); + + this.lockType = type; + this.#update(); + + this.add( + <box vertical halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER} className={`lock ${type}`}> + <label vexpand className="icon" label={icon} /> + <label vexpand className="text" label={type.slice(0, 1).toUpperCase() + type.slice(1) + "lock"} /> + </box> + ); + + // Clickthrough + this.connect("size-allocate", () => this.input_shape_combine_region(new Cairo.Region())); + + // Move over when other indicator opens/closes + this.hook(App, "window-toggled", (_, window) => { + if (window !== this && window instanceof LockOsd) { + const child = this.get_child(); + if (!child) return; + this[right ? "marginLeft" : "marginRight"] = window.visible ? child.get_preferred_width()[1] + 5 : 0; + } + }); + } + + #update() { + execAsync(`fish -c 'cat /sys/class/leds/input*::${this.lockType}lock/brightness'`) + .then(out => (this.get_child() as Widget.Box | null)?.toggleClassName("enabled", out.includes("1"))) + .catch(console.error); + } + + show() { + super.show(); + this.#update(); + this.#timeout?.cancel(); + this.#timeout = timeout(config.lock[this.lockType].hideDelay, () => this.hide()); + } +} + +export default () => { + if (AstalWp.get_default()) <Volume audio={AstalWp.get_default()!.audio} />; + Monitors.get_default().forEach(monitor => <Brightness monitor={monitor} />); + + <LockOsd type="caps" icon="keyboard_capslock" />; + <LockOsd right type="num" icon="filter_1" />; return null; }; diff --git a/scss/osds.scss b/scss/osds.scss index 7ad3d5b..a3cd4f2 100644 --- a/scss/osds.scss +++ b/scss/osds.scss @@ -25,3 +25,27 @@ .volume .inner.mute { background-color: scheme.$overlay0; } + +.lock { + @include lib.rounded(8); + @include lib.border(scheme.$overlay0, 0.1); + @include lib.shadow; + @include lib.element-decel; + @include font.mono; + + min-width: lib.s(80); + min-height: lib.s(80); + padding: lib.s(10); + background-color: scheme.$base; + color: scheme.$overlay0; + font-size: lib.s(16); + font-weight: bold; + + &.enabled { + color: scheme.$text; + } + + .icon { + font-size: lib.s(48); + } +} |