repo: implement ags
Implement ags, a wayland bar / GTK shell. Also added a few dependencies required for it to work properly.
This commit is contained in:
parent
6acfe1b0ef
commit
fcd88bfa8e
10 changed files with 225 additions and 104 deletions
|
@ -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 {};
|
|
@ -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"];
|
||||
};
|
||||
}
|
||||
|
|
124
home/programs/ags/notificationPopups.js
Normal file
124
home/programs/ags/notificationPopups.js
Normal file
|
@ -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))
|
||||
}),
|
||||
});
|
||||
}
|
|
@ -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;
|
||||
}
|
57
home/programs/ags/style.css
Normal file
57
home/programs/ags/style.css
Normal file
|
@ -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;
|
||||
}
|
|
@ -19,5 +19,7 @@
|
|||
vesktop
|
||||
wl-clipboard
|
||||
cinny-desktop
|
||||
gnome-control-center
|
||||
playerctl
|
||||
];
|
||||
}
|
||||
|
|
|
@ -19,8 +19,9 @@
|
|||
++ [
|
||||
./vamos
|
||||
|
||||
../system/services/regreet.nix
|
||||
"${mod}/services/regreet.nix"
|
||||
"${mod}/programs/niri"
|
||||
"${mod}/services/gnome.nix"
|
||||
|
||||
self.nixosModules.style
|
||||
|
||||
|
|
13
system/services/gnome.nix
Normal file
13
system/services/gnome.nix
Normal file
|
@ -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;
|
||||
};
|
||||
}
|
Loading…
Reference in a new issue