diff --git a/package.json b/package.json index 44f95a4..a5b3eb6 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,7 @@ "sync-config": "sh ./scripts/sync-config.sh" }, "devDependencies": { - "ags": "link:../../../../usr/share/ags/js" - }, - "dependencies": { + "ags": "link:../../../../usr/share/ags/js", "gnim-utils": "github:retrozinndev/gnim-utils" } } diff --git a/resources/styles/_control-center.scss b/resources/styles/_control-center.scss index 2a76c28..ae5717b 100644 --- a/resources/styles/_control-center.scss +++ b/resources/styles/_control-center.scss @@ -203,7 +203,7 @@ $radius: 18px; $padding: 4px; - background: rgba(colors.$bg-primary, .5); + background: color.scale($color: colors.$bg-primary, $lightness: -25%); border-radius: $radius; padding: $padding; min-height: 40px; diff --git a/src/modules/bluetooth.ts b/src/modules/bluetooth.ts index c96baea..3a8fe91 100644 --- a/src/modules/bluetooth.ts +++ b/src/modules/bluetooth.ts @@ -4,6 +4,7 @@ import { userData } from "../config"; import GObject, { getter, gtype, property, register, setter } from "ags/gobject"; import AstalBluetooth from "gi://AstalBluetooth"; +import { createScopedConnection } from "gnim-utils"; /** AstalBluetooth helper (implements the default adapter feature) */ @@ -62,7 +63,7 @@ export class Bluetooth extends GObject.Object { constructor() { super(); - createRoot((_) => { + createRoot(async () => { this.#scope = getScope(); if(this.astalBl.adapters.length > 0) { @@ -77,37 +78,33 @@ export class Bluetooth extends GObject.Object { if(dataDefaultAdapter !== undefined && foundAdapter !== undefined) this.adapter = foundAdapter; - this.#connections.set( - AstalBluetooth.get_default(), [ - AstalBluetooth.get_default().connect("adapter-added", (self, adapter) => { - if(self.adapters.length === 1) // adapter was just added - this.adapter = adapter; - }), - AstalBluetooth.get_default().connect("adapter-removed", (self, adapter) => { - if(self.adapters.length < 1) { - this.adapter = null; - this.#isAvailable = false; - this.notify("is-available"); - } + createScopedConnection(AstalBluetooth.get_default(), "adapter-added", (adapter) => { + if(this.astalBl.adapters.length === 1) // adapter was just added + this.adapter = adapter; + }); + createScopedConnection(AstalBluetooth.get_default(), "adapter-removed", (adapter) => { + if(this.astalBl.adapters.length < 1) { + this.adapter = null; + this.#isAvailable = false; + this.notify("is-available"); + } - if(this.#adapter?.address !== adapter.address) - return; + if(this.#adapter?.address !== adapter.address) + return; - // the removed adapter was the default + // the removed adapter was the default - if(self.adapters.length < 1) { - this.adapter = null; - this.#isAvailable = false; - this.notify("is-available"); + if(this.astalBl.adapters.length < 1) { + this.adapter = null; + this.#isAvailable = false; + this.notify("is-available"); - return; - } - - this.#adapter = self.adapters[0]; - }) - ] - ); + return; + } + this.#adapter = this.astalBl.adapters[0]; + }); + // async to prevent slow start setTimeout(() => { this.#lastDevice = this.getLastConnectedDevice(); diff --git a/src/modules/utils.ts b/src/modules/utils.ts index c3aef5e..663c8d4 100644 --- a/src/modules/utils.ts +++ b/src/modules/utils.ts @@ -2,12 +2,7 @@ import { createPoll } from "ags/time"; import { exec, execAsync } from "ags/process"; import { Astal, Gtk } from "ags/gtk4"; import { getSymbolicIcon } from "./apps"; - -import GLib from "gi://GLib?version=2.0"; -import Gio from "gi://Gio?version=2.0"; - export { - type JSXNode as WidgetNodeType, toBoolean as variableToBoolean, construct, transform, @@ -19,6 +14,9 @@ export { createSecureAccessorBinding as secureBaseBinding, } from "gnim-utils"; +import GLib from "gi://GLib?version=2.0"; +import Gio from "gi://Gio?version=2.0"; + export const decoder = new TextDecoder("utf-8"), encoder = new TextEncoder(); diff --git a/src/widget/CustomDialog.tsx b/src/widget/CustomDialog.tsx index 2062d42..24ab17a 100644 --- a/src/widget/CustomDialog.tsx +++ b/src/widget/CustomDialog.tsx @@ -3,8 +3,8 @@ import { Windows } from "../windows"; import { getPopupWindowContainer, PopupWindow } from "./PopupWindow"; import { Separator } from "./Separator"; import { tr } from "../i18n/intl"; -import { Accessor } from "ags"; -import { transformWidget, variableToBoolean, WidgetNodeType } from "../modules/utils"; +import { Accessor, Node } from "ags"; +import { transformWidget, variableToBoolean } from "../modules/utils"; export type CustomDialogProps = { @@ -16,7 +16,7 @@ export type CustomDialogProps = { heightRequest?: number | Accessor; widthRequest?: number | Accessor; childOrientation?: Gtk.Orientation | Accessor; - children?: WidgetNodeType; + children?: Node; onFinish?: () => void; options?: Array | Accessor>; optionsOrientation?: Gtk.Orientation | Accessor; diff --git a/src/window/bar/widgets/Clock.tsx b/src/window/bar/widgets/Clock.tsx index 34a128b..583621b 100644 --- a/src/window/bar/widgets/Clock.tsx +++ b/src/window/bar/widgets/Clock.tsx @@ -8,12 +8,7 @@ import { generalConfig } from "../../../config"; export const Clock = () => `clock ${wins.includes("center-window") ? "open" : ""}`)} - $={(self) => { - const conns: Array = [ - self.connect("clicked", (_) => Windows.getDefault().toggle("center-window")), - self.connect("destroy", (_) => conns.forEach(id => self.disconnect(id))) - ]; - }} + onClicked={() => Windows.getDefault().toggle("center-window")} label={time((dt) => dt.format( generalConfig.getProperty("clock.date_format", "string")) ?? "An error occurred" diff --git a/src/window/control-center/widgets/Page.tsx b/src/window/control-center/widgets/Page.tsx index 369235f..2f3183f 100644 --- a/src/window/control-center/widgets/Page.tsx +++ b/src/window/control-center/widgets/Page.tsx @@ -1,24 +1,13 @@ import { Gtk } from "ags/gtk4"; import { Separator } from "../../../widget/Separator"; -import { Accessor, createRoot } from "ags"; -import { transformWidget, variableToBoolean, WidgetNodeType } from "../../../modules/utils"; +import { Accessor, createBinding, createRoot, For, Node } from "ags"; +import { gtype, property, register } from "ags/gobject"; +import { variableToBoolean } from "../../../modules/utils"; import Pango from "gi://Pango?version=1.0"; +import GObject from "gi://GObject?version=2.0"; -export type PageProps = { - id: string; - title: string; - description?: string; - $?: (self: Gtk.Box) => void; - headerButtons?: Array | Accessor>; - bottomButtons?: Array | Accessor>; - orientation?: Gtk.Orientation | Accessor; - spacing?: number | Accessor; - content: () => WidgetNodeType; - actionClosed?: () => void; -}; - export type BottomButton = { title: string | Accessor; description?: string | Accessor; @@ -35,93 +24,129 @@ export type HeaderButton = { actionClicked?: () => void; }; -export class Page { - #title: string; - #description?: string; - #orientation: Gtk.Orientation|Accessor< - Gtk.Orientation> = Gtk.Orientation.VERTICAL; - #spacing: number|Accessor = 4; - #headerButtons?: Array|Accessor>; - #bottomButtons?: Array|Accessor>; - #setup?: (self: Gtk.Box) => void; - readonly #id?: string; - readonly #create: () => WidgetNodeType; +@register({ GTypeName: "Page" }) +export class Page extends GObject.Object { + readonly #id: string; + readonly #create: () => Node; - public get id() { return this.#id; } - public get title() { return this.#title; } - public get description() { return this.#description; } - public get headerButtons() { return this.#headerButtons; } - public get bottomButtons() { return this.#bottomButtons; } public readonly actionClosed?: () => void; + public readonly actionOpen?: () => void; + public get id() { return this.#id; } + + @property(String) + title: string; + + @property(gtype(String)) + description: string|null = null; + + @property(gtype(Number)) + orientation: Gtk.Orientation = Gtk.Orientation.VERTICAL; + + @property(Number) + spacing: number = 4; + + @property(Array) + headerButtons: Array = []; + @property(Array) + bottomButtons: Array = []; + + + constructor(props: { + id: string; + title: string; + description?: string; + headerButtons?: Array; + bottomButtons?: Array; + orientation?: Gtk.Orientation; + spacing?: number; + content: () => Node; + actionOpen?: () => void; + actionClosed?: () => void; + }) { + super(); - constructor(props: PageProps) { this.#id = props.id; - this.#title = props.title; - this.#description = props.description; this.#create = props.content; + + this.title = props.title; this.actionClosed = props.actionClosed; + this.actionOpen = props.actionOpen; if(props.orientation != null) - this.#orientation = props.orientation; + this.orientation = props.orientation; + + if(props.description != null) + this.description = props.description; if(props.spacing != null) - this.#spacing = props.spacing; + this.spacing = props.spacing; if(props.headerButtons != null) - this.#headerButtons = props.headerButtons; + this.headerButtons = props.headerButtons; - if(props.$ != null) - this.#setup = props.$; + if(props.bottomButtons != null) + this.bottomButtons = props.bottomButtons; + + if(props.actionOpen != null) + this.actionOpen = props.actionOpen; + + if(props.actionClosed != null) + this.actionClosed = props.actionClosed; } public create(): Gtk.Box { return createRoot((dispose) => dispose()} - $={this.#setup}> + orientation={Gtk.Orientation.VERTICAL} + onDestroy={() => dispose()}> - - + + desc ?? "" + )} xalign={0} ellipsize={Pango.EllipsizeMode.END} + visible={variableToBoolean(createBinding(this, "description"))} /> - + - {this.#headerButtons && transformWidget(this.#headerButtons, (button) => - button.actionClicked?.()} - tooltipText={button.tooltipText} tooltipMarkup={button.tooltipMarkup} - /> - )} + + {(button: HeaderButton) => + button.actionClicked?.()} + tooltipText={button.tooltipText} tooltipMarkup={button.tooltipMarkup} + /> + } + - + {this.#create()} + visible={variableToBoolean(createBinding(this, "bottomButtons"))} spacing={2}> - {this.#bottomButtons && transformWidget(this.#bottomButtons, (button) => - button?.actionClicked?.()} tooltipText={button?.tooltipText} - tooltipMarkup={button?.tooltipMarkup}> - - - - - )} + + {(button: BottomButton) => + button.actionClicked?.()} + tooltipText={button.tooltipText} + tooltipMarkup={button.tooltipMarkup} + title={button.title} + description={button.description} + /> + } + as Gtk.Box ); @@ -136,9 +161,9 @@ export function PageButton({ onUnmap, ...props }: { class?: string | Accessor; icon?: string | Accessor; title: string | Accessor; - endWidget?: WidgetNodeType; + endWidget?: Node; description?: string | Accessor; - extraButtons?: Array | WidgetNodeType; + extraButtons?: Node; maxWidthChars?: number | Accessor; onUnmap?: (self: Gtk.Box) => void; actionClicked?: (self: Gtk.Button) => void; @@ -170,7 +195,7 @@ export function PageButton({ onUnmap, ...props }: { - {props.extraButtons} + {props.extraButtons as Node} as Gtk.Box; } diff --git a/src/window/control-center/widgets/pages/Backlight.tsx b/src/window/control-center/widgets/pages/Backlight.tsx index 02f6175..60f5c9f 100644 --- a/src/window/control-center/widgets/pages/Backlight.tsx +++ b/src/window/control-center/widgets/pages/Backlight.tsx @@ -7,11 +7,11 @@ import { addSliderMarksFromMinMax } from "../../../../modules/utils"; import { userData } from "../../../../config"; -export const PageBacklight = new Page({ - id: "backlight", - title: tr("control_center.pages.backlight.title"), - description: tr("control_center.pages.backlight.description"), - $: () => { +export const PageBacklight = { const dataDefaultBacklight = userData.getProperty("control_center.default_backlight", "any"); if(typeof dataDefaultBacklight === "string" && Backlights.getDefault().default?.name !== dataDefaultBacklight) { @@ -21,8 +21,8 @@ export const PageBacklight = new Page({ Backlights.getDefault().setDefault(bk); } - }, - content: () => ( + }} + content={() => ( {(bklights: Array) => bklights.length > 0 && @@ -75,10 +75,10 @@ export const PageBacklight = new Page({ } - ), - headerButtons: [{ + )} + headerButtons={[{ icon: "arrow-circular-top-right", tooltipText: tr("control_center.pages.backlight.refresh"), actionClicked: () => Backlights.getDefault().scan() - }] -}); + }]} +/> as Page; diff --git a/src/window/control-center/widgets/pages/Bluetooth.tsx b/src/window/control-center/widgets/pages/Bluetooth.tsx index eb9b0a0..79d6c9f 100644 --- a/src/window/control-center/widgets/pages/Bluetooth.tsx +++ b/src/window/control-center/widgets/pages/Bluetooth.tsx @@ -5,6 +5,7 @@ import { Windows } from "../../../../windows"; import { Notifications } from "../../../../modules/notifications"; import { execApp } from "../../../../modules/apps"; import { createBinding, createComputed, For, With } from "ags"; +import { variableToBoolean } from "../../../../modules/utils"; import { Bluetooth } from "../../../../modules/bluetooth"; import AstalNotifd from "gi://AstalNotifd"; @@ -13,12 +14,12 @@ import Adw from "gi://Adw?version=1"; import Gio from "gi://Gio?version=2.0"; -export const BluetoothPage = new Page({ - id: "bluetooth", - title: tr("control_center.pages.bluetooth.title"), - spacing: 6, - description: tr("control_center.pages.bluetooth.description"), - headerButtons: createBinding(Bluetooth.getDefault(), "adapter").as(adapter => adapter ? [{ +export const BluetoothPage = adapter ? [{ icon: createBinding(adapter, "discovering") .as(discovering => !discovering ? "arrow-circular-top-right-symbolic" @@ -36,20 +37,25 @@ export const BluetoothPage = new Page({ adapter.start_discovery(); } - }]: []), - actionClosed: () => Bluetooth.getDefault().adapter?.discovering && - Bluetooth.getDefault().adapter?.stop_discovery(), - bottomButtons: [{ + }]: [])} + actionClosed={() => Bluetooth.getDefault().adapter?.discovering && + Bluetooth.getDefault().adapter?.stop_discovery()} + bottomButtons={[{ title: tr("control_center.pages.more_settings"), actionClicked: () => { Windows.getDefault().close("control-center"); execApp("overskride", "[float; animation slide right]"); } - }], - content: () => { - const adapter = createBinding(Bluetooth.getDefault(), "adapter"); + }]} + content={() => { const adapters = createBinding(AstalBluetooth.get_default(), "adapters"); const devices = createBinding(AstalBluetooth.get_default(), "devices"); + const knownDevices = devices.as(devs => devs.filter(dev => + dev.trusted || dev.paired || dev.connected + ).sort(dev => dev.connected ? 1 : 0)); + const discoveredDevices = devices.as(devs => devs.filter(dev => + !dev.trusted && !dev.paired && !dev.connected) + ); return [ adptrs.length > 1) @@ -86,40 +92,26 @@ export const BluetoothPage = new Page({ spacing={2}> devs.filter(dev => - (dev.adapter as AstalBluetooth.Adapter).address === adapter.get()?.address && - dev.paired || dev.connected || dev.trusted).length > 0) - }> + visible={variableToBoolean(knownDevices)}> - devs.filter(dev => - (dev.adapter as AstalBluetooth.Adapter).address === adapter.get()?.address && - dev.paired || dev.connected || dev.trusted)) - }> - + {(dev: AstalBluetooth.Device) => } devs.filter(dev => - (dev.adapter as AstalBluetooth.Adapter).address === adapter.get()?.address && - !dev.connected && !dev.paired && !dev.trusted).length > 0) - }> + visible={variableToBoolean(discoveredDevices)}> - devs.filter(dev => - (dev.adapter as AstalBluetooth.Adapter).address === adapter.get()?.address && - !dev.connected && !dev.paired && !dev.trusted)) - }> - + {(dev: AstalBluetooth.Device) => } ]; - } -}); + }} +/> as Page; function DeviceWidget({ device }: { device: AstalBluetooth.Device }): Gtk.Widget { const pair = async () => { diff --git a/src/window/control-center/widgets/pages/Microphone.tsx b/src/window/control-center/widgets/pages/Microphone.tsx index 455bd21..4cb3115 100644 --- a/src/window/control-center/widgets/pages/Microphone.tsx +++ b/src/window/control-center/widgets/pages/Microphone.tsx @@ -8,11 +8,11 @@ import { lookupIcon } from "../../../../modules/apps"; import AstalWp from "gi://AstalWp?version=0.1"; -export const PageMicrophone = new Page({ - id: "microphone", - title: tr("control_center.pages.microphone.title"), - description: tr("control_center.pages.microphone.description"), - content: () => [ +export const PageMicrophone = [ , @@ -30,5 +30,5 @@ export const PageMicrophone = new Page({ />} - ] -}); + ]} +/> as Page; diff --git a/src/window/control-center/widgets/pages/Network.tsx b/src/window/control-center/widgets/pages/Network.tsx index 714653e..8d58ed8 100644 --- a/src/window/control-center/widgets/pages/Network.tsx +++ b/src/window/control-center/widgets/pages/Network.tsx @@ -13,24 +13,24 @@ import NM from "gi://NM"; import AstalNetwork from "gi://AstalNetwork"; -export const PageNetwork = new Page({ - id: "network", - title: tr("control_center.pages.network.title"), - headerButtons: createBinding(AstalNetwork.get_default(), "primary").as(primary => +export const PageNetwork = primary === AstalNetwork.Primary.WIFI ? [{ icon: "arrow-circular-top-right-symbolic", tooltipText: "Re-scan networks", actionClicked: () => AstalNetwork.get_default().wifi.scan() }] : [] - ), - bottomButtons: [{ + )} + bottomButtons={[{ title: tr("control_center.pages.more_settings"), actionClicked: () => { Windows.getDefault().close("control-center"); execApp("nm-connection-editor", "[animationstyle gnomed]"); } - }], - content: () => [ + }]} + content={() => [ @@ -130,8 +130,8 @@ export const PageNetwork = new Page({ } - ] -}); + ]} +/> as Page; function activateWirelessConnection(connection: NM.RemoteConnection, ssid: string): void { AstalNetwork.get_default().get_client().activate_connection_async( diff --git a/src/window/control-center/widgets/pages/NightLight.tsx b/src/window/control-center/widgets/pages/NightLight.tsx index d894878..96dd3cd 100644 --- a/src/window/control-center/widgets/pages/NightLight.tsx +++ b/src/window/control-center/widgets/pages/NightLight.tsx @@ -5,11 +5,12 @@ import { Astal, Gtk } from "ags/gtk4"; import { addSliderMarksFromMinMax } from "../../../../modules/utils"; import { createBinding } from "ags"; -export const PageNightLight = new Page({ - id: "night-light", - title: tr("control_center.pages.night_light.title"), - description: tr("control_center.pages.night_light.description"), - content: () => [ + +export const PageNightLight = [ , @@ -39,5 +40,5 @@ export const PageNightLight = new Page({ NightLight.getDefault().gamma = Math.floor(value) }} /> - ] -}); + ]} +/> as Page; diff --git a/src/window/control-center/widgets/pages/Sound.tsx b/src/window/control-center/widgets/pages/Sound.tsx index 7bba0d7..9b80459 100644 --- a/src/window/control-center/widgets/pages/Sound.tsx +++ b/src/window/control-center/widgets/pages/Sound.tsx @@ -4,18 +4,18 @@ import { getAppIcon, lookupIcon } from "../../../../modules/apps"; import { Wireplumber } from "../../../../modules/volume"; import { tr } from "../../../../i18n/intl"; import { createBinding, For } from "ags"; -import { variableToBoolean } from "../../../../modules/utils"; +import { createScopedConnection, variableToBoolean } from "../../../../modules/utils"; import AstalWp from "gi://AstalWp"; import GObject from "gi://GObject?version=2.0"; import Pango from "gi://Pango?version=1.0"; -export const PageSound = new Page({ - id: "sound", - title: tr("control_center.pages.sound.title"), - description: tr("control_center.pages.sound.description"), - content: () => [ +export const PageSound = [ , @@ -45,27 +45,17 @@ export const PageSound = new Page({ {(stream: AstalWp.Stream) => { - const conns: Map> = new Map(); const controllerMotion = Gtk.EventControllerMotion.new(); self.add_controller(controllerMotion); - - conns.set(controllerMotion, [ - controllerMotion.connect("enter", () => { - const revealer = self.get_last_child()!.get_first_child() as Gtk.Revealer; - revealer.set_reveal_child(true); - }), - controllerMotion.connect("leave", () => { - const revealer = self.get_last_child()!.get_first_child() as Gtk.Revealer; - revealer.set_reveal_child(false); - }) - ]); - - conns.set(self, [ - self.connect("destroy", () => conns.forEach((ids, obj) => - ids.forEach(id => obj.disconnect(id)) - )) - ]); + createScopedConnection(controllerMotion, "enter", () => { + const revealer = self.get_last_child()!.get_first_child() as Gtk.Revealer; + revealer.set_reveal_child(true); + }); + createScopedConnection(controllerMotion, "leave", () => { + const revealer = self.get_last_child()!.get_first_child() as Gtk.Revealer; + revealer.set_reveal_child(false); + }); }}> getAppIcon(name.split(' ')[0]) ?? "application-x-executable-symbolic")} @@ -92,5 +82,5 @@ export const PageSound = new Page({ } - ] -}); + ]} +/> as Page; diff --git a/src/window/control-center/widgets/pages/index.tsx b/src/window/control-center/widgets/pages/index.tsx index 867f86e..1c7fd8b 100644 --- a/src/window/control-center/widgets/pages/index.tsx +++ b/src/window/control-center/widgets/pages/index.tsx @@ -1,26 +1,27 @@ -import { register } from "ags/gobject"; +import GObject, { getter, gtype, register } from "ags/gobject"; import { Gtk } from "ags/gtk4"; import { Page } from "../Page"; import GLib from "gi://GLib?version=2.0"; -export type PagesProps = { - initialPage?: Page; - transitionDuration?: number; -}; - @register({ GTypeName: "Pages" }) export class Pages extends Gtk.Box { #timeouts: Array<[GLib.Source, (() => void)|undefined]> = []; - #page: (Page|undefined); + #page: Page|undefined; #transDuration: number; #transType: Gtk.RevealerTransitionType = Gtk.RevealerTransitionType.SLIDE_DOWN; + @getter(Boolean) get isOpen() { return Boolean(this.#page); } + + @getter(gtype(Page)) get page() { return this.#page; } - constructor(props?: PagesProps) { + constructor(props?: { + initialPage?: Page; + transitionDuration?: number; + }) { super({ orientation: Gtk.Orientation.VERTICAL, cssName: "pages", @@ -36,7 +37,9 @@ export class Pages extends Gtk.Box { const destroyId = this.connect("destroy", () => { - this.disconnect(destroyId); + GObject.signal_handler_is_connected(this, destroyId) && + this.disconnect(destroyId); + this.#timeouts.forEach((tmout) => { tmout[0].destroy(); (async () => tmout[1]?.())().catch((err: Error) => { diff --git a/src/window/control-center/widgets/tiles/Bluetooth.tsx b/src/window/control-center/widgets/tiles/Bluetooth.tsx index df2ae1a..2199f48 100644 --- a/src/window/control-center/widgets/tiles/Bluetooth.tsx +++ b/src/window/control-center/widgets/tiles/Bluetooth.tsx @@ -9,12 +9,16 @@ import AstalBluetooth from "gi://AstalBluetooth"; export const TileBluetooth = () => { - if(!connected) return ""; + description={createComputed([ + createBinding(Bluetooth.getDefault(), "adapter"), + createBinding(AstalBluetooth.get_default(), "devices") + ], (adapter, devices) => { + const lastConnectedDevice = devices.filter(d => d.connected)[devices.length - 1]; - const connectedDevs = AstalBluetooth.get_default().devices.filter(dev => dev.connected); - const connectedDev = connectedDevs[connectedDevs.length - 1]; // last connected device is on display - return connectedDev ? connectedDev.get_alias() : "" + if(!adapter || !lastConnectedDevice) + return ""; + + return lastConnectedDevice.alias; })} onEnabled={() => Bluetooth.getDefault().adapter?.set_powered(true)} onDisabled={() => Bluetooth.getDefault().adapter?.set_powered(false)} diff --git a/src/window/control-center/widgets/tiles/index.tsx b/src/window/control-center/widgets/tiles/index.tsx index 8744a34..5de9bbc 100644 --- a/src/window/control-center/widgets/tiles/index.tsx +++ b/src/window/control-center/widgets/tiles/index.tsx @@ -5,6 +5,7 @@ import { TileDND } from "./DoNotDisturb"; import { TileRecording } from "./Recording"; import { TileNightLight } from "./NightLight"; import { Pages } from "../pages"; +import { createRoot, getScope } from "ags"; export let TilesPages: Pages|undefined; @@ -17,16 +18,20 @@ export const tileList: Array<() => JSX.Element|Gtk.Widget> = [ ] as Array<() => Gtk.Widget>; export function Tiles(): Gtk.Widget { - return TilesPages = undefined}> + return createRoot((dispose) => { + getScope().onCleanup(() => TilesPages = undefined); - + return dispose()}> - {tileList.map(t => t())} - + - TilesPages = self} /> - as Gtk.Box; + {tileList.map(t => t())} + + + TilesPages = self} /> + as Gtk.Box; + }); } diff --git a/src/windows.ts b/src/windows.ts index 3eb3fbe..7e45d05 100644 --- a/src/windows.ts +++ b/src/windows.ts @@ -203,7 +203,10 @@ export class Windows extends GObject.Object { this.#scope.onMount(scope.dispose); - scope.onCleanup(() => instance.disconnect(connection)); + scope.onCleanup(() => + GObject.signal_handler_is_connected(instance, connection) && + instance.disconnect(connection) + ); return instance; }) @@ -231,7 +234,10 @@ export class Windows extends GObject.Object { const connection = instance.connect("close-request", () => dispose()); this.#scope.onMount(dispose) - scope.onCleanup(() => instance.disconnect(connection)); + scope.onCleanup(() => + GObject.signal_handler_is_connected(instance, connection) && + instance.disconnect(connection) + ); return instance; });