From 89cc58175f574f8538180a1b690b6a370e4517b3 Mon Sep 17 00:00:00 2001 From: matt1432 Date: Mon, 4 Dec 2023 15:39:12 -0500 Subject: [PATCH] feat(ags): make wifi ap list better --- .../ags/js/quick-settings/button-grid.js | 33 +-- .../config/ags/js/quick-settings/network.js | 207 ++++++++++++++++++ .../ags/scss/widgets/quick-settings.scss | 17 +- devices/wim/config/hypr/main.conf | 1 - flake.lock | 18 +- modules/ags/default.nix | 1 - 6 files changed, 234 insertions(+), 43 deletions(-) create mode 100644 devices/wim/config/ags/js/quick-settings/network.js diff --git a/devices/wim/config/ags/js/quick-settings/button-grid.js b/devices/wim/config/ags/js/quick-settings/button-grid.js index 1b80b5c2..1c6a5c48 100644 --- a/devices/wim/config/ags/js/quick-settings/button-grid.js +++ b/devices/wim/config/ags/js/quick-settings/button-grid.js @@ -10,6 +10,8 @@ import { SpeakerIcon, MicIcon } from '../misc/audio-icons.js'; import EventBox from '../misc/cursorbox.js'; import Separator from '../misc/separator.js'; +import { NetworkMenu } from './network.js'; + const SPACING = 28; @@ -190,36 +192,7 @@ const FirstRow = () => Row({ self.label = Network.wifi?.ssid || Network.wired?.internet; }], - menu: Box({ - className: 'menu', - vertical: true, - - connections: [[Network, (box) => { - box.children = Network.wifi - ?.access_points.map((ap) => EventBox({ - isButton: true, - - on_clicked: () => { - execAsync(`nmcli device wifi - connect ${ap.bssid}`).catch(print); - }, - child: Box({ - - children: [ - Icon(ap.iconName), - - Label(ap.ssid || ''), - - ap.active && Icon({ - icon: 'object-select-symbolic', - hexpand: true, - hpack: 'end', - }), - ], - }), - })); - }]], - }), + menu: NetworkMenu(), }), GridButton({ diff --git a/devices/wim/config/ags/js/quick-settings/network.js b/devices/wim/config/ags/js/quick-settings/network.js new file mode 100644 index 00000000..6ba7b814 --- /dev/null +++ b/devices/wim/config/ags/js/quick-settings/network.js @@ -0,0 +1,207 @@ +import App from 'resource:///com/github/Aylur/ags/app.js'; +import Network from 'resource:///com/github/Aylur/ags/service/network.js'; +import Variable from 'resource:///com/github/Aylur/ags/variable.js'; + +import { Box, Icon, Label, Overlay, Revealer, Scrollable, Widget } from 'resource:///com/github/Aylur/ags/widget.js'; +import { execAsync } from 'resource:///com/github/Aylur/ags/utils.js'; + +import EventBox from '../misc/cursorbox.js'; + +// FIXME: wait for https://github.com/Aylur/ags/pull/199 +const ListBox = Widget.subclass(imports.gi.Gtk.ListBox); +const SCROLL_THRESHOLD_H = 200; +const SCROLL_THRESHOLD_N = 7; + + +const AccessPoint = (ap) => { + const widget = Box({ + className: 'ap', + }); + + widget.ap = Variable(ap); + + const child = Box({ + hexpand: true, + children: [ + Icon({ + connections: [[widget.ap, (self) => { + self.icon = widget.ap.value.iconName; + }]], + }), + + Label({ + connections: [[widget.ap, (self) => { + self.label = widget.ap.value.ssid || ''; + }]], + }), + + Icon({ + icon: 'object-select-symbolic', + hexpand: true, + hpack: 'end', + connections: [[Network, (self) => { + self.visible = widget.ap.value.ssid === Network.wifi.ssid; + }]], + }), + ], + }); + + widget.add(Revealer({ + revealChild: true, + transition: 'slide_down', + child: EventBox({ + onPrimaryClickRelease: () => { + execAsync(`nmcli device wifi connect + ${widget.ap.value.bssid}`).catch(print); + }, + child, + }), + })); + widget.show_all(); + + return widget; +}; + +export const NetworkMenu = () => { + const APList = new Map(); + const topArrow = Revealer({ + child: Icon({ + icon: `${App.configDir }/icons/down-large.svg`, + className: 'scrolled-indicator', + size: 16, + css: '-gtk-icon-transform: rotate(180deg);', + }), + }); + + const bottomArrow = Revealer({ + child: Icon({ + icon: `${App.configDir }/icons/down-large.svg`, + className: 'scrolled-indicator', + size: 16, + }), + }); + + return Overlay({ + pass_through: true, + overlays: [ + Box({ + vpack: 'start', + hpack: 'center', + css: 'margin-top: 12px', + children: [topArrow], + }), + + Box({ + vpack: 'end', + hpack: 'center', + css: 'margin-bottom: 12px', + children: [bottomArrow], + }), + ], + + child: Box({ + className: 'menu', + + child: Scrollable({ + hscroll: 'never', + vscroll: 'never', + + connections: [['edge-reached', (_, pos) => { + // Manage scroll indicators + if (pos === 2) { + topArrow.revealChild = false; + bottomArrow.revealChild = true; + } + else if (pos === 3) { + topArrow.revealChild = true; + bottomArrow.revealChild = false; + } + }]], + + child: ListBox({ + setup: (self) => { + self.set_sort_func((a, b) => { + return b.get_children()[0].ap.value.strength - + a.get_children()[0].ap.value.strength; + }); + }, + + connections: [[Network, (box) => { + // Add missing APs + Network.wifi?.access_points.forEach((ap) => { + if (ap.ssid !== 'Unknown') { + if (APList.has(ap.ssid)) { + const accesPoint = APList.get(ap.ssid) + .ap.value; + + if (accesPoint.strength < ap.strength) { + APList.get(ap.ssid).ap.value = ap; + } + } + else { + APList.set(ap.ssid, AccessPoint(ap)); + + box.add(APList.get(ap.ssid)); + box.show_all(); + } + } + }); + + // Delete ones that don't exist anymore + const difference = Array.from(APList.keys()) + .filter((ssid) => !Network.wifi.access_points + .find((ap) => ap.ssid === ssid) && + ssid !== 'Unknown'); + + difference.forEach((ssid) => { + const apWidget = APList.get(ssid); + + if (apWidget) { + if (apWidget.toDestroy) { + apWidget.get_parent().destroy(); + APList.delete(ssid); + } + else { + apWidget.children[0].revealChild = false; + apWidget.toDestroy = true; + } + } + }); + + // Start scrolling after a specified height + // is reached by the children + const height = Math.max( + box.get_parent().get_allocated_height(), + SCROLL_THRESHOLD_H, + ); + + const scroll = box.get_parent().get_parent(); + + if (box.get_children().length > SCROLL_THRESHOLD_N) { + scroll.vscroll = 'always'; + scroll.setCss(`min-height: ${height}px;`); + + // Make bottom scroll indicator appear only + // when first getting overflowing children + if (!(bottomArrow.revealChild === true || + topArrow.revealChild === true)) { + bottomArrow.revealChild = true; + } + } + else { + scroll.vscroll = 'never'; + scroll.setCss(''); + topArrow.revealChild = false; + bottomArrow.revealChild = false; + } + + // Trigger sort_func + box.get_children().forEach((ch) => { + ch.changed(); + }); + }]], + }), + }), + }), + }); +}; diff --git a/devices/wim/config/ags/scss/widgets/quick-settings.scss b/devices/wim/config/ags/scss/widgets/quick-settings.scss index 73e7e81c..468623ae 100644 --- a/devices/wim/config/ags/scss/widgets/quick-settings.scss +++ b/devices/wim/config/ags/scss/widgets/quick-settings.scss @@ -20,14 +20,27 @@ transition: color 0.3s ease-in-out; } +.scrolled-indicator { + margin: 5px 0; +} + .menu { margin: 10px; - padding: 5px; + padding: 0; border: 1.5px solid $contrast-bg; border-radius: 10px; font-size: 12px; - button { + scrolledwindow { + padding: 3px; + } + + row { + padding: 0; + margin: 0; + } + + .ap { margin: 5px; label { diff --git a/devices/wim/config/hypr/main.conf b/devices/wim/config/hypr/main.conf index 22eda475..923e1a39 100644 --- a/devices/wim/config/hypr/main.conf +++ b/devices/wim/config/hypr/main.conf @@ -16,7 +16,6 @@ plugin { } # Autostart programs -exec-once = sleep 3; nm-applet exec-once = sleep 4; blueberry-tray exec-once = sleep 6; nextcloud --background exec-once = squeekboard diff --git a/flake.lock b/flake.lock index 8868378b..ff2d766a 100644 --- a/flake.lock +++ b/flake.lock @@ -7,11 +7,11 @@ ] }, "locked": { - "lastModified": 1701617970, - "narHash": "sha256-sazyPlciTd7MlQ5Nz1JDVq1jruG85+BgdjMsTgnF3es=", + "lastModified": 1701706567, + "narHash": "sha256-mGPrmR7rpA9UlNicedNDbw6ec4tDCoLe5y/7HEZ5Cww=", "owner": "Aylur", "repo": "ags", - "rev": "a6a1b872c3353dd23829845d03b9a8681ab8568e", + "rev": "52363e5fd175311368a64596a27a3007abe0c3e5", "type": "github" }, "original": { @@ -358,11 +358,11 @@ "xdph": "xdph" }, "locked": { - "lastModified": 1701661974, - "narHash": "sha256-oq9CVdLjH70QmgA8FZTqC/rhzCJJRZKbg5Q2jA4xnVw=", + "lastModified": 1701715678, + "narHash": "sha256-cCW30ir7VojARVLCVGFf4zSWDe8QOTlgWjYo9VOzTRc=", "owner": "hyprwm", "repo": "Hyprland", - "rev": "68783d904d850df65887adb1bab7eff59943c1ac", + "rev": "aa46aaed04f089b849c45a077e81e8b562be23df", "type": "github" }, "original": { @@ -749,11 +749,11 @@ }, "nur": { "locked": { - "lastModified": 1701701082, - "narHash": "sha256-XXSnqw2SwcJwjwIMYtEFenb0QL86PF3fFCuXe3ilXxc=", + "lastModified": 1701714282, + "narHash": "sha256-zyKTUmKWi7Cp8z+zizQ2+MNvEti5CFF/RPd8sCz+Q8M=", "owner": "nix-community", "repo": "NUR", - "rev": "6b4561fe34f1f4bf5058a7ba282dd40486efb921", + "rev": "39a4843b4eda2835c8c39b0d0eff9ffacba41bc6", "type": "github" }, "original": { diff --git a/modules/ags/default.nix b/modules/ags/default.nix index 590646e1..e3ca9101 100644 --- a/modules/ags/default.nix +++ b/modules/ags/default.nix @@ -36,7 +36,6 @@ in { ## gui pavucontrol # TODO: replace with ags widget - networkmanagerapplet # TODO: replace with ags widget ]) ++ (optionals isTouchscreen (with pkgs; [ # touchscreen