This commit is contained in:
agryphus 2024-03-18 17:14:15 -04:00
parent 3648827637
commit c8d7b4c090
8 changed files with 415 additions and 0 deletions

View file

@ -0,0 +1,50 @@
const Network = await Service.import("network");
const Bluetooth = await Service.import("bluetooth");
// Widgets
import { Workspaces, ClientTitle } from "./workspaces.js";
import { Battery } from "./battery.js";
import { Date, Time } from "./clock.js";
const Left = (monitor) => Widget.Box({
children: [
Workspaces(monitor),
ClientTitle(),
],
});
const Center = () => Widget.Box({
children: [ ],
});
const Right = () => Widget.Box({
hpack: "end",
children: [
Battery(),
Date(),
Time(),
],
});
const hyprlandMonitors = Utils.exec(`/bin/sh -c "hyprctl monitors | grep -o '(ID [0-9]*' | awk '{print $2}' | sort"`).split('\n').map(Number);
const getHyprlandMonitor = monitor => {
if (monitor >= hyprlandMonitors.length) {
return 0;
}
return hyprlandMonitors[monitor];
};
export const Bar = (monitor = 0) => Widget.Window({
name: `bar-${monitor}`, // name has to be unique
class_name: 'bar',
monitor,
anchor: ['top', 'left', 'right'],
exclusivity: 'exclusive',
child: Widget.CenterBox({
start_widget: Left(getHyprlandMonitor(monitor)),
center_widget: Center(),
end_widget: Right(),
}),
});

View file

@ -0,0 +1,24 @@
const { exec, execAsync } = Utils;
export const Battery = () => Widget.Button({
class_name: "battery",
child: Widget.Label({
setup: (self) => {self.poll(1000, (self) =>
execAsync("block_battery")
.then((out) => {
self.label = out;
let num = Number(out.substring(2, out.length - 1));
// Turn red at low battery
if (num < 20) {
self.class_name = "module, critical";
} else {
self.class_name = "module";
};
})
.catch(console.error),
)},
}),
on_clicked: () => {},
});

View file

@ -0,0 +1,32 @@
const { exec, execAsync } = Utils;
export const Date = () => Widget.Button({
child: Widget.Label({
class_name: "module",
setup: (self) => {
self.poll(1000, (self) =>
execAsync(["date", "+ %a %b %e"])
.then((time) => (self.label = time))
.catch(console.error),
);
},
}),
on_clicked: () => {
},
});
export const Time = () => Widget.Button({
child: Widget.Label({
class_name: "module",
setup: (self) => {
self.poll(1000, (self) =>
execAsync(["date", "+ %R"])
.then((time) => (self.label = time))
.catch(console.error),
);
},
}),
on_clicked: () => {
},
});

View file

@ -0,0 +1,53 @@
const Hyprland = await Service.import("hyprland");
var monitorsActiveWorkspaceCache = new Array(10).fill(-1);
const is_active = (workspaceID) => {
let monitorID = Hyprland.getWorkspace(workspaceID).monitorID;
var monitor = Hyprland.getMonitor(monitorID);
// Somewhat of a hack going on here. For some reason, the
// Hyprland.monitors array only contains the monitorIDs up to the
// currently focused one. That means if I am currently focused on
// monitor 0, then I cannot check what the focused workspace is on
// monitor 3. Thus, I had to create a global cache on top to reference
// for when the result of Hyprland.getMonitor(monitorID) is undefined.
let activeID = -1;
if (monitor == undefined) {
activeID = monitorsActiveWorkspaceCache[monitorID];
} else {
monitorsActiveWorkspaceCache[monitorID] = monitor.activeWorkspace.id;
activeID = monitor.activeWorkspace.id;
}
return workspaceID == activeID;
}
export const Workspaces = (monitorID) => Widget.Box({
class_name: 'workspaces',
children: Hyprland.bind('workspaces').transform(ws => {
return ws
.filter(({ id, name }) => {
if (name == "special") {
// Hyprland sometimes uses a special workspace, which
// wouldn't appear on the bar.
return false;
}
// Only show the workspaces belonging to the monitor
return Hyprland.getWorkspace(id).monitorID == monitorID;
})
.sort((a, b) => a.id - b.id) // Show in order of ID
.map(({ id, name }) => Widget.Button({
on_clicked: () => Hyprland.sendMessage(`dispatch workspace ${id}`),
child: Widget.Label(`${name}`),
class_name: Hyprland.active.workspace.bind('id')
.transform(i => `${is_active(id) ? 'focused' : ''}`),
}));
}),
});
export const ClientTitle = () => Widget.Label({
class_name: 'module',
label: Hyprland.active.client.bind('title'),
});

View file

@ -0,0 +1,105 @@
import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js';
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
import { lookUpIcon } from 'resource:///com/github/Aylur/ags/utils.js';
/** @param {import('resource:///com/github/Aylur/ags/service/notifications.js').Notification} n */
const 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 = 'add';
if (lookUpIcon(app_icon))
icon = app_icon;
if (app_entry && lookUpIcon(app_entry))
icon = app_entry;
return Widget.Icon(icon);
};
/** @param {import('resource:///com/github/Aylur/ags/service/notifications.js').Notification} n */
export const 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),
hexpand: true,
child: Widget.Label(label),
})),
});
return Widget.EventBox({
on_primary_click: () => n.dismiss(),
child: Widget.Box({
class_name: `notification ${n.urgency}`,
vertical: true,
children: [
Widget.Box({
children: [
icon,
Widget.Box({
vertical: true,
children: [
title,
body,
],
}),
],
}),
actions,
],
}),
});
};
export const Popups = monitor => Widget.Window({
name: `notifications`,
monitor,
layer: 'overlay',
anchor: ['top', 'right'],
child: Widget.Box({
class_name: 'notifications',
vertical: true,
children: Notifications.bind('popups').transform(popups => {
return popups.map(Notification);
}),
}),
});