🔧 chore(osd, modules/utils): better OSDMode implementation, introduce secureBinding
secureBinding is used to bind to an object's property, just like in createBinding, but with the possibility of adding a default value, for when errors occur, it returns that value
This commit is contained in:
+27
-3
@@ -6,7 +6,7 @@ import { getSymbolicIcon } from "./apps";
|
|||||||
|
|
||||||
import GLib from "gi://GLib?version=2.0";
|
import GLib from "gi://GLib?version=2.0";
|
||||||
import Gio from "gi://Gio?version=2.0";
|
import Gio from "gi://Gio?version=2.0";
|
||||||
import GObject from "gi://GObject?version=2.0";
|
import GObject from "ags/gobject";
|
||||||
|
|
||||||
|
|
||||||
/** gnim doesn't export this, so we need to do it again */
|
/** gnim doesn't export this, so we need to do it again */
|
||||||
@@ -230,8 +230,7 @@ export function construct<Class extends object>(klass: Class, props: Record<any,
|
|||||||
subs.push(v.subscribe(() => {
|
subs.push(v.subscribe(() => {
|
||||||
klass[k as keyof Class] = v.get() as Class[keyof Class];
|
klass[k as keyof Class] = v.get() as Class[keyof Class];
|
||||||
if(isGObject)
|
if(isGObject)
|
||||||
klass.notify(k.replace(/[A-Z]/g, (s) => `-${s.toLowerCase()}`
|
klass.notify(k.replace(/[A-Z]/g, (s) => `-${s.toLowerCase()}`));
|
||||||
));
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
klass[k as keyof Class] = v.get() as Class[keyof Class];
|
klass[k as keyof Class] = v.get() as Class[keyof Class];
|
||||||
@@ -277,3 +276,28 @@ export function createConnetions<
|
|||||||
add(gobj, gobj.connect(sig as string, callback as never));
|
add(gobj, gobj.connect(sig as string, callback as never));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function secureBinding<
|
||||||
|
GObj extends GObject.Object,
|
||||||
|
Prop extends keyof GObj,
|
||||||
|
Returns extends unknown|undefined
|
||||||
|
>(
|
||||||
|
gobj: GObj,
|
||||||
|
prop: Prop,
|
||||||
|
defaultValue: Returns
|
||||||
|
): Accessor<GObj[Prop]|Returns> {
|
||||||
|
const get = () => gobj ? gobj[prop] : defaultValue;
|
||||||
|
|
||||||
|
return new Accessor<GObj[Prop]|Returns>(
|
||||||
|
get,
|
||||||
|
(notify) => {
|
||||||
|
const gobjectProp = (prop as string).replace(/[A-Z]/g, (s) => `-${s.toLowerCase()}`);
|
||||||
|
const id = gobj.connect(`notify::${gobjectProp}`, () => notify());
|
||||||
|
return () => {
|
||||||
|
try {
|
||||||
|
gobj.disconnect(id);
|
||||||
|
} catch(e) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
+20
-48
@@ -1,64 +1,35 @@
|
|||||||
import { Astal, Gtk } from "ags/gtk4";
|
import { Astal, Gtk } from "ags/gtk4";
|
||||||
import { Accessor, createBinding, createState, With } from "ags";
|
import { createBinding, createState, With } from "ags";
|
||||||
import { Wireplumber } from "../../modules/volume";
|
import { Wireplumber } from "../../modules/volume";
|
||||||
import { Windows } from "../../windows";
|
import { Windows } from "../../windows";
|
||||||
import { Backlights } from "../../modules/backlight";
|
import { Backlights } from "../../modules/backlight";
|
||||||
import { construct, variableToBoolean } from "../../modules/utils";
|
import { secureBinding, variableToBoolean } from "../../modules/utils";
|
||||||
|
|
||||||
import GObject, { ParamSpec, property, register } from "ags/gobject";
|
|
||||||
import Pango from "gi://Pango?version=1.0";
|
import Pango from "gi://Pango?version=1.0";
|
||||||
import GLib from "gi://GLib?version=2.0";
|
import GLib from "gi://GLib?version=2.0";
|
||||||
|
import AstalWp from "gi://AstalWp?version=0.1";
|
||||||
|
import OSDMode from "./modules/osdmode";
|
||||||
|
|
||||||
|
|
||||||
@register({ GTypeName: "OSDMode" })
|
|
||||||
export class OSDMode extends GObject.Object {
|
|
||||||
readonly #subs: Array<() => void> = [];
|
|
||||||
@property(String)
|
|
||||||
icon: string = "image-missing";
|
|
||||||
@property(Number)
|
|
||||||
value: number = 0;
|
|
||||||
@property(Number)
|
|
||||||
max: number = 100;
|
|
||||||
@property(String as unknown as ParamSpec<string|null>)
|
|
||||||
text: string|null = null;
|
|
||||||
|
|
||||||
constructor(props: {
|
|
||||||
icon: string | Accessor<string>;
|
|
||||||
value: number | Accessor<number>;
|
|
||||||
max?: number | Accessor<number>;
|
|
||||||
text?: string | Accessor<string>;
|
|
||||||
}) {
|
|
||||||
super();
|
|
||||||
this.#subs = construct(this, props);
|
|
||||||
}
|
|
||||||
|
|
||||||
vfunc_dispose(): void {
|
|
||||||
this.#subs.forEach(s => s());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const OSDModes = {
|
export const OSDModes = {
|
||||||
SINK: () => new OSDMode({
|
sink: new OSDMode({
|
||||||
icon: createBinding(Wireplumber.getWireplumber().defaultSpeaker, "volumeIcon"),
|
icon: secureBinding(AstalWp.get_default().defaultSpeaker, "volumeIcon",
|
||||||
value: createBinding(Wireplumber.getWireplumber().defaultSpeaker, "volume"),
|
"audio-volume-high-symbolic"),
|
||||||
text: createBinding(Wireplumber.getWireplumber().defaultSpeaker, "description"),
|
value: secureBinding(Wireplumber.getWireplumber().defaultSpeaker, "volume", 50),
|
||||||
|
text: secureBinding(Wireplumber.getWireplumber().defaultSpeaker, "description",
|
||||||
|
"Unknown Speaker"),
|
||||||
max: Wireplumber.getDefault().getMaxSinkVolume() / 100
|
max: Wireplumber.getDefault().getMaxSinkVolume() / 100
|
||||||
}),
|
}),
|
||||||
BRIGHTNESS: () => Backlights.getDefault().available ? new OSDMode({
|
brightness: new OSDMode({
|
||||||
icon: "display-brightness-symbolic",
|
|
||||||
value: createBinding(Backlights.getDefault().default, "brightness"),
|
|
||||||
max: createBinding(Backlights.getDefault().default, "maxBrightness"),
|
|
||||||
text: createBinding(Backlights.getDefault().default, "name")
|
|
||||||
})
|
|
||||||
: new OSDMode({
|
|
||||||
icon: "display-brightness-symbolic",
|
icon: "display-brightness-symbolic",
|
||||||
value: 100,
|
value: secureBinding(Backlights.getDefault().default, "brightness", 100),
|
||||||
max: 100,
|
max: secureBinding(Backlights.getDefault().default, "maxBrightness", 100),
|
||||||
text: "No Backlight found"
|
text: secureBinding(Backlights.getDefault().default, "name", "Unknown Backlight"),
|
||||||
|
available: createBinding(Backlights.getDefault(), "available")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const [osdMode, setOSDMode] = createState(OSDModes.SINK);
|
const [osdMode, setOSDMode] = createState(OSDModes.sink);
|
||||||
let osdTimer: (GLib.Source|undefined), osdTimeout = 3500;
|
let osdTimer: (GLib.Source|undefined), osdTimeout = 3500;
|
||||||
|
|
||||||
export const OSD = (mon: number) =>
|
export const OSD = (mon: number) =>
|
||||||
@@ -66,9 +37,10 @@ export const OSD = (mon: number) =>
|
|||||||
anchor={Astal.WindowAnchor.BOTTOM} focusable={false} marginBottom={80} monitor={mon}>
|
anchor={Astal.WindowAnchor.BOTTOM} focusable={false} marginBottom={80} monitor={mon}>
|
||||||
|
|
||||||
<Gtk.Box class={"osd"}>
|
<Gtk.Box class={"osd"}>
|
||||||
<With value={osdMode(f => f)}>
|
<With value={osdMode}>
|
||||||
{(_: () => OSDMode) => {
|
{(mode: OSDMode) => {
|
||||||
const mode = _ as unknown as OSDMode; // for some reason, gnim runs this function :broken_heart:
|
if(!mode.available) return;
|
||||||
|
|
||||||
return <Gtk.Box>
|
return <Gtk.Box>
|
||||||
<Gtk.Image class={"icon"} iconName={
|
<Gtk.Image class={"icon"} iconName={
|
||||||
createBinding(mode, "icon")
|
createBinding(mode, "icon")
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { Accessor } from "ags";
|
||||||
|
import { construct } from "../../../modules/utils";
|
||||||
|
import GObject, { gtype, property, register } from "ags/gobject";
|
||||||
|
|
||||||
|
|
||||||
|
@register({ GTypeName: "OSDMode" })
|
||||||
|
export default class OSDMode extends GObject.Object {
|
||||||
|
readonly #subs: Array<() => void> = [];
|
||||||
|
|
||||||
|
@property(String)
|
||||||
|
icon: string = "image-missing";
|
||||||
|
|
||||||
|
@property(Number)
|
||||||
|
value: number = 0;
|
||||||
|
|
||||||
|
@property(Number)
|
||||||
|
max: number = 100;
|
||||||
|
|
||||||
|
@property(gtype<string|null>(String))
|
||||||
|
text: string|null = null;
|
||||||
|
|
||||||
|
@property(Boolean)
|
||||||
|
available: boolean = true;
|
||||||
|
|
||||||
|
constructor(props: {
|
||||||
|
icon: string | Accessor<string>;
|
||||||
|
value: number | Accessor<number>;
|
||||||
|
max?: number | Accessor<number>;
|
||||||
|
text?: string | Accessor<string>;
|
||||||
|
available?: boolean | Accessor<boolean>;
|
||||||
|
}) {
|
||||||
|
super();
|
||||||
|
this.#subs = construct(this, props);
|
||||||
|
}
|
||||||
|
|
||||||
|
vfunc_dispose(): void {
|
||||||
|
this.#subs.forEach(s => s());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user