💥 fix(control-center/page, control-center/pages): scope errors

i basically restructured the page widget lol
This commit is contained in:
retrozinndev
2025-07-30 17:54:44 -03:00
parent d417eb8c05
commit 22a17e14db
15 changed files with 378 additions and 382 deletions
+26 -33
View File
@@ -2,16 +2,13 @@ import { register } from "ags/gobject";
import { Gtk } from "ags/gtk4"; import { Gtk } from "ags/gtk4";
import { Page } from "./pages/Page"; import { Page } from "./pages/Page";
import { timeout } from "ags/time"; import { timeout } from "ags/time";
import { variableToBoolean } from "../../scripts/utils";
import AstalIO from "gi://AstalIO"; import AstalIO from "gi://AstalIO";
import { createRoot } from "ags";
export { Pages }; export { Pages };
export type PagesProps = { export type PagesProps = {
initialPage?: Page; initialPage?: Page;
class?: string;
transitionDuration?: number; transitionDuration?: number;
}; };
@@ -19,21 +16,20 @@ export type PagesProps = {
class Pages extends Gtk.Box { class Pages extends Gtk.Box {
#timeouts: Array<[AstalIO.Time, (() => void)|undefined]> = []; #timeouts: Array<[AstalIO.Time, (() => void)|undefined]> = [];
#page: (Page|undefined); #page: (Page|undefined);
#pageWidget: (Gtk.Revealer|undefined);
#transDuration: number; #transDuration: number;
#transType: Gtk.RevealerTransitionType = Gtk.RevealerTransitionType.SLIDE_DOWN; #transType: Gtk.RevealerTransitionType = Gtk.RevealerTransitionType.SLIDE_DOWN;
get isOpen() { return Boolean(this.get_first_child()); } get isOpen() { return Boolean(this.#page); }
get page() { return this.#page; }
constructor(props?: PagesProps) { constructor(props?: PagesProps) {
super({ super({
orientation: Gtk.Orientation.VERTICAL, orientation: Gtk.Orientation.VERTICAL,
cssName: "pages" cssName: "pages",
name: "pages"
}); });
this.name = "pages"; this.add_css_class("pages");
props?.class?.split(' ').filter(variableToBoolean).forEach(clss =>
this.add_css_class(clss));
this.#transDuration = props?.transitionDuration ?? 280; this.#transDuration = props?.transitionDuration ?? 280;
@@ -53,7 +49,7 @@ class Pages extends Gtk.Box {
} }
toggle(newPage?: Page, onToggled?: () => void): void { toggle(newPage?: Page, onToggled?: () => void): void {
if(!newPage || (this.#page?.id === newPage?.id)) { if(!newPage || (this.#page?.id === newPage.id)) {
this.close(onToggled); this.close(onToggled);
return; return;
} }
@@ -69,38 +65,35 @@ class Pages extends Gtk.Box {
} }
} }
open(newPage: Page, onOpened?: () => void) { open(newPage: Page, onOpen?: () => void) {
const pageWidget = createRoot(() => <Gtk.Revealer
transitionDuration={this.#transDuration}
transitionType={this.#transType}
revealChild={false}>
{newPage as unknown as Gtk.Widget}
</Gtk.Revealer> as Gtk.Revealer);
this.prepend(pageWidget);
this.#pageWidget = pageWidget;
this.#page = newPage; this.#page = newPage;
this.reorder_child_after(this.get_last_child()!, null); this.prepend(
(this.get_first_child() as Gtk.Revealer).revealChild = true; <Gtk.Revealer revealChild={false} transitionType={this.#transType}
onOpened?.(); transitionDuration={this.#transDuration}>
{newPage.create()}
</Gtk.Revealer> as Gtk.Revealer
);
(this.get_first_child() as Gtk.Revealer)?.set_reveal_child(true);
onOpen?.();
} }
close(onClosed?: () => void): void { close(onClosed?: () => void): void {
if(!this.#pageWidget) return; const page = this.get_first_child() as Gtk.Revealer|null;
if(!page) return;
this.#pageWidget.revealChild = false; this.#page?.actionClosed?.();
const closingPage = this.#pageWidget!; this.#page = undefined;
page.set_reveal_child(false);
this.#timeouts.push([ this.#timeouts.push([
timeout(closingPage.transitionDuration, () => { timeout(page.transitionDuration, () => {
this.remove(closingPage); this.remove(page);
onClosed?.(); onClosed?.();
}), }),
onClosed]); onClosed
]);
this.#pageWidget = undefined;
} }
} }
+12 -7
View File
@@ -2,12 +2,14 @@ import { Gtk } from "ags/gtk4";
import { Windows } from "../../windows"; import { Windows } from "../../windows";
import { Wallpaper } from "../../scripts/wallpaper"; import { Wallpaper } from "../../scripts/wallpaper";
import { execApp } from "../../scripts/apps"; import { execApp } from "../../scripts/apps";
import GLib from "gi://GLib?version=2.0";
import { Accessor } from "ags"; import { Accessor } from "ags";
import { createPoll } from "ags/time"; import { createPoll } from "ags/time";
import GLib from "gi://GLib?version=2.0";
import Gio from "gi://Gio?version=2.0";
const userFace: Gio.File = Gio.File.new_for_path(`${GLib.get_home_dir()}/.face`);
const uptime: Accessor<string> = createPoll("Just turned on", 1000, "uptime -p"); const uptime: Accessor<string> = createPoll("Just turned on", 1000, "uptime -p");
function LockButton(): Gtk.Button { function LockButton(): Gtk.Button {
@@ -57,9 +59,13 @@ function LogoutButton(): Gtk.Button {
export const QuickActions = () => export const QuickActions = () =>
<Gtk.Box class={"quickactions"}> <Gtk.Box class={"quickactions"}>
<Gtk.Box orientation={Gtk.Orientation.VERTICAL} halign={Gtk.Align.START} <Gtk.Box halign={Gtk.Align.START} class={"left"} hexpand>
hexpand={true} class={"left"}> {userFace.query_exists(null) &&
<Gtk.Box class={"user-face"} css={
`background-image: url("${userFace.get_path()!}");`}
/>
}
<Gtk.Box orientation={Gtk.Orientation.VERTICAL}>
<Gtk.Label class={"hostname"} xalign={0} tooltipText={"Host name"} <Gtk.Label class={"hostname"} xalign={0} tooltipText={"Host name"}
label={GLib.get_host_name()} /> label={GLib.get_host_name()} />
@@ -69,10 +75,9 @@ export const QuickActions = () =>
label={uptime.as(str => str.replace(/^up /, ""))} /> label={uptime.as(str => str.replace(/^up /, ""))} />
</Gtk.Box> </Gtk.Box>
</Gtk.Box> </Gtk.Box>
</Gtk.Box>
<Gtk.Box class={"right button-row"} halign={Gtk.Align.END} <Gtk.Box class={"right button-row"} halign={Gtk.Align.END} hexpand>
hexpand={true}>
<LockButton /> <LockButton />
<ColorPickerButton /> <ColorPickerButton />
<ScreenshotButton /> <ScreenshotButton />
+20 -28
View File
@@ -3,7 +3,7 @@ import { Wireplumber } from "../../scripts/volume";
import { Pages } from "./Pages"; import { Pages } from "./Pages";
import { PageSound } from "./pages/Sound"; import { PageSound } from "./pages/Sound";
import { PageMicrophone } from "./pages/Microphone"; import { PageMicrophone } from "./pages/Microphone";
import { createBinding, createRoot, With } from "ags"; import { createBinding, With } from "ags";
import AstalWp from "gi://AstalWp"; import AstalWp from "gi://AstalWp";
@@ -11,51 +11,43 @@ import AstalWp from "gi://AstalWp";
export let slidersPages: Pages|undefined; export let slidersPages: Pages|undefined;
export function Sliders() { export function Sliders() {
slidersPages = createRoot(() => new Pages())!;
return <Gtk.Box class={"sliders"} orientation={Gtk.Orientation.VERTICAL} return <Gtk.Box class={"sliders"} orientation={Gtk.Orientation.VERTICAL}
hexpand spacing={10} onDestroy={() => slidersPages = undefined}> hexpand spacing={10} onUnmap={() => slidersPages = undefined}>
<With value={createBinding(Wireplumber.getWireplumber(), "defaultSpeaker")}> <With value={createBinding(Wireplumber.getWireplumber(), "defaultSpeaker")}>
{(sink: AstalWp.Endpoint) => <Gtk.Box class={"sink speaker"} spacing={3}> {(sink: AstalWp.Endpoint) => <Gtk.Box class={"sink speaker"} spacing={3}>
<Gtk.Button onClicked={Wireplumber.getDefault().toggleMuteSink} <Gtk.Button onClicked={() => Wireplumber.getDefault().toggleMuteSink()}
iconName={createBinding(sink, "volumeIcon").as((icon) => iconName={createBinding(sink, "volumeIcon").as((icon) =>
(!Wireplumber.getDefault().isMutedSink() && (!Wireplumber.getDefault().isMutedSink() &&
Wireplumber.getDefault().getSinkVolume() > 0) ? Wireplumber.getDefault().getSinkVolume() > 0
icon ) ? icon : "audio-volume-muted-symbolic"
: "audio-volume-muted-symbolic"
)} /> )} />
<Astal.Slider drawValue={false} hexpand={true} <Astal.Slider drawValue={false} hexpand value={createBinding(sink, "volume")}
$={(self) => self.value = Math.floor(sink.volume * 100)} max={Wireplumber.getDefault().getMaxSinkVolume() / 100}
value={createBinding(sink, "volume").as(v => Math.floor(v * 100))} onChangeValue={(_, __, value) => sink.set_volume(value)} />
max={Wireplumber.getDefault().getMaxSinkVolume()}
onChangeValue={(_, _scrollType, value) => sink.set_volume(value / 100)} />
<Gtk.Button class={"more"} iconName={"go-next-symbolic"} onClicked={(_) => <Gtk.Button class={"more"} iconName={"go-next-symbolic"} onClicked={() =>
slidersPages!.toggle(PageSound())} /> slidersPages?.toggle(PageSound)} />
</Gtk.Box>} </Gtk.Box>}
</With> </With>
<With value={createBinding(Wireplumber.getWireplumber(), "defaultMicrophone")}> <With value={createBinding(Wireplumber.getWireplumber(), "defaultMicrophone")}>
{(source: AstalWp.Endpoint) => <Gtk.Box class={"source microphone"} spacing={3}> {(source: AstalWp.Endpoint) => <Gtk.Box class={"source microphone"} spacing={3}>
<Gtk.Button onClicked={Wireplumber.getDefault().toggleMuteSink} <Gtk.Button onClicked={() => Wireplumber.getDefault().toggleMuteSource()}
iconName={createBinding(source, "volumeIcon").as((icon) => iconName={createBinding(source, "volumeIcon").as((icon) =>
(!Wireplumber.getDefault().isMutedSink() && (!Wireplumber.getDefault().isMutedSource() &&
Wireplumber.getDefault().getSinkVolume() > 0) ? Wireplumber.getDefault().getSourceVolume() > 0
icon ) ? icon : "microphone-sensitivity-muted-symbolic"
: "microphone-sensitivity-muted-symbolic"
)} /> )} />
<Astal.Slider drawValue={false} hexpand={true} <Astal.Slider drawValue={false} hexpand value={createBinding(source, "volume")}
$={(self) => self.value = Math.floor(source.volume * 100)} max={Wireplumber.getDefault().getMaxSourceVolume() / 100}
value={createBinding(source, "volume").as(v => Math.floor(v * 100))} onChangeValue={(_, __, value) => source.set_volume(value)} />
max={Wireplumber.getDefault().getMaxSinkVolume()}
onChangeValue={(_, _scrollType, value) => source.set_volume(value / 100)} />
<Gtk.Button class={"more"} iconName={"go-next-symbolic"} onClicked={(_) => <Gtk.Button class={"more"} iconName={"go-next-symbolic"} onClicked={() =>
slidersPages!.toggle(PageMicrophone())} /> slidersPages?.toggle(PageMicrophone)} />
</Gtk.Box>} </Gtk.Box>}
</With> </With>
{slidersPages} <Pages $={(self) => slidersPages = self} />
</Gtk.Box> </Gtk.Box>
} }
+4 -8
View File
@@ -5,7 +5,6 @@ import { TileDND } from "./tiles/DoNotDisturb";
import { TileRecording } from "./tiles/Recording"; import { TileRecording } from "./tiles/Recording";
import { TileNightLight } from "./tiles/NightLight"; import { TileNightLight } from "./tiles/NightLight";
import { Pages } from "./Pages"; import { Pages } from "./Pages";
import { createRoot } from "/usr/share/ags/js/gnim/src/jsx/scope";
export let TilesPages: Pages|undefined; export let TilesPages: Pages|undefined;
@@ -19,18 +18,15 @@ export const tileList: Array<() => JSX.Element|Gtk.Widget> = [
export function Tiles(): Gtk.Widget { export function Tiles(): Gtk.Widget {
return <Gtk.Box class={"tiles-container"} orientation={Gtk.Orientation.VERTICAL} return <Gtk.Box class={"tiles-container"} orientation={Gtk.Orientation.VERTICAL}
onDestroy={() => TilesPages = undefined} $={(self) => { onUnmap={() => TilesPages = undefined}>
if(!TilesPages)
TilesPages = createRoot(() => new Pages({ class: "tile-pages" }));
self.append(TilesPages!);
}}>
<Gtk.FlowBox orientation={Gtk.Orientation.HORIZONTAL} rowSpacing={6} <Gtk.FlowBox orientation={Gtk.Orientation.HORIZONTAL} rowSpacing={6}
columnSpacing={6} minChildrenPerLine={2} activateOnSingleClick columnSpacing={6} minChildrenPerLine={2} activateOnSingleClick
maxChildrenPerLine={2} hexpand vexpand homogeneous> maxChildrenPerLine={2} hexpand homogeneous>
{tileList.map(t => t())} {tileList.map(t => t())}
</Gtk.FlowBox> </Gtk.FlowBox>
<Pages class={"tile-pages"} $={(self) => TilesPages = self} />
</Gtk.Box> as Gtk.Box; </Gtk.Box> as Gtk.Box;
} }
+44 -32
View File
@@ -4,49 +4,56 @@ import { tr } from "../../../i18n/intl";
import { Windows } from "../../../windows"; import { Windows } from "../../../windows";
import { Notifications } from "../../../scripts/notifications"; import { Notifications } from "../../../scripts/notifications";
import { execApp } from "../../../scripts/apps"; import { execApp } from "../../../scripts/apps";
import { createBinding, createComputed, For, With } from "ags";
import AstalNotifd from "gi://AstalNotifd"; import AstalNotifd from "gi://AstalNotifd";
import AstalBluetooth from "gi://AstalBluetooth"; import AstalBluetooth from "gi://AstalBluetooth";
import { variableToBoolean } from "../../../scripts/utils";
import { createBinding, createComputed, For, With } from "ags";
export const BluetoothPage = () => <Page export const BluetoothPage = new Page({
id={"bluetooth"} title={tr("control_center.pages.bluetooth.title")} id: "bluetooth",
description={tr("control_center.pages.bluetooth.description")} title: tr("control_center.pages.bluetooth.title"),
class={"bluetooth"} headerButtons={[ spacing: 6,
<Gtk.Button class={"discover"} iconName={createBinding( description: tr("control_center.pages.bluetooth.description"),
AstalBluetooth.get_default().adapter, "discovering" headerButtons: [{
).as(discovering => discovering ? icon: createBinding(AstalBluetooth.get_default().adapter, "discovering")
.as(discovering => !discovering ?
"arrow-circular-top-right-symbolic" "arrow-circular-top-right-symbolic"
: "media-playback-stop-symbolic")} tooltipText={ : "media-playback-stop-symbolic"
createBinding(AstalBluetooth.get_default().adapter, "discovering").as((discovering) => ),
!discovering ? tooltipText: createBinding(AstalBluetooth.get_default().adapter, "discovering")
.as((discovering) => !discovering ?
tr("control_center.pages.bluetooth.start_discovering") tr("control_center.pages.bluetooth.start_discovering")
: tr("control_center.pages.bluetooth.stop_discovering"))} : tr("control_center.pages.bluetooth.stop_discovering")),
onClicked={() => { actionClicked: () => {
if(AstalBluetooth.get_default().adapter.discovering) { if(AstalBluetooth.get_default().adapter.discovering) {
AstalBluetooth.get_default().adapter.stop_discovery(); AstalBluetooth.get_default().adapter.stop_discovery();
return; return;
} }
AstalBluetooth.get_default().adapter.start_discovery(); AstalBluetooth.get_default().adapter.start_discovery();
}} }
/> }],
]} actionClosed: () => AstalBluetooth.get_default().adapter.discovering &&
actionClose={() => AstalBluetooth.get_default().adapter.discovering && AstalBluetooth.get_default().adapter.stop_discovery(),
AstalBluetooth.get_default().adapter.stop_discovery()} bottomButtons: [{
bottomButtons={[{
title: tr("control_center.pages.more_settings"), title: tr("control_center.pages.more_settings"),
onClick: () => { actionClicked: () => {
Windows.getDefault().close("control-center"); Windows.getDefault().close("control-center");
execApp("overskride", "[float; animation slide right]"); execApp("overskride", "[float; animation slide right]");
} }
}]} spacing={2}> }],
<Gtk.Box class={"adapters"} visible={variableToBoolean(createBinding( content: () => [
AstalBluetooth.get_default(), "adapters"))} spacing={2}> <Gtk.Box class={"adapters"} visible={createBinding(AstalBluetooth.get_default(), "adapters")
.as(adptrs => adptrs.length > 1)
} spacing={2}>
<Gtk.Label class={"sub-header"} label={tr("control_center.pages.bluetooth.adapters")} /> <Gtk.Label class={"sub-header"} label={tr("control_center.pages.bluetooth.adapters")} />
<With value={createBinding(AstalBluetooth.get_default(), "adapters").as(adpts =>
adpts.length > 1)}>
{(hasMoreAdapters: boolean) => hasMoreAdapters &&
<Gtk.Box orientation={Gtk.Orientation.VERTICAL} spacing={2}>
<For each={createBinding(AstalBluetooth.get_default(), "adapters")}> <For each={createBinding(AstalBluetooth.get_default(), "adapters")}>
{(adapter: AstalBluetooth.Adapter) => {(adapter: AstalBluetooth.Adapter) =>
<PageButton title={adapter.alias ?? "Adapter"} <PageButton title={adapter.alias ?? "Adapter"}
@@ -54,10 +61,13 @@ export const BluetoothPage = () => <Page
} }
</For> </For>
</Gtk.Box> </Gtk.Box>
}
</With>
</Gtk.Box>,
<Gtk.Box class={"connections"} orientation={Gtk.Orientation.VERTICAL} hexpand={true} <Gtk.Box class={"connections"} orientation={Gtk.Orientation.VERTICAL} hexpand={true}
spacing={2}> spacing={2}>
<Gtk.Box class={"paired"} orientation={Gtk.Orientation.VERTICAL} spacing={2} <Gtk.Box class={"paired"} orientation={Gtk.Orientation.VERTICAL} spacing={4}
visible={createBinding(AstalBluetooth.get_default(), "devices").as(devs => visible={createBinding(AstalBluetooth.get_default(), "devices").as(devs =>
devs.filter(dev => dev.paired || dev.connected || dev.trusted).length > 0)}> devs.filter(dev => dev.paired || dev.connected || dev.trusted).length > 0)}>
@@ -68,7 +78,7 @@ export const BluetoothPage = () => <Page
{(dev: AstalBluetooth.Device) => <DeviceWidget device={dev} />} {(dev: AstalBluetooth.Device) => <DeviceWidget device={dev} />}
</For> </For>
</Gtk.Box> </Gtk.Box>
<Gtk.Box class={"discovered"} orientation={Gtk.Orientation.VERTICAL} spacing={2} <Gtk.Box class={"discovered"} orientation={Gtk.Orientation.VERTICAL} spacing={4}
visible={createBinding(AstalBluetooth.get_default(), "devices").as(devs => visible={createBinding(AstalBluetooth.get_default(), "devices").as(devs =>
devs.filter(dev => !dev.connected && !dev.paired && !dev.trusted).length > 0)}> devs.filter(dev => !dev.connected && !dev.paired && !dev.trusted).length > 0)}>
@@ -80,7 +90,8 @@ export const BluetoothPage = () => <Page
</For> </For>
</Gtk.Box> </Gtk.Box>
</Gtk.Box> </Gtk.Box>
</Page> as Page; ]
});
function DeviceWidget({ device }: { device: AstalBluetooth.Device }): Gtk.Widget { function DeviceWidget({ device }: { device: AstalBluetooth.Device }): Gtk.Widget {
return <PageButton class={createBinding(device, "connected").as(conn => return <PageButton class={createBinding(device, "connected").as(conn =>
@@ -93,7 +104,7 @@ function DeviceWidget({ device }: { device: AstalBluetooth.Device }): Gtk.Widget
tooltipText={ tooltipText={
createBinding(device, "connected").as(connected => createBinding(device, "connected").as(connected =>
!connected ? tr("connect") : "") !connected ? tr("connect") : "")
} onClick={() => { } actionClicked={() => {
if(device.connected) return; if(device.connected) return;
let skipConnection: boolean = false; let skipConnection: boolean = false;
@@ -135,8 +146,8 @@ function DeviceWidget({ device }: { device: AstalBluetooth.Device }): Gtk.Widget
createBinding(device, "connected"), createBinding(device, "connected"),
createBinding(device, "trusted") createBinding(device, "trusted")
])}> ])}>
{([connected, trusted]: [boolean, boolean]) => trusted && {([connected, trusted]: [boolean, boolean]) =>
<Gtk.Box class={"button-row"}> <Gtk.Box visible={connected || trusted}>
{<Gtk.Button iconName={connected ? {<Gtk.Button iconName={connected ?
"list-remove-symbolic" "list-remove-symbolic"
: "user-trash-symbolic"} tooltipText={tr(connected ? : "user-trash-symbolic"} tooltipText={tr(connected ?
@@ -157,7 +168,8 @@ function DeviceWidget({ device }: { device: AstalBluetooth.Device }): Gtk.Widget
`control_center.pages.bluetooth.${trusted ? "un" : ""}trust_device` `control_center.pages.bluetooth.${trusted ? "un" : ""}trust_device`
)} onClicked={() => device.set_trusted(!trusted)} )} onClicked={() => device.set_trusted(!trusted)}
/> />
</Gtk.Box>} </Gtk.Box>
}
</With>} </With>}
/> as Page; /> as Gtk.Widget;
} }
+12 -7
View File
@@ -3,22 +3,25 @@ import { Wireplumber } from "../../../scripts/volume";
import { Gtk } from "ags/gtk4"; import { Gtk } from "ags/gtk4";
import { tr } from "../../../i18n/intl"; import { tr } from "../../../i18n/intl";
import { createBinding, For } from "ags"; import { createBinding, For } from "ags";
import AstalWp from "gi://AstalWp?version=0.1";
import { lookupIcon } from "../../../scripts/apps"; import { lookupIcon } from "../../../scripts/apps";
import AstalWp from "gi://AstalWp?version=0.1";
export const PageMicrophone = () =>
<Page id={"microphone"} title={tr("control_center.pages.microphone.title")}
description={tr("control_center.pages.microphone.description")}>
<Gtk.Label class={"sub-header"} label={tr("devices")} xalign={0} /> export const PageMicrophone = new Page({
id: "microphone",
title: tr("control_center.pages.microphone.title"),
description: tr("control_center.pages.microphone.description"),
content: () => [
<Gtk.Label class={"sub-header"} label={tr("devices")} xalign={0} />,
<Gtk.Box orientation={Gtk.Orientation.VERTICAL} spacing={4}>
<For each={createBinding(Wireplumber.getWireplumber().get_audio()!, "microphones")}> <For each={createBinding(Wireplumber.getWireplumber().get_audio()!, "microphones")}>
{(source: AstalWp.Endpoint) => <PageButton class={ {(source: AstalWp.Endpoint) => <PageButton class={
createBinding(source, "isDefault").as(isDefault => isDefault ? "default" : "") createBinding(source, "isDefault").as(isDefault => isDefault ? "default" : "")
} icon={createBinding(source, "icon").as(ico => lookupIcon(ico) ? } icon={createBinding(source, "icon").as(ico => lookupIcon(ico) ?
ico : "audio-input-microphone-symbolic")} title={ ico : "audio-input-microphone-symbolic")} title={
createBinding(source, "description").as(desc => desc ?? "Microphone") createBinding(source, "description").as(desc => desc ?? "Microphone")
} onClick={() => !source.isDefault && source.set_is_default(true)} } actionClicked={() => !source.isDefault && source.set_is_default(true)}
endWidget={ endWidget={
<Gtk.Image iconName={"object-select-symbolic"} visible={ <Gtk.Image iconName={"object-select-symbolic"} visible={
createBinding(source, "isDefault")} css={"font-size: 18px;"} createBinding(source, "isDefault")} css={"font-size: 18px;"}
@@ -26,4 +29,6 @@ export const PageMicrophone = () =>
} }
/>} />}
</For> </For>
</Page> as Page; </Gtk.Box>
]
});
+22 -20
View File
@@ -6,32 +6,34 @@ import { execApp } from "../../../scripts/apps";
import { Notifications } from "../../../scripts/notifications"; import { Notifications } from "../../../scripts/notifications";
import { AskPopup, AskPopupProps } from "../../AskPopup"; import { AskPopup, AskPopupProps } from "../../AskPopup";
import { encoder, variableToBoolean } from "../../../scripts/utils"; import { encoder, variableToBoolean } from "../../../scripts/utils";
import { createBinding, For, With } from "ags";
import GLib from "gi://GLib?version=2.0"; import GLib from "gi://GLib?version=2.0";
import NM from "gi://NM"; import NM from "gi://NM";
import AstalNetwork from "gi://AstalNetwork"; import AstalNetwork from "gi://AstalNetwork";
import { createBinding, For, With } from "ags";
export const PageNetwork = () => export const PageNetwork = new Page({
<Page id={"network"} title={tr("control_center.pages.network.title")} id: "network",
class={"network"} headerButtons={[ title: tr("control_center.pages.network.title"),
<Gtk.Button class={"reload"} iconName={"arrow-circular-top-right-symbolic"} headerButtons: createBinding(AstalNetwork.get_default(), "primary").as(primary =>
visible={createBinding(AstalNetwork.get_default(), "primary").as(primary => primary === AstalNetwork.Primary.WIFI ? [{
primary === AstalNetwork.Primary.WIFI)} icon: "arrow-circular-top-right-symbolic",
tooltipText={"Re-scan networks"} onClicked={() => tooltipText: "Re-scan networks",
AstalNetwork.get_default().wifi.scan()} actionClicked: () => AstalNetwork.get_default().wifi.scan()
/> }] : []
]} bottomButtons={[{ ),
bottomButtons: [{
title: tr("control_center.pages.more_settings"), title: tr("control_center.pages.more_settings"),
onClick: () => { actionClicked: () => {
Windows.getDefault().close("control-center"); Windows.getDefault().close("control-center");
execApp("nm-connection-editor", "[animationstyle gnomed]"); execApp("nm-connection-editor", "[animationstyle gnomed]");
} }
}]}> }],
content: () => [
<Gtk.Box class={"devices"} hexpand={true} orientation={Gtk.Orientation.VERTICAL} <Gtk.Box class={"devices"} hexpand orientation={Gtk.Orientation.VERTICAL}
visible={variableToBoolean(createBinding(AstalNetwork.get_default().client, "devices"))}> visible={variableToBoolean(createBinding(AstalNetwork.get_default().client, "devices"))}
spacing={4}>
<Gtk.Label label={tr("devices")} xalign={0} class={"sub-header"} /> <Gtk.Label label={tr("devices")} xalign={0} class={"sub-header"} />
<For each={createBinding(AstalNetwork.get_default().client, "devices").as(devs => <For each={createBinding(AstalNetwork.get_default().client, "devices").as(devs =>
@@ -52,8 +54,7 @@ export const PageNetwork = () =>
]} ]}
/>} />}
</For> </For>
</Gtk.Box> </Gtk.Box>,
<With value={createBinding(AstalNetwork.get_default(), "primary").as(primary => <With value={createBinding(AstalNetwork.get_default(), "primary").as(primary =>
primary === AstalNetwork.Primary.WIFI)}> primary === AstalNetwork.Primary.WIFI)}>
@@ -95,7 +96,7 @@ export const PageNetwork = () =>
}) })
} }
}}/> }}/>
]} onClick={() => { ]} actionClicked={() => {
const uuid = NM.utils_uuid_generate(); const uuid = NM.utils_uuid_generate();
const ssidBytes = GLib.Bytes.new(encoder.encode(ap.ssid)); const ssidBytes = GLib.Bytes.new(encoder.encode(ap.ssid));
@@ -129,7 +130,8 @@ export const PageNetwork = () =>
</For> </For>
</Gtk.Box>} </Gtk.Box>}
</With> </With>
</Page> as Page; ]
});
function activateWirelessConnection(connection: NM.RemoteConnection, ssid: string): void { function activateWirelessConnection(connection: NM.RemoteConnection, ssid: string): void {
AstalNetwork.get_default().get_client().activate_connection_async( AstalNetwork.get_default().get_client().activate_connection_async(
+10 -9
View File
@@ -5,14 +5,14 @@ import { Astal, Gtk } from "ags/gtk4";
import { addSliderMarksFromMinMax } from "../../../scripts/utils"; import { addSliderMarksFromMinMax } from "../../../scripts/utils";
import { createBinding } from "ags"; import { createBinding } from "ags";
export const PageNightLight = () => export const PageNightLight = new Page({
<Page id={"night-light"} title={tr("control_center.pages.night_light.title")} id: "night-light",
description={tr("control_center.pages.night_light.description")} title: tr("control_center.pages.night_light.title"),
class={"night-light"}> description: tr("control_center.pages.night_light.description"),
content: () => [
<Gtk.Label class={"sub-header"} label={tr( <Gtk.Label class={"sub-header"} label={tr(
"control_center.pages.night_light.temperature" "control_center.pages.night_light.temperature"
)} xalign={0} /> )} xalign={0} />,
<Astal.Slider class={"temperature"} $={(self) => { <Astal.Slider class={"temperature"} $={(self) => {
self.value = NightLight.getDefault().temperature; self.value = NightLight.getDefault().temperature;
addSliderMarksFromMinMax(self, 5, "{}K"); addSliderMarksFromMinMax(self, 5, "{}K");
@@ -24,10 +24,10 @@ export const PageNightLight = () =>
if(type != undefined && type !== null) if(type != undefined && type !== null)
NightLight.getDefault().temperature = Math.floor(value) NightLight.getDefault().temperature = Math.floor(value)
}} }}
/> />,
<Gtk.Label class={"sub-header"} label={tr( <Gtk.Label class={"sub-header"} label={tr(
"control_center.pages.night_light.gamma" "control_center.pages.night_light.gamma"
)} xalign={0} /> )} xalign={0} />,
<Astal.Slider class={"gamma"} $={(self) => { <Astal.Slider class={"gamma"} $={(self) => {
self.value = NightLight.getDefault().gamma; self.value = NightLight.getDefault().gamma;
addSliderMarksFromMinMax(self, 5, "{}%"); addSliderMarksFromMinMax(self, 5, "{}%");
@@ -39,4 +39,5 @@ export const PageNightLight = () =>
NightLight.getDefault().gamma = Math.floor(value) NightLight.getDefault().gamma = Math.floor(value)
}} }}
/> />
</Page> as Page; ]
});
+92 -103
View File
@@ -1,24 +1,21 @@
import { register } from "ags/gobject";
import { Gtk } from "ags/gtk4"; import { Gtk } from "ags/gtk4";
import { Separator } from "../../Separator"; import { Separator } from "../../Separator";
import { Accessor, For } from "ags"; import { Accessor, createRoot } from "ags";
import { transform, transformWidget, variableToBoolean, WidgetNodeType } from "../../../scripts/utils"; import { transformWidget, variableToBoolean, WidgetNodeType } from "../../../scripts/utils";
import Pango from "gi://Pango?version=1.0"; import Pango from "gi://Pango?version=1.0";
export type PageProps = { export type PageProps = {
$?: () => void;
actionClose?: () => void;
id: string; id: string;
class?: string | Accessor<string>; title: string;
title: string | Accessor<string>; description?: string;
description?: string | Accessor<string>; headerButtons?: Array<HeaderButton> | Accessor<Array<HeaderButton>>;
headerButtons?: Array<JSX.Element> | Accessor<Array<JSX.Element>>;
bottomButtons?: Array<BottomButton> | Accessor<Array<BottomButton>>; bottomButtons?: Array<BottomButton> | Accessor<Array<BottomButton>>;
orientation?: Gtk.Orientation | Accessor<Gtk.Orientation>; orientation?: Gtk.Orientation | Accessor<Gtk.Orientation>;
spacing?: number; spacing?: number | Accessor<number>;
children?: WidgetNodeType; content: () => WidgetNodeType;
actionClosed?: () => void;
}; };
export type BottomButton = { export type BottomButton = {
@@ -26,91 +23,92 @@ export type BottomButton = {
description?: string | Accessor<string>; description?: string | Accessor<string>;
tooltipText?: string | Accessor<string>; tooltipText?: string | Accessor<string>;
tooltipMarkup?: string | Accessor<string>; tooltipMarkup?: string | Accessor<string>;
onClick?: () => void; actionClicked?: () => void;
}; };
export { Page }; export type HeaderButton = {
label?: string|Accessor<string>;
icon: string|Accessor<string>;
tooltipText?: string | Accessor<string>;
tooltipMarkup?: string | Accessor<string>;
actionClicked?: () => void;
};
@register({ GTypeName: "Page" }) export class Page {
class Page extends Gtk.Box { #title: string;
#id: string | number = ""; #description?: string;
readonly bottomButtons?: Array<BottomButton> = []; #orientation: Gtk.Orientation|Accessor<
Gtk.Orientation> = Gtk.Orientation.VERTICAL;
#subs: Array<() => void> = []; #spacing: number|Accessor<number> = 4;
#title: string | Accessor<string> = ""; #headerButtons?: Array<HeaderButton>|Accessor<Array<HeaderButton>>;
#description?: string | Accessor<string>; #bottomButtons?: Array<BottomButton>|Accessor<Array<BottomButton>>;
readonly #id?: string;
readonly #create: () => WidgetNodeType;
public get id() { return this.#id; }
public get title() { return this.#title; } public get title() { return this.#title; }
public get description() { return this.#description; } public get description() { return this.#description; }
public get id() { return this.#id; } public get headerButtons() { return this.#headerButtons; }
public actionClose?: () => void = () => {}; public get bottomButtons() { return this.#bottomButtons; }
public readonly actionClosed?: () => void;
constructor(props: PageProps) { constructor(props: PageProps) {
super();
this.set_hexpand(true);
this.set_orientation(Gtk.Orientation.VERTICAL);
if(props.class instanceof Accessor) {
this.#subs.push(props.class.subscribe(() => {
const clss = (props.class as Accessor<string>).get();
this.cssClasses = ["page", ...clss.split(' ').filter(s => s !== "")];
}));
} else {
if(props.class)
this.cssClasses = ["page",
...(props.class as string).split(' ').filter(s => s)];
else
this.add_css_class("page");
}
this.#id = props.id; this.#id = props.id;
this.#title = props.title; this.#title = props.title;
this.#description = props.description; this.#description = props.description;
this.actionClose = props.actionClose; this.#create = props.content;
this.actionClosed = props.actionClosed;
this.prepend(<Gtk.Box class={"header"} orientation={Gtk.Orientation.VERTICAL} if(props.orientation != null)
hexpand={true}> this.#orientation = props.orientation;
<Gtk.Box class={"top"}> if(props.spacing != null)
<Gtk.Label hexpand={true} class={"title"} ellipsize={Pango.EllipsizeMode.END} this.#spacing = props.spacing;
visible={variableToBoolean(props.title)} label={props.title}
halign={Gtk.Align.START} />
{props.headerButtons && <Gtk.Box class={"button-row"} visible={variableToBoolean(props.headerButtons)}> if(props.headerButtons != null)
{ this.#headerButtons = props.headerButtons;
(props.headerButtons instanceof Accessor) ?
<For each={props.headerButtons}>
{(button) => button}
</For>
: props.headerButtons
} }
</Gtk.Box>}
<Gtk.Label class={"description"} hexpand={true} ellipsize={Pango.EllipsizeMode.END} public create(): Gtk.Box {
xalign={0} visible={variableToBoolean(props.description)} label={props.description} /> return createRoot((dispose) =>
<Gtk.Box hexpand class={`page container ${this.#id ?? ""}`} cssName={"page"} name={"page"}
orientation={Gtk.Orientation.VERTICAL} onUnmap={() => dispose()}>
<Gtk.Box class={"header"} orientation={Gtk.Orientation.VERTICAL}>
<Gtk.Box class={"top"} hexpand>
<Gtk.Box orientation={Gtk.Orientation.VERTICAL} hexpand>
<Gtk.Label class={"title"} label={this.#title} xalign={0}
ellipsize={Pango.EllipsizeMode.END} />
<Gtk.Label class={"description"} label={this.#description}
xalign={0} ellipsize={Pango.EllipsizeMode.END}
visible={variableToBoolean(this.#description)} />
</Gtk.Box> </Gtk.Box>
</Gtk.Box> as Gtk.Box); <Gtk.Box class={"button-row"} visible={variableToBoolean(this.#headerButtons)}
hexpand={false}>
this.append(<Gtk.Box class={"content"} spacing={props.spacing ?? 4} hexpand={true} vexpand={true} {this.#headerButtons && transformWidget(this.#headerButtons, (button) =>
orientation={props.orientation ?? Gtk.Orientation.VERTICAL}> <Gtk.Button class={"header-button"} label={button.label}
iconName={button.icon} onClicked={() => button.actionClicked?.()}
tooltipText={button.tooltipText} tooltipMarkup={button.tooltipMarkup}
/>
)}
</Gtk.Box>
</Gtk.Box>
</Gtk.Box>
<Gtk.Box class={"content"} hexpand={false} orientation={this.#orientation}
spacing={this.#spacing}>
{props.children} {this.#create()}
</Gtk.Box> as Gtk.Box); </Gtk.Box>
<Separator alpha={.2} spacing={6} orientation={Gtk.Orientation.VERTICAL}
visible={variableToBoolean(this.#bottomButtons)}
/>
<Gtk.Box class={"bottom-buttons"} orientation={Gtk.Orientation.VERTICAL}
visible={variableToBoolean(this.#bottomButtons)} spacing={2}>
this.append(<Separator alpha={.2} spacing={6} orientation={Gtk.Orientation.VERTICAL} {this.#bottomButtons && transformWidget(this.#bottomButtons, (button) =>
visible={(props.bottomButtons instanceof Accessor) ? <Gtk.Button onClicked={() => button?.actionClicked?.()} tooltipText={button?.tooltipText}
props.bottomButtons.as(buttons => buttons.length > 0)
: (!props.bottomButtons ? false : props.bottomButtons.length > 0)}
/> as Gtk.Widget);
this.append(<Gtk.Box class={"bottom-buttons"} orientation={Gtk.Orientation.VERTICAL}
visible={variableToBoolean(props.bottomButtons)} spacing={2}>
{transformWidget(props.bottomButtons, (button) =>
<Gtk.Button onClicked={button?.onClick} tooltipText={button?.tooltipText}
tooltipMarkup={button?.tooltipMarkup}> tooltipMarkup={button?.tooltipMarkup}>
<Gtk.Label class={"title"} label={button?.title} xalign={0} /> <Gtk.Label class={"title"} label={button?.title} xalign={0} />
@@ -118,50 +116,41 @@ class Page extends Gtk.Box {
xalign={0} visible={variableToBoolean(button?.description)} /> xalign={0} visible={variableToBoolean(button?.description)} />
</Gtk.Button> </Gtk.Button>
)} )}
</Gtk.Box> as Gtk.Box);
props.$?.();
}
}
function BottomButton(props: BottomButton) {
return <Gtk.Button onClicked={props.onClick} tooltipMarkup={props.tooltipMarkup}
tooltipText={props.tooltipText}>
<Gtk.Box orientation={Gtk.Orientation.VERTICAL}>
<Gtk.Label class={"title"} label={props.title} xalign={0} />
<Gtk.Label class={"description"} label={props.description}
visible={Boolean(props.description)} xalign={0} />
</Gtk.Box> </Gtk.Box>
</Gtk.Button> as Gtk.Button; </Gtk.Box> as Gtk.Box
);
} }
export function PageButton({ onDestroy, ...props }: { public static getContent(pageWidget: Gtk.Box) {
return pageWidget.get_first_child()!.get_next_sibling()! as Gtk.Box;
}
}
export function PageButton({ onUnmap, ...props }: {
class?: string | Accessor<string>; class?: string | Accessor<string>;
icon?: string | Accessor<string>; icon?: string | Accessor<string>;
title: string | Accessor<string>; title: string | Accessor<string>;
endWidget?: WidgetNodeType; endWidget?: WidgetNodeType;
description?: string | Accessor<string>; description?: string | Accessor<string>;
extraButtons?: Array<WidgetNodeType> | WidgetNodeType; extraButtons?: Array<WidgetNodeType> | WidgetNodeType;
onDestroy?: (self: Gtk.Box) => void; maxWidthChars?: number | Accessor<number>;
onClick?: (self: Gtk.Button) => void; onUnmap?: (self: Gtk.Box) => void;
actionClicked?: (self: Gtk.Button) => void;
tooltipText?: string | Accessor<string>; tooltipText?: string | Accessor<string>;
tooltipMarkup?: string | Accessor<string>; tooltipMarkup?: string | Accessor<string>;
}) { }): Gtk.Box {
return <Gtk.Box onDestroy={onDestroy}> return <Gtk.Box onUnmap={(self) => onUnmap?.(self)} class={"page-button"}>
<Gtk.Button onClicked={props.onClick} class={props.class} hexpand={true} <Gtk.Button onClicked={props.actionClicked} class={props.class} hexpand
tooltipText={props.tooltipText} tooltipMarkup={props.tooltipMarkup}> tooltipText={props.tooltipText} tooltipMarkup={props.tooltipMarkup}>
<Gtk.Box class={"page-button"} hexpand={true} vexpand={true}> <Gtk.Box class={"container"} hexpand>
{props.icon && <Gtk.Image iconName={props.icon} visible={variableToBoolean(props.icon)} {props.icon && <Gtk.Image iconName={props.icon} visible={variableToBoolean(props.icon)}
css={"font-size: 20px; margin-right: 6px;"} />} css={"font-size: 20px; margin-right: 6px;"} />}
<Gtk.Box orientation={Gtk.Orientation.VERTICAL} hexpand={true} vexpand={false}> <Gtk.Box orientation={Gtk.Orientation.VERTICAL} hexpand vexpand={false}>
<Gtk.Label class={"title"} xalign={0} tooltipText={props.title} <Gtk.Label class={"title"} xalign={0} tooltipText={props.title}
ellipsize={Pango.EllipsizeMode.END} label={ ellipsize={Pango.EllipsizeMode.END} label={props.title}
transform(props.title, (title) => maxWidthChars={props.maxWidthChars ?? 28}
`${title.substring(0, 35)}${title.length > 35 ? '…' : ""}`)
}
/> />
<Gtk.Label class={"description"} xalign={0} visible={variableToBoolean(props.description)} <Gtk.Label class={"description"} xalign={0} visible={variableToBoolean(props.description)}
label={props.description} ellipsize={Pango.EllipsizeMode.END} label={props.description} ellipsize={Pango.EllipsizeMode.END}
@@ -174,7 +163,7 @@ export function PageButton({ onDestroy, ...props }: {
</Gtk.Box> </Gtk.Box>
</Gtk.Button> </Gtk.Button>
<Gtk.Box class={"extra-buttons button-row"} visible={variableToBoolean(props.extraButtons)}> <Gtk.Box class={"extra-buttons"} visible={variableToBoolean(props.extraButtons)}>
{props.extraButtons} {props.extraButtons}
</Gtk.Box> </Gtk.Box>
</Gtk.Box> as Gtk.Box; </Gtk.Box> as Gtk.Box;
+23 -22
View File
@@ -4,16 +4,20 @@ import { getAppIcon, lookupIcon } from "../../../scripts/apps";
import { Wireplumber } from "../../../scripts/volume"; import { Wireplumber } from "../../../scripts/volume";
import { tr } from "../../../i18n/intl"; import { tr } from "../../../i18n/intl";
import { createBinding, For } from "ags"; import { createBinding, For } from "ags";
import AstalWp from "gi://AstalWp";
import { variableToBoolean } from "../../../scripts/utils"; import { variableToBoolean } from "../../../scripts/utils";
import AstalWp from "gi://AstalWp";
import GObject from "gi://GObject?version=2.0"; import GObject from "gi://GObject?version=2.0";
import Pango from "gi://Pango?version=1.0"; import Pango from "gi://Pango?version=1.0";
export const PageSound = () =>
<Page id={"sound"} title={tr("control_center.pages.sound.title")}
description={tr("control_center.pages.sound.description")}>
<Gtk.Label class={"sub-header"} label={tr("devices")} xalign={0} /> export const PageSound = new Page({
id: "sound",
title: tr("control_center.pages.sound.title"),
description: tr("control_center.pages.sound.description"),
content: () => [
<Gtk.Label class={"sub-header"} label={tr("devices")} xalign={0} />,
<Gtk.Box orientation={Gtk.Orientation.VERTICAL} spacing={4}>
<For each={createBinding(Wireplumber.getWireplumber().audio!, "speakers")}> <For each={createBinding(Wireplumber.getWireplumber().audio!, "speakers")}>
{(sink: AstalWp.Endpoint) => {(sink: AstalWp.Endpoint) =>
<PageButton class={createBinding(sink, "isDefault").as(isDefault => <PageButton class={createBinding(sink, "isDefault").as(isDefault =>
@@ -22,7 +26,7 @@ export const PageSound = () =>
lookupIcon(ico) ? ico : "audio-card-symbolic")} lookupIcon(ico) ? ico : "audio-card-symbolic")}
title={createBinding(sink, "description").as(desc => title={createBinding(sink, "description").as(desc =>
desc ?? "Speaker")} desc ?? "Speaker")}
onClick={() => !sink.isDefault && sink.set_is_default(true)} actionClicked={() => !sink.isDefault && sink.set_is_default(true)}
endWidget={ endWidget={
<Gtk.Image iconName={"object-select-symbolic"} <Gtk.Image iconName={"object-select-symbolic"}
visible={createBinding(sink, "isDefault")} visible={createBinding(sink, "isDefault")}
@@ -31,7 +35,8 @@ export const PageSound = () =>
} }
/>} />}
</For> </For>
</Gtk.Box>,
<Gtk.Box orientation={Gtk.Orientation.VERTICAL} spacing={8}>
<Gtk.Label class={"sub-header"} label={tr("apps")} xalign={0} <Gtk.Label class={"sub-header"} label={tr("apps")} xalign={0}
visible={variableToBoolean( visible={variableToBoolean(
createBinding(Wireplumber.getWireplumber().audio!, "streams") createBinding(Wireplumber.getWireplumber().audio!, "streams")
@@ -39,7 +44,7 @@ export const PageSound = () =>
/> />
<For each={createBinding(Wireplumber.getWireplumber().audio!, "streams")}> <For each={createBinding(Wireplumber.getWireplumber().audio!, "streams")}>
{(stream: AstalWp.Stream) => {(stream: AstalWp.Stream) =>
<Gtk.Box hexpand={true} $={(self) => { <Gtk.Box hexpand $={(self) => {
const conns: Map<GObject.Object, Array<number>> = new Map(); const conns: Map<GObject.Object, Array<number>> = new Map();
const controllerMotion = Gtk.EventControllerMotion.new(); const controllerMotion = Gtk.EventControllerMotion.new();
@@ -51,8 +56,8 @@ export const PageSound = () =>
revealer.set_reveal_child(true); revealer.set_reveal_child(true);
}), }),
controllerMotion.connect("leave", () => { controllerMotion.connect("leave", () => {
const revealer = self.get_first_child()!.get_first_child() as Gtk.Revealer; const revealer = self.get_last_child()!.get_first_child() as Gtk.Revealer;
revealer.set_reveal_child(true); revealer.set_reveal_child(false);
}) })
]); ]);
@@ -62,7 +67,6 @@ export const PageSound = () =>
)) ))
]); ]);
}}> }}>
<Gtk.Image iconName={createBinding(stream, "name").as(name => <Gtk.Image iconName={createBinding(stream, "name").as(name =>
getAppIcon(name.split(' ')[0]) ?? "application-x-executable-symbolic")} getAppIcon(name.split(' ')[0]) ?? "application-x-executable-symbolic")}
css={"font-size: 18px; margin-right: 6px;"} /> css={"font-size: 18px; margin-right: 6px;"} />
@@ -71,25 +75,22 @@ export const PageSound = () =>
<Gtk.Revealer transitionDuration={180} <Gtk.Revealer transitionDuration={180}
transitionType={Gtk.RevealerTransitionType.SLIDE_DOWN}> transitionType={Gtk.RevealerTransitionType.SLIDE_DOWN}>
<Gtk.Label label={createBinding(stream, "name").as(name => <Gtk.Label label={createBinding(stream, "description").as(desc =>
name ?? "Unnamed audio stream")} desc ?? "Unnamed audio stream")}
ellipsize={Pango.EllipsizeMode.END} ellipsize={Pango.EllipsizeMode.END}
tooltipText={createBinding(stream, "name")} tooltipText={createBinding(stream, "name")}
class={"name"} xalign={0} class={"name"} xalign={0}
/> />
</Gtk.Revealer> </Gtk.Revealer>
<Astal.Slider drawValue={false} max={100} $={(self) => { <Astal.Slider drawValue={false} value={createBinding(stream, "volume")}
self.value = Math.floor(stream.volume * 100); onChangeValue={(_, __, value) => stream.set_volume(value)}
}} value={createBinding(stream, "volume").as(vol => hexpand min={0} max={1.5}
Math.floor(vol * 100))}
onChangeValue={(_, type, value) => {
if(type !== undefined && type !== null)
stream.volume = Math.floor(value / 100);
}}
/> />
</Gtk.Box> </Gtk.Box>
</Gtk.Box> </Gtk.Box>
} }
</For> </For>
</Page> as Page; </Gtk.Box>
]
});
@@ -13,7 +13,7 @@ export const TileBluetooth = () =>
return connected && connectedDev ? connectedDev.get_alias() : "" return connected && connectedDev ? connectedDev.get_alias() : ""
})} onToggledOn={() => AstalBluetooth.get_default().adapter?.set_powered(true)} })} onToggledOn={() => AstalBluetooth.get_default().adapter?.set_powered(true)}
onToggledOff={() => AstalBluetooth.get_default().adapter?.set_powered(false)} onToggledOff={() => AstalBluetooth.get_default().adapter?.set_powered(false)}
onClickMore={() => TilesPages?.toggle(BluetoothPage())} onClickMore={() => TilesPages?.toggle(BluetoothPage)}
enableOnClickMore={true} iconSize={16} enableOnClickMore={true} iconSize={16}
toggleState={createBinding(AstalBluetooth.get_default(), "isPowered")} toggleState={createBinding(AstalBluetooth.get_default(), "isPowered")}
icon={createComputed([ icon={createComputed([
+3 -3
View File
@@ -31,7 +31,7 @@ export const TileNetwork = () => <Gtk.Box>
})() })()
)} onToggledOn={() => wifi.set_enabled(true)} )} onToggledOn={() => wifi.set_enabled(true)}
onToggledOff={() => wifi.set_enabled(false)} onToggledOff={() => wifi.set_enabled(false)}
onClickMore={() => TilesPages?.toggle(PageNetwork())} onClickMore={() => TilesPages?.toggle(PageNetwork)}
icon={"network-wireless-signal-excellent-symbolic"} icon={"network-wireless-signal-excellent-symbolic"}
toggleState={createBinding(wifi, "enabled")} toggleState={createBinding(wifi, "enabled")}
/> />
@@ -50,7 +50,7 @@ export const TileNetwork = () => <Gtk.Box>
})} })}
onToggledOn={() => execAsync("nmcli n on")} onToggledOn={() => execAsync("nmcli n on")}
onToggledOff={() => execAsync("nmcli n off")} onToggledOff={() => execAsync("nmcli n off")}
onClickMore={() => TilesPages?.toggle(PageNetwork())} onClickMore={() => TilesPages?.toggle(PageNetwork)}
icon={createBinding(wired, "internet").as((internet: AstalNetwork.Internet) => { icon={createBinding(wired, "internet").as((internet: AstalNetwork.Internet) => {
switch(internet) { switch(internet) {
case AstalNetwork.Internet.CONNECTED: case AstalNetwork.Internet.CONNECTED:
@@ -74,7 +74,7 @@ export const TileNetwork = () => <Gtk.Box>
description={tr("disconnected")} description={tr("disconnected")}
onToggledOn={() => execAsync("nmcli n on")} onToggledOn={() => execAsync("nmcli n on")}
onToggledOff={() => execAsync("nmcli n off")} onToggledOff={() => execAsync("nmcli n off")}
onClickMore={() => TilesPages?.toggle(PageNetwork())} onClickMore={() => TilesPages?.toggle(PageNetwork)}
icon={"network-wired-disconnected-symbolic"} icon={"network-wired-disconnected-symbolic"}
iconSize={16} iconSize={16}
toggleState={createBinding(wired, "internet").as((internet: AstalNetwork.Internet) => toggleState={createBinding(wired, "internet").as((internet: AstalNetwork.Internet) =>
@@ -20,6 +20,6 @@ export const TileNightLight = () =>
onToggledOff={() => NightLight.getDefault().identity = true} onToggledOff={() => NightLight.getDefault().identity = true}
onToggledOn={() => NightLight.getDefault().identity = false} onToggledOn={() => NightLight.getDefault().identity = false}
enableOnClickMore={true} enableOnClickMore={true}
onClickMore={() => TilesPages?.toggle(PageNightLight())} onClickMore={() => TilesPages?.toggle(PageNightLight)}
toggleState={createBinding(NightLight.getDefault(), "identity").as(identity => !identity)} toggleState={createBinding(NightLight.getDefault(), "identity").as(identity => !identity)}
/> />
@@ -3,7 +3,6 @@ import { Recording } from "../../../scripts/recording";
import { tr } from "../../../i18n/intl"; import { tr } from "../../../i18n/intl";
import { isInstalled, time } from "../../../scripts/utils"; import { isInstalled, time } from "../../../scripts/utils";
import { createBinding, createComputed } from "ags"; import { createBinding, createComputed } from "ags";
import { Gtk } from "ags/gtk4";
export const TileRecording = () => export const TileRecording = () =>
+3 -2
View File
@@ -15,7 +15,7 @@ export type TileProps = {
description?: string | Accessor<string>; description?: string | Accessor<string>;
toggleState?: boolean | Accessor<boolean>; toggleState?: boolean | Accessor<boolean>;
enableOnClickMore?: boolean | Accessor<boolean>; enableOnClickMore?: boolean | Accessor<boolean>;
onDestroy?: (self: Gtk.Box) => void; onUnmap?: (self: Gtk.Box) => void;
onToggledOn: () => void; onToggledOn: () => void;
onToggledOff: () => void; onToggledOff: () => void;
onClickMore?: () => void; onClickMore?: () => void;
@@ -35,7 +35,8 @@ export function Tile(props: TileProps): Gtk.Widget {
onCleanup(() => subs.forEach(s => s())); onCleanup(() => subs.forEach(s => s()));
return <Gtk.Box hexpand visible={props.visible} onDestroy={props.onDestroy} class={ return <Gtk.Box hexpand visible={props.visible} onUnmap={props.onUnmap}
canFocus focusable={false} class={
(props.class instanceof Accessor) ? (props.class instanceof Accessor) ?
createComputed([props.class, toggled], (clss, isToggled) => createComputed([props.class, toggled], (clss, isToggled) =>
`tile ${clss} ${isToggled ? "toggled" : ""} ${ `tile ${clss} ${isToggled ? "toggled" : ""} ${