osds: lock keys popups

This commit is contained in:
2 * r + 2 * t 2025-01-16 15:21:20 +11:00
parent a2d73b585f
commit 5f2febe534
4 changed files with 103 additions and 15 deletions

View file

@ -22,15 +22,14 @@ App.start({
<Launcher />; <Launcher />;
<NotifPopups />; <NotifPopups />;
Monitors.get_default().forEach(m => { <Osds />;
<Osds monitor={m} />; Monitors.get_default().forEach(m => <Bar monitor={m} />);
<Bar monitor={m} />;
});
console.log("Caelestia started"); console.log("Caelestia started");
}, },
requestHandler(request, res) { requestHandler(request, res) {
if (request === "reload css") loadStyleAsync().catch(console.error); 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 play pause") Players.get_default().lastPlayer?.play_pause();
else if (request === "media next") Players.get_default().lastPlayer?.next(); else if (request === "media next") Players.get_default().lastPlayer?.next();
else if (request === "media previous") Players.get_default().lastPlayer?.previous(); else if (request === "media previous") Players.get_default().lastPlayer?.previous();

View file

@ -36,6 +36,14 @@ export const osds = {
hideDelay: 1500, hideDelay: 1500,
showValue: true, showValue: true,
}, },
lock: {
caps: {
hideDelay: 1000,
},
num: {
hideDelay: 1000,
},
},
}; };
// Services // Services

View file

@ -1,11 +1,12 @@
import { timeout, Variable, type Time } from "astal"; import { execAsync, register, timeout, Variable, type Time } from "astal";
import { Astal, Gtk, type Widget } from "astal/gtk3"; import { App, Astal, Gtk, Widget } from "astal/gtk3";
import cairo from "cairo"; import cairo from "cairo";
import AstalWp from "gi://AstalWp"; import AstalWp from "gi://AstalWp";
import Cairo from "gi://cairo";
import Pango from "gi://Pango"; import Pango from "gi://Pango";
import PangoCairo from "gi://PangoCairo"; import PangoCairo from "gi://PangoCairo";
import { osds as config } from "../config"; import { osds as config } from "../config";
import { type Monitor } from "../services/monitors"; import Monitors, { type Monitor } from "../services/monitors";
import { PopupWindow } from "../utils/widgets"; import { PopupWindow } from "../utils/widgets";
const getStyle = (context: Gtk.StyleContext, prop: string) => context.get_property(prop, Gtk.StateFlags.NORMAL); const getStyle = (context: Gtk.StyleContext, prop: string) => context.get_property(prop, Gtk.StateFlags.NORMAL);
@ -40,8 +41,8 @@ const SliderOsd = ({
drawAreaSetup, drawAreaSetup,
}: { }: {
fillIcons?: boolean; fillIcons?: boolean;
monitor: Monitor; monitor?: Monitor;
type: keyof typeof config; type: "volume" | "brightness";
windowSetup: (self: Widget.Window, show: () => void) => void; windowSetup: (self: Widget.Window, show: () => void) => void;
className?: string; className?: string;
initValue: number; initValue: number;
@ -49,7 +50,7 @@ const SliderOsd = ({
}) => ( }) => (
<PopupWindow <PopupWindow
name={type} name={type}
monitor={monitor.id} monitor={monitor?.id}
keymode={Astal.Keymode.NONE} keymode={Astal.Keymode.NONE}
anchor={config[type].position} anchor={config[type].position}
margin={config[type].margin} margin={config[type].margin}
@ -206,10 +207,9 @@ const SliderOsd = ({
</PopupWindow> </PopupWindow>
); );
const Volume = ({ monitor, audio }: { monitor: Monitor; audio: AstalWp.Audio }) => ( const Volume = ({ audio }: { audio: AstalWp.Audio }) => (
<SliderOsd <SliderOsd
fillIcons fillIcons
monitor={monitor}
type="volume" type="volume"
windowSetup={(self, show) => { windowSetup={(self, show) => {
self.hook(audio.defaultSpeaker, "notify::volume", show); self.hook(audio.defaultSpeaker, "notify::volume", show);
@ -259,9 +259,66 @@ const Brightness = ({ monitor }: { monitor: Monitor }) => (
/> />
); );
export default ({ monitor }: { monitor: Monitor }) => { @register()
if (AstalWp.get_default()) <Volume monitor={monitor} audio={AstalWp.get_default()!.audio} />; class LockOsd extends Widget.Window {
<Brightness monitor={monitor} />; 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; return null;
}; };

View file

@ -25,3 +25,27 @@
.volume .inner.mute { .volume .inner.mute {
background-color: scheme.$overlay0; 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);
}
}