summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app.tsx7
-rw-r--r--config.ts8
-rw-r--r--modules/osds.tsx79
-rw-r--r--scss/osds.scss24
4 files changed, 103 insertions, 15 deletions
diff --git a/app.tsx b/app.tsx
index 287361f..be3231c 100644
--- a/app.tsx
+++ b/app.tsx
@@ -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();
diff --git a/config.ts b/config.ts
index 3c22905..70166b2 100644
--- a/config.ts
+++ b/config.ts
@@ -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);
+ }
+}