From fcd88bfa8ed5053ef7464c79d121b3b1d0d78ffc Mon Sep 17 00:00:00 2001 From: Anthony Rodriguez Date: Wed, 9 Oct 2024 19:22:20 +0200 Subject: [PATCH] repo: implement ags Implement ags, a wayland bar / GTK shell. Also added a few dependencies required for it to work properly. --- home/programs/ags/{src => }/config.js | 65 +----------- home/programs/ags/default.nix | 25 ++++- home/programs/ags/notificationPopups.js | 124 ++++++++++++++++++++++ home/programs/ags/src/style.css | 40 ------- home/programs/ags/style.css | 57 ++++++++++ home/programs/ags/{src => }/tsconfig.json | 0 home/programs/ags/{src => }/types | 0 home/programs/default.nix | 2 + hosts/default.nix | 3 +- system/services/gnome.nix | 13 +++ 10 files changed, 225 insertions(+), 104 deletions(-) rename home/programs/ags/{src => }/config.js (69%) create mode 100644 home/programs/ags/notificationPopups.js delete mode 100644 home/programs/ags/src/style.css create mode 100644 home/programs/ags/style.css rename home/programs/ags/{src => }/tsconfig.json (100%) rename home/programs/ags/{src => }/types (100%) create mode 100644 system/services/gnome.nix diff --git a/home/programs/ags/src/config.js b/home/programs/ags/config.js similarity index 69% rename from home/programs/ags/src/config.js rename to home/programs/ags/config.js index 246fbef..3759540 100644 --- a/home/programs/ags/src/config.js +++ b/home/programs/ags/config.js @@ -1,10 +1,8 @@ -// const hyprland = await Service.import("hyprland"); -const notifications = await Service.import("notifications"); const mpris = await Service.import("mpris"); const audio = await Service.import("audio"); const battery = await Service.import("battery"); const systemtray = await Service.import("systemtray"); - +import { NotificationPopups } from "./notificationPopups.js"; const date = Variable("", { poll: [1000, 'date "+%H:%M %b %e."'], }); @@ -13,31 +11,6 @@ const date = Variable("", { // so to make a reuseable widget, make it a function // then you can simply instantiate one by calling it -//function Workspaces() { -// const activeId = hyprland.active.workspace.bind("id"); -// const workspaces = hyprland.bind("workspaces").as((ws) => -// ws.map(({ id }) => -// Widget.Button({ -// on_clicked: () => hyprland.messageAsync(`dispatch workspace ${id}`), -// child: Widget.Label(`${id}`), -// class_name: activeId.as((i) => `${i === id ? "focused" : ""}`), -// }), -// ), -// ); -// -// return Widget.Box({ -// class_name: "workspaces", -// children: workspaces, -// }); -//} - -function ClientTitle() { - return Widget.Label({ - class_name: "client-title", - label: "Label!", // hyprland.active.client.bind("title"), - }); -} - function Clock() { return Widget.Label({ class_name: "clock", @@ -45,24 +18,6 @@ function Clock() { }); } -// we don't need dunst or any other notification daemon -// because the Notifications module is a notification daemon itself -function Notification() { - const popups = notifications.bind("popups"); - return Widget.Box({ - class_name: "notification", - visible: popups.as((p) => p.length > 0), - children: [ - Widget.Icon({ - icon: "preferences-system-notifications-symbolic", - }), - Widget.Label({ - label: popups.as((p) => p[0]?.summary || ""), - }), - ], - }); -} - function Media() { const label = Utils.watch("", mpris, "player-changed", () => { if (mpris.players[0]) { @@ -161,19 +116,13 @@ function SysTray() { // layout of the bar function Left() { - return Widget.Box({ - spacing: 8, - children: [ - // Workspaces(), - ClientTitle(), - ], - }); + return Widget.Box({}); } function Center() { return Widget.Box({ spacing: 8, - children: [Media(), Notification()], + children: [Media()], }); } @@ -202,13 +151,7 @@ function Bar(monitor = 0) { App.config({ style: "./style.css", - windows: [ - Bar(), - - // you can call it, for each monitor - // Bar(0), - // Bar(1) - ], + windows: [Bar(), NotificationPopups()], }); export {}; diff --git a/home/programs/ags/default.nix b/home/programs/ags/default.nix index bf4f2dd..3d65b1b 100644 --- a/home/programs/ags/default.nix +++ b/home/programs/ags/default.nix @@ -1,15 +1,23 @@ { inputs, pkgs, + config, ... -}: { +}: let + cfg = config.programs.ags; +in { imports = [inputs.ags.homeManagerModules.default]; + home.packages = with pkgs; [ + libdbusmenu-gtk3 + dart-sass + ]; + programs.ags = { enable = true; # null or path, leave as null if you don't want hm to manage the config - configDir = ./src; + configDir = ./.; # additional packages to add to gjs's runtime extraPackages = with pkgs; [ @@ -18,4 +26,17 @@ accountsservice ]; }; + systemd.user.services.ags = { + Unit = { + Description = "Aylur's Gtk Shell"; + PartOf = [ + "graphical-session.target" + ]; + }; + Service = { + ExecStart = "${cfg.package}/bin/ags"; + Restart = "on-failure"; + }; + Install.WantedBy = ["graphical-session.target"]; + }; } diff --git a/home/programs/ags/notificationPopups.js b/home/programs/ags/notificationPopups.js new file mode 100644 index 0000000..70c92b4 --- /dev/null +++ b/home/programs/ags/notificationPopups.js @@ -0,0 +1,124 @@ +const notifications = await Service.import("notifications"); + +/** @param {import('resource:///com/github/Aylur/ags/service/notifications.js').Notification} n */ +function NotificationIcon({ app_entry, app_icon, image }) { + if (image) { + return Widget.Box({ + css: + `background-image: url("${image}");` + + "background-size: contain;" + + "background-repeat: no-repeat;" + + "background-position: center;", + }); + } + + let icon = "dialog-information-symbolic"; + if (Utils.lookUpIcon(app_icon)) icon = app_icon; + + if (app_entry && Utils.lookUpIcon(app_entry)) icon = app_entry; + + return Widget.Box({ + child: Widget.Icon(icon), + }); +} + +/** @param {import('resource:///com/github/Aylur/ags/service/notifications.js').Notification} n */ +function Notification(n) { + const icon = Widget.Box({ + vpack: "start", + class_name: "icon", + child: NotificationIcon(n), + }); + + const title = Widget.Label({ + class_name: "title", + xalign: 0, + justification: "left", + hexpand: true, + max_width_chars: 24, + truncate: "end", + wrap: true, + label: n.summary, + use_markup: true, + }); + + const body = Widget.Label({ + class_name: "body", + hexpand: true, + use_markup: true, + xalign: 0, + justification: "left", + label: n.body, + wrap: true, + }); + + const actions = Widget.Box({ + class_name: "actions", + children: n.actions.map(({ id, label }) => + Widget.Button({ + class_name: "action-button", + on_clicked: () => { + n.invoke(id); + n.dismiss(); + }, + hexpand: true, + child: Widget.Label(label), + }), + ), + }); + + return Widget.EventBox( + { + attribute: { id: n.id }, + on_primary_click: n.dismiss, + }, + Widget.Box( + { + class_name: `notification ${n.urgency}`, + vertical: true, + }, + Widget.Box([icon, Widget.Box({ vertical: true }, title, body)]), + actions, + ), + ); +} + +export function NotificationPopups(monitor = 0) { + const list = Widget.Box({ + vertical: true, + children: notifications.popups.map(Notification), + }); + + function onNotified(_, /** @type {number} */ id) { + const n = notifications.getNotification(id); + if (n) list.children = [Notification(n), ...list.children]; + } + + function onDismissed(_, /** @type {number} */ id) { + list.children.find((n) => n.attribute.id === id)?.destroy(); + } + + list + .hook(notifications, onNotified, "notified") + .hook(notifications, onDismissed, "dismissed"); + + return Widget.Window({ + monitor, + name: `notifications${monitor}`, + class_name: "notification-popups", + anchor: ["top", "right"], + child: Widget.Box({ + css: "min-width: 2px; min-height: 2px;", + class_name: "notifications", + vertical: true, + child: list, + + /** this is a simple one liner that could be used instead of + hooking into the 'notified' and 'dismissed' signals. + but its not very optimized becuase it will recreate + the whole list everytime a notification is added or dismissed */ + // children: notifications.bind('popups') + // .as(popups => popups.map(Notification)) + }), + }); +} diff --git a/home/programs/ags/src/style.css b/home/programs/ags/src/style.css deleted file mode 100644 index 9ac7355..0000000 --- a/home/programs/ags/src/style.css +++ /dev/null @@ -1,40 +0,0 @@ -window.bar { - background-color: @theme_bg_color; - color: @theme_fg_color; -} - -button { - min-width: 0; - padding-top: 0; - padding-bottom: 0; - background-color: transparent; -} - -button:active { - background-color: @theme_selected_bg_color; -} - -button:hover { - border-bottom: 3px solid @theme_fg_color; -} - -label { - font-weight: bold; -} - -.workspaces button.focused { - border-bottom: 3px solid @theme_selected_bg_color; -} - -.client-title { - color: @theme_selected_bg_color; -} - -.notification { - color: yellow; -} - -levelbar block, -highlight { - min-height: 10px; -} diff --git a/home/programs/ags/style.css b/home/programs/ags/style.css new file mode 100644 index 0000000..aeded67 --- /dev/null +++ b/home/programs/ags/style.css @@ -0,0 +1,57 @@ +window.notification-popups box.notifications { + padding: 0.5em; +} + +.icon { + min-width: 68px; + min-height: 68px; + margin-right: 1em; +} + +.icon image { + font-size: 58px; + /* to center the icon */ + margin: 5px; + color: @theme_fg_color; +} + +.icon box { + min-width: 68px; + min-height: 68px; + border-radius: 7px; +} + +.notification { + min-width: 350px; + border-radius: 11px; + padding: 1em; + margin: 0.5em; + border: 1px solid @wm_borders_edge; + background-color: @theme_bg_color; +} + +.notification.critical { + border: 1px solid lightcoral; +} + +.title { + color: @theme_fg_color; + font-size: 1.4em; +} + +.body { + color: @theme_unfocused_fg_color; +} + +.actions .action-button { + margin: 0 0.4em; + margin-top: 0.8em; +} + +.actions .action-button:first-child { + margin-left: 0; +} + +.actions .action-button:last-child { + margin-right: 0; +} diff --git a/home/programs/ags/src/tsconfig.json b/home/programs/ags/tsconfig.json similarity index 100% rename from home/programs/ags/src/tsconfig.json rename to home/programs/ags/tsconfig.json diff --git a/home/programs/ags/src/types b/home/programs/ags/types similarity index 100% rename from home/programs/ags/src/types rename to home/programs/ags/types diff --git a/home/programs/default.nix b/home/programs/default.nix index 870b8d9..e5dffdf 100644 --- a/home/programs/default.nix +++ b/home/programs/default.nix @@ -19,5 +19,7 @@ vesktop wl-clipboard cinny-desktop + gnome-control-center + playerctl ]; } diff --git a/hosts/default.nix b/hosts/default.nix index b463b90..4414e65 100644 --- a/hosts/default.nix +++ b/hosts/default.nix @@ -19,8 +19,9 @@ ++ [ ./vamos - ../system/services/regreet.nix + "${mod}/services/regreet.nix" "${mod}/programs/niri" + "${mod}/services/gnome.nix" self.nixosModules.style diff --git a/system/services/gnome.nix b/system/services/gnome.nix new file mode 100644 index 0000000..c5233bb --- /dev/null +++ b/system/services/gnome.nix @@ -0,0 +1,13 @@ +{pkgs, ...}: { + services = { + # needed for GNOME services outside of GNOME Desktop + dbus.packages = with pkgs; [ + gcr + gnome-settings-daemon + ]; + + gnome.gnome-keyring.enable = true; + + gvfs.enable = true; + }; +}