repo: add ags, a gtk shell
commit75cf6a4d67
Author: Anthony Rodriguez <anthony@nezia.dev> Date: Fri Oct 11 15:34:14 2024 +0200 home/programs/niri: use gtk portals for most things, and gnome for screencast commit5ccb424079
Author: Anthony Rodriguez <anthony@nezia.dev> Date: Thu Oct 10 21:19:33 2024 +0200 home/programs/niri: add play/pause, add settings button on fn key f12 commit8b7164739d
Author: Anthony Rodriguez <anthony@nezia.dev> Date: Thu Oct 10 00:32:40 2024 +0200 home/programs/niri: replace desktop-portal-gtk with gnome commitfe40c6c72c
Author: Anthony Rodriguez <anthony@nezia.dev> Date: Thu Oct 10 00:21:31 2024 +0200 home/programs/niri: add xdg-desktop-portal-gtk commit74b7df1245
Author: Anthony Rodriguez <anthony@nezia.dev> Date: Thu Oct 10 00:21:14 2024 +0200 home/programs: add fractal commit3773095069
Author: Anthony Rodriguez <anthony@nezia.dev> Date: Thu Oct 10 00:21:05 2024 +0200 home/programs/gtk: use color-scheme for gtk4.0 commit8fef768f4f
Author: Anthony Rodriguez <anthony@nezia.dev> Date: Wed Oct 9 19:22:43 2024 +0200 system/services/regreet: ensure that regreet starts dbus session commitfcd88bfa8e
Author: Anthony Rodriguez <anthony@nezia.dev> Date: Wed Oct 9 19:22:20 2024 +0200 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
59b57de785
commit
d0466f96da
14 changed files with 278 additions and 118 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;
|
||||
}
|
|
@ -18,6 +18,9 @@
|
|||
proton-pass
|
||||
vesktop
|
||||
wl-clipboard
|
||||
fractal
|
||||
cinny-desktop
|
||||
gnome-control-center
|
||||
playerctl
|
||||
];
|
||||
}
|
||||
|
|
|
@ -4,20 +4,16 @@
|
|||
config,
|
||||
nixosConfig,
|
||||
...
|
||||
}: {
|
||||
}: let
|
||||
isDark = inputs.basix.schemeData.base16.${nixosConfig.style.scheme}.variant == "dark";
|
||||
in {
|
||||
home.pointerCursor = {
|
||||
inherit (nixosConfig.style.cursorTheme) name package size;
|
||||
gtk.enable = true;
|
||||
x11.enable = true;
|
||||
};
|
||||
|
||||
gtk = let
|
||||
isDark = inputs.basix.schemeData.base16.${nixosConfig.style.scheme}.variant == "dark";
|
||||
commonGtkConfig = {
|
||||
gtk-decoration-layout = ":menu";
|
||||
gtk-application-prefer-dark-theme = isDark;
|
||||
};
|
||||
in {
|
||||
gtk = {
|
||||
enable = true;
|
||||
|
||||
font = {
|
||||
|
@ -26,8 +22,14 @@
|
|||
size = 11;
|
||||
};
|
||||
|
||||
gtk3.extraConfig = commonGtkConfig;
|
||||
gtk4.extraConfig = commonGtkConfig;
|
||||
gtk3.extraConfig = {
|
||||
gtk-decoration-layout = ":menu";
|
||||
gtk-application-prefer-dark-theme = isDark;
|
||||
};
|
||||
|
||||
gtk4.extraConfig = {
|
||||
gtk-decoration-layout = ":menu";
|
||||
};
|
||||
|
||||
gtk2.configLocation = "${config.xdg.configHome}/gtk-2.0/gtkrc";
|
||||
|
||||
|
@ -39,4 +41,12 @@
|
|||
inherit (nixosConfig.style.gtk.theme) name package;
|
||||
};
|
||||
};
|
||||
dconf.settings = {
|
||||
"org/gnome/desktop/interface" = {
|
||||
color-scheme =
|
||||
if isDark
|
||||
then "prefer-dark"
|
||||
else "default";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -140,6 +140,9 @@ in {
|
|||
action.spawn = ["wpctl" "set-mute" "@DEFAULT_AUDIO_SOURCE@" "toggle"];
|
||||
allow-when-locked = true;
|
||||
};
|
||||
"XF86AudioPlay" = {
|
||||
action.spawn = ["playerctl" "play-pause"];
|
||||
};
|
||||
"XF86MonBrightnessUp" = {
|
||||
action.spawn = ["brillo" "-q" "-u 300000" "-A 5"];
|
||||
allow-when-locked = true;
|
||||
|
@ -148,5 +151,9 @@ in {
|
|||
action.spawn = ["brillo" "-q" "-u 300000" "-U 5"];
|
||||
allow-when-locked = true;
|
||||
};
|
||||
# set on media key since framework laptops have a gear as the button label
|
||||
"XF86AudioMedia" = {
|
||||
action.spawn = ["sh" "-c" "env XDG_CURRENT_DESKTOP=gnome gnome-control-center"];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -8,6 +8,20 @@
|
|||
colors = inputs.basix.schemeData.base16.${osConfig.style.scheme}.palette;
|
||||
in {
|
||||
imports = [./binds.nix];
|
||||
|
||||
xdg.portal = {
|
||||
enable = true;
|
||||
xdgOpenUsePortal = true;
|
||||
extraPortals = [pkgs.xdg-desktop-portal-gtk pkgs.xdg-desktop-portal-gnome pkgs.gnome-keyring];
|
||||
config = {
|
||||
common = {
|
||||
default = ["gtk"];
|
||||
"org.freedesktop.impl.portal.ScreenCast" = ["gnome"];
|
||||
"org.freedesktop.impl.portal.Secret" = ["gnome-keyring"];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
programs.niri = {
|
||||
settings = {
|
||||
input.keyboard.xkb = {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
|
@ -16,10 +16,6 @@ in {
|
|||
programs.regreet = {
|
||||
enable = true;
|
||||
package = pkgs.greetd.regreet;
|
||||
cageArgs = [
|
||||
"-s"
|
||||
"-d"
|
||||
];
|
||||
settings = {
|
||||
background = {
|
||||
path = mkDefault config.style.wallpaper;
|
||||
|
@ -36,6 +32,17 @@ in {
|
|||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.greetd = {
|
||||
enable = true;
|
||||
settings = rec {
|
||||
default_session = {
|
||||
command = "${lib.getExe pkgs.cage} -s -d -- ${lib.getExe config.programs.regreet.package}";
|
||||
user = "greeter";
|
||||
};
|
||||
initial_session = default_session;
|
||||
};
|
||||
};
|
||||
security.pam.services.greetd = {
|
||||
enableGnomeKeyring = true;
|
||||
fprintAuth = false;
|
||||
|
|
Loading…
Reference in a new issue