diff --git a/home/desktops/hyprland/ags/config.js b/home/desktops/hyprland/ags/config.js index da2476d..a9f8f3c 100644 --- a/home/desktops/hyprland/ags/config.js +++ b/home/desktops/hyprland/ags/config.js @@ -1,22 +1,2 @@ -import { readFile } from "resource:///com/github/Aylur/ags/utils.js"; -import App from "resource:///com/github/Aylur/ags/app.js"; -import { timeout } from "resource:///com/github/Aylur/ags/utils.js"; -const pkgjson = JSON.parse(readFile(App.configDir + "/package.json")); - -timeout(1000, () => JSON.stringify(App)); - -const v = { - ags: `v${pkg.version}`, - expected: `v${pkgjson.version}`, -}; - -function mismatch() { - print(`my config expects ${v.expected}, but your ags is ${v.ags}`); - App.connect("config-parsed", (app) => app.Quit()); - return {}; -} - -/*export default v.ags === v.expected - ? (await import("./js/main.js")).default - : mismatch();*/ -export default (await import("./js/main.js")).default; +import { default as main } from "./js/main.js"; +export default main; diff --git a/home/desktops/hyprland/ags/js/about/about.js b/home/desktops/hyprland/ags/js/about/about.js index b4f1877..242fe56 100644 --- a/home/desktops/hyprland/ags/js/about/about.js +++ b/home/desktops/hyprland/ags/js/about/about.js @@ -1,6 +1,3 @@ -import App from "resource:///com/github/Aylur/ags/app.js"; -import * as Utils from "resource:///com/github/Aylur/ags/utils.js"; -import Widget from "resource:///com/github/Aylur/ags/widget.js"; import PopupWindow from "../misc/PopupWindow.js"; import icons from "../icons.js"; diff --git a/home/desktops/hyprland/ags/js/applauncher/AppItem.js b/home/desktops/hyprland/ags/js/applauncher/AppItem.js index e607483..6fd7a34 100644 --- a/home/desktops/hyprland/ags/js/applauncher/AppItem.js +++ b/home/desktops/hyprland/ags/js/applauncher/AppItem.js @@ -1,7 +1,4 @@ -import Widget from "resource:///com/github/Aylur/ags/widget.js"; -import App from "resource:///com/github/Aylur/ags/app.js"; import options from "../options.js"; -import { lookUpIcon } from "resource:///com/github/Aylur/ags/utils.js"; /** @param {import('resource:///com/github/Aylur/ags/service/applications.js').Application} app */ export default (app) => { @@ -23,7 +20,7 @@ export default (app) => { }); const icon = Widget.Icon({ - icon: lookUpIcon(app.icon_name || "") ? app.icon_name || "" : "", + icon: Utils.lookUpIcon(app.icon_name || "") ? app.icon_name || "" : "", size: options.applauncher.icon_size.bind("value"), }); diff --git a/home/desktops/hyprland/ags/js/applauncher/Applauncher.js b/home/desktops/hyprland/ags/js/applauncher/Applauncher.js index bc7d856..f351d82 100644 --- a/home/desktops/hyprland/ags/js/applauncher/Applauncher.js +++ b/home/desktops/hyprland/ags/js/applauncher/Applauncher.js @@ -1,10 +1,9 @@ -import Widget from "resource:///com/github/Aylur/ags/widget.js"; -import App from "resource:///com/github/Aylur/ags/app.js"; import Applications from "resource:///com/github/Aylur/ags/service/applications.js"; import PopupWindow from "../misc/PopupWindow.js"; import AppItem from "./AppItem.js"; import icons from "../icons.js"; import { launchApp } from "../utils.js"; +import options from "../options.js"; const WINDOW_NAME = "applauncher"; @@ -30,6 +29,7 @@ const Applauncher = () => { let items = mkItems(); const list = Widget.Box({ + class_name: "app-list", vertical: true, children: items, }); @@ -86,4 +86,5 @@ export default () => name: WINDOW_NAME, transition: "slide_down", child: Applauncher(), + anchor: options.applauncher.anchor.bind("value"), }); diff --git a/home/desktops/hyprland/ags/js/bar/PanelButton.js b/home/desktops/hyprland/ags/js/bar/PanelButton.js index b0cdc3d..626b28d 100644 --- a/home/desktops/hyprland/ags/js/bar/PanelButton.js +++ b/home/desktops/hyprland/ags/js/bar/PanelButton.js @@ -1,45 +1,34 @@ -import Widget from "resource:///com/github/Aylur/ags/widget.js"; -import App from "resource:///com/github/Aylur/ags/app.js"; - /** * @typedef {Object} PanelButtonProps - * @property {any} content + * @property {import('types/widgets/button').ButtonProps['child']} content * @property {string=} window */ /** * @param {import('types/widgets/button').ButtonProps & PanelButtonProps} o */ -export default ({ - class_name, - content, - window = "", - connections = [], - ...rest -}) => { - let open = false; - - const connection = [ - App, - (self, win, visible) => { - if (win !== window) return; - - if (open && !visible) { - open = false; - self.toggleClassName("active", false); - } - - if (visible) { - open = true; - self.toggleClassName("active"); - } - }, - ]; - - return Widget.Button({ +export default ({ class_name, content, window = "", setup, ...rest }) => + Widget.Button({ class_name: `panel-button ${class_name}`, child: Widget.Box({ children: [content] }), - connections: connections.concat([connection]), + setup: (self) => { + let open = false; + + self.hook(App, (_, win, visible) => { + if (win !== window) return; + + if (open && !visible) { + open = false; + self.toggleClassName("active", false); + } + + if (visible) { + open = true; + self.toggleClassName("active"); + } + }); + + if (setup) setup(self); + }, ...rest, }); -}; diff --git a/home/desktops/hyprland/ags/js/bar/TopBar.js b/home/desktops/hyprland/ags/js/bar/TopBar.js index 071dce0..8f6a081 100644 --- a/home/desktops/hyprland/ags/js/bar/TopBar.js +++ b/home/desktops/hyprland/ags/js/bar/TopBar.js @@ -1,6 +1,4 @@ import SystemTray from "resource:///com/github/Aylur/ags/service/systemtray.js"; -import Widget from "resource:///com/github/Aylur/ags/widget.js"; -import Variable from "resource:///com/github/Aylur/ags/variable.js"; import Notifications from "resource:///com/github/Aylur/ags/service/notifications.js"; import Mpris from "resource:///com/github/Aylur/ags/service/mpris.js"; import Battery from "resource:///com/github/Aylur/ags/service/battery.js"; @@ -28,25 +26,29 @@ SystemTray.connect("changed", () => { }); /** - * @template T + * @template {import('types/service').default} T * @param {T=} service - * @param {(self: T) => boolean=} condition + * @param {(service: T) => boolean=} condition */ -const SeparatorDot = (service, condition) => { - const visibility = (self) => { - if (!options.bar.separators.value) return (self.visible = false); - - self.visible = - condition && service ? condition(service) : options.bar.separators.value; - }; - - const conn = service ? [[service, visibility]] : []; - return Widget.Separator({ - connections: [["draw", visibility], ...conn], - binds: [["visible", options.bar.separators]], +const SeparatorDot = (service, condition) => + Widget.Separator({ vpack: "center", + setup: (self) => { + const visibility = () => { + if (!options.bar.separators.value) return (self.visible = false); + + self.visible = + condition && service + ? condition(service) + : options.bar.separators.value; + }; + + if (service && condition) self.hook(service, visibility); + + self.on("draw", visibility); + self.bind("visible", options.bar.separators); + }, }); -}; const Start = () => Widget.Box({ @@ -99,14 +101,9 @@ export default (monitor) => class_name: "transparent", exclusivity: "exclusive", monitor, - binds: [ - [ - "anchor", - options.bar.position, - "value", - (pos) => [pos, "left", "right"], - ], - ], + anchor: options.bar.position + .bind("value") + .transform((pos) => [pos, "left", "right"]), child: Widget.CenterBox({ class_name: "panel", start_widget: Start(), diff --git a/home/desktops/hyprland/ags/js/bar/buttons/BatteryBar.js b/home/desktops/hyprland/ags/js/bar/buttons/BatteryBar.js index 99b25b8..39ecdae 100644 --- a/home/desktops/hyprland/ags/js/bar/buttons/BatteryBar.js +++ b/home/desktops/hyprland/ags/js/bar/buttons/BatteryBar.js @@ -7,43 +7,35 @@ import PanelButton from "../PanelButton.js"; const Indicator = () => Widget.Stack({ - items: [ - ["false", Widget.Icon({ binds: [["icon", Battery, "icon-name"]] })], - ["true", FontIcon(icons.battery.charging)], - ], - binds: [["visible", options.battery.bar.show_icon]], - connections: [ - [ - Battery, - (stack) => { - stack.shown = `${Battery.charging || Battery.charged}`; - }, - ], - ], + children: { + false: Widget.Icon({ icon: Battery.bind("icon_name") }), + true: FontIcon(icons.battery.charging), + }, + visible: options.battery.bar.show_icon.bind("value"), + setup: (self) => + self.hook(Battery, () => { + self.shown = `${Battery.charging || Battery.charged}`; + }), }); const PercentLabel = () => Widget.Revealer({ transition: "slide_right", - binds: [["reveal-child", options.battery.show_percentage]], + reveal_child: options.battery.show_percentage.bind("value"), child: Widget.Label({ - binds: [["label", Battery, "percent", (p) => `${p}%`]], + label: Battery.bind("percent").transform((p) => `${p}%`), }), }); const LevelBar = () => Widget.LevelBar({ - connections: [ - [ - options.battery.bar.full, - (self) => { - const full = options.battery.bar.full.value; - self.vpack = full ? "fill" : "center"; - self.hpack = full ? "fill" : "center"; - }, - ], - ], - binds: [["value", Battery, "percent", (p) => p / 100]], + value: Battery.bind("percent").transform((p) => p / 100), + setup: (self) => + self.hook(options.battery.bar.full, () => { + const full = options.battery.bar.full.value; + self.vpack = full ? "fill" : "center"; + self.hpack = full ? "fill" : "center"; + }), }); const WholeButton = () => @@ -57,7 +49,7 @@ const WholeButton = () => children: [ FontIcon({ icon: icons.battery.charging, - binds: [["visible", Battery, "charging"]], + visible: Battery.bind("charging"), }), Widget.Box({ hpack: "center", @@ -77,32 +69,21 @@ export default () => options.battery.show_percentage.value = !v; }, content: Widget.Box({ - connections: [ - [ - Battery, - (w) => { - w.toggleClassName("charging", Battery.charging || Battery.charged); - w.toggleClassName( - "medium", - Battery.percent < options.battery.medium.value, - ); - w.toggleClassName( - "low", - Battery.percent < options.battery.low.value, - ); - w.toggleClassName("half", Battery.percent < 48); - }, - ], - ], - binds: [ - ["visible", Battery, "available"], - [ - "children", - options.battery.bar.full, - "value", - (full) => - full ? [WholeButton()] : [Indicator(), PercentLabel(), LevelBar()], - ], - ], + visible: Battery.bind("available"), + children: options.battery.bar.full + .bind("value") + .transform((full) => + full ? [WholeButton()] : [Indicator(), PercentLabel(), LevelBar()], + ), + setup: (self) => + self.hook(Battery, (w) => { + w.toggleClassName("charging", Battery.charging || Battery.charged); + w.toggleClassName( + "medium", + Battery.percent < options.battery.medium.value, + ); + w.toggleClassName("low", Battery.percent < options.battery.low.value); + w.toggleClassName("half", Battery.percent < 48); + }), }), }); diff --git a/home/desktops/hyprland/ags/js/bar/buttons/ColorPicker.js b/home/desktops/hyprland/ags/js/bar/buttons/ColorPicker.js index 3b8f211..b04d01c 100644 --- a/home/desktops/hyprland/ags/js/bar/buttons/ColorPicker.js +++ b/home/desktops/hyprland/ags/js/bar/buttons/ColorPicker.js @@ -7,9 +7,8 @@ export default () => PanelButton({ class_name: "color-picker", content: Widget.Icon("color-select-symbolic"), - binds: [["tooltip-text", Colors, "colors", (v) => `${v.length} colors`]], + tooltip_text: Colors.bind("colors").transform((v) => `${v.length} colors`), on_clicked: () => Colors.pick(), - on_secondary_click: (btn) => { if (Colors.colors.length === 0) return; diff --git a/home/desktops/hyprland/ags/js/bar/buttons/FocusedClient.js b/home/desktops/hyprland/ags/js/bar/buttons/FocusedClient.js index 39423d9..86d9e30 100644 --- a/home/desktops/hyprland/ags/js/bar/buttons/FocusedClient.js +++ b/home/desktops/hyprland/ags/js/bar/buttons/FocusedClient.js @@ -7,49 +7,38 @@ import { substitute } from "../../utils.js"; export const ClientLabel = () => Widget.Label({ - binds: [ - [ - "label", - Hyprland.active.client, - "class", - (c) => { - const { titles } = options.substitutions; - return substitute(titles, c); - }, - ], - ], + label: Hyprland.active.client.bind("class").transform((c) => { + const { titles } = options.substitutions; + return substitute(titles, c); + }), }); export const ClientIcon = () => Widget.Icon({ - connections: [ - [ - Hyprland.active.client, - (self) => { - const { icons } = options.substitutions; - const { client } = Hyprland.active; + setup: (self) => + self.hook(Hyprland.active.client, () => { + const { icons } = options.substitutions; + const { client } = Hyprland.active; - const classIcon = substitute(icons, client.class) + "-symbolic"; - const titleIcon = substitute(icons, client.class) + "-symbolic"; + const classIcon = substitute(icons, client.class) + "-symbolic"; + const titleIcon = substitute(icons, client.class) + "-symbolic"; - const hasTitleIcon = Utils.lookUpIcon(titleIcon); - const hasClassIcon = Utils.lookUpIcon(classIcon); + const hasTitleIcon = Utils.lookUpIcon(titleIcon); + const hasClassIcon = Utils.lookUpIcon(classIcon); - if (hasClassIcon) self.icon = classIcon; + if (hasClassIcon) self.icon = classIcon; - if (hasTitleIcon) self.icon = titleIcon; + if (hasTitleIcon) self.icon = titleIcon; - self.visible = !!(hasTitleIcon || hasClassIcon); - }, - ], - ], + self.visible = !!(hasTitleIcon || hasClassIcon); + }), }); export default () => PanelButton({ class_name: "focused-client", content: Widget.Box({ + tooltip_text: Hyprland.active.bind("client").transform((c) => c.title), children: [ClientIcon(), ClientLabel()], - binds: [["tooltip-text", Hyprland.active, "client", (c) => c.title]], }), }); diff --git a/home/desktops/hyprland/ags/js/bar/buttons/MediaIndicator.js b/home/desktops/hyprland/ags/js/bar/buttons/MediaIndicator.js index 16e9488..2912b04 100644 --- a/home/desktops/hyprland/ags/js/bar/buttons/MediaIndicator.js +++ b/home/desktops/hyprland/ags/js/bar/buttons/MediaIndicator.js @@ -26,31 +26,24 @@ const Indicator = ({ player, direction = "right" }) => vexpand: true, truncate: "end", max_width_chars: 40, - connections: [ - [ - player, - (label) => { - label.label = `${player.track_artists.join(", ")} - ${ - player.track_title - }`; - }, - ], - ], + label: player + .bind("track_title") + .transform( + () => `${player.track_artists.join(", ")} - ${player.track_title}`, + ), }), - connections: [ - [ - player, - (revealer) => { - if (revealer._current === player.track_title) return; + setupRevealer: (self) => { + let current = ""; + self.hook(player, () => { + if (current === player.track_title) return; - revealer._current = player.track_title; - revealer.reveal_child = true; - Utils.timeout(3000, () => { - revealer.reveal_child = false; - }); - }, - ], - ], + current = player.track_title; + self.reveal_child = true; + Utils.timeout(3000, () => { + self.reveal_child = false; + }); + }); + }, }); /** @@ -75,10 +68,7 @@ export default ({ direction = "right" } = {}) => { box.children = [Indicator({ player, direction })]; }; - return Widget.Box({ - connections: [ - [options.mpris.preferred, update], - [Mpris, update, "notify::players"], - ], - }); + return Widget.Box() + .hook(options.mpris.preferred, update) + .hook(Mpris, update, "notify::players"); }; diff --git a/home/desktops/hyprland/ags/js/bar/buttons/NotificationIndicator.js b/home/desktops/hyprland/ags/js/bar/buttons/NotificationIndicator.js index 89c8ffd..d8ee697 100644 --- a/home/desktops/hyprland/ags/js/bar/buttons/NotificationIndicator.js +++ b/home/desktops/hyprland/ags/js/bar/buttons/NotificationIndicator.js @@ -12,52 +12,39 @@ import HoverRevealer from "../../misc/HoverRevealer.js"; export default ({ direction = "left" } = {}) => HoverRevealer({ class_name: "notifications panel-button", - eventboxConnections: [ - ["button-press-event", () => App.openWindow("dashboard")], - [ - Notifications, - (box) => - (box.visible = - Notifications.notifications.length > 0 || Notifications.dnd), - ], - ], - connections: [ - [ - Notifications, - (revealer) => { - const title = Notifications.notifications[0]?.summary; - if (revealer._title === title) return; + setupEventBox: (box) => + box + .on("button-press-event", () => App.openWindow("dashboard")) + .hook( + Notifications, + () => + (box.visible = + Notifications.notifications.length > 0 || Notifications.dnd), + ), - revealer._title = title; - revealer.reveal_child = true; - Utils.timeout(3000, () => { - revealer.reveal_child = false; - }); - }, - ], - ], + setupRevealer: (self) => + self.hook(Notifications, () => { + let title = ""; + const summary = Notifications.notifications[0]?.summary; + if (title === summary) return; + + title = summary; + self.reveal_child = true; + Utils.timeout(3000, () => { + self.reveal_child = false; + }); + }), direction, indicator: Widget.Icon({ - binds: [ - [ - "icon", - Notifications, - "dnd", - (dnd) => - dnd ? icons.notifications.silent : icons.notifications.noisy, - ], - ], + icon: Notifications.bind("dnd").transform( + (dnd) => icons.notifications[dnd ? "silent" : "noisy"], + ), }), child: Widget.Label({ truncate: "end", max_width_chars: 40, - binds: [ - [ - "label", - Notifications, - "notifications", - (n) => n.reverse()[0]?.summary || "", - ], - ], + label: Notifications.bind("notifications").transform( + (n) => n.reverse()[0]?.summary || "", + ), }), }); diff --git a/home/desktops/hyprland/ags/js/bar/buttons/OverviewButton.js b/home/desktops/hyprland/ags/js/bar/buttons/OverviewButton.js index bc7e784..9d275cb 100644 --- a/home/desktops/hyprland/ags/js/bar/buttons/OverviewButton.js +++ b/home/desktops/hyprland/ags/js/bar/buttons/OverviewButton.js @@ -10,15 +10,8 @@ export default () => window: "overview", on_clicked: () => App.toggleWindow("overview"), content: FontIcon({ - binds: [ - [ - "icon", - options.bar.icon, - "value", - (v) => { - return v === "distro-icon" ? distroIcon : v; - }, - ], - ], + label: options.bar.icon.bind("value").transform((v) => { + return v === "distro-icon" ? distroIcon : v; + }), }), }); diff --git a/home/desktops/hyprland/ags/js/bar/buttons/ScreenRecord.js b/home/desktops/hyprland/ags/js/bar/buttons/ScreenRecord.js index 26b54cc..4e2d515 100644 --- a/home/desktops/hyprland/ags/js/bar/buttons/ScreenRecord.js +++ b/home/desktops/hyprland/ags/js/bar/buttons/ScreenRecord.js @@ -7,23 +7,16 @@ export default () => PanelButton({ class_name: "recorder", on_clicked: () => Recorder.stop(), - binds: [["visible", Recorder, "recording"]], + visible: Recorder.bind("recording"), content: Widget.Box({ children: [ Widget.Icon(icons.recorder.recording), Widget.Label({ - binds: [ - [ - "label", - Recorder, - "timer", - (time) => { - const sec = time % 60; - const min = Math.floor(time / 60); - return `${min}:${sec < 10 ? "0" + sec : sec}`; - }, - ], - ], + label: Recorder.bind("timer").transform((time) => { + const sec = time % 60; + const min = Math.floor(time / 60); + return `${min}:${sec < 10 ? "0" + sec : sec}`; + }), }), ], }), diff --git a/home/desktops/hyprland/ags/js/bar/buttons/SubMenu.js b/home/desktops/hyprland/ags/js/bar/buttons/SubMenu.js index 372d0fc..163fcf3 100644 --- a/home/desktops/hyprland/ags/js/bar/buttons/SubMenu.js +++ b/home/desktops/hyprland/ags/js/bar/buttons/SubMenu.js @@ -7,7 +7,7 @@ import options from "../../options.js"; /** * @param {import('types/widgets/revealer').default} revealer * @param {'left' | 'right' | 'up' | 'down'} direction - * @param {import('types/variable').Variable} items + * @param {import('types/variable').Variable} items */ const Arrow = (revealer, direction, items) => { let deg = 0; @@ -29,14 +29,7 @@ const Arrow = (revealer, direction, items) => { return Widget.Button({ class_name: "panel-button sub-menu", - connections: [ - [ - items, - (btn) => { - btn.tooltip_text = `${items.value} Items`; - }, - ], - ], + tooltip_text: items.bind().transform((v) => `${v} Items`), on_clicked: () => { animate(); revealer.reveal_child = !revealer.reveal_child; diff --git a/home/desktops/hyprland/ags/js/bar/buttons/System.js b/home/desktops/hyprland/ags/js/bar/buttons/System.js index 4feb2f3..f8a6825 100644 --- a/home/desktops/hyprland/ags/js/bar/buttons/System.js +++ b/home/desktops/hyprland/ags/js/bar/buttons/System.js @@ -13,23 +13,16 @@ const System = (type) => { const progress = Widget.Box({ class_name: "progress", child: Widget.CircularProgress({ - binds: [["value", variables[type]]], + value: variables[type].bind(), }), }); const revealer = Widget.Revealer({ transition: "slide_right", child: Widget.Label({ - binds: [ - [ - "label", - variables[type], - "value", - (v) => { - return ` ${type}: ${Math.round(v * 100)}%`; - }, - ], - ], + label: variables[type].bind("value").transform((v) => { + return ` ${type}: ${Math.round(v * 100)}%`; + }), }), }); diff --git a/home/desktops/hyprland/ags/js/bar/buttons/SystemIndicators.js b/home/desktops/hyprland/ags/js/bar/buttons/SystemIndicators.js index b1a1f24..2ba0387 100644 --- a/home/desktops/hyprland/ags/js/bar/buttons/SystemIndicators.js +++ b/home/desktops/hyprland/ags/js/bar/buttons/SystemIndicators.js @@ -28,7 +28,6 @@ const MicrophoneIndicator = () => if (!Audio.microphone) return; const { muted, low, medium, high } = icons.audio.mic; - if (Audio.microphone.is_muted) return (icon.icon = muted); /** @type {Array<[number, string]>} */ const cons = [ @@ -42,7 +41,7 @@ const MicrophoneIndicator = () => icon.visible = Audio.recorders.length > 0 || Audio.microphone.is_muted; }, - "speaker-changed", + "microphone-changed", ); const DNDIndicator = () => diff --git a/home/desktops/hyprland/ags/js/bar/buttons/Taskbar.js b/home/desktops/hyprland/ags/js/bar/buttons/Taskbar.js index dd447ea..27ab730 100644 --- a/home/desktops/hyprland/ags/js/bar/buttons/Taskbar.js +++ b/home/desktops/hyprland/ags/js/bar/buttons/Taskbar.js @@ -26,9 +26,6 @@ const setChildren = (box) => })); export default () => - Widget.Box({ - connections: [ - [Hyprland, setChildren, "notify::clients"], - [Hyprland, setChildren, "notify::active"], - ], - }); + Widget.Box() + .hook(Hyprland, setChildren, "notify::clients") + .hook(Hyprland, setChildren, "notify::active"); diff --git a/home/desktops/hyprland/ags/js/bar/buttons/Workspaces.js b/home/desktops/hyprland/ags/js/bar/buttons/Workspaces.js index 195f1a7..3400b0d 100644 --- a/home/desktops/hyprland/ags/js/bar/buttons/Workspaces.js +++ b/home/desktops/hyprland/ags/js/bar/buttons/Workspaces.js @@ -12,40 +12,34 @@ const Workspaces = () => { return Widget.Box({ children: range(ws || 20).map((i) => Widget.Button({ - setup: (btn) => (btn.id = i), + attribute: i, on_clicked: () => dispatch(i), child: Widget.Label({ label: `${i}`, class_name: "indicator", vpack: "center", }), - connections: [ - [ - Hyprland, - (btn) => { - btn.toggleClassName("active", Hyprland.active.workspace.id === i); - btn.toggleClassName( - "occupied", - Hyprland.getWorkspace(i)?.windows > 0, - ); - }, - ], - ], + setup: (self) => + self.hook(Hyprland, () => { + self.toggleClassName("active", Hyprland.active.workspace.id === i); + self.toggleClassName( + "occupied", + (Hyprland.getWorkspace(i)?.windows || 0) > 0, + ); + }), }), ), - connections: ws - ? [] - : [ - [ - Hyprland.active.workspace, - (box) => - box.children.map((btn) => { - btn.visible = Hyprland.workspaces.some( - (ws) => ws.id === btn.id, - ); - }), - ], - ], + setup: (box) => { + if (ws === 0) { + box.hook(Hyprland.active.workspace, () => + box.children.map((btn) => { + btn.visible = Hyprland.workspaces.some( + (ws) => ws.id === btn.attribute, + ); + }), + ); + } + }, }); }; @@ -58,7 +52,7 @@ export default () => on_scroll_up: () => dispatch("m+1"), on_scroll_down: () => dispatch("m-1"), class_name: "eventbox", - binds: [["child", options.workspaces, "value", Workspaces]], + child: options.workspaces.bind("value").transform(Workspaces), }), }), }); diff --git a/home/desktops/hyprland/ags/js/dashboard/Dashboard.js b/home/desktops/hyprland/ags/js/dashboard/Dashboard.js index 3d22305..faad0c6 100644 --- a/home/desktops/hyprland/ags/js/dashboard/Dashboard.js +++ b/home/desktops/hyprland/ags/js/dashboard/Dashboard.js @@ -7,19 +7,15 @@ import options from "../options.js"; export default () => PopupWindow({ name: "dashboard", - connections: [ - [ - options.bar.position, - (self) => { - self.anchor = [options.bar.position.value]; - if (options.bar.position.value === "top") - self.transition = "slide_down"; + setup: (self) => + self.hook(options.bar.position, () => { + self.anchor = [options.bar.position.value]; + if (options.bar.position.value === "top") + self.transition = "slide_down"; - if (options.bar.position.value === "bottom") - self.transition = "slide_up"; - }, - ], - ], + if (options.bar.position.value === "bottom") + self.transition = "slide_up"; + }), child: Widget.Box({ children: [ NotificationColumn(), diff --git a/home/desktops/hyprland/ags/js/dashboard/DateColumn.js b/home/desktops/hyprland/ags/js/dashboard/DateColumn.js index 0a1c819..809bd93 100644 --- a/home/desktops/hyprland/ags/js/dashboard/DateColumn.js +++ b/home/desktops/hyprland/ags/js/dashboard/DateColumn.js @@ -13,23 +13,16 @@ const SysProgress = (type, title, unit) => Widget.Box({ class_name: `circular-progress-box ${type}`, hexpand: true, - binds: [ - [ - "tooltipText", - vars[type], - "value", - (v) => `${title}: ${Math.floor(v * 100)}${unit}`, - ], - ], + tooltip_text: vars[type] + .bind("value") + .transform((v) => `${title}: ${Math.floor(v * 100)}${unit}`), child: Widget.CircularProgress({ hexpand: true, class_name: `circular-progress ${type}`, child: Widget.Icon(icons.system[type]), start_at: 0.75, - binds: [ - ["value", vars[type]], - ["rounded", options.radii, "value", (v) => v > 0], - ], + value: vars[type].bind(), + rounded: options.radii.bind("value").transform((v) => v > 0), }), }); @@ -45,7 +38,7 @@ export default () => Clock({ format: "%H:%M" }), Widget.Label({ class_name: "uptime", - binds: [["label", vars.uptime, "value", (t) => `uptime: ${t}`]], + label: vars.uptime.bind("value").transform((t) => `uptime: ${t}`), }), ], }), diff --git a/home/desktops/hyprland/ags/js/dashboard/NotificationColumn.js b/home/desktops/hyprland/ags/js/dashboard/NotificationColumn.js index accf0b4..15a1af4 100644 --- a/home/desktops/hyprland/ags/js/dashboard/NotificationColumn.js +++ b/home/desktops/hyprland/ags/js/dashboard/NotificationColumn.js @@ -11,19 +11,16 @@ const ClearButton = () => for (let i = 0; i < list.length; i++) timeout(50 * i, () => list[i]?.close()); }, - binds: [["sensitive", Notifications, "notifications", (n) => n.length > 0]], + sensitive: Notifications.bind("notifications").transform( + (n) => n.length > 0, + ), child: Widget.Box({ children: [ Widget.Label("Clear "), Widget.Icon({ - binds: [ - [ - "icon", - Notifications, - "notifications", - (n) => (n.length > 0 ? icons.trash.full : icons.trash.empty), - ], - ], + icon: Notifications.bind("notifications").transform( + (n) => icons.trash[n.length > 0 ? "full" : "empty"], + ), }), ], }), @@ -42,18 +39,10 @@ const NotificationList = () => Widget.Box({ vertical: true, vexpand: true, - connections: [ - [ - Notifications, - (box) => { - box.children = Notifications.notifications - .reverse() - .map(Notification); - - box.visible = Notifications.notifications.length > 0; - }, - ], - ], + children: Notifications.bind("notifications").transform((n) => + n.reverse().map(Notification), + ), + visible: Notifications.bind("notifications").transform((n) => n.length > 0), }); const Placeholder = () => @@ -64,11 +53,13 @@ const Placeholder = () => hpack: "center", vexpand: true, hexpand: true, + visible: Notifications.bind("notifications").transform( + (n) => n.length === 0, + ), children: [ Widget.Icon(icons.notifications.silent), Widget.Label("Your inbox is empty"), ], - binds: [["visible", Notifications, "notifications", (n) => n.length === 0]], }); export default () => diff --git a/home/desktops/hyprland/ags/js/desktop/Desktop.js b/home/desktops/hyprland/ags/js/desktop/Desktop.js index 198ed1d..161ae82 100644 --- a/home/desktops/hyprland/ags/js/desktop/Desktop.js +++ b/home/desktops/hyprland/ags/js/desktop/Desktop.js @@ -8,28 +8,26 @@ const DesktopClock = () => class_name: "clock-box-shadow", child: Widget.CenterBox({ class_name: "clock-box", - children: [ - Clock({ - class_name: "clock", - hpack: "center", - format: "%H", - }), - Widget.Box({ - class_name: "separator-box", - vertical: true, - hexpand: true, - hpack: "center", - children: [ - Widget.Separator({ vpack: "center", vexpand: true }), - Widget.Separator({ vpack: "center", vexpand: true }), - ], - }), - Clock({ - class_name: "clock", - hpack: "center", - format: "%M", - }), - ], + start_widget: Clock({ + class_name: "clock", + hpack: "center", + format: "%H", + }), + center_widget: Widget.Box({ + class_name: "separator-box", + vertical: true, + hexpand: true, + hpack: "center", + children: [ + Widget.Separator({ vpack: "center", vexpand: true }), + Widget.Separator({ vpack: "center", vexpand: true }), + ], + }), + end_widget: Clock({ + class_name: "clock", + hpack: "center", + format: "%M", + }), }), }); @@ -40,21 +38,17 @@ const Desktop = () => vertical: true, vexpand: true, hexpand: true, - binds: [["visible", options.desktop.clock.enable]], - connections: [ - [ - options.desktop.clock.position, - (box) => { - const [hpack = "center", vpack = "center", offset = 64] = - options.desktop.clock.position.value.split(" ") || []; + visible: options.desktop.clock.enable.bind("value"), + setup: (self) => + self.hook(options.desktop.clock.position, () => { + const [hpack = "center", vpack = "center", offset = 64] = + options.desktop.clock.position.value.split(" ") || []; - // @ts-expect-error - box.hpack = hpack; - box.vpack = vpack; - box.setCss(`margin: ${Number(offset)}px;`); - }, - ], - ], + // @ts-expect-error + self.hpack = hpack; + self.vpack = vpack; + self.setCss(`margin: ${Number(offset)}px;`); + }), children: [ DesktopClock(), Clock({ format: "%B %e. %A", class_name: "date" }), @@ -66,6 +60,7 @@ const Desktop = () => export default (monitor) => Widget.Window({ monitor, + keymode: "on-demand", name: `desktop${monitor}`, layer: "background", class_name: "desktop", diff --git a/home/desktops/hyprland/ags/js/dock/Dock.js b/home/desktops/hyprland/ags/js/dock/Dock.js index 44410e1..c660ce4 100644 --- a/home/desktops/hyprland/ags/js/dock/Dock.js +++ b/home/desktops/hyprland/ags/js/dock/Dock.js @@ -22,113 +22,95 @@ const AppButton = ({ icon, pinned = false, ...rest }) => { ), }); - const button = Widget.Button({ + return Widget.Button({ ...rest, + attribute: indicators, child: Widget.Box({ class_name: "box", child: Widget.Overlay({ child: Widget.Icon({ icon, - binds: [["size", options.desktop.dock.icon_size]], + size: options.desktop.dock.icon_size.bind("value"), }), pass_through: true, overlays: pinned ? [indicators] : [], }), }), }); - - return Object.assign(button, { indicators }); }; const Taskbar = () => Widget.Box({ - binds: [ - [ - "children", - Hyprland, - "clients", - (c) => - c.map((client) => { - for (const appName of options.desktop.dock.pinned_apps.value) { - if (client.class.toLowerCase().includes(appName.toLowerCase())) - return null; - } - for (const app of Applications.list) { - if ( - (client.title && app.match(client.title)) || - (client.class && app.match(client.class)) - ) { - return AppButton({ - icon: app.icon_name || "", - tooltip_text: app.name, - on_primary_click: () => focus(client), - on_middle_click: () => launchApp(app), - }); - } - } - }), - ], - ], + children: Hyprland.bind("clients").transform((c) => + c.map((client) => { + for (const appName of options.desktop.dock.pinned_apps.value) { + if (client.class.toLowerCase().includes(appName.toLowerCase())) + return null; + } + for (const app of Applications.list) { + if ( + (client.title && app.match(client.title)) || + (client.class && app.match(client.class)) + ) { + return AppButton({ + icon: app.icon_name || "", + tooltip_text: app.name, + on_primary_click: () => focus(client), + on_middle_click: () => launchApp(app), + }); + } + } + }), + ), }); const PinnedApps = () => Widget.Box({ class_name: "pins", homogeneous: true, - binds: [ - [ - "children", - options.desktop.dock.pinned_apps, - "value", - (v) => - v - .map((term) => ({ app: Applications.query(term)?.[0], term })) - .filter(({ app }) => app) - .map(({ app, term = true }) => - AppButton({ - pinned: true, - icon: app.icon_name || "", - on_primary_click: () => { - for (const client of Hyprland.clients) { - if (client.class.toLowerCase().includes(term)) - return focus(client); - } + children: options.desktop.dock.pinned_apps.bind("value").transform((v) => + v + .map((term) => ({ app: Applications.query(term)?.[0], term })) + .filter(({ app }) => app) + .map(({ app, term }) => + AppButton({ + pinned: true, + icon: app.icon_name || "", + on_primary_click: () => { + for (const client of Hyprland.clients) { + if (client.class.toLowerCase().includes(term)) + return focus(client); + } - launchApp(app); - }, - on_middle_click: () => launchApp(app), - tooltip_text: app.name, - connections: [ - [ - Hyprland, - (button) => { - const running = Hyprland.clients.filter((client) => - client.class.toLowerCase().includes(term), - ); + launchApp(app); + }, + on_middle_click: () => launchApp(app), + tooltip_text: app.name, + setup: (button) => + button.hook(Hyprland, () => { + const running = Hyprland.clients.filter((client) => + client.class.toLowerCase().includes(term), + ); - const focused = running.find( - (client) => - client.address === Hyprland.active.client.address, - ); + const focused = running.find( + (client) => client.address === Hyprland.active.client.address, + ); - const index = running.findIndex((c) => c === focused); + const index = running.findIndex((c) => c === focused); - for (let i = 0; i < 5; ++i) { - const indicator = button.indicators.children[i]; - indicator.visible = i < running.length; - indicator.toggleClassName("focused", i === index); - } + for (let i = 0; i < 5; ++i) { + const indicator = button.attribute.children[i]; + indicator.visible = i < running.length; + indicator.toggleClassName("focused", i === index); + } - button.set_tooltip_text( - running.length === 1 ? running[0].title : app.name, - ); - }, - ], - ], + button.set_tooltip_text( + running.length === 1 ? running[0].title : app.name, + ); }), - ), - ], - ], + }), + ), + ), }); export default () => { @@ -144,9 +126,14 @@ export default () => { vpack: "center", hpack: "center", orientation: 1, - connections: [ - [Hyprland, (box) => (box.visible = taskbar.children.length > 0)], - ], + setup: (self) => + self.hook( + taskbar, + () => { + self.visible = taskbar.children.length > 0; + }, + "notify::children", + ), }); return Widget.Box({ class_name: "dock", diff --git a/home/desktops/hyprland/ags/js/dock/FloatingDock.js b/home/desktops/hyprland/ags/js/dock/FloatingDock.js index 667d82c..e6728d1 100644 --- a/home/desktops/hyprland/ags/js/dock/FloatingDock.js +++ b/home/desktops/hyprland/ags/js/dock/FloatingDock.js @@ -35,20 +35,10 @@ export default (monitor) => { }), ], }), - connections: [ - [ - "enter-notify-event", - () => { - revealer.reveal_child = true; - }, - ], - [ - "leave-notify-event", - () => { - revealer.reveal_child = false; - }, - ], - ], - binds: [["visible", options.bar.position, "value", (v) => v !== "bottom"]], + setup: (self) => + self + .on("enter-notify-event", () => (revealer.reveal_child = true)) + .on("leave-notify-event", () => (revealer.reveal_child = false)) + .bind("visible", options.bar.position, "value", (v) => v !== "bottom"), }); }; diff --git a/home/desktops/hyprland/ags/js/icons.js b/home/desktops/hyprland/ags/js/icons.js index f937169..c02bdde 100644 --- a/home/desktops/hyprland/ags/js/icons.js +++ b/home/desktops/hyprland/ags/js/icons.js @@ -87,6 +87,7 @@ export default { next: "󰒭", }, ui: { + colorpicker: "color-select-symbolic", close: "window-close-symbolic", info: "info-symbolic", menu: "open-menu-symbolic", diff --git a/home/desktops/hyprland/ags/js/lockscreen/Lockscreen.js b/home/desktops/hyprland/ags/js/lockscreen/Lockscreen.js index 31fa98c..a200b8d 100644 --- a/home/desktops/hyprland/ags/js/lockscreen/Lockscreen.js +++ b/home/desktops/hyprland/ags/js/lockscreen/Lockscreen.js @@ -7,7 +7,7 @@ const PasswordEntry = () => Widget.Box({ children: [ Widget.Entry({ - connections: [[Lockscreen, (entry) => (entry.text = ""), "lock"]], + setup: (self) => self.hook(Lockscreen, () => (self.text = ""), "lock"), visibility: false, placeholder_text: "Password", on_accept: ({ text }) => Lockscreen.auth(text || ""), @@ -17,9 +17,12 @@ const PasswordEntry = () => Widget.Spinner({ active: true, vpack: "center", - connections: [ - [Lockscreen, (w, auth) => (w.visible = auth), "authenticating"], - ], + setup: (self) => + self.hook( + Lockscreen, + (_, auth) => (self.visible = auth), + "authenticating", + ), }), ], }); @@ -32,7 +35,8 @@ export default (monitor) => { monitor, layer: "overlay", visible: false, - connections: [[Lockscreen, (w, lock) => (w.visible = lock), "lock"]], + setup: (self) => + self.hook(Lockscreen, (_, lock) => (self.visible = lock), "lock"), child: Widget.Box({ css: "min-width: 3000px; min-height: 2000px;", class_name: "shader", diff --git a/home/desktops/hyprland/ags/js/main.js b/home/desktops/hyprland/ags/js/main.js index f472849..5cd99ea 100644 --- a/home/desktops/hyprland/ags/js/main.js +++ b/home/desktops/hyprland/ags/js/main.js @@ -1,6 +1,6 @@ -import Applauncher from "./applauncher/Applauncher.js"; import Dashboard from "./dashboard/Dashboard.js"; import Desktop from "./desktop/Desktop.js"; +import FloatingDock from "./dock/FloatingDock.js"; import Lockscreen from "./lockscreen/Lockscreen.js"; import Notifications from "./notifications/Notifications.js"; import OSD from "./osd/OSD.js"; @@ -15,20 +15,19 @@ import { init } from "./settings/setup.js"; import { forMonitors } from "./utils.js"; import { initWallpaper } from "./settings/wallpaper.js"; import options from "./options.js"; -import Dock from "./dock/Dock.js"; initWallpaper(); const windows = () => [ forMonitors(Desktop), + forMonitors(FloatingDock), forMonitors(Lockscreen), forMonitors(Notifications), forMonitors(OSD), forMonitors(ScreenCorners), forMonitors(TopBar), - Applauncher(), + Dashboard(), - Dock(), Overview(), PowerMenu(), QuickSettings(), @@ -39,8 +38,6 @@ const windows = () => [ export default { onConfigParsed: init, windows: windows().flat(1), - maxStreamVolume: 1.05, - cacheNotificationActions: false, closeWindowDelay: { quicksettings: options.transition.value, dashboard: options.transition.value, diff --git a/home/desktops/hyprland/ags/js/misc/BatteryIcon.js b/home/desktops/hyprland/ags/js/misc/BatteryIcon.js index 754f061..3551a11 100644 --- a/home/desktops/hyprland/ags/js/misc/BatteryIcon.js +++ b/home/desktops/hyprland/ags/js/misc/BatteryIcon.js @@ -4,15 +4,11 @@ import Battery from "resource:///com/github/Aylur/ags/service/battery.js"; export default () => Widget.Icon({ class_name: "battery", - binds: [["icon", Battery, "icon-name"]], - connections: [ - [ - Battery, - (icon) => { - icon.toggleClassName("charging", Battery.charging); - icon.toggleClassName("charged", Battery.charged); - icon.toggleClassName("low", Battery.percent < 30); - }, - ], - ], + icon: Battery.bind("icon_name"), + setup: (icon) => + icon.hook(Battery, () => { + icon.toggleClassName("charging", Battery.charging); + icon.toggleClassName("charged", Battery.charged); + icon.toggleClassName("low", Battery.percent < 30); + }), }); diff --git a/home/desktops/hyprland/ags/js/misc/Clock.js b/home/desktops/hyprland/ags/js/misc/Clock.js index 5ab4f7e..69cda04 100644 --- a/home/desktops/hyprland/ags/js/misc/Clock.js +++ b/home/desktops/hyprland/ags/js/misc/Clock.js @@ -1,5 +1,5 @@ +import { clock } from "../variables.js"; import Widget from "resource:///com/github/Aylur/ags/widget.js"; -import GLib from "gi://GLib"; /** * @param {import('types/widgets/label').Props & { @@ -7,20 +7,11 @@ import GLib from "gi://GLib"; * interval?: number, * }} o */ -export default ({ - format = "%H:%M:%S %B %e. %A", - interval = 1000, - ...rest -} = {}) => +export default ({ format = "%H:%M:%S %B %e. %A", ...rest } = {}) => Widget.Label({ class_name: "clock", + label: clock.bind("value").transform((time) => { + return time.format(format) || "wrong format"; + }), ...rest, - connections: [ - [ - interval, - (label) => - (label.label = - GLib.DateTime.new_now_local().format(format) || "wrong format"), - ], - ], }); diff --git a/home/desktops/hyprland/ags/js/misc/FontIcon.js b/home/desktops/hyprland/ags/js/misc/FontIcon.js index b37681d..e1cd61c 100644 --- a/home/desktops/hyprland/ags/js/misc/FontIcon.js +++ b/home/desktops/hyprland/ags/js/misc/FontIcon.js @@ -1,49 +1,48 @@ -import Gtk from "gi://Gtk"; -import { createCtor } from "resource:///com/github/Aylur/ags/widget.js"; +import Gtk from "gi://Gtk?version=3.0"; +import { subclass, register } from "resource:///com/github/Aylur/ags/widget.js"; import AgsLabel from "resource:///com/github/Aylur/ags/widgets/label.js"; -import GObject from "gi://GObject"; -export default createCtor( - class FontIcon extends AgsLabel { - static { - GObject.registerClass(this); - } +class FontIcon extends AgsLabel { + static { + register(this); + } - /** @param {string | import('types/widgets/label').Props & { icon?: string }} params */ - constructor(params = "") { - // @ts-expect-error - const { icon = "", ...rest } = params; + /** @param {string | import('types/widgets/label').Props & { icon?: string }} params */ + constructor(params = "") { + // @ts-expect-error + const { icon = "", ...rest } = params; - super(typeof params === "string" ? {} : rest); - this.toggleClassName("font-icon"); + super(typeof params === "string" ? {} : rest); + this.toggleClassName("font-icon"); - if (typeof params === "object") this.icon = icon; + if (typeof params === "object") this.icon = icon; - if (typeof params === "string") this.icon = params; - } + if (typeof params === "string") this.icon = params; + } - get icon() { - return this.label; - } - set icon(icon) { - this.label = icon; - } + get icon() { + return this.label; + } + set icon(icon) { + this.label = icon; + } - get size() { - return this.get_style_context().get_property( - "font-size", - Gtk.StateFlags.NORMAL, - ); - } + get size() { + return this.get_style_context().get_property( + "font-size", + Gtk.StateFlags.NORMAL, + ); + } - /** @returns {[number, number]} */ - vfunc_get_preferred_height() { - return [this.size, this.size]; - } + /** @returns {[number, number]} */ + vfunc_get_preferred_height() { + return [this.size, this.size]; + } - /** @returns {[number, number]} */ - vfunc_get_preferred_width() { - return [this.size, this.size]; - } - }, -); + /** @returns {[number, number]} */ + vfunc_get_preferred_width() { + return [this.size, this.size]; + } +} + +export default subclass(FontIcon); diff --git a/home/desktops/hyprland/ags/js/misc/HoverRevealer.js b/home/desktops/hyprland/ags/js/misc/HoverRevealer.js index 8d31fea..30217db 100644 --- a/home/desktops/hyprland/ags/js/misc/HoverRevealer.js +++ b/home/desktops/hyprland/ags/js/misc/HoverRevealer.js @@ -6,8 +6,8 @@ import * as Utils from "resource:///com/github/Aylur/ags/utils.js"; * indicator?: import('types/widgets/box').BoxProps['child'] * direction?: 'left' | 'right' | 'down' | 'up' * duration?: number - * eventboxConnections?: import('types/widgets/box').BoxProps['connections'] - * connections?: import('types/widgets/revealer').RevealerProps['connections'] + * setupRevealer?: (rev: ReturnType) => void + * setupEventBox?: (rev: ReturnType) => void * }} HoverRevealProps */ @@ -19,9 +19,8 @@ export default ({ child, direction = "left", duration = 300, - connections = [], - eventboxConnections = [], - binds = [], + setupEventBox, + setupRevealer, ...rest }) => { let open = false; @@ -31,15 +30,14 @@ export default ({ const revealer = Widget.Revealer({ transition: `slide_${direction}`, - connections, - binds, + setup: setupRevealer, transition_duration: duration, child, }); const eventbox = Widget.EventBox({ ...rest, - connections: eventboxConnections, + setup: setupEventBox, on_hover: () => { if (open) return; diff --git a/home/desktops/hyprland/ags/js/misc/PopupWindow.js b/home/desktops/hyprland/ags/js/misc/PopupWindow.js index 64bde36..48fd3e0 100644 --- a/home/desktops/hyprland/ags/js/misc/PopupWindow.js +++ b/home/desktops/hyprland/ags/js/misc/PopupWindow.js @@ -4,23 +4,42 @@ import Widget from "resource:///com/github/Aylur/ags/widget.js"; import options from "../options.js"; import GObject from "gi://GObject"; -class PopupWindow extends AgsWindow { +const keyGrabber = Widget.Window({ + name: "key-grabber", + popup: true, + anchor: ["top", "left", "right", "bottom"], + css: "background-color: transparent;", + visible: false, + exclusivity: "ignore", + keymode: "on-demand", + layer: "top", + attribute: { list: [] }, + setup: (self) => + self.on("notify::visible", ({ visible }) => { + if (!visible) + self.attribute?.list.forEach((name) => App.closeWindow(name)); + }), + child: Widget.EventBox({ vexpand: true }).on("button-press-event", () => { + App.closeWindow("key-grabber"); + keyGrabber.attribute?.list.forEach((name) => App.closeWindow(name)); + }), +}); + +// add before any PopupWindow is instantiated +App.addWindow(keyGrabber); + +export class PopupWindow extends AgsWindow { static { GObject.registerClass(this); } - /** @param {import('types/widgets/window').WindowProps & { - * name: string - * child: import('types/widgets/box').default - * transition?: import('types/widgets/revealer').RevealerProps['transition'] - * }} o - */ constructor({ name, child, transition = "none", visible = false, ...rest }) { super({ ...rest, name, popup: true, - focusable: true, + keymode: "exclusive", + layer: "overlay", class_names: ["popup-window", name], }); @@ -28,15 +47,11 @@ class PopupWindow extends AgsWindow { this.revealer = Widget.Revealer({ transition, child, - transitionDuration: options.transition.value, - connections: [ - [ - App, - (_, wname, visible) => { - if (wname === name) this.revealer.reveal_child = visible; - }, - ], - ], + transition_duration: options.transition.value, + setup: (self) => + self.hook(App, (_, wname, visible) => { + if (wname === name) this.revealer.reveal_child = visible; + }), }); this.child = Widget.Box({ @@ -46,6 +61,9 @@ class PopupWindow extends AgsWindow { this.show_all(); this.visible = visible; + + keyGrabber.bind("visible", this, "visible"); + keyGrabber.attribute?.list.push(name); } set transition(dir) { diff --git a/home/desktops/hyprland/ags/js/misc/Progress.js b/home/desktops/hyprland/ags/js/misc/Progress.js index d7cccfc..3b6fafb 100644 --- a/home/desktops/hyprland/ags/js/misc/Progress.js +++ b/home/desktops/hyprland/ags/js/misc/Progress.js @@ -1,6 +1,10 @@ import Widget from "resource:///com/github/Aylur/ags/widget.js"; import * as Utils from "resource:///com/github/Aylur/ags/utils.js"; +/** @param {import('types/widgets/box').BoxProps & { + * width: number + * height: number + * }} o */ export default ({ height = 18, width = 180, @@ -27,31 +31,30 @@ export default ({ min-height: ${height}px; `, children: [fill], - setup: (progress) => - (progress.setValue = (value) => { - if (value < 0) return; + attribute: (value) => { + if (value < 0) return; - const axis = vertical ? "height" : "width"; - const axisv = vertical ? height : width; - const min = vertical ? width : height; - const preferred = (axisv - min) * value + min; + const axis = vertical ? "height" : "width"; + const axisv = vertical ? height : width; + const min = vertical ? width : height; + const preferred = (axisv - min) * value + min; - if (!fill_size) { - fill_size = preferred; - fill.setCss(`min-${axis}: ${preferred}px;`); - return; - } + if (!fill_size) { + fill_size = preferred; + fill.setCss(`min-${axis}: ${preferred}px;`); + return; + } - const frames = 10; - const goal = preferred - fill_size; - const step = goal / frames; + const frames = 10; + const goal = preferred - fill_size; + const step = goal / frames; - for (let i = 0; i < frames; ++i) { - Utils.timeout(5 * i, () => { - fill_size += step; - fill.setCss(`min-${axis}: ${fill_size}px`); - }); - } - }), + for (let i = 0; i < frames; ++i) { + Utils.timeout(5 * i, () => { + fill_size += step; + fill.setCss(`min-${axis}: ${fill_size}px`); + }); + } + }, }); }; diff --git a/home/desktops/hyprland/ags/js/misc/RegularWindow.js b/home/desktops/hyprland/ags/js/misc/RegularWindow.js index b4875e4..45a3641 100644 --- a/home/desktops/hyprland/ags/js/misc/RegularWindow.js +++ b/home/desktops/hyprland/ags/js/misc/RegularWindow.js @@ -1,19 +1,4 @@ -import Widget from "resource:///com/github/Aylur/ags/widget.js"; +import { subclass } from "resource:///com/github/Aylur/ags/widget.js"; import Gtk from "gi://Gtk"; -import AgsWidget from "resource:///com/github/Aylur/ags/widgets/widget.js"; -class RegularWindow extends AgsWidget(Gtk.Window, "RegularWindow") { - static { - AgsWidget.register(this); - } - /** - * @param {import('types/widgets/widget').BaseProps< - * RegularWindow, Gtk.Window.ConstructorProperties - * >} params */ - constructor(params) { - // @ts-expect-error - super(params); - } -} - -export default Widget.createCtor(RegularWindow); +export default subclass(Gtk.Window, "RegularWindow"); diff --git a/home/desktops/hyprland/ags/js/misc/mpris.js b/home/desktops/hyprland/ags/js/misc/mpris.js index 96ea960..3ef88a9 100644 --- a/home/desktops/hyprland/ags/js/misc/mpris.js +++ b/home/desktops/hyprland/ags/js/misc/mpris.js @@ -11,14 +11,9 @@ export const CoverArt = (player, props) => Widget.Box({ ...props, class_name: "cover", - binds: [ - [ - "css", - player, - "cover-path", - (path) => `background-image: url("${path}")`, - ], - ], + css: player + .bind("cover_path") + .transform((p) => `background-image: url("${p}")`), }); /** @@ -29,16 +24,15 @@ export const BlurredCoverArt = (player, props) => Widget.Box({ ...props, class_name: "blurred-cover", - connections: [ - [ + setup: (self) => + self.hook( player, (box) => blurImg(player.cover_path).then((img) => { img && box.setCss(`background-image: url("${img}")`); }), "notify::cover-path", - ], - ], + ), }); /** @@ -49,7 +43,7 @@ export const TitleLabel = (player, props) => Widget.Label({ ...props, class_name: "title", - binds: [["label", player, "track-title"]], + label: player.bind("track_title"), }); /** @@ -60,7 +54,7 @@ export const ArtistLabel = (player, props) => Widget.Label({ ...props, class_name: "artist", - binds: [["label", player, "track-artists", (a) => a.join(", ") || ""]], + label: player.bind("track_artists").transform((a) => a.join(", ") || ""), }); /** @@ -72,17 +66,13 @@ export const PlayerIcon = (player, { symbolic = true, ...props } = {}) => ...props, class_name: "player-icon", tooltip_text: player.identity || "", - connections: [ - [ - player, - (icon) => { - const name = `${player.entry}${symbolic ? "-symbolic" : ""}`; - Utils.lookUpIcon(name) - ? (icon.icon = name) - : (icon.icon = icons.mpris.fallback); - }, - ], - ], + setup: (self) => + self.hook(player, (icon) => { + const name = `${player.entry}${symbolic ? "-symbolic" : ""}`; + Utils.lookUpIcon(name) + ? (icon.icon = name) + : (icon.icon = icons.mpris.fallback); + }), }); /** @@ -94,25 +84,18 @@ export const PositionSlider = (player, props) => ...props, class_name: "position-slider", draw_value: false, - on_change: ({ value }) => { - player.position = player.length * value; - }, - properties: [ - [ - "update", - (slider) => { - if (slider.dragging) return; + on_change: ({ value }) => (player.position = player.length * value), + setup: (self) => { + const update = () => { + if (self.dragging) return; - slider.visible = player.length > 0; - if (player.length > 0) slider.value = player.position / player.length; - }, - ], - ], - connections: [ - [player, (s) => s._update(s)], - [player, (s) => s._update(s), "position"], - [1000, (s) => s._update(s)], - ], + self.visible = player.length > 0; + if (player.length > 0) self.value = player.position / player.length; + }; + self.hook(player, update); + self.hook(player, update, "position"); + self.poll(1000, update); + }, }); /** @param {number} length */ @@ -126,90 +109,73 @@ function lengthStr(length) { /** @param {import('types/service/mpris').MprisPlayer} player */ export const PositionLabel = (player) => Widget.Label({ - properties: [ - [ - "update", - (label, time) => { - player.length > 0 - ? (label.label = lengthStr(time || player.position)) - : (label.visible = !!player); - }, - ], - ], - connections: [ - [player, (l, time) => l._update(l, time), "position"], - [1000, (l) => l._update(l)], - ], + setup: (self) => { + const update = (_, time) => { + player.length > 0 + ? (self.label = lengthStr(time || player.position)) + : (self.visible = !!player); + }; + self.hook(player, update, "position"); + self.poll(1000, update); + }, }); /** @param {import('types/service/mpris').MprisPlayer} player */ export const LengthLabel = (player) => Widget.Label({ - connections: [ - [ - player, - (label) => { - player.length > 0 - ? (label.label = lengthStr(player.length)) - : (label.visible = !!player); - }, - ], - ], + label: player.bind("length").transform((l) => lengthStr(l)), + visible: player.bind("length").transform((l) => l > 0), }); /** @param {import('types/service/mpris').MprisPlayer} player */ export const Slash = (player) => Widget.Label({ label: "/", - connections: [ - [ - player, - (label) => { - label.visible = player.length > 0; - }, - ], - ], + visible: player.bind("length").transform((l) => l > 0), }); /** * @param {Object} o * @param {import('types/service/mpris').MprisPlayer} o.player - * @param {import('types/widgets/stack').StackProps['items']} o.items + * @param {import('types/widgets/stack').StackProps['children']} o.children * @param {'shuffle' | 'loop' | 'playPause' | 'previous' | 'next'} o.onClick * @param {string} o.prop * @param {string} o.canProp * @param {any} o.cantValue */ -const PlayerButton = ({ player, items, onClick, prop, canProp, cantValue }) => +const PlayerButton = ({ + player, + children, + onClick, + prop, + canProp, + cantValue, +}) => Widget.Button({ - child: Widget.Stack({ - items, - binds: [["shown", player, prop, (p) => `${p}`]], - }), - on_clicked: player[onClick].bind(player), - binds: [["visible", player, canProp, (c) => c !== cantValue]], + child: Widget.Stack({ children }).bind( + "shown", + player, + prop, + (p) => `${p}`, + ), + on_clicked: () => player[onClick](), + visible: player.bind(canProp).transform((c) => c !== cantValue), }); /** @param {import('types/service/mpris').MprisPlayer} player */ export const ShuffleButton = (player) => PlayerButton({ player, - items: [ - [ - "true", - Widget.Label({ - class_name: "shuffle enabled", - label: icons.mpris.shuffle.enabled, - }), - ], - [ - "false", - Widget.Label({ - class_name: "shuffle disabled", - label: icons.mpris.shuffle.disabled, - }), - ], - ], + children: { + true: Widget.Label({ + class_name: "shuffle enabled", + label: icons.mpris.shuffle.enabled, + }), + false: Widget.Label({ + class_name: "shuffle disabled", + label: icons.mpris.shuffle.disabled, + }), + }, onClick: "shuffle", prop: "shuffle-status", canProp: "shuffle-status", @@ -220,29 +186,20 @@ export const ShuffleButton = (player) => export const LoopButton = (player) => PlayerButton({ player, - items: [ - [ - "None", - Widget.Label({ - class_name: "loop none", - label: icons.mpris.loop.none, - }), - ], - [ - "Track", - Widget.Label({ - class_name: "loop track", - label: icons.mpris.loop.track, - }), - ], - [ - "Playlist", - Widget.Label({ - class_name: "loop playlist", - label: icons.mpris.loop.playlist, - }), - ], - ], + children: { + None: Widget.Label({ + class_name: "loop none", + label: icons.mpris.loop.none, + }), + Track: Widget.Label({ + class_name: "loop track", + label: icons.mpris.loop.track, + }), + Playlist: Widget.Label({ + class_name: "loop playlist", + label: icons.mpris.loop.playlist, + }), + }, onClick: "loop", prop: "loop-status", canProp: "loop-status", @@ -253,29 +210,20 @@ export const LoopButton = (player) => export const PlayPauseButton = (player) => PlayerButton({ player, - items: [ - [ - "Playing", - Widget.Label({ - class_name: "playing", - label: icons.mpris.playing, - }), - ], - [ - "Paused", - Widget.Label({ - class_name: "paused", - label: icons.mpris.paused, - }), - ], - [ - "Stopped", - Widget.Label({ - class_name: "stopped", - label: icons.mpris.stopped, - }), - ], - ], + children: { + Playing: Widget.Label({ + class_name: "playing", + label: icons.mpris.playing, + }), + Paused: Widget.Label({ + class_name: "paused", + label: icons.mpris.paused, + }), + Stopped: Widget.Label({ + class_name: "stopped", + label: icons.mpris.stopped, + }), + }, onClick: "playPause", prop: "play-back-status", canProp: "can-play", @@ -286,15 +234,12 @@ export const PlayPauseButton = (player) => export const PreviousButton = (player) => PlayerButton({ player, - items: [ - [ - "true", - Widget.Label({ - class_name: "previous", - label: icons.mpris.prev, - }), - ], - ], + children: { + true: Widget.Label({ + class_name: "previous", + label: icons.mpris.prev, + }), + }, onClick: "previous", prop: "can-go-prev", canProp: "can-go-prev", @@ -305,15 +250,12 @@ export const PreviousButton = (player) => export const NextButton = (player) => PlayerButton({ player, - items: [ - [ - "true", - Widget.Label({ - class_name: "next", - label: icons.mpris.next, - }), - ], - ], + children: { + true: Widget.Label({ + class_name: "next", + label: icons.mpris.next, + }), + }, onClick: "next", prop: "can-go-next", canProp: "can-go-next", diff --git a/home/desktops/hyprland/ags/js/notifications/Notifications.js b/home/desktops/hyprland/ags/js/notifications/Notifications.js index 1425c3e..60ab9d9 100644 --- a/home/desktops/hyprland/ags/js/notifications/Notifications.js +++ b/home/desktops/hyprland/ags/js/notifications/Notifications.js @@ -39,14 +39,10 @@ const Popups = (parent) => { }); }; - return Widget.Box({ - vertical: true, - connections: [ - [Notifications, onNotified, "notified"], - [Notifications, onDismissed, "dismissed"], - [Notifications, (box, id) => onDismissed(box, id, true), "closed"], - ], - }); + return Widget.Box({ vertical: true }) + .hook(Notifications, onNotified, "notified") + .hook(Notifications, onDismissed, "dismissed") + .hook(Notifications, (box, id) => onDismissed(box, id, true), "closed"); }; /** @param {import('types/widgets/revealer').RevealerProps['transition']} transition */ @@ -67,6 +63,6 @@ export default (monitor) => monitor, name: `notifications${monitor}`, class_name: "notifications", - binds: [["anchor", options.notifications.position]], + anchor: options.notifications.position.bind("value"), child: PopupList(), }); diff --git a/home/desktops/hyprland/ags/js/osd/OSD.js b/home/desktops/hyprland/ags/js/osd/OSD.js index 5bf44cd..8c6ee0a 100644 --- a/home/desktops/hyprland/ags/js/osd/OSD.js +++ b/home/desktops/hyprland/ags/js/osd/OSD.js @@ -10,56 +10,39 @@ export const OnScreenIndicator = ({ height = 300, width = 48 } = {}) => css: "padding: 1px;", child: Widget.Revealer({ transition: "slide_left", - connections: [ - [ - Indicator, - (revealer, value) => { - revealer.reveal_child = value > -1; - }, - ], - ], + setup: (self) => + self.hook(Indicator, (_, value) => { + self.reveal_child = value > -1; + }), child: Progress({ width, height, vertical: true, - connections: [ - [Indicator, (progress, value) => progress.setValue(value)], - ], + setup: (self) => + self.hook(Indicator, (_, value) => self.attribute(value)), child: Widget.Stack({ vpack: "start", hpack: "center", hexpand: false, - items: [ - [ - "true", - Widget.Icon({ - hpack: "center", - size: width, - connections: [ - [Indicator, (icon, _v, name) => (icon.icon = name || "")], - ], - }), - ], - [ - "false", - FontIcon({ - hpack: "center", - hexpand: true, - css: `font-size: ${width}px;`, - connections: [ - [Indicator, (icon, _v, name) => (icon.icon = name || "")], - ], - }), - ], - ], - connections: [ - [ - Indicator, - (stack, _v, name) => { - stack.shown = `${!!Utils.lookUpIcon(name)}`; - }, - ], - ], + children: { + true: Widget.Icon({ + hpack: "center", + size: width, + setup: (w) => + w.hook(Indicator, (_, _v, name) => (w.icon = name || "")), + }), + false: FontIcon({ + hpack: "center", + hexpand: true, + css: `font-size: ${width}px;`, + setup: (w) => + w.hook(Indicator, (_, _v, name) => (w.label = name || "")), + }), + }, + setup: (self) => + self.hook(Indicator, (_, _v, name) => { + self.shown = `${!!Utils.lookUpIcon(name)}`; + }), }), }), }), diff --git a/home/desktops/hyprland/ags/js/overview/Client.js b/home/desktops/hyprland/ags/js/overview/Client.js index eceb608..8c89eac 100644 --- a/home/desktops/hyprland/ags/js/overview/Client.js +++ b/home/desktops/hyprland/ags/js/overview/Client.js @@ -2,9 +2,10 @@ import Widget from "resource:///com/github/Aylur/ags/widget.js"; import App from "resource:///com/github/Aylur/ags/app.js"; import { createSurfaceFromWidget, substitute } from "../utils.js"; import Gdk from "gi://Gdk"; -import Gtk from "gi://Gtk"; +import Gtk from "gi://Gtk?version=3.0"; import options from "../options.js"; import Hyprland from "resource:///com/github/Aylur/ags/service/hyprland.js"; +import icons from "../icons.js"; const SCALE = 0.08; const TARGET = [Gtk.TargetEntry.new("text/plain", Gtk.TargetFlags.SAME_APP, 0)]; @@ -13,7 +14,13 @@ const TARGET = [Gtk.TargetEntry.new("text/plain", Gtk.TargetFlags.SAME_APP, 0)]; const dispatch = (args) => Hyprland.sendMessage(`dispatch ${args}`); /** @param {string} str */ -const icon = (str) => substitute(options.substitutions.icons, str); +const icon = (str) => { + const icon = substitute(options.substitutions.icons, str); + if (Utils.lookUpIcon(icon)) return icon; + + console.warn("no icon", icon); + return icons.fallback.executable; +}; export default ({ address, size: [w, h], class: c, title }) => Widget.Button({ @@ -32,19 +39,19 @@ export default ({ address, size: [w, h], class: c, title }) => App.closeWindow("overview"), ), - setup: (btn) => { - btn.drag_source_set( - Gdk.ModifierType.BUTTON1_MASK, - TARGET, - Gdk.DragAction.COPY, - ); - btn.connect("drag-data-get", (_w, _c, data) => - data.set_text(address, address.length), - ); - btn.connect("drag-begin", (_, context) => { - Gtk.drag_set_icon_surface(context, createSurfaceFromWidget(btn)); - btn.toggleClassName("hidden", true); - }); - btn.connect("drag-end", () => btn.toggleClassName("hidden", false)); - }, + setup: (btn) => + btn + .on("drag-data-get", (_w, _c, data) => + data.set_text(address, address.length), + ) + .on("drag-begin", (_, context) => { + Gtk.drag_set_icon_surface(context, createSurfaceFromWidget(btn)); + btn.toggleClassName("hidden", true); + }) + .on("drag-end", () => btn.toggleClassName("hidden", false)) + .drag_source_set( + Gdk.ModifierType.BUTTON1_MASK, + TARGET, + Gdk.DragAction.COPY, + ), }); diff --git a/home/desktops/hyprland/ags/js/overview/Overview.js b/home/desktops/hyprland/ags/js/overview/Overview.js index 0a23b6d..6812108 100644 --- a/home/desktops/hyprland/ags/js/overview/Overview.js +++ b/home/desktops/hyprland/ags/js/overview/Overview.js @@ -1,5 +1,3 @@ -import Widget from "resource:///com/github/Aylur/ags/widget.js"; -import App from "resource:///com/github/Aylur/ags/app.js"; import Hyprland from "resource:///com/github/Aylur/ags/service/hyprland.js"; import PopupWindow from "../misc/PopupWindow.js"; import Workspace from "./Workspace.js"; @@ -8,15 +6,30 @@ import { range } from "../utils.js"; const ws = options.workspaces; -/** @param {import('types/widgets/box').default} box */ +const Overview = () => + Widget.Box({ + children: [Workspace(0)], // for type infer + setup: (self) => + Utils.idle(() => { + self.hook(ws, () => { + self.children = range(ws.value).map(Workspace); + update(self); + children(self); + }); + self.hook(Hyprland, update); + self.hook(Hyprland, children, "notify::workspaces"); + update(self); + children(self); + }), + }); + +/** @param {ReturnType} box */ const update = (box) => { - if (App.windows.has("overview") && !App.getWindow("overview")?.visible) - return; + if (!box.get_parent()?.visible) return; Hyprland.sendMessage("j/clients") .then((clients) => { box.children.forEach((ws) => { - // @ts-expect-error ws.attribute(JSON.parse(clients)); }); }) @@ -35,19 +48,5 @@ const children = (box) => { export default () => PopupWindow({ name: "overview", - child: Widget.Box({ - setup: update, - connections: [ - [ - ws, - (box) => { - box.children = range(ws.value).map(Workspace); - update(box); - children(box); - }, - ], - [Hyprland, update], - [Hyprland, children, "notify::workspaces"], - ], - }), + child: Overview(), }); diff --git a/home/desktops/hyprland/ags/js/overview/Workspace.js b/home/desktops/hyprland/ags/js/overview/Workspace.js index 3086033..2a9ed84 100644 --- a/home/desktops/hyprland/ags/js/overview/Workspace.js +++ b/home/desktops/hyprland/ags/js/overview/Workspace.js @@ -2,7 +2,7 @@ import Hyprland from "resource:///com/github/Aylur/ags/service/hyprland.js"; import Widget from "resource:///com/github/Aylur/ags/widget.js"; import * as Utils from "resource:///com/github/Aylur/ags/utils.js"; import Gdk from "gi://Gdk"; -import Gtk from "gi://Gtk"; +import Gtk from "gi://Gtk?version=3.0"; import Client from "./Client.js"; const SCALE = 0.08; @@ -22,14 +22,10 @@ export default (index) => { min-width: ${3840 * SCALE}px; min-height: ${2160 * SCALE}px; `, - connections: [ - [ - Hyprland, - (box) => { - box.toggleClassName("active", Hyprland.active.workspace.id === index); - }, - ], - ], + setup: (box) => + box.hook(Hyprland, () => { + box.toggleClassName("active", Hyprland.active.workspace.id === index); + }), child: Widget.EventBox({ hexpand: true, vexpand: true, diff --git a/home/desktops/hyprland/ags/js/powermenu/ShadedPopup.js b/home/desktops/hyprland/ags/js/powermenu/ShadedPopup.js index 0e7f4bf..606fe70 100644 --- a/home/desktops/hyprland/ags/js/powermenu/ShadedPopup.js +++ b/home/desktops/hyprland/ags/js/powermenu/ShadedPopup.js @@ -7,11 +7,13 @@ const Padding = (windowName) => class_name: "padding", hexpand: true, vexpand: true, - connections: [["button-press-event", () => App.toggleWindow(windowName)]], + setup: (w) => + w.on("button-press-event", () => App.toggleWindow(windowName)), }); /** - * @param {import('types/widgets/window').WindowProps & { + * @template {import('gi://Gtk?version=3.0').default.Widget} T + * @param {import('types/widgets/window').WindowProps & { * name: string * child: import('types/widgets/box').default * }} o @@ -23,20 +25,20 @@ export default ({ name, child, ...rest }) => name, visible: false, popup: true, - focusable: true, + keymode: "on-demand", setup() { child.toggleClassName("window-content"); }, child: Widget.CenterBox({ class_name: "shader", css: "min-width: 5000px; min-height: 3000px;", - children: [ - Padding(name), - Widget.CenterBox({ - vertical: true, - children: [Padding(name), child, Padding(name)], - }), - Padding(name), - ], + start_widget: Padding(name), + end_widget: Padding(name), + center_widget: Widget.CenterBox({ + vertical: true, + start_widget: Padding(name), + end_widget: Padding(name), + center_widget: child, + }), }), }); diff --git a/home/desktops/hyprland/ags/js/powermenu/Verification.js b/home/desktops/hyprland/ags/js/powermenu/Verification.js index 894ef29..02555b4 100644 --- a/home/desktops/hyprland/ags/js/powermenu/Verification.js +++ b/home/desktops/hyprland/ags/js/powermenu/Verification.js @@ -17,7 +17,7 @@ export default () => children: [ Widget.Label({ class_name: "title", - binds: [["label", PowerMenu, "title"]], + label: PowerMenu.bind("title"), }), Widget.Label({ class_name: "desc", diff --git a/home/desktops/hyprland/ags/js/quicksettings/QuickSettings.js b/home/desktops/hyprland/ags/js/quicksettings/QuickSettings.js index dda1a4e..d714851 100644 --- a/home/desktops/hyprland/ags/js/quicksettings/QuickSettings.js +++ b/home/desktops/hyprland/ags/js/quicksettings/QuickSettings.js @@ -33,19 +33,15 @@ const Homogeneous = (toggles) => export default () => PopupWindow({ name: "quicksettings", - connections: [ - [ - options.bar.position, - (self) => { - self.anchor = ["right", options.bar.position.value]; - if (options.bar.position.value === "top") - self.transition = "slide_down"; + setup: (self) => + self.hook(options.bar.position, () => { + self.anchor = ["right", options.bar.position.value]; + if (options.bar.position.value === "top") + self.transition = "slide_down"; - if (options.bar.position.value === "bottom") - self.transition = "slide_up"; - }, - ], - ], + if (options.bar.position.value === "bottom") + self.transition = "slide_up"; + }), child: Widget.Box({ vertical: true, children: [ @@ -60,12 +56,8 @@ export default () => ], }), Row( - [Homogeneous([/*NetworkToggle(),*/ BluetoothToggle()]), DND()], - [/*WifiSelection()*/ BluetoothDevices()], - ), - Row( - [Homogeneous([/*ProfileToggle(),*/ ThemeToggle()]), MicMute()], - [/*ProfileSelector(),*/ ThemeSelector()], + [Homogeneous([ThemeToggle(), BluetoothToggle()]), MicMute()], + [ThemeSelector(), BluetoothDevices()], ), Media(), ], diff --git a/home/desktops/hyprland/ags/js/quicksettings/ToggleButton.js b/home/desktops/hyprland/ags/js/quicksettings/ToggleButton.js index 5012b36..743af09 100644 --- a/home/desktops/hyprland/ags/js/quicksettings/ToggleButton.js +++ b/home/desktops/hyprland/ags/js/quicksettings/ToggleButton.js @@ -18,30 +18,23 @@ App.connect("window-toggled", (_, name, visible) => { export const Arrow = (name, activate) => { let deg = 0; let iconOpened = false; + const icon = Widget.Icon(icons.ui.arrow.right).hook(opened, () => { + if ( + (opened.value === name && !iconOpened) || + (opened.value !== name && iconOpened) + ) { + const step = opened.value === name ? 10 : -10; + iconOpened = !iconOpened; + for (let i = 0; i < 9; ++i) { + Utils.timeout(15 * i, () => { + deg += step; + icon.setCss(`-gtk-icon-transform: rotate(${deg}deg);`); + }); + } + } + }); return Widget.Button({ - child: Widget.Icon({ - icon: icons.ui.arrow.right, - connections: [ - [ - opened, - (icon) => { - if ( - (opened.value === name && !iconOpened) || - (opened.value !== name && iconOpened) - ) { - const step = opened.value === name ? 10 : -10; - iconOpened = !iconOpened; - for (let i = 0; i < 9; ++i) { - Utils.timeout(15 * i, () => { - deg += step; - icon.setCss(`-gtk-icon-transform: rotate(${deg}deg);`); - }); - } - } - }, - ], - ], - }), + child: icon, on_clicked: () => { opened.value = opened.value === name ? "" : name; if (typeof activate === "function") activate(); @@ -70,14 +63,10 @@ export const ArrowToggleButton = ({ }) => Widget.Box({ class_name: "toggle-button", - connections: [ - [ - service, - (box) => { - box.toggleClassName("active", condition()); - }, - ], - ], + setup: (self) => + self.hook(service, () => { + self.toggleClassName("active", condition()); + }), children: [ Widget.Button({ child: Widget.Box({ @@ -108,7 +97,7 @@ export const ArrowToggleButton = ({ export const Menu = ({ name, icon, title, content }) => Widget.Revealer({ transition: "slide_down", - binds: [["reveal-child", opened, "value", (v) => v === name]], + reveal_child: opened.bind().transform((v) => v === name), child: Widget.Box({ class_names: ["menu", name], vertical: true, @@ -136,14 +125,10 @@ export const SimpleToggleButton = ({ }) => Widget.Button({ class_name: "simple-toggle", - connections: [ - [ - service, - (box) => { - box.toggleClassName("active", condition()); - }, - ], - ], + setup: (self) => + self.hook(service, () => { + self.toggleClassName("active", condition()); + }), child: icon, on_clicked: toggle, }); diff --git a/home/desktops/hyprland/ags/js/quicksettings/widgets/AsusProfile.js b/home/desktops/hyprland/ags/js/quicksettings/widgets/AsusProfile.js index e2a919d..c9b0abc 100644 --- a/home/desktops/hyprland/ags/js/quicksettings/widgets/AsusProfile.js +++ b/home/desktops/hyprland/ags/js/quicksettings/widgets/AsusProfile.js @@ -8,10 +8,10 @@ export const ProfileToggle = () => ArrowToggleButton({ name: "asusctl-profile", icon: Widget.Icon({ - binds: [["icon", Asusctl, "profile", (p) => icons.asusctl.profile[p]]], + icon: Asusctl.bind("profile").transform((p) => icons.asusctl.profile[p]), }), label: Widget.Label({ - binds: [["label", Asusctl, "profile"]], + label: Asusctl.bind("profile"), }), connection: [Asusctl, () => Asusctl.profile !== "Balanced"], activate: () => Asusctl.setProfile("Quiet"), @@ -23,7 +23,7 @@ export const ProfileSelector = () => Menu({ name: "asusctl-profile", icon: Widget.Icon({ - binds: [["icon", Asusctl, "profile", (p) => icons.asusctl.profile[p]]], + icon: Asusctl.bind("profile").transform((p) => icons.asusctl.profile[p]), }), title: Widget.Label("Profile Selector"), content: [ diff --git a/home/desktops/hyprland/ags/js/quicksettings/widgets/Bluetooth.js b/home/desktops/hyprland/ags/js/quicksettings/widgets/Bluetooth.js index 142d8bc..06b6e68 100644 --- a/home/desktops/hyprland/ags/js/quicksettings/widgets/Bluetooth.js +++ b/home/desktops/hyprland/ags/js/quicksettings/widgets/Bluetooth.js @@ -7,41 +7,31 @@ export const BluetoothToggle = () => ArrowToggleButton({ name: "bluetooth", icon: Widget.Icon({ - connections: [ - [ - Bluetooth, - (icon) => { - icon.icon = Bluetooth.enabled - ? icons.bluetooth.enabled - : icons.bluetooth.disabled; - }, - ], - ], + icon: Bluetooth.bind("enabled").transform( + (p) => icons.bluetooth[p ? "enabled" : "disabled"], + ), }), label: Widget.Label({ truncate: "end", - connections: [ - [ - Bluetooth, - (label) => { - if (!Bluetooth.enabled) return (label.label = "Disabled"); + setup: (self) => + self.hook(Bluetooth, () => { + if (!Bluetooth.enabled) return (self.label = "Disabled"); - if (Bluetooth.connectedDevices.length === 0) - return (label.label = "Not Connected"); + if (Bluetooth.connected_devices.length === 0) + return (self.label = "Not Connected"); - if (Bluetooth.connectedDevices.length === 1) - return (label.label = Bluetooth.connectedDevices[0].alias); + if (Bluetooth.connected_devices.length === 1) + return (self.label = Bluetooth.connected_devices[0].alias); - label.label = `${Bluetooth.connectedDevices.length} Connected`; - }, - ], - ], + self.label = `${Bluetooth.connected_devices.length} Connected`; + }), }), connection: [Bluetooth, () => Bluetooth.enabled], deactivate: () => (Bluetooth.enabled = false), activate: () => (Bluetooth.enabled = true), }); +/** @param {import('types/service/bluetooth').BluetoothDevice} device */ const DeviceItem = (device) => Widget.Box({ children: [ @@ -49,26 +39,20 @@ const DeviceItem = (device) => Widget.Label(device.name), Widget.Label({ label: `${device.battery_percentage}%`, - binds: [["visible", device, "battery-percentage", (p) => p > 0]], + visible: device.bind("battery_percentage").transform((p) => p > 0), }), Widget.Box({ hexpand: true }), Widget.Spinner({ - binds: [ - ["active", device, "connecting"], - ["visible", device, "connecting"], - ], + active: device.bind("connecting"), + visible: device.bind("connecting"), }), Widget.Switch({ active: device.connected, - binds: [["visible", device, "connecting", (c) => !c]], - connections: [ - [ - "notify::active", - ({ active }) => { - device.setConnection(active); - }, - ], - ], + visible: device.bind("connecting").transform((p) => !p), + setup: (self) => + self.on("notify::active", () => { + device.setConnection(self.active); + }), }), ], }); @@ -82,14 +66,9 @@ export const BluetoothDevices = () => Widget.Box({ hexpand: true, vertical: true, - binds: [ - [ - "children", - Bluetooth, - "devices", - (ds) => ds.filter((d) => d.name).map(DeviceItem), - ], - ], + children: Bluetooth.bind("devices").transform((ds) => + ds.filter((d) => d.name).map(DeviceItem), + ), }), ], }); diff --git a/home/desktops/hyprland/ags/js/quicksettings/widgets/Brightness.js b/home/desktops/hyprland/ags/js/quicksettings/widgets/Brightness.js index 993dcfb..309aae9 100644 --- a/home/desktops/hyprland/ags/js/quicksettings/widgets/Brightness.js +++ b/home/desktops/hyprland/ags/js/quicksettings/widgets/Brightness.js @@ -1,15 +1,12 @@ import Widget from "resource:///com/github/Aylur/ags/widget.js"; import icons from "../../icons.js"; import Brightness from "../../services/brightness.js"; -import * as Utils from "resource:///com/github/Aylur/ags/utils.js"; - -const CanSupportBrightness = () => Utils.exec("which gbmonctl"); const BrightnessSlider = () => Widget.Slider({ draw_value: false, hexpand: true, - binds: [["value", Brightness, "screen"]], + value: Brightness.bind("screen"), on_change: ({ value }) => (Brightness.screen = value), }); @@ -18,14 +15,9 @@ export default () => children: [ Widget.Button({ child: Widget.Icon(icons.brightness.indicator), - binds: [ - [ - "tooltip-text", - Brightness, - "screen", - (v) => `Screen Brightness: ${Math.floor(v * 100)}%`, - ], - ], + tooltip_text: Brightness.bind("screen").transform( + (v) => `Screen Brightness: ${Math.floor(v * 100)}%`, + ), }), BrightnessSlider(), ], diff --git a/home/desktops/hyprland/ags/js/quicksettings/widgets/DND.js b/home/desktops/hyprland/ags/js/quicksettings/widgets/DND.js index 72e6c26..3ebc49a 100644 --- a/home/desktops/hyprland/ags/js/quicksettings/widgets/DND.js +++ b/home/desktops/hyprland/ags/js/quicksettings/widgets/DND.js @@ -6,17 +6,9 @@ import { SimpleToggleButton } from "../ToggleButton.js"; export default () => SimpleToggleButton({ icon: Widget.Icon({ - connections: [ - [ - Notifications, - (icon) => { - icon.icon = Notifications.dnd - ? icons.notifications.silent - : icons.notifications.noisy; - }, - "notify::dnd", - ], - ], + icon: Notifications.bind("dnd").transform( + (dnd) => icons.notifications[dnd ? "silent" : "noisy"], + ), }), toggle: () => (Notifications.dnd = !Notifications.dnd), connection: [Notifications, () => Notifications.dnd], diff --git a/home/desktops/hyprland/ags/js/quicksettings/widgets/Header.js b/home/desktops/hyprland/ags/js/quicksettings/widgets/Header.js index d45cc23..3b6d28b 100644 --- a/home/desktops/hyprland/ags/js/quicksettings/widgets/Header.js +++ b/home/desktops/hyprland/ags/js/quicksettings/widgets/Header.js @@ -6,6 +6,7 @@ import Avatar from "../../misc/Avatar.js"; import icons from "../../icons.js"; import { openSettings } from "../../settings/theme.js"; import { uptime } from "../../variables.js"; +import DND from "./DND.js"; export default () => Widget.Box({ @@ -20,16 +21,17 @@ export default () => /*Widget.Box({ class_name: "battery horizontal", children: [ - Widget.Icon({ binds: [["icon", Battery, "icon-name"]] }), + Widget.Icon({ icon: Battery.bind("icon_name") }), Widget.Label({ - binds: [["label", Battery, "percent", (p) => `${p}%`]], + label: Battery.bind("percent").transform((p) => `${p}%`), }), ], - }), + }),*/ + DND(), Widget.Label({ class_name: "uptime", - binds: [["label", uptime, "value", (v) => `up: ${v}`]], - }),*/ + label: uptime.bind().transform((v) => `up: ${v}`), + }), Widget.Button({ on_clicked: openSettings, child: Widget.Icon(icons.ui.settings), diff --git a/home/desktops/hyprland/ags/js/quicksettings/widgets/Media.js b/home/desktops/hyprland/ags/js/quicksettings/widgets/Media.js index b6c5694..699a099 100644 --- a/home/desktops/hyprland/ags/js/quicksettings/widgets/Media.js +++ b/home/desktops/hyprland/ags/js/quicksettings/widgets/Media.js @@ -7,31 +7,29 @@ import options from "../../options.js"; const Footer = (player) => Widget.CenterBox({ class_name: "footer-box", - children: [ - Widget.Box({ - class_name: "position", - children: [ - mpris.PositionLabel(player), - mpris.Slash(player), - mpris.LengthLabel(player), - ], - }), - Widget.Box({ - class_name: "controls", - children: [ - mpris.ShuffleButton(player), - mpris.PreviousButton(player), - mpris.PlayPauseButton(player), - mpris.NextButton(player), - mpris.LoopButton(player), - ], - }), - mpris.PlayerIcon(player, { - symbolic: false, - hexpand: true, - hpack: "end", - }), - ], + start_widget: Widget.Box({ + class_name: "position", + children: [ + mpris.PositionLabel(player), + mpris.Slash(player), + mpris.LengthLabel(player), + ], + }), + center_widget: Widget.Box({ + class_name: "controls", + children: [ + mpris.ShuffleButton(player), + mpris.PreviousButton(player), + mpris.PlayPauseButton(player), + mpris.NextButton(player), + mpris.LoopButton(player), + ], + }), + end_widget: mpris.PlayerIcon(player, { + symbolic: false, + hexpand: true, + hpack: "end", + }), }); /** @param {import('types/service/mpris').MprisPlayer} player */ @@ -84,23 +82,10 @@ export default () => Widget.Box({ vertical: true, class_name: "media vertical", - connections: [ - [ - "draw", - (self) => { - self.visible = Mpris.players.length > 0; - }, - ], - ], - binds: [ - [ - "children", - Mpris, - "players", - (ps) => - ps - .filter((p) => !options.mpris.black_list.value.includes(p.identity)) - .map(PlayerBox), - ], - ], + visible: Mpris.bind("players").transform((p) => p.length > 0), + children: Mpris.bind("players").transform((ps) => + ps + .filter((p) => !options.mpris.black_list.value.includes(p.identity)) + .map(PlayerBox), + ), }); diff --git a/home/desktops/hyprland/ags/js/quicksettings/widgets/MicMute.js b/home/desktops/hyprland/ags/js/quicksettings/widgets/MicMute.js index 3905c7c..4e797ce 100644 --- a/home/desktops/hyprland/ags/js/quicksettings/widgets/MicMute.js +++ b/home/desktops/hyprland/ags/js/quicksettings/widgets/MicMute.js @@ -5,19 +5,15 @@ import { SimpleToggleButton } from "../ToggleButton.js"; export default () => SimpleToggleButton({ - icon: Widget.Icon({ - connections: [ - [ - Audio, - (icon) => { - icon.icon = Audio.microphone?.is_muted - ? icons.audio.mic.muted - : icons.audio.mic.high; - }, - "microphone-changed", - ], - ], - }), + icon: Widget.Icon().hook( + Audio, + (self) => { + self.icon = Audio.microphone?.is_muted + ? icons.audio.mic.muted + : icons.audio.mic.high; + }, + "microphone-changed", + ), toggle: () => (Audio.microphone.is_muted = !Audio.microphone.is_muted), - connection: [Audio, () => Audio.microphone?.is_muted], + connection: [Audio, () => Audio.microphone?.is_muted || false], }); diff --git a/home/desktops/hyprland/ags/js/quicksettings/widgets/Network.js b/home/desktops/hyprland/ags/js/quicksettings/widgets/Network.js index 87404d6..07f1e2c 100644 --- a/home/desktops/hyprland/ags/js/quicksettings/widgets/Network.js +++ b/home/desktops/hyprland/ags/js/quicksettings/widgets/Network.js @@ -9,25 +9,13 @@ export const NetworkToggle = () => ArrowToggleButton({ name: "network", icon: Widget.Icon({ - connections: [ - [ - Network, - (icon) => { - icon.icon = Network.wifi.icon_name || ""; - }, - ], - ], + icon: Network.wifi.bind("icon_name"), }), label: Widget.Label({ truncate: "end", - connections: [ - [ - Network, - (label) => { - label.label = Network.wifi.ssid || "Not Connected"; - }, - ], - ], + label: Network.wifi + .bind("ssid") + .transform((ssid) => ssid || "Not Connected"), }), connection: [Network, () => Network.wifi.enabled], deactivate: () => (Network.wifi.enabled = false), @@ -41,24 +29,17 @@ export const WifiSelection = () => Menu({ name: "network", icon: Widget.Icon({ - connections: [ - [ - Network, - (icon) => { - icon.icon = Network.wifi.icon_name; - }, - ], - ], + icon: Network.wifi.bind("icon_name"), }), title: Widget.Label("Wifi Selection"), content: [ Widget.Box({ vertical: true, - connections: [ - [ + setup: (self) => + self.hook( Network, - (box) => - (box.children = Network.wifi?.access_points.map((ap) => + () => + (self.children = Network.wifi?.access_points.map((ap) => Widget.Button({ on_clicked: () => Utils.execAsync(`nmcli device wifi connect ${ap.bssid}`), @@ -76,8 +57,7 @@ export const WifiSelection = () => }), }), )), - ], - ], + ), }), Widget.Separator(), Widget.Button({ diff --git a/home/desktops/hyprland/ags/js/quicksettings/widgets/Theme.js b/home/desktops/hyprland/ags/js/quicksettings/widgets/Theme.js index 186bf7e..f326a99 100644 --- a/home/desktops/hyprland/ags/js/quicksettings/widgets/Theme.js +++ b/home/desktops/hyprland/ags/js/quicksettings/widgets/Theme.js @@ -8,8 +8,8 @@ import { setTheme, openSettings } from "../../settings/theme.js"; export const ThemeToggle = () => ArrowToggleButton({ name: "theme", - icon: Widget.Label({ binds: [["label", options.theme.icon]] }), - label: Widget.Label({ binds: [["label", options.theme.name]] }), + icon: Widget.Label().bind("label", options.theme.icon), + label: Widget.Label().bind("label", options.theme.name), connection: [opened, () => opened.value === "theme"], activate: () => opened.setValue("theme"), activateOnArrow: false, @@ -19,9 +19,7 @@ export const ThemeToggle = () => export const ThemeSelector = () => Menu({ name: "theme", - icon: Widget.Label({ - binds: [["label", options.theme.icon]], - }), + icon: Widget.Label().bind("label", options.theme.icon), title: Widget.Label("Theme Selector"), content: [ ...themes.map(({ name, icon }) => @@ -35,9 +33,9 @@ export const ThemeSelector = () => icon: icons.ui.tick, hexpand: true, hpack: "end", - binds: [ - ["visible", options.theme.name, "value", (v) => v === name], - ], + visible: options.theme.name + .bind("value") + .transform((v) => v === name), }), ], }), diff --git a/home/desktops/hyprland/ags/js/quicksettings/widgets/Volume.js b/home/desktops/hyprland/ags/js/quicksettings/widgets/Volume.js index b972a10..c80a1ff 100644 --- a/home/desktops/hyprland/ags/js/quicksettings/widgets/Volume.js +++ b/home/desktops/hyprland/ags/js/quicksettings/widgets/Volume.js @@ -11,25 +11,13 @@ import { Menu } from "../ToggleButton.js"; const VolumeIndicator = (type = "speaker") => Widget.Button({ on_clicked: () => (Audio[type].is_muted = !Audio[type].is_muted), - child: Widget.Icon({ - connections: [ - [ - Audio, - (icon) => { - if (!Audio[type]) return; + child: Widget.Icon().hook(Audio[type], (icon) => { + icon.icon = + type === "speaker" + ? getAudioTypeIcon(Audio[type].icon_name || "") + : icons.audio.mic.high; - icon.icon = - type === "speaker" - ? getAudioTypeIcon(Audio[type].icon_name || "") - : icons.audio.mic.high; - - icon.tooltip_text = `Volume ${Math.floor( - Audio[type].volume * 100, - )}%`; - }, - `${type}-changed`, - ], - ], + icon.tooltip_text = `Volume ${Math.floor(Audio[type].volume * 100)}%`; }), }); @@ -39,15 +27,10 @@ const VolumeSlider = (type = "speaker") => hexpand: true, draw_value: false, on_change: ({ value }) => (Audio[type].volume = value), - connections: [ - [ - Audio, - (slider) => { - slider.value = Audio[type]?.volume; - }, - `${type}-changed`, - ], - ], + setup: (self) => + self.hook(Audio[type], () => { + self.value = Audio[type].volume || 0; + }), }); export const Volume = () => @@ -62,14 +45,7 @@ export const Volume = () => Widget.Box({ vpack: "center", child: Arrow("app-mixer"), - connections: [ - [ - Audio, - (box) => { - box.visible = Audio.apps.length > 0; - }, - ], - ], + visible: Audio.bind("apps").transform((a) => a.length > 0), }), ], }); @@ -77,7 +53,7 @@ export const Volume = () => export const Microhone = () => Widget.Box({ class_name: "slider horizontal", - binds: [["visible", Audio, "recorders", (r) => r.length > 0]], + visible: Audio.bind("recorders").transform((a) => a.length > 0), children: [VolumeIndicator("microphone"), VolumeSlider("microphone")], }); @@ -88,17 +64,10 @@ const MixerItem = (stream) => class_name: "mixer-item horizontal", children: [ Widget.Icon({ - binds: [["tooltipText", stream, "name"]], - connections: [ - [ - stream, - (icon) => { - icon.icon = Utils.lookUpIcon(stream.name || "") - ? stream.name || "" - : icons.mpris.fallback; - }, - ], - ], + tooltip_text: stream.bind("name").transform((n) => n || ""), + icon: stream.bind("name").transform((n) => { + return Utils.lookUpIcon(n || "") ? n || "" : icons.mpris.fallback; + }), }), Widget.Box({ vertical: true, @@ -106,26 +75,19 @@ const MixerItem = (stream) => Widget.Label({ xalign: 0, truncate: "end", - binds: [["label", stream, "description"]], + label: stream.bind("description").transform((d) => d || ""), }), Widget.Slider({ hexpand: true, draw_value: false, - binds: [["value", stream, "volume"]], + value: stream.bind("volume"), on_change: ({ value }) => (stream.volume = value), }), ], }), Widget.Label({ xalign: 1, - connections: [ - [ - stream, - (l) => { - l.label = `${Math.floor(stream.volume * 100)}%`; - }, - ], - ], + label: stream.bind("volume").transform((v) => `${Math.floor(v * 100)}`), }), ], }); @@ -148,7 +110,9 @@ const SinkItem = (stream) => icon: icons.ui.tick, hexpand: true, hpack: "end", - binds: [["visible", Audio, "speaker", (s) => s === stream]], + visible: Audio.speaker + .bind("stream") + .transform((s) => s === stream.stream), }), ], }), @@ -171,7 +135,7 @@ export const AppMixer = () => content: [ Widget.Box({ vertical: true, - binds: [["children", Audio, "apps", (a) => a.map(MixerItem)]], + children: Audio.bind("apps").transform((a) => a.map(MixerItem)), }), Widget.Separator(), SettingsButton(), @@ -186,7 +150,7 @@ export const SinkSelector = () => content: [ Widget.Box({ vertical: true, - binds: [["children", Audio, "speakers", (s) => s.map(SinkItem)]], + children: Audio.bind("speakers").transform((a) => a.map(SinkItem)), }), Widget.Separator(), SettingsButton(), diff --git a/home/desktops/hyprland/ags/js/screencorner/ScreenCorners.js b/home/desktops/hyprland/ags/js/screencorner/ScreenCorners.js index 87c7d1b..f6ce275 100644 --- a/home/desktops/hyprland/ags/js/screencorner/ScreenCorners.js +++ b/home/desktops/hyprland/ags/js/screencorner/ScreenCorners.js @@ -10,50 +10,49 @@ const Corner = (place) => vexpand: true, hpack: place.includes("left") ? "start" : "end", vpack: place.includes("top") ? "start" : "end", - connections: [ - [ - options.radii, - (self) => { + setup: (self) => + self + .hook(options.radii, () => { const r = options.radii.value * 2; self.set_size_request(r, r); - }, - ], - ], - setup: (self) => - self.connect("draw", (self, cr) => { - const context = self.get_style_context(); - const c = context.get_property( - "background-color", - Gtk.StateFlags.NORMAL, - ); - const r = context.get_property("border-radius", Gtk.StateFlags.NORMAL); + }) + .connect("draw", (self, cr) => { + const context = self.get_style_context(); + const c = context.get_property( + "background-color", + Gtk.StateFlags.NORMAL, + ); + const r = context.get_property( + "border-radius", + Gtk.StateFlags.NORMAL, + ); - switch (place) { - case "topleft": - cr.arc(r, r, r, Math.PI, (3 * Math.PI) / 2); - cr.lineTo(0, 0); - break; + switch (place) { + case "topleft": + cr.arc(r, r, r, Math.PI, (3 * Math.PI) / 2); + cr.lineTo(0, 0); + break; - case "topright": - cr.arc(0, r, r, (3 * Math.PI) / 2, 2 * Math.PI); - cr.lineTo(r, 0); - break; + case "topright": + cr.arc(0, r, r, (3 * Math.PI) / 2, 2 * Math.PI); + cr.lineTo(r, 0); + break; - case "bottomleft": - cr.arc(r, 0, r, Math.PI / 2, Math.PI); - cr.lineTo(0, r); - break; + case "bottomleft": + cr.arc(r, 0, r, Math.PI / 2, Math.PI); + cr.lineTo(0, r); + break; - case "bottomright": - cr.arc(0, 0, r, 0, Math.PI / 2); - cr.lineTo(r, r); - break; - } + case "bottomright": + cr.arc(0, 0, r, 0, Math.PI / 2); + cr.lineTo(r, r); + break; + } - cr.closePath(); - cr.setSourceRGBA(c.red, c.green, c.blue, c.alpha); - cr.fill(); - }), + cr.closePath(); + cr.setSourceRGBA(c.red, c.green, c.blue, c.alpha); + cr.fill(); + }), }); /** @type {Array<'topleft' | 'topright' | 'bottomleft' | 'bottomright'>} */ @@ -70,7 +69,7 @@ export default (monitor) => place.includes("top") ? "top" : "bottom", place.includes("right") ? "right" : "left", ], - binds: [["visible", options.desktop.screen_corners]], + visible: options.desktop.screen_corners.bind("value"), child: Corner(place), }), ); diff --git a/home/desktops/hyprland/ags/js/services/asusctl.js b/home/desktops/hyprland/ags/js/services/asusctl.js index df2372c..a1ecca6 100644 --- a/home/desktops/hyprland/ags/js/services/asusctl.js +++ b/home/desktops/hyprland/ags/js/services/asusctl.js @@ -13,7 +13,7 @@ class Asusctl extends Service { ); } - profiles = Object.freeze(["Performance", "Balanced", "Quiet"]); + profiles = /** @type {const} */ (["Performance", "Balanced", "Quiet"]); #profile = "Balanced"; #mode = "Hyprid"; diff --git a/home/desktops/hyprland/ags/js/services/colorpicker.js b/home/desktops/hyprland/ags/js/services/colorpicker.js index 65398a2..3ad33e7 100644 --- a/home/desktops/hyprland/ags/js/services/colorpicker.js +++ b/home/desktops/hyprland/ags/js/services/colorpicker.js @@ -1,8 +1,8 @@ -import Notifications from "resource:///com/github/Aylur/ags/service/notifications.js"; import { Variable } from "resource:///com/github/Aylur/ags/variable.js"; import * as Utils from "resource:///com/github/Aylur/ags/utils.js"; import Service from "resource:///com/github/Aylur/ags/service.js"; import { dependencies } from "../utils.js"; +import icons from "../icons.js"; const COLORS_CACHE = Utils.CACHE_DIR + "/colorpicker.json"; @@ -58,15 +58,15 @@ class Colors extends Service { ); } - this.#notifID = Notifications.Notify( - "Color Picker", - this.#notifID, - "color-select-symbolic", - color, - "", - [], - {}, - ); + const n = await Utils.notify({ + id: this.#notifID, + iconName: icons.ui.colorpicker, + summary: color, + actions: { + Copy: () => this.wlCopy(color), + }, + }); + this.#notifID = n.id; } } diff --git a/home/desktops/hyprland/ags/js/settings/SettingsDialog.js b/home/desktops/hyprland/ags/js/settings/SettingsDialog.js index e80b15f..6f9b4c1 100644 --- a/home/desktops/hyprland/ags/js/settings/SettingsDialog.js +++ b/home/desktops/hyprland/ags/js/settings/SettingsDialog.js @@ -21,7 +21,7 @@ showSearch.connect("changed", ({ value }) => { /** @param {import('./option.js').Opt} opt */ const EnumSetter = (opt) => { - const lbl = Widget.Label({ binds: [["label", opt]] }); + const lbl = Widget.Label().bind("label", opt); const step = (dir = 1) => { const i = opt.enums.findIndex((i) => i === lbl.label); opt.setValue( @@ -57,51 +57,42 @@ const Setter = (opt) => { setup(self) { self.set_range(0, 1000); self.set_increments(1, 5); + self.on("value-changed", () => opt.setValue(self.value, true)); + self.hook(opt, () => (self.value = opt.value)); }, - connections: [ - ["value-changed", (self) => opt.setValue(self.value, true)], - [opt, (self) => (self.value = opt.value)], - ], }); case "float": case "object": return Widget.Entry({ on_accept: (self) => opt.setValue(JSON.parse(self.text || ""), true), - connections: [[opt, (self) => (self.text = JSON.stringify(opt.value))]], + setup: (self) => + self.hook(opt, () => (self.text = JSON.stringify(opt.value))), }); case "string": return Widget.Entry({ on_accept: (self) => opt.setValue(self.text, true), - connections: [[opt, (self) => (self.text = opt.value)]], + setup: (self) => self.hook(opt, () => (self.text = opt.value)), }); case "enum": return EnumSetter(opt); case "boolean": - return Widget.Switch({ - connections: [ - ["notify::active", (self) => opt.setValue(self.active, true)], - [opt, (self) => (self.active = opt.value)], - ], - }); + return Widget.Switch() + .on("notify::active", (self) => opt.setValue(self.active, true)) + .hook(opt, (self) => (self.active = opt.value)); + case "img": - return Widget.FileChooserButton({ - connections: [ - [ - "selection-changed", - (self) => { - opt.setValue(self.get_uri()?.replace("file://", ""), true); - }, - ], - ], + return Widget.FileChooserButton().on("selection-changed", (self) => { + opt.setValue(self.get_uri()?.replace("file://", ""), true); }); + case "font": return Widget.FontButton({ show_size: false, use_size: false, - connections: [ - ["notify::font", ({ font }) => opt.setValue(font, true)], - [opt, (self) => (self.font = opt.value)], - ], + setup: (self) => + self + .on("notify::font", ({ font }) => opt.setValue(font, true)) + .hook(opt, () => (self.font = opt.value)), }); default: return Widget.Label({ @@ -114,7 +105,7 @@ const Setter = (opt) => { const Row = (opt) => Widget.Box({ class_name: "row", - setup: (self) => (self.opt = opt), + attribute: opt, children: [ Widget.Box({ vertical: true, @@ -161,19 +152,15 @@ const Page = (category) => child: Widget.Box({ class_name: "page-content vertical", vertical: true, - connections: [ - [ - search, - (self) => { - for (const child of self.children) { - child.visible = - child.opt.id.includes(search.value) || - child.opt.title.includes(search.value) || - child.opt.note.includes(search.value); - } - }, - ], - ], + setup: (self) => + self.hook(search, () => { + for (const child of self.children) { + child.visible = + child.attribute.id.includes(search.value) || + child.attribute.title.includes(search.value) || + child.attribute.note.includes(search.value); + } + }), children: optionsList .filter((opt) => opt.category.includes(category)) .map(Row), @@ -181,7 +168,7 @@ const Page = (category) => }); const sidebar = Widget.Revealer({ - binds: [["reveal-child", search, "value", (v) => !v]], + reveal_child: search.bind().transform((v) => !v), transition: "slide_right", child: Widget.Box({ hexpand: false, @@ -213,14 +200,9 @@ const sidebar = Widget.Revealer({ Widget.Button({ label: (icons.dialog[name] || "") + " " + name, xalign: 0, - binds: [ - [ - "class-name", - currentPage, - "value", - (v) => (v === name ? "active" : ""), - ], - ], + class_name: currentPage + .bind() + .transform((v) => `${v === name ? "active" : ""}`), on_clicked: () => currentPage.setValue(name), }), ), @@ -254,21 +236,15 @@ const sidebar = Widget.Revealer({ const searchEntry = Widget.Revealer({ transition: "slide_down", - binds: [ - ["reveal-child", showSearch], - ["transition-duration", options.transition], - ], + reveal_child: showSearch.bind(), + transition_duration: options.transition.bind("value"), child: Widget.Entry({ - connections: [ - [ - showSearch, - (self) => { - if (!showSearch.value) self.text = ""; + setup: (self) => + self.hook(showSearch, () => { + if (!showSearch.value) self.text = ""; - if (showSearch.value) self.grab_focus(); - }, - ], - ], + if (showSearch.value) self.grab_focus(); + }), hexpand: true, class_name: "search", placeholder_text: "Search Options", @@ -279,41 +255,36 @@ const searchEntry = Widget.Revealer({ const categoriesStack = Widget.Stack({ transition: "slide_left_right", - items: categories.map((name) => [name, Page(name)]), - binds: [ - ["shown", currentPage], - ["visible", search, "value", (v) => !v], - ], + children: categories.reduce((obj, name) => { + obj[name] = Page(name); + return obj; + }, {}), + shown: currentPage.bind(), + visible: search.bind().transform((v) => !v), }); const searchPage = Widget.Box({ - binds: [["visible", search, "value", (v) => !!v]], + visible: search.bind().transform((v) => !!v), child: Page(""), }); export default RegularWindow({ name: "settings-dialog", title: "Settings", - setup: (win) => win.set_default_size(800, 500), - connections: [ - [ - "delete-event", - (win) => { + setup: (win) => + win + .on("delete-event", () => { win.hide(); return true; - }, - ], - [ - "key-press-event", - (self, event) => { + }) + .on("key-press-event", (_, event) => { if (event.get_keyval()[1] === imports.gi.Gdk.KEY_Escape) { - self.text = ""; showSearch.setValue(false); search.setValue(""); } - }, - ], - ], + }) + .set_default_size(800, 500), + child: Widget.Box({ children: [ sidebar, diff --git a/home/desktops/hyprland/ags/js/settings/globals.js b/home/desktops/hyprland/ags/js/settings/globals.js index 1e72e17..33c44d9 100644 --- a/home/desktops/hyprland/ags/js/settings/globals.js +++ b/home/desktops/hyprland/ags/js/settings/globals.js @@ -15,6 +15,9 @@ export async function globals() { globalThis.indicator = ( await import("../services/onScreenIndicator.js") ).default; + globalThis.app = ( + await import("resource:///com/github/Aylur/ags/app.js") + ).default; Mpris.players.forEach((player) => { player.connect("changed", (player) => { diff --git a/home/desktops/hyprland/ags/js/settings/option.js b/home/desktops/hyprland/ags/js/settings/option.js index 30e00bc..cd1e945 100644 --- a/home/desktops/hyprland/ags/js/settings/option.js +++ b/home/desktops/hyprland/ags/js/settings/option.js @@ -5,7 +5,7 @@ import { } from "resource:///com/github/Aylur/ags/utils.js"; import { exec } from "resource:///com/github/Aylur/ags/utils.js"; import options from "../options.js"; -import Service, { Binding } from "resource:///com/github/Aylur/ags/service.js"; +import Service from "resource:///com/github/Aylur/ags/service.js"; import { reloadScss } from "./scss.js"; import { setupHyprland } from "./hyprland.js"; const CACHE_FILE = CACHE_DIR + "/options.json"; diff --git a/home/desktops/hyprland/ags/js/settings/scss.js b/home/desktops/hyprland/ags/js/settings/scss.js index 45bba7a..be4f48e 100644 --- a/home/desktops/hyprland/ags/js/settings/scss.js +++ b/home/desktops/hyprland/ags/js/settings/scss.js @@ -1,7 +1,6 @@ import App from "resource:///com/github/Aylur/ags/app.js"; import * as Utils from "resource:///com/github/Aylur/ags/utils.js"; import { getOptions } from "./option.js"; -import { dependencies } from "../utils.js"; export function scssWatcher() { return Utils.subprocess( @@ -26,8 +25,6 @@ export function scssWatcher() { * options.bar.style.value => $bar-style */ export async function reloadScss() { - if (!dependencies(["sassc"])) return; - const opts = getOptions(); const vars = opts.map((opt) => { if (opt.scss === "exclude") return ""; diff --git a/home/desktops/hyprland/ags/js/settings/setup.js b/home/desktops/hyprland/ags/js/settings/setup.js index b0bacfc..8c8efe6 100644 --- a/home/desktops/hyprland/ags/js/settings/setup.js +++ b/home/desktops/hyprland/ags/js/settings/setup.js @@ -8,7 +8,7 @@ import { wallpaper } from "./wallpaper.js"; import { hyprlandInit, setupHyprland } from "./hyprland.js"; import { globals } from "./globals.js"; import { showAbout } from "../about/about.js"; -import Gtk from "gi://Gtk"; +import Gtk from "gi://Gtk?version=3.0"; export function init() { notificationBlacklist(); diff --git a/home/desktops/hyprland/ags/js/utils.js b/home/desktops/hyprland/ags/js/utils.js index c4ce70f..3dcf568 100644 --- a/home/desktops/hyprland/ags/js/utils.js +++ b/home/desktops/hyprland/ags/js/utils.js @@ -32,7 +32,7 @@ export function forMonitors(widget) { } /** - * @param {import('gi://Gtk').Gtk.Widget} widget + * @param {import('gi://Gtk?version=3.0').default.Widget} widget * @returns {any} - missing cairo type */ export function createSurfaceFromWidget(widget) { diff --git a/home/desktops/hyprland/ags/js/variables.js b/home/desktops/hyprland/ags/js/variables.js index 6c306f1..f3a94c3 100644 --- a/home/desktops/hyprland/ags/js/variables.js +++ b/home/desktops/hyprland/ags/js/variables.js @@ -4,6 +4,10 @@ import options from "./options.js"; const intval = options.systemFetchInterval; +export const clock = Variable(GLib.DateTime.new_now_local(), { + poll: [1000, () => GLib.DateTime.new_now_local()], +}); + export const uptime = Variable("", { poll: [ 60_000, diff --git a/home/desktops/hyprland/ags/package.json b/home/desktops/hyprland/ags/package.json index affc916..c39777e 100644 --- a/home/desktops/hyprland/ags/package.json +++ b/home/desktops/hyprland/ags/package.json @@ -1,11 +1,13 @@ { "name": "ags-dotfiles", - "version": "1.6.4", + "version": "1.7.5", "description": "My config files for AGS", "main": "config.js", "scripts": { + "check": "tsc --noEmit", "lint": "eslint . --fix", - "stylelint": "stylelint ./scss --fix" + "stylelint": "stylelint ./scss --fix", + "format": "prettier --write ." }, "repository": { "type": "git", @@ -18,10 +20,16 @@ "homepage": "https://github.com/Aylur/dotfiles#readme", "kofi": "https://ko-fi.com/aylur", "devDependencies": { - "@girs/dbusmenugtk3-0.4": "^0.4.0-3.2.7", - "@girs/gobject-2.0": "^2.78.0-3.2.7", - "@girs/gtk-3.0": "^3.24.38-3.2.7", - "@girs/gvc-1.0": "^1.0.0-3.2.7", - "@girs/nm-1.0": "^1.43.1-3.1.0" + "@girs/dbusmenugtk3-0.4": "^0.4.0-3.2.0", + "@girs/gobject-2.0": "^2.76.1-3.2.3", + "@girs/gtk-3.0": "^3.24.39-3.2.2", + "@girs/gvc-1.0": "^1.0.0-3.1.0", + "@girs/nm-1.0": "^1.43.1-3.1.0", + "@typescript-eslint/eslint-plugin": "^5.33.0", + "@typescript-eslint/parser": "^5.33.0", + "eslint": "^8.44.0", + "prettier": "^3.2.5", + "stylelint-config-standard-scss": "^10.0.0", + "typescript": "^5.3.3" } } diff --git a/home/desktops/hyprland/ags/tsconfig.json b/home/desktops/hyprland/ags/tsconfig.json index 7026c9c..0cb538f 100644 --- a/home/desktops/hyprland/ags/tsconfig.json +++ b/home/desktops/hyprland/ags/tsconfig.json @@ -8,7 +8,7 @@ "strict": true, "noImplicitAny": false, "baseUrl": ".", - "typeRoots": ["./types/ags.d.ts", "./node_modules/@girs"], + "typeRoots": ["./types"], "skipLibCheck": true, "forceConsistentCasingInFileNames": true } diff --git a/home/desktops/hyprland/default.nix b/home/desktops/hyprland/default.nix index b7064a3..c820979 100644 --- a/home/desktops/hyprland/default.nix +++ b/home/desktops/hyprland/default.nix @@ -160,6 +160,7 @@ brightnessctl ydotool kitty + hyprpicker ]}"; }; Install = {