networks: make popup window

This commit is contained in:
2 * r + 2 * t 2025-01-17 14:55:50 +11:00
parent 10cb02793a
commit 1f731072ac
6 changed files with 146 additions and 3 deletions

View file

@ -1,3 +1,4 @@
@forward "notifications"; @forward "notifications";
@forward "updates"; @forward "updates";
@forward "bluetoothdevices"; @forward "bluetoothdevices";
@forward "networks";

View file

@ -0,0 +1,43 @@
@use "sass:color";
@use "../scheme";
@use "../lib";
@use "../font";
$-accent: scheme.$rosewater;
.networks {
@include lib.popdown-window($-accent);
min-width: lib.s(500);
min-height: lib.s(400);
.list {
@include lib.spacing($vertical: true);
color: scheme.$text;
.network {
@include lib.spacing(8);
.icon {
font-size: lib.s(18);
}
button {
@include lib.rounded(5);
@include lib.element-decel;
padding: lib.s(3) lib.s(8);
&:hover,
&:focus {
background-color: scheme.$surface0;
}
&:active {
background-color: scheme.$surface1;
}
}
}
}
}

View file

@ -219,10 +219,10 @@ const Tray = () => <box className="module tray">{bind(AstalTray.get_default(), "
const Network = () => ( const Network = () => (
<button <button
onClick={(_, event) => { onClick={(self, event) => {
const network = AstalNetwork.get_default(); const network = AstalNetwork.get_default();
if (event.button === Astal.MouseButton.PRIMARY) { if (event.button === Astal.MouseButton.PRIMARY) {
// TODO: networks panel togglePopup(self, event, "networks");
} else if (event.button === Astal.MouseButton.SECONDARY) network.wifi.enabled = !network.wifi.enabled; } else if (event.button === Astal.MouseButton.SECONDARY) network.wifi.enabled = !network.wifi.enabled;
else if (event.button === Astal.MouseButton.MIDDLE) else if (event.button === Astal.MouseButton.MIDDLE)
execAsync("uwsm app -- gnome-control-center wifi").catch(() => { execAsync("uwsm app -- gnome-control-center wifi").catch(() => {

View file

@ -1,4 +1,5 @@
import BluetoothDevices from "./bluetoothdevices"; import BluetoothDevices from "./bluetoothdevices";
import Networks from "./networks";
import Notifications from "./notifications"; import Notifications from "./notifications";
import Updates from "./updates"; import Updates from "./updates";
@ -6,6 +7,7 @@ export default () => {
<Notifications />; <Notifications />;
<Updates />; <Updates />;
<BluetoothDevices />; <BluetoothDevices />;
<Networks />;
return null; return null;
}; };

View file

@ -0,0 +1,97 @@
import { bind, execAsync, Variable } from "astal";
import { Gtk } from "astal/gtk3";
import AstalNetwork from "gi://AstalNetwork";
import PopdownWindow from "../../widgets/popdownwindow";
const Network = (accessPoint: AstalNetwork.AccessPoint) => (
<box className="network">
<icon className="icon" icon={bind(accessPoint, "iconName")} />
<label
truncate
xalign={0}
setup={self => {
const update = () =>
(self.label = `${accessPoint.ssid}${` (${accessPoint.frequency > 5000 ? 5 : 2.4}GHz | ${
accessPoint.strength
}/100)`}`);
self.hook(accessPoint, "notify::ssid", update);
self.hook(accessPoint, "notify::frequency", update);
self.hook(accessPoint, "notify::strength", update);
update();
}}
/>
<box hexpand />
<button
cursor="pointer"
onClicked={self => {
const cmd =
AstalNetwork.get_default().wifi.activeAccessPoint === accessPoint ? "c down id" : "d wifi connect";
execAsync(`nmcli ${cmd} '${accessPoint.ssid}'`)
.then(() => (self.sensitive = true))
.catch(console.error);
self.sensitive = false;
}}
label={bind(AstalNetwork.get_default().wifi, "activeAccessPoint").as(a =>
a === accessPoint ? "Disconnect" : "Connect"
)}
/>
<button
cursor="pointer"
onClicked={() => execAsync(`nmcli c delete id '${accessPoint.ssid}'`).catch(() => {})}
label="Forget"
/>
</box>
);
const List = () => {
const { wifi } = AstalNetwork.get_default();
const children = Variable.derive([bind(wifi, "accessPoints"), bind(wifi, "activeAccessPoint")], (aps, ac) =>
aps
.filter(a => a.ssid)
.sort((a, b) => (a === ac ? -1 : b.strength - a.strength))
.map(Network)
);
return (
<box vertical valign={Gtk.Align.START} className="list" onDestroy={() => children.drop()}>
{bind(children)}
</box>
);
};
export default () => {
const network = AstalNetwork.get_default();
const label = Variable("");
const update = () => {
if (network.primary === AstalNetwork.Primary.WIFI) label.set(network.wifi.ssid ?? "Disconnected");
else if (network.primary === AstalNetwork.Primary.WIRED) label.set(`Ethernet (${network.wired.speed})`);
else label.set("No Wifi");
};
network.connect("notify::primary", update);
network.get_wifi()?.connect("notify::ssid", update);
network.get_wired()?.connect("notify::speed", update);
update();
return (
<PopdownWindow
name="networks"
count={bind(network.wifi, "accessPoints").as(a => a.length)}
countLabel={bind(label)}
headerButtons={[
{
label: bind(network.wifi, "enabled").as(p => (p ? "Disable" : "Enable")),
onClicked: () => (network.wifi.enabled = !network.wifi.enabled),
},
{
label: "Scan",
onClicked: () => network.wifi.scan(),
enabled: bind(network.wifi, "scanning"),
},
]}
emptyIcon="wifi_off"
emptyLabel={bind(network.wifi, "enabled").as(p => (p ? "No available networks" : "Wifi is off"))}
list={<List />}
/>
);
};

View file

@ -16,7 +16,7 @@ export default ({
countLabel?: Binding<string>; countLabel?: Binding<string>;
headerButtons: { label: string | Binding<string>; onClicked: () => void; enabled?: Binding<boolean> }[]; headerButtons: { label: string | Binding<string>; onClicked: () => void; enabled?: Binding<boolean> }[];
emptyIcon: string; emptyIcon: string;
emptyLabel: string; emptyLabel: string | Binding<string>;
list: JSX.Element; list: JSX.Element;
}) => ( }) => (
<PopupWindow name={name}> <PopupWindow name={name}>