🔧 chore(control-center/tiles): better implementation for the network tile

This commit is contained in:
retrozinndev
2025-10-17 20:02:01 -03:00
parent 6793a68bb8
commit 4861337067
5 changed files with 202 additions and 97 deletions
+33 -3
View File
@@ -1,5 +1,6 @@
import { Scope } from "ags";
import { createScopedConnection, decoder, encoder } from "../modules/utils";
import { showWorkspaceNumber } from "../window/bar/widgets/Workspaces";
import windows from "./modules/windows";
import volume from "./modules/volume";
@@ -9,6 +10,7 @@ import Gio from "gi://Gio?version=2.0";
import GLib from "gi://GLib?version=2.0";
/** cli implementation for colorshell */
export namespace Cli {
let rootScope: Scope;
let initialized: boolean = false;
@@ -27,6 +29,14 @@ export namespace Cli {
type: "out"
};
}
},
{
name: "peek-workspace-num",
help: "peek the workspace numbers in the workspace indicator. (optional: time in millis)",
onCalled: () => {
showWorkspaceNumber(true);
return "Peeking workspace IDs...";
}
}
],
arguments: [
@@ -214,13 +224,33 @@ export namespace Cli {
);
}
for(let i = 0; i < args.length; i++) {
const arg = args[i];
function handleCommandArguments(cmd: Module|Command, args: Array<string>, index: number, printFun: (out: Output) => void): void {
const argNameRegEx = /^--/, argAliasRegEx = /^-/;
let argName: string;
if(i === 0) {
if(args[index].startsWith("--")) {
}
}
const firstFoundMod = modules.filter(mod => mod.prefix === args[0])[0];
mod = firstFoundMod ?? modules[0];
if(!mod) {
print({
content: `No command module found with the name ${args[0]}!`,
type: "err"
});
return;
}
for(let i = 1; i < args.length; i++) {
const arg = args[i];
if(/^-/.test(arg)) {
handleCommandArguments(command ?? mod, args, i, print);
continue;
}
}
function outputToString(out: Output): string {
+10 -10
View File
@@ -1,8 +1,11 @@
import { CompositorHyprland } from "./hyprland";
import GObject, { getter, gtype, register } from "ags/gobject";
import GObject, { getter, gtype, property, register } from "ags/gobject";
import GLib from "gi://GLib?version=2.0";
export default Compositors;
/** WIP modular implementation of a system that supports implementing
* a variety of Wayland Compositors
* @todo implement more general compositor info + a lot of stuff
@@ -14,7 +17,6 @@ export namespace Compositors {
export class Monitor extends GObject.Object {
#width: number;
#height: number;
#scaling: number;
@getter(Number)
get width() { return this.#width; }
@@ -22,15 +24,15 @@ export namespace Compositors {
@getter(Number)
get height() { return this.#height; }
@getter(Number)
get scaling() { return this.#scaling; }
@property(Number)
scaling: number;
constructor(width: number, height: number, scaling: number = 1) {
super();
this.#width = width;
this.#height = height;
this.#scaling = scaling;
this.scaling = scaling;
}
}
@@ -49,7 +51,6 @@ export namespace Compositors {
super();
this.#monitor = monitor;
this.notify("monitor");
this.#id = id;
}
}
@@ -110,10 +111,9 @@ export namespace Compositors {
if(props.position !== undefined)
this.#position = props.position;
if(props.initialClass !== undefined)
this.#initialClass = props.initialClass;
else
this.#initialClass = props.class;
this.#initialClass = props.initialClass !== undefined ?
props.initialClass
: props.class;
}
}
@@ -1,85 +1,153 @@
import { execAsync } from "ags/process";
import { Tile } from "./Tile";
import { execAsync } from "ags/process";
import { PageNetwork } from "../pages/Network";
import { tr } from "../../../../i18n/intl";
import { TilesPages } from "../tiles";
import { Gtk } from "ags/gtk4";
import { createBinding, createComputed, With } from "ags";
import { Accessor, createBinding, createComputed } from "ags";
import { secureBaseBinding } from "../../../../modules/utils";
import AstalNetwork from "gi://AstalNetwork";
import { Notifications } from "../../../../modules/notifications";
export const TileNetwork = () => <Gtk.Box>
<With value={createComputed([
createBinding(AstalNetwork.get_default(), "primary"),
const { WIFI, WIRED } = AstalNetwork.Primary,
{ CONNECTED, CONNECTING } = AstalNetwork.Internet;
const wiredInternet = secureBaseBinding<AstalNetwork.Wired>(
createBinding(AstalNetwork.get_default(), "wired"),
createBinding(AstalNetwork.get_default(), "wifi")
])}>
"internet",
AstalNetwork.Internet.DISCONNECTED
) as Accessor<AstalNetwork.Internet>;
{([primary, wired, wifi]: [AstalNetwork.Primary, AstalNetwork.Wired, AstalNetwork.Wifi]) => {
if(primary === AstalNetwork.Primary.WIFI) {
return <Tile title={tr("control_center.tiles.network.wireless")}
description={createComputed([
createBinding(wifi, "ssid"), createBinding(wifi, "internet")
], (ssid, internet) => ssid ? ssid : (() => {
switch(internet) {
case AstalNetwork.Internet.CONNECTED:
return tr("connected");
case AstalNetwork.Internet.DISCONNECTED:
return tr("disconnected");
case AstalNetwork.Internet.CONNECTING:
return tr("connecting") + "...";
}
})()
)} onEnabled={() => wifi.set_enabled(true)}
onDisabled={() => wifi.set_enabled(false)}
hasArrow onClicked={() => TilesPages?.toggle(PageNetwork)}
icon={"network-wireless-signal-excellent-symbolic"}
state={createBinding(wifi, "enabled")}
/>
const wifiInternet = secureBaseBinding<AstalNetwork.Wifi>(
createBinding(AstalNetwork.get_default(), "wifi"),
"internet",
AstalNetwork.Internet.DISCONNECTED
) as Accessor<AstalNetwork.Internet>;
} else if(primary === AstalNetwork.Primary.WIRED) {
return <Tile title={tr("control_center.tiles.network.wired")}
description={createBinding(wired, "internet").as((internet: AstalNetwork.Internet) => {
switch(internet) {
case AstalNetwork.Internet.CONNECTED:
return tr("connected");
case AstalNetwork.Internet.DISCONNECTED:
return tr("disconnected");
case AstalNetwork.Internet.CONNECTING:
return tr("connecting") + "...";
const wifiSSID = secureBaseBinding<AstalNetwork.Wifi>(
createBinding(AstalNetwork.get_default(), "wifi"),
"ssid",
"Unknown"
) as Accessor<string>;
const wifiIcon = secureBaseBinding<AstalNetwork.Wifi>(
createBinding(AstalNetwork.get_default(), "wifi"),
"iconName",
"network-wireless-symbolic"
);
const wiredIcon = secureBaseBinding<AstalNetwork.Wired>(
createBinding(AstalNetwork.get_default(), "wired"),
"iconName",
"network-wired-symbolic"
);
const primary = createBinding(AstalNetwork.get_default(), "primary");
export const TileNetwork = () =>
<Tile hasArrow title={createComputed([
primary,
wifiInternet,
wifiSSID
], (primary, wiInternet, wiSSID) => {
switch(primary) {
case WIFI:
if(wiInternet === CONNECTED)
return wiSSID;
return tr("control_center.tiles.network.wireless");
case WIRED:
return tr("control_center.tiles.network.wired");
}
return tr("control_center.tiles.network.network");
})}
hasArrow onEnabled={() => execAsync("nmcli n on")}
onDisabled={() => execAsync("nmcli n off")}
onClicked={() => TilesPages?.toggle(PageNetwork)}
icon={createBinding(wired, "internet").as((internet: AstalNetwork.Internet) => {
switch(internet) {
case AstalNetwork.Internet.CONNECTED:
return "network-wired-symbolic";
case AstalNetwork.Internet.DISCONNECTED:
return "network-wired-disconnected-symbolic";
icon={createComputed([
primary,
wifiIcon,
wiredIcon
], (primary, wifiIcon, wiredIcon) => {
switch(primary) {
case WIFI:
return wifiIcon;
case WIRED:
return wiredIcon;
}
return "network-wired-no-route-symbolic";
})}
state={createBinding(wired, "internet").as((internet: AstalNetwork.Internet) =>
internet === AstalNetwork.Internet.CONNECTING
|| internet === AstalNetwork.Internet.CONNECTED
)}
/>
state={createComputed([
primary,
secureBaseBinding<AstalNetwork.Wifi>(
createBinding(AstalNetwork.get_default(), "wifi"),
"enabled",
false
),
wiredInternet.as(internet => internet === CONNECTED || internet === CONNECTING)
], (primary, wifiEnabled, wiredEnabled) => {
switch(primary) {
case WIFI:
return wifiEnabled;
case WIRED:
return wiredEnabled;
}
return <Tile
title={tr("control_center.tiles.network.network")}
description={tr("disconnected")}
onEnabled={() => execAsync("nmcli n on")}
onDisabled={() => execAsync("nmcli n off")}
hasArrow onClicked={() => TilesPages?.toggle(PageNetwork)}
icon={"network-wired-disconnected-symbolic"}
state={createBinding(wired, "internet").as((internet: AstalNetwork.Internet) =>
internet === AstalNetwork.Internet.CONNECTING || internet === AstalNetwork.Internet.CONNECTED)}
/>
return false;
})}
description={createComputed([
primary,
wifiInternet,
wiredInternet
], (primary, wifiInternet, wiredInternet) => {
switch(primary) {
case WIFI:
return internetToTranslatedString(wifiInternet);
case WIRED:
return internetToTranslatedString(wiredInternet);
}
return tr("disconnected");
})}
onToggled={(self, state) => {
switch(AstalNetwork.get_default().primary) {
case WIFI:
AstalNetwork.get_default().wifi.set_enabled(state);
return;
case WIRED:
(state ?
execAsync("nmcli n off")
: execAsync("nmcli n on")
).catch(e => {
Notifications.getDefault().sendNotification({
appName: "network",
summary: "Couldn't turn off network",
body: `Turning off networking with nmcli failed: ${
e?.message ?? "(no error message)"}`
});
});
return;
}
// disable if no device available
self.state = false;
}}
</With>
</Gtk.Box>;
/>;
function internetToTranslatedString(internet: AstalNetwork.Internet): string {
switch(internet) {
case AstalNetwork.Internet.CONNECTED:
return tr("connected");
case AstalNetwork.Internet.CONNECTING:
return tr("connecting") + "...";
}
return tr("disconnected");
}
@@ -30,7 +30,7 @@ export class Tile extends Gtk.Box {
public hasArrow: boolean = false;
declare $signals: Gtk.Box.SignalSignatures & {
"toggled": (_state: boolean) => void;
"toggled": (state: boolean) => void;
"enabled": () => void;
"disabled": () => void;
"clicked": () => void;
+16 -9
View File
@@ -16,34 +16,41 @@ export const FloatingNotifications = (mon: number) =>
generalConfig.bindProperty("notifications.position_h", "string"),
generalConfig.bindProperty("notifications.position_v", "string")
]).as(([posH, posV]) => {
let horizontal: Astal.WindowAnchor = Astal.WindowAnchor.RIGHT,
vertical: Astal.WindowAnchor = Astal.WindowAnchor.TOP;
const pos: Array<Astal.WindowAnchor> = [];
switch(posH) {
case "left":
horizontal = Astal.WindowAnchor.LEFT;
pos.push(Astal.WindowAnchor.LEFT);
break;
case "center":
horizontal = Astal.WindowAnchor.LEFT | Astal.WindowAnchor.RIGHT;
pos.push(Astal.WindowAnchor.LEFT);
pos.push(Astal.WindowAnchor.RIGHT);
break;
case "right":
horizontal = Astal.WindowAnchor.RIGHT;
pos.push(Astal.WindowAnchor.RIGHT);
break;
}
switch(posV) {
case "top":
vertical = Astal.WindowAnchor.TOP;
pos.push(Astal.WindowAnchor.TOP);
break;
case "center":
vertical = Astal.WindowAnchor.TOP | Astal.WindowAnchor.BOTTOM;
pos.push(Astal.WindowAnchor.TOP);
pos.push(Astal.WindowAnchor.BOTTOM);
break;
case "bottom":
vertical = Astal.WindowAnchor.BOTTOM;
pos.push(Astal.WindowAnchor.BOTTOM);
break;
}
return horizontal | vertical;
let finalPos: Astal.WindowAnchor;
pos.forEach(pos => finalPos = (finalPos !== undefined ?
finalPos | pos
: pos));
return finalPos!;
})} exclusivity={Astal.Exclusivity.NORMAL}
resizable={false} widthRequest={450}>