🔧 chore(modules/backlight, modules/config): fix property name not found, add support for setting properties in config

This commit is contained in:
retrozinndev
2025-08-28 18:33:39 -03:00
parent 09568ac738
commit f213d994aa
4 changed files with 113 additions and 61 deletions
+14
View File
@@ -350,6 +350,20 @@ const generalConfigDefaults = {
} }
}; };
const userDataDefaults = {
control_center: {
default_backlight: undefined
}
};
export const userData = new Config<
keyof typeof userDataDefaults,
(typeof userDataDefaults)[keyof typeof userDataDefaults]
>(
`${GLib.get_user_data_dir()}/colorshell/data.json`,
userDataDefaults
);
export const generalConfig = new Config<keyof typeof generalConfigDefaults, export const generalConfig = new Config<keyof typeof generalConfigDefaults,
typeof generalConfigDefaults[keyof typeof generalConfigDefaults]>( typeof generalConfigDefaults[keyof typeof generalConfigDefaults]>(
`${GLib.get_user_config_dir()}/colorshell/config.json`, generalConfigDefaults `${GLib.get_user_config_dir()}/colorshell/config.json`, generalConfigDefaults
+10 -18
View File
@@ -1,15 +1,12 @@
import { monitorFile, readFile } from "ags/file"; import { monitorFile, readFile } from "ags/file";
import { exec } from "ags/process"; import { exec } from "ags/process";
import GObject, { getter, ParamSpec, setter, signal } from "ags/gobject"; import GObject, { getter, ParamSpec, register, setter, signal } from "ags/gobject";
import Gio from "gi://Gio?version=2.0"; import Gio from "gi://Gio?version=2.0";
export namespace Backlights { export namespace Backlights {
const BacklightsParamSpec = (name: string, flags: GObject.ParamFlags) =>
GObject.ParamSpec.object(name, null, null, flags) as ParamSpec<Backlights>;
const BacklightParamSpec = (name: string, flags: GObject.ParamFlags) => const BacklightParamSpec = (name: string, flags: GObject.ParamFlags) =>
GObject.ParamSpec.object(name, null, null, flags) as ParamSpec<Backlight>; GObject.ParamSpec.object(name, null, null, flags) as ParamSpec<Backlight>;
@@ -22,13 +19,8 @@ export namespace Backlights {
return instance; return instance;
} }
export class Backlights extends GObject.Object { @register({ GTypeName: "Backlights" })
static { class _Backlights extends GObject.Object {
GObject.registerClass({
GTypeName: "Backlights"
}, this);
}
#backlights: Array<Backlight> = []; #backlights: Array<Backlight> = [];
#default: Backlight|null = null; #default: Backlight|null = null;
@@ -101,13 +93,8 @@ export namespace Backlights {
} }
} }
export class Backlight extends GObject.Object { @register({ GTypeName: "Backlight" })
static { class _Backlight extends GObject.Object {
GObject.registerClass({
GTypeName: "Backlight"
}, this);
}
declare $signals: GObject.Object.SignalSignatures & { declare $signals: GObject.Object.SignalSignatures & {
"brightness-changed": (value: number) => void "brightness-changed": (value: number) => void
@@ -207,4 +194,9 @@ export namespace Backlights {
super.emit(signal, ...args); super.emit(signal, ...args);
} }
} }
export const Backlights = _Backlights;
export const Backlight = _Backlight;
export type Backlight = InstanceType<typeof Backlight>;
export type Backlights = InstanceType<typeof Backlights>;
} }
+40 -26
View File
@@ -1,11 +1,9 @@
import { timeout } from "ags/time"; import { timeout } from "ags/time";
import { monitorFile, readFileAsync } from "ags/file"; import { monitorFile, readFileAsync, writeFileAsync } from "ags/file";
import { Notifications } from "./notifications"; import { Notifications } from "./notifications";
import { encoder } from "./utils";
import { Accessor } from "ags"; import { Accessor } from "ags";
import GObject, { getter, register } from "ags/gobject"; import GObject, { getter, ParamSpec, register } from "ags/gobject";
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 AstalIO from "gi://AstalIO"; import AstalIO from "gi://AstalIO";
import AstalNotifd from "gi://AstalNotifd"; import AstalNotifd from "gi://AstalNotifd";
@@ -24,8 +22,8 @@ class Config<K extends NonNullable<string|number|symbol>, V extends string|objec
* in the `entries` field */ * in the `entries` field */
public readonly defaults: Record<K, V>; public readonly defaults: Record<K, V>;
@getter(Object) @getter(Object as unknown as ParamSpec<Record<K, V>>)
public get entries(): object { return this.#entries; } public get entries() { return this.#entries; }
#file: Gio.File; #file: Gio.File;
#entries: Record<K, V>; #entries: Record<K, V>;
@@ -47,26 +45,12 @@ class Config<K extends NonNullable<string|number|symbol>, V extends string|objec
this.#file.make_directory_with_parents(null); this.#file.make_directory_with_parents(null);
this.#file.delete(null); this.#file.delete(null);
this.#file.create_readwrite_async( this.writeFile().catch(e => Notifications.getDefault().sendNotification({
Gio.FileCreateFlags.NONE, GLib.PRIORITY_DEFAULT, appName: "colorshell",
null, (_, asyncRes) => { summary: "Write error",
const ioStream = this.#file.create_readwrite_finish(asyncRes); body: `Couldn't write default configuration file to "${this.#file.get_path()!
}".\nStderr: ${e}`
ioStream.outputStream.write_bytes_async( }));
GLib.Bytes.new(encoder.encode(JSON.stringify(this.entries, undefined, 4))),
GLib.PRIORITY_DEFAULT, null,
(_, asyncRes) => {
const writtenBytes = ioStream.outputStream.write_bytes_finish(asyncRes);
if(!writtenBytes)
Notifications.getDefault().sendNotification({
appName: "colorshell",
summary: "Write error",
body: `Couldn't write default configuration file to "${this.#file.get_path()!}"`
});
}
);
});
} }
monitorFile(this.#file.get_path()!, monitorFile(this.#file.get_path()!,
@@ -97,6 +81,13 @@ class Config<K extends NonNullable<string|number|symbol>, V extends string|objec
)); ));
} }
private async writeFile(): Promise<void> {
this.timeout = true;
await writeFileAsync(
this.#file.get_path()!, JSON.stringify(this.entries, undefined, 4)
).finally(() => this.timeout = false);
}
private async readFile(): Promise<void> { private async readFile(): Promise<void> {
await readFileAsync(this.#file.get_path()!).then((content) => { await readFileAsync(this.#file.get_path()!).then((content) => {
let config: (Record<K, V>|undefined); let config: (Record<K, V>|undefined);
@@ -174,6 +165,29 @@ class Config<K extends NonNullable<string|number|symbol>, V extends string|objec
return this._getProperty(path, this.defaults, expectType); return this._getProperty(path, this.defaults, expectType);
} }
public setProperty(path: string, value: any, write?: boolean): void {
let property: any = this.#entries,
obj: typeof this.entries = property;
const pathArray = path.split('.').filter(str => str);
for(let i = 0; i < pathArray.length; i++) {
const currentPath = pathArray[i];
property = property[currentPath as keyof typeof property];
if(typeof property === "object") {
obj = property;
} else {
obj[pathArray[pathArray.length - 1] as keyof typeof obj] = value;
break;
}
}
write && this.writeFile().catch(e => console.error(
`Config: Couldn't save file. Stderr: ${e}`
));
}
private _getProperty(path: string, entries: Record<K, V>, expectType?: ValueTypes): (any|undefined) { private _getProperty(path: string, entries: Record<K, V>, expectType?: ValueTypes): (any|undefined) {
let property: any = entries; let property: any = entries;
const pathArray = path.split('.').filter(str => str); const pathArray = path.split('.').filter(str => str);
+49 -17
View File
@@ -1,9 +1,10 @@
import { Astal, Gtk } from "ags/gtk4"; import { Astal, Gtk } from "ags/gtk4";
import { tr } from "../../../i18n/intl"; import { tr } from "../../../i18n/intl";
import { Backlights } from "../../../modules/backlight"; import { Backlights } from "../../../modules/backlight";
import { Page } from "./Page"; import { Page, PageButton } from "./Page";
import { createBinding, With } from "ags"; import { createBinding, For, With } from "ags";
import { addSliderMarksFromMinMax } from "../../../modules/utils"; import { addSliderMarksFromMinMax } from "../../../modules/utils";
import { userData } from "../../../app";
export const PageBacklight = new Page({ export const PageBacklight = new Page({
@@ -13,22 +14,53 @@ export const PageBacklight = new Page({
content: () => ( content: () => (
<With value={createBinding(Backlights.getDefault(), "backlights")}> <With value={createBinding(Backlights.getDefault(), "backlights")}>
{(bklights: Array<Backlights.Backlight>) => bklights.length > 0 && {(bklights: Array<Backlights.Backlight>) => bklights.length > 0 &&
<Gtk.Box orientation={Gtk.Orientation.VERTICAL} spacing={6}> <Gtk.Box orientation={Gtk.Orientation.VERTICAL} spacing={4}>
{bklights.map((bklight, i) => <Gtk.Box class={"list"} visible={createBinding(Backlights.getDefault(), "backlights")
<Gtk.Box class={"bklight"} orientation={Gtk.Orientation.VERTICAL} .as((bklights) => bklights.length > 1)}>
spacing={4}>
<Gtk.Label class={"subheader"} label={`Backlight ${i+1} (${bklight.name})`} <Gtk.Label label={"Default"} />
xalign={0} /> <For each={createBinding(Backlights.getDefault(), "backlights")}>
<Astal.Slider $={(self) => addSliderMarksFromMinMax(self)} {(bk: Backlights.Backlight) =>
min={0} max={bklight.maxBrightness} <PageButton class={createBinding(bk, "isDefault").as(is => is ? "highlight" : "")}
value={createBinding(bklight, "brightness")} title={bk.name}
onChangeValue={(_, __, value) => { icon={"video-display-symbolic"}
bklight.brightness = value actionClicked={() => {
}} if(Backlights.getDefault().default?.path !== bk.path) {
/> Backlights.getDefault().setDefault(bk);
</Gtk.Box> // save data
)} userData.setProperty(
"control_center.default_backlight",
bk.name,
true
);
}
}}
endWidget={
<Gtk.Image iconName={"object-select-symbolic"}
visible={createBinding(bk, "isDefault")}
/>
}
/>
}
</For>
</Gtk.Box>
<Gtk.Box class={"sliders"} orientation={Gtk.Orientation.VERTICAL} spacing={6}>
{bklights.map((bklight, i) =>
<Gtk.Box class={"bklight"} orientation={Gtk.Orientation.VERTICAL}
spacing={4}>
<Gtk.Label class={"subheader"} label={`Backlight ${i+1} (${bklight.name})`}
xalign={0} />
<Astal.Slider $={(self) => addSliderMarksFromMinMax(self)}
min={0} max={bklight.maxBrightness}
value={createBinding(bklight, "brightness")}
onChangeValue={(_, __, value) => {
bklight.brightness = value
}}
/>
</Gtk.Box>
)}
</Gtk.Box>
</Gtk.Box> </Gtk.Box>
} }
</With> </With>