ags: add ask popup, make notifications work(finally :3) and more improvements

This commit is contained in:
retrozinndev
2025-03-09 13:45:07 -03:00
parent 161c811841
commit 59ef5e4aa7
67 changed files with 2005 additions and 731 deletions
+78 -68
View File
@@ -1,98 +1,108 @@
import { bind, timeout } from "astal";
import { Gtk, Widget } from "astal/gtk3";
import AstalBluetooth from "gi://AstalBluetooth?version=0.1";
import AstalBluetooth from "gi://AstalBluetooth";
import { Page } from "./Page";
import { Separator, SeparatorProps } from "../../Separator";
let watchingDevices: boolean = false;
export function BluetoothPage() {
watchNewDevices();
return new Widget.Box({
className: "page bluetooth container",
export const BluetoothPage: Page = new Page({
title: "Bluetooth Devices",
description: "Manage your Bluetooth devices and add new ones.",
className: "bluetooth",
setup: () => {
watchingDevices = true;
watchNewDevices();
},
onClose: stopBluetoothDevicesWatch,
pageChild: () => new Widget.Box({
className: "connections",
orientation: Gtk.Orientation.VERTICAL,
expand: true,
hexpand: true,
children: [
new Widget.Box({
className: "header",
children: [
new Widget.Label({
hexpand: true,
className: "title",
label: "Bluetooth",
halign: Gtk.Align.START
} as Widget.LabelProps),
]
} as Widget.BoxProps),
new Widget.Box({
className: "connections",
className: "paired",
orientation: Gtk.Orientation.VERTICAL,
expand: true,
children: bind(AstalBluetooth.get_default(), "devices").as((devices: Array<AstalBluetooth.Device>) =>
devices.filter((device: AstalBluetooth.Device) => device.connected
).map((dev: AstalBluetooth.Device) =>
new Widget.Button({
onClick: () => dev.connected ? dev.disconnect_device(null) : dev.connect_device(null),
child: new Widget.Box({
className: "device",
orientation: Gtk.Orientation.HORIZONTAL,
expand: true,
children: [
new Widget.Label({
className: "alias",
halign: Gtk.Align.START,
label: bind(dev, "alias")
} as Widget.LabelProps),
new Widget.Label({
className: "battery",
halign: Gtk.Align.END,
label: bind(dev, "batteryPercentage").as(String)
} as Widget.LabelProps)
]
} as Widget.BoxProps)
} as Widget.ButtonProps)).concat(
devices.filter((device: AstalBluetooth.Device) => !device.connected
).map((dev: AstalBluetooth.Device) =>
new Widget.Button({
onClick: () => dev.connect_device(() => {}),
child: new Widget.Box({
className: "device",
orientation: Gtk.Orientation.HORIZONTAL,
expand: true,
children: [
new Widget.Label({
className: "alias",
halign: Gtk.Align.START,
label: bind(dev, "alias")
} as Widget.LabelProps),
new Widget.Label({
className: "battery",
halign: Gtk.Align.END,
label: bind(dev, "batteryPercentage").as(String)
} as Widget.LabelProps)
]
} as Widget.BoxProps)
} as Widget.ButtonProps))
devices.filter((device: AstalBluetooth.Device) => device.connected || device.paired)
.map((dev: AstalBluetooth.Device) =>
DeviceWidget(dev)
)
)
} as Widget.BoxProps),
Separator({
size: .5,
orientation: Gtk.Orientation.VERTICAL,
alpha: .7
} as SeparatorProps),
new Widget.Box({
className: "discovered",
orientation: Gtk.Orientation.VERTICAL,
children: bind(AstalBluetooth.get_default(), "devices").as((devices: Array<AstalBluetooth.Device>) =>
devices.filter((device: AstalBluetooth.Device) => !device.connected && !device.paired)
.map((dev: AstalBluetooth.Device) =>
DeviceWidget(dev)
)
)
} as Widget.BoxProps)
]
} as Widget.BoxProps)
})
function DeviceWidget(dev: AstalBluetooth.Device): Gtk.Widget {
return new Widget.Button({
onClick: () => dev.connected ? dev.disconnect_device(null) : dev.connect_device(null),
className: bind(dev, "connected").as((connected) => connected ? "connected" : ""),
child: new Widget.Box({
className: "device",
orientation: Gtk.Orientation.HORIZONTAL,
expand: true,
children: [
new Widget.Icon({
className: "icon",
icon: bind(dev, "icon").as((icon: string) =>
icon ? icon : "bluetooth-active-symbolic"),
css: "font-size: 20px; margin-right: 6px;"
} as Widget.IconProps),
new Widget.Label({
className: "alias",
halign: Gtk.Align.START,
hexpand: true,
label: bind(dev, "alias")
} as Widget.LabelProps),
new Widget.Label({
className: "battery",
halign: Gtk.Align.END,
visible: bind(dev, "batteryPercentage").as((bat: number) =>
bat <= -1 ? false : true),
label: bind(dev, "batteryPercentage").as((bat: number) =>
`󰁹 ${Math.floor(bat * 100)}%`)
} as Widget.LabelProps)
]
} as Widget.BoxProps)
} as Widget.ButtonProps)
}
function watchNewDevices(): void {
if(watchingDevices) {
timeout(10000, () => {
reloadDevicesList();
timeout(8000, () => {
reloadBluetoothDevicesList();
watchNewDevices();
});
return;
}
stopBluetoothDevicesWatch();
}
function stopDeviceWatch(): void {
export function stopBluetoothDevicesWatch(): void {
watchingDevices = false;
AstalBluetooth.get_default().adapter.discovering &&
AstalBluetooth.get_default().adapter.stop_discovery();
}
function reloadDevicesList(): void {
export function reloadBluetoothDevicesList(): void {
AstalBluetooth.get_default().adapter.start_discovery();
timeout(5000, () => AstalBluetooth.get_default().adapter.stop_discovery());
timeout(4000, () => AstalBluetooth.get_default().adapter.stop_discovery());
}
@@ -1,38 +0,0 @@
import { bind } from "astal";
import { Gtk, Widget } from "astal/gtk3";
import AstalBluetooth from "gi://AstalBluetooth";
export function WifiPage() {
return new Widget.Box({
className: "page bluetooth container",
orientation: Gtk.Orientation.VERTICAL,
hexpand: true,
children: [
new Widget.Box({
className: "connections",
orientation: Gtk.Orientation.VERTICAL,
expand: true,
children: bind(AstalBluetooth.get_default(), "devices").as((devices: Array<AstalBluetooth.Device>) =>
devices && devices.filter((device: AstalBluetooth.Device) => device.connected
).map((dev: AstalBluetooth.Device) =>
new Widget.Box({
className: "device",
orientation: Gtk.Orientation.HORIZONTAL,
expand: true,
children: [
new Widget.Label({
className: "alias",
halign: Gtk.Align.START,
label: bind(dev, "alias")
} as Widget.LabelProps),
new Widget.Label({
className: "battery",
halign: Gtk.Align.END,
} as Widget.LabelProps)
]
} as Widget.BoxProps)
))
} as Widget.BoxProps)
]
} as Widget.BoxProps);
}
@@ -0,0 +1,23 @@
import { Widget } from "astal/gtk3";
import { Page } from "./Page";
import AstalNetwork from "gi://AstalNetwork";
import { bind } from "astal";
export const PageNetwork = new Page({
title: "Network",
className: "network",
headerButtons: () => [
new Widget.Button({
className: "reload nf",
label: "󰑓",
visible: bind(AstalNetwork.get_default(), "primary").as(
(primary: AstalNetwork.Primary) => primary === AstalNetwork.Primary.WIFI
),
tooltipText: "Re-scan connections",
onClick: () => AstalNetwork.get_default().wifi.scan()
} as Widget.ButtonProps)
],
pageChild: () => new Widget.Box({
} as Widget.BoxProps)
});
+88
View File
@@ -0,0 +1,88 @@
import { Binding, GObject, register } from "astal";
import { Gtk, Widget } from "astal/gtk3";
export type PageProps = {
setup?: () => void;
onClose?: () => void;
onOpen?: () => void;
className?: string | Binding<string | undefined>;
title: string | Binding<string | undefined>;
description?: string | Binding<string | undefined>;
headerButtons?: () => Array<Gtk.Widget>;
pageChild: () => Gtk.Widget;
};
@register({ GTypeName: "Page" })
class Page extends GObject.Object {
readonly #props: PageProps;
get props() { return this.#props; }
constructor(props: PageProps) {
super();
this.#props = props;
}
public getHeaderButtons(): (Array<Gtk.Widget>|null) {
return this.props.headerButtons ?
this.props.headerButtons()
: null;
}
public getPage(): Gtk.Widget {
return new Widget.Box({
className: (this.props.className instanceof Binding) ?
this.props.className.as((clsName: (string|undefined)) => `page ${ clsName || "" }`) : `page ${this.#props.className || ""}`,
orientation: Gtk.Orientation.VERTICAL,
hexpand: true,
setup: this.props.setup,
children: [
new Widget.Box({
className: "header",
orientation: Gtk.Orientation.VERTICAL,
hexpand: true,
children: [
new Widget.Box({
className: "title",
children: [
new Widget.Label({
hexpand: true,
className: "title",
truncate: true,
visible: (this.props.title instanceof Binding) ?
this.props.title.as(Boolean)
: (this.props.title ? true : false),
label: this.props.title,
halign: Gtk.Align.START
} as Widget.LabelProps),
new Widget.Box({
className: "button-row",
visible: Boolean(this.getHeaderButtons()),
children: this.getHeaderButtons() || undefined
} as Widget.BoxProps)
]
} as Widget.BoxProps),
new Widget.Label({
className: "description",
hexpand: true,
truncate: true,
xalign: 0,
visible: (this.props.description instanceof Binding) ?
this.props.description.as(Boolean)
: this.props.description ? true : false,
label: this.props.description
} as Widget.LabelProps),
]
} as Widget.BoxProps),
new Widget.Box({
className: "content",
orientation: Gtk.Orientation.VERTICAL,
expand: true,
setup: (_) => _.add(this.props.pageChild())
} as Widget.BoxProps)
]
} as Widget.BoxProps);
}
}
export { Page };