✨ ags: add ask popup, make notifications work(finally :3) and more improvements
This commit is contained in:
@@ -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)
|
||||
});
|
||||
|
||||
@@ -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 };
|
||||
Reference in New Issue
Block a user