From 46e8013b5929bd6a87d51a657d70fd2ffc270918 Mon Sep 17 00:00:00 2001 From: Emilien Marquegnies Date: Fri, 31 Oct 2025 18:15:53 +0100 Subject: [PATCH 1/2] Add status bar battery support --- src/modules/battery.ts | 38 ++++ src/window/bar/widgets/Status.tsx | 294 +++++++++++++++++++----------- 2 files changed, 229 insertions(+), 103 deletions(-) create mode 100644 src/modules/battery.ts diff --git a/src/modules/battery.ts b/src/modules/battery.ts new file mode 100644 index 0000000..8aff7e5 --- /dev/null +++ b/src/modules/battery.ts @@ -0,0 +1,38 @@ +import { Accessor, createBinding } from "ags"; +import AstalBattery from "gi://AstalBattery?version=0.1"; + +export class Battery { + private static astalBattery: AstalBattery.Device = AstalBattery.get_default(); + + private static batteryInst: Battery; + + constructor() { + AstalBattery.get_default(); + } + + public static getDefault(): Battery { + if (!this.batteryInst) { + this.batteryInst = new Battery(); + } + + return this.batteryInst; + } + + public static getBattery(): AstalBattery.Device { + return this.astalBattery; + } + + public bindHasBattery(): Accessor { + return createBinding(Battery.getBattery(), "isBattery"); + } + + public bindPercentage(): Accessor { + return createBinding(Battery.getBattery(), "percentage").as( + (v) => Math.round(v * 100) + "%" + ); + } + + public bindIcon(): Accessor { + return createBinding(Battery.getBattery(), "battery_icon_name"); + } +} diff --git a/src/window/bar/widgets/Status.tsx b/src/window/bar/widgets/Status.tsx index 554452b..18e7216 100644 --- a/src/window/bar/widgets/Status.tsx +++ b/src/window/bar/widgets/Status.tsx @@ -1,5 +1,6 @@ import { Gtk } from "ags/gtk4"; import { Wireplumber } from "../../../modules/volume"; +import { Battery } from "../../../modules/battery"; import { Notifications } from "../../../modules/notifications"; import { Windows } from "../../../windows"; import { Recording } from "../../../modules/recording"; @@ -11,121 +12,208 @@ import GObject from "ags/gobject"; import AstalBluetooth from "gi://AstalBluetooth"; import AstalNetwork from "gi://AstalNetwork"; import AstalWp from "gi://AstalWp"; +import Vips from "gi://Vips?version=8.0"; +export const Status = () => + ( + + openWins.includes("control-center") ? "open status" : "status" + )} + onClicked={() => Windows.getDefault().toggle("control-center")} + > + + + + + !Wireplumber.getDefault().isMutedSink() && + Wireplumber.getDefault().getSinkVolume() > 0 + ? icon + : "audio-volume-muted-symbolic" + )} + /> -export const Status = () => - - openWins.includes("control-center") ? "open status" : "status")} - onClicked={() => Windows.getDefault().toggle("control-center")}> - - - - - !Wireplumber.getDefault().isMutedSink() && - Wireplumber.getDefault().getSinkVolume() > 0 ? - icon - : "audio-volume-muted-symbolic") - } /> - - - !Wireplumber.getDefault().isMutedSource() && - Wireplumber.getDefault().getSourceVolume() > 0 ? - icon - : "microphone-sensitivity-muted-symbolic") - } /> - - - - - - - - - - + + !Wireplumber.getDefault().isMutedSource() && + Wireplumber.getDefault().getSourceVolume() > 0 + ? icon + : "microphone-sensitivity-muted-symbolic" + )} + /> - as Gtk.Button; + + + -function VolumeStatus(props: { class?: string, endpoint: AstalWp.Endpoint, icon?: (string|Accessor) }) { - return { - const conns: Map = new Map(); - const controllerScroll = Gtk.EventControllerScroll.new(Gtk.EventControllerScrollFlags.VERTICAL - | Gtk.EventControllerScrollFlags.KINETIC); + + + + + + + ) as Gtk.Button; - conns.set(controllerScroll, controllerScroll.connect("scroll", (_, _dx, dy) => { - console.log`Scrolled! dx: ${_dx}; dy: ${dy}`; - dy > 0 ? - Wireplumber.getDefault().decreaseEndpointVolume(props.endpoint, 5) - : Wireplumber.getDefault().increaseEndpointVolume(props.endpoint, 5); +function VolumeStatus(props: { + class?: string; + endpoint: AstalWp.Endpoint; + icon?: string | Accessor; +}) { + return ( + { + const conns: Map = new Map(); + const controllerScroll = Gtk.EventControllerScroll.new( + Gtk.EventControllerScrollFlags.VERTICAL | + Gtk.EventControllerScrollFlags.KINETIC + ); - return true; - })); + conns.set( + controllerScroll, + controllerScroll.connect("scroll", (_, _dx, dy) => { + console.log`Scrolled! dx: ${_dx}; dy: ${dy}`; + dy > 0 + ? Wireplumber.getDefault().decreaseEndpointVolume( + props.endpoint, + 5 + ) + : Wireplumber.getDefault().increaseEndpointVolume( + props.endpoint, + 5 + ); - conns.set(self, self.connect("destroy", () => conns.forEach((id, obj) => - obj.disconnect(id)))); - }}> + return true; + }) + ); - {props.icon && } - - `${Math.floor(vol * 100)}%`)} /> - as Gtk.Box; + conns.set( + self, + self.connect("destroy", () => + conns.forEach((id, obj) => obj.disconnect(id)) + ) + ); + }} + > + {props.icon && } + `${Math.floor(vol * 100)}%` + )} + /> + + ) as Gtk.Box; +} + +function BatteryStatus(props: { + visible?: Accessor; + class?: string; + percentage?: Accessor; + icon?: string | Accessor; +}) { + return ( + + {props.icon && } + + + ) as Gtk.Box; } function StatusIcons() { - return - { - return powered ? ( - connected ? - "bluetooth-active-symbolic" - : "bluetooth-symbolic" - ) : "bluetooth-disabled-symbolic" - })} class={"bluetooth state"} visible={ - createBinding(Bluetooth.getDefault(), "adapter").as(Boolean) + return ( + + { + return powered + ? connected + ? "bluetooth-active-symbolic" + : "bluetooth-symbolic" + : "bluetooth-disabled-symbolic"; } + )} + class={"bluetooth state"} + visible={createBinding(Bluetooth.getDefault(), "adapter").as(Boolean)} + /> + + primary !== AstalNetwork.Primary.UNKNOWN + )} + > + + {(primary: AstalNetwork.Primary) => { + let device: AstalNetwork.Wifi | AstalNetwork.Wired; + switch (primary) { + case AstalNetwork.Primary.WIRED: + device = AstalNetwork.get_default().wired; + break; + case AstalNetwork.Primary.WIFI: + device = AstalNetwork.get_default().wifi; + break; + + default: + return ; + } + + return ; + }} + + + + + + dnd + ? "minus-circle-filled-symbolic" + : "preferences-system-notifications-symbolic" + )} /> - - - primary !== AstalNetwork.Primary.UNKNOWN)}> - - - {(primary: AstalNetwork.Primary) => { - let device: AstalNetwork.Wifi|AstalNetwork.Wired; - switch(primary) { - case AstalNetwork.Primary.WIRED: - device = AstalNetwork.get_default().wired; - break; - case AstalNetwork.Primary.WIFI: - device = AstalNetwork.get_default().wifi; - break; - - default: - return ; - } - - return ; - }} - - - - - dnd ? - "minus-circle-filled-symbolic" - : "preferences-system-notifications-symbolic") - } - /> - - + + + ); } From 51e91c2e9daaa472576ef46a964d125a109a0c7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Dias?= Date: Fri, 31 Oct 2025 14:33:36 -0300 Subject: [PATCH 2/2] :recycle: refactor(bar/status): remove unused import elgemp4 accidentally imported a library he didn't want to use :P (auto-completion also does this to me sometimes lol) --- src/window/bar/widgets/Status.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/window/bar/widgets/Status.tsx b/src/window/bar/widgets/Status.tsx index 18e7216..0fab601 100644 --- a/src/window/bar/widgets/Status.tsx +++ b/src/window/bar/widgets/Status.tsx @@ -12,7 +12,6 @@ import GObject from "ags/gobject"; import AstalBluetooth from "gi://AstalBluetooth"; import AstalNetwork from "gi://AstalNetwork"; import AstalWp from "gi://AstalWp"; -import Vips from "gi://Vips?version=8.0"; export const Status = () => (