mirror of
https://github.com/Theaninova/TheaninovOS.git
synced 2026-01-23 10:02:45 +00:00
feat: update system
This commit is contained in:
@@ -1,22 +1,2 @@
|
||||
import { readFile } from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
import { timeout } from "resource:///com/github/Aylur/ags/utils.js";
|
||||
const pkgjson = JSON.parse(readFile(App.configDir + "/package.json"));
|
||||
|
||||
timeout(1000, () => JSON.stringify(App));
|
||||
|
||||
const v = {
|
||||
ags: `v${pkg.version}`,
|
||||
expected: `v${pkgjson.version}`,
|
||||
};
|
||||
|
||||
function mismatch() {
|
||||
print(`my config expects ${v.expected}, but your ags is ${v.ags}`);
|
||||
App.connect("config-parsed", (app) => app.Quit());
|
||||
return {};
|
||||
}
|
||||
|
||||
/*export default v.ags === v.expected
|
||||
? (await import("./js/main.js")).default
|
||||
: mismatch();*/
|
||||
export default (await import("./js/main.js")).default;
|
||||
import { default as main } from "./js/main.js";
|
||||
export default main;
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import PopupWindow from "../misc/PopupWindow.js";
|
||||
import icons from "../icons.js";
|
||||
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
import options from "../options.js";
|
||||
import { lookUpIcon } from "resource:///com/github/Aylur/ags/utils.js";
|
||||
|
||||
/** @param {import('resource:///com/github/Aylur/ags/service/applications.js').Application} app */
|
||||
export default (app) => {
|
||||
@@ -23,7 +20,7 @@ export default (app) => {
|
||||
});
|
||||
|
||||
const icon = Widget.Icon({
|
||||
icon: lookUpIcon(app.icon_name || "") ? app.icon_name || "" : "",
|
||||
icon: Utils.lookUpIcon(app.icon_name || "") ? app.icon_name || "" : "",
|
||||
size: options.applauncher.icon_size.bind("value"),
|
||||
});
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
import Applications from "resource:///com/github/Aylur/ags/service/applications.js";
|
||||
import PopupWindow from "../misc/PopupWindow.js";
|
||||
import AppItem from "./AppItem.js";
|
||||
import icons from "../icons.js";
|
||||
import { launchApp } from "../utils.js";
|
||||
import options from "../options.js";
|
||||
|
||||
const WINDOW_NAME = "applauncher";
|
||||
|
||||
@@ -30,6 +29,7 @@ const Applauncher = () => {
|
||||
let items = mkItems();
|
||||
|
||||
const list = Widget.Box({
|
||||
class_name: "app-list",
|
||||
vertical: true,
|
||||
children: items,
|
||||
});
|
||||
@@ -86,4 +86,5 @@ export default () =>
|
||||
name: WINDOW_NAME,
|
||||
transition: "slide_down",
|
||||
child: Applauncher(),
|
||||
anchor: options.applauncher.anchor.bind("value"),
|
||||
});
|
||||
|
||||
@@ -1,45 +1,34 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
|
||||
/**
|
||||
* @typedef {Object} PanelButtonProps
|
||||
* @property {any} content
|
||||
* @property {import('types/widgets/button').ButtonProps['child']} content
|
||||
* @property {string=} window
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {import('types/widgets/button').ButtonProps & PanelButtonProps} o
|
||||
*/
|
||||
export default ({
|
||||
class_name,
|
||||
content,
|
||||
window = "",
|
||||
connections = [],
|
||||
...rest
|
||||
}) => {
|
||||
let open = false;
|
||||
|
||||
const connection = [
|
||||
App,
|
||||
(self, win, visible) => {
|
||||
if (win !== window) return;
|
||||
|
||||
if (open && !visible) {
|
||||
open = false;
|
||||
self.toggleClassName("active", false);
|
||||
}
|
||||
|
||||
if (visible) {
|
||||
open = true;
|
||||
self.toggleClassName("active");
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
return Widget.Button({
|
||||
export default ({ class_name, content, window = "", setup, ...rest }) =>
|
||||
Widget.Button({
|
||||
class_name: `panel-button ${class_name}`,
|
||||
child: Widget.Box({ children: [content] }),
|
||||
connections: connections.concat([connection]),
|
||||
setup: (self) => {
|
||||
let open = false;
|
||||
|
||||
self.hook(App, (_, win, visible) => {
|
||||
if (win !== window) return;
|
||||
|
||||
if (open && !visible) {
|
||||
open = false;
|
||||
self.toggleClassName("active", false);
|
||||
}
|
||||
|
||||
if (visible) {
|
||||
open = true;
|
||||
self.toggleClassName("active");
|
||||
}
|
||||
});
|
||||
|
||||
if (setup) setup(self);
|
||||
},
|
||||
...rest,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import SystemTray from "resource:///com/github/Aylur/ags/service/systemtray.js";
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import Variable from "resource:///com/github/Aylur/ags/variable.js";
|
||||
import Notifications from "resource:///com/github/Aylur/ags/service/notifications.js";
|
||||
import Mpris from "resource:///com/github/Aylur/ags/service/mpris.js";
|
||||
import Battery from "resource:///com/github/Aylur/ags/service/battery.js";
|
||||
@@ -28,25 +26,29 @@ SystemTray.connect("changed", () => {
|
||||
});
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @template {import('types/service').default} T
|
||||
* @param {T=} service
|
||||
* @param {(self: T) => boolean=} condition
|
||||
* @param {(service: T) => boolean=} condition
|
||||
*/
|
||||
const SeparatorDot = (service, condition) => {
|
||||
const visibility = (self) => {
|
||||
if (!options.bar.separators.value) return (self.visible = false);
|
||||
|
||||
self.visible =
|
||||
condition && service ? condition(service) : options.bar.separators.value;
|
||||
};
|
||||
|
||||
const conn = service ? [[service, visibility]] : [];
|
||||
return Widget.Separator({
|
||||
connections: [["draw", visibility], ...conn],
|
||||
binds: [["visible", options.bar.separators]],
|
||||
const SeparatorDot = (service, condition) =>
|
||||
Widget.Separator({
|
||||
vpack: "center",
|
||||
setup: (self) => {
|
||||
const visibility = () => {
|
||||
if (!options.bar.separators.value) return (self.visible = false);
|
||||
|
||||
self.visible =
|
||||
condition && service
|
||||
? condition(service)
|
||||
: options.bar.separators.value;
|
||||
};
|
||||
|
||||
if (service && condition) self.hook(service, visibility);
|
||||
|
||||
self.on("draw", visibility);
|
||||
self.bind("visible", options.bar.separators);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const Start = () =>
|
||||
Widget.Box({
|
||||
@@ -99,14 +101,9 @@ export default (monitor) =>
|
||||
class_name: "transparent",
|
||||
exclusivity: "exclusive",
|
||||
monitor,
|
||||
binds: [
|
||||
[
|
||||
"anchor",
|
||||
options.bar.position,
|
||||
"value",
|
||||
(pos) => [pos, "left", "right"],
|
||||
],
|
||||
],
|
||||
anchor: options.bar.position
|
||||
.bind("value")
|
||||
.transform((pos) => [pos, "left", "right"]),
|
||||
child: Widget.CenterBox({
|
||||
class_name: "panel",
|
||||
start_widget: Start(),
|
||||
|
||||
@@ -7,43 +7,35 @@ import PanelButton from "../PanelButton.js";
|
||||
|
||||
const Indicator = () =>
|
||||
Widget.Stack({
|
||||
items: [
|
||||
["false", Widget.Icon({ binds: [["icon", Battery, "icon-name"]] })],
|
||||
["true", FontIcon(icons.battery.charging)],
|
||||
],
|
||||
binds: [["visible", options.battery.bar.show_icon]],
|
||||
connections: [
|
||||
[
|
||||
Battery,
|
||||
(stack) => {
|
||||
stack.shown = `${Battery.charging || Battery.charged}`;
|
||||
},
|
||||
],
|
||||
],
|
||||
children: {
|
||||
false: Widget.Icon({ icon: Battery.bind("icon_name") }),
|
||||
true: FontIcon(icons.battery.charging),
|
||||
},
|
||||
visible: options.battery.bar.show_icon.bind("value"),
|
||||
setup: (self) =>
|
||||
self.hook(Battery, () => {
|
||||
self.shown = `${Battery.charging || Battery.charged}`;
|
||||
}),
|
||||
});
|
||||
|
||||
const PercentLabel = () =>
|
||||
Widget.Revealer({
|
||||
transition: "slide_right",
|
||||
binds: [["reveal-child", options.battery.show_percentage]],
|
||||
reveal_child: options.battery.show_percentage.bind("value"),
|
||||
child: Widget.Label({
|
||||
binds: [["label", Battery, "percent", (p) => `${p}%`]],
|
||||
label: Battery.bind("percent").transform((p) => `${p}%`),
|
||||
}),
|
||||
});
|
||||
|
||||
const LevelBar = () =>
|
||||
Widget.LevelBar({
|
||||
connections: [
|
||||
[
|
||||
options.battery.bar.full,
|
||||
(self) => {
|
||||
const full = options.battery.bar.full.value;
|
||||
self.vpack = full ? "fill" : "center";
|
||||
self.hpack = full ? "fill" : "center";
|
||||
},
|
||||
],
|
||||
],
|
||||
binds: [["value", Battery, "percent", (p) => p / 100]],
|
||||
value: Battery.bind("percent").transform((p) => p / 100),
|
||||
setup: (self) =>
|
||||
self.hook(options.battery.bar.full, () => {
|
||||
const full = options.battery.bar.full.value;
|
||||
self.vpack = full ? "fill" : "center";
|
||||
self.hpack = full ? "fill" : "center";
|
||||
}),
|
||||
});
|
||||
|
||||
const WholeButton = () =>
|
||||
@@ -57,7 +49,7 @@ const WholeButton = () =>
|
||||
children: [
|
||||
FontIcon({
|
||||
icon: icons.battery.charging,
|
||||
binds: [["visible", Battery, "charging"]],
|
||||
visible: Battery.bind("charging"),
|
||||
}),
|
||||
Widget.Box({
|
||||
hpack: "center",
|
||||
@@ -77,32 +69,21 @@ export default () =>
|
||||
options.battery.show_percentage.value = !v;
|
||||
},
|
||||
content: Widget.Box({
|
||||
connections: [
|
||||
[
|
||||
Battery,
|
||||
(w) => {
|
||||
w.toggleClassName("charging", Battery.charging || Battery.charged);
|
||||
w.toggleClassName(
|
||||
"medium",
|
||||
Battery.percent < options.battery.medium.value,
|
||||
);
|
||||
w.toggleClassName(
|
||||
"low",
|
||||
Battery.percent < options.battery.low.value,
|
||||
);
|
||||
w.toggleClassName("half", Battery.percent < 48);
|
||||
},
|
||||
],
|
||||
],
|
||||
binds: [
|
||||
["visible", Battery, "available"],
|
||||
[
|
||||
"children",
|
||||
options.battery.bar.full,
|
||||
"value",
|
||||
(full) =>
|
||||
full ? [WholeButton()] : [Indicator(), PercentLabel(), LevelBar()],
|
||||
],
|
||||
],
|
||||
visible: Battery.bind("available"),
|
||||
children: options.battery.bar.full
|
||||
.bind("value")
|
||||
.transform((full) =>
|
||||
full ? [WholeButton()] : [Indicator(), PercentLabel(), LevelBar()],
|
||||
),
|
||||
setup: (self) =>
|
||||
self.hook(Battery, (w) => {
|
||||
w.toggleClassName("charging", Battery.charging || Battery.charged);
|
||||
w.toggleClassName(
|
||||
"medium",
|
||||
Battery.percent < options.battery.medium.value,
|
||||
);
|
||||
w.toggleClassName("low", Battery.percent < options.battery.low.value);
|
||||
w.toggleClassName("half", Battery.percent < 48);
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -7,9 +7,8 @@ export default () =>
|
||||
PanelButton({
|
||||
class_name: "color-picker",
|
||||
content: Widget.Icon("color-select-symbolic"),
|
||||
binds: [["tooltip-text", Colors, "colors", (v) => `${v.length} colors`]],
|
||||
tooltip_text: Colors.bind("colors").transform((v) => `${v.length} colors`),
|
||||
on_clicked: () => Colors.pick(),
|
||||
|
||||
on_secondary_click: (btn) => {
|
||||
if (Colors.colors.length === 0) return;
|
||||
|
||||
|
||||
@@ -7,49 +7,38 @@ import { substitute } from "../../utils.js";
|
||||
|
||||
export const ClientLabel = () =>
|
||||
Widget.Label({
|
||||
binds: [
|
||||
[
|
||||
"label",
|
||||
Hyprland.active.client,
|
||||
"class",
|
||||
(c) => {
|
||||
const { titles } = options.substitutions;
|
||||
return substitute(titles, c);
|
||||
},
|
||||
],
|
||||
],
|
||||
label: Hyprland.active.client.bind("class").transform((c) => {
|
||||
const { titles } = options.substitutions;
|
||||
return substitute(titles, c);
|
||||
}),
|
||||
});
|
||||
|
||||
export const ClientIcon = () =>
|
||||
Widget.Icon({
|
||||
connections: [
|
||||
[
|
||||
Hyprland.active.client,
|
||||
(self) => {
|
||||
const { icons } = options.substitutions;
|
||||
const { client } = Hyprland.active;
|
||||
setup: (self) =>
|
||||
self.hook(Hyprland.active.client, () => {
|
||||
const { icons } = options.substitutions;
|
||||
const { client } = Hyprland.active;
|
||||
|
||||
const classIcon = substitute(icons, client.class) + "-symbolic";
|
||||
const titleIcon = substitute(icons, client.class) + "-symbolic";
|
||||
const classIcon = substitute(icons, client.class) + "-symbolic";
|
||||
const titleIcon = substitute(icons, client.class) + "-symbolic";
|
||||
|
||||
const hasTitleIcon = Utils.lookUpIcon(titleIcon);
|
||||
const hasClassIcon = Utils.lookUpIcon(classIcon);
|
||||
const hasTitleIcon = Utils.lookUpIcon(titleIcon);
|
||||
const hasClassIcon = Utils.lookUpIcon(classIcon);
|
||||
|
||||
if (hasClassIcon) self.icon = classIcon;
|
||||
if (hasClassIcon) self.icon = classIcon;
|
||||
|
||||
if (hasTitleIcon) self.icon = titleIcon;
|
||||
if (hasTitleIcon) self.icon = titleIcon;
|
||||
|
||||
self.visible = !!(hasTitleIcon || hasClassIcon);
|
||||
},
|
||||
],
|
||||
],
|
||||
self.visible = !!(hasTitleIcon || hasClassIcon);
|
||||
}),
|
||||
});
|
||||
|
||||
export default () =>
|
||||
PanelButton({
|
||||
class_name: "focused-client",
|
||||
content: Widget.Box({
|
||||
tooltip_text: Hyprland.active.bind("client").transform((c) => c.title),
|
||||
children: [ClientIcon(), ClientLabel()],
|
||||
binds: [["tooltip-text", Hyprland.active, "client", (c) => c.title]],
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -26,31 +26,24 @@ const Indicator = ({ player, direction = "right" }) =>
|
||||
vexpand: true,
|
||||
truncate: "end",
|
||||
max_width_chars: 40,
|
||||
connections: [
|
||||
[
|
||||
player,
|
||||
(label) => {
|
||||
label.label = `${player.track_artists.join(", ")} - ${
|
||||
player.track_title
|
||||
}`;
|
||||
},
|
||||
],
|
||||
],
|
||||
label: player
|
||||
.bind("track_title")
|
||||
.transform(
|
||||
() => `${player.track_artists.join(", ")} - ${player.track_title}`,
|
||||
),
|
||||
}),
|
||||
connections: [
|
||||
[
|
||||
player,
|
||||
(revealer) => {
|
||||
if (revealer._current === player.track_title) return;
|
||||
setupRevealer: (self) => {
|
||||
let current = "";
|
||||
self.hook(player, () => {
|
||||
if (current === player.track_title) return;
|
||||
|
||||
revealer._current = player.track_title;
|
||||
revealer.reveal_child = true;
|
||||
Utils.timeout(3000, () => {
|
||||
revealer.reveal_child = false;
|
||||
});
|
||||
},
|
||||
],
|
||||
],
|
||||
current = player.track_title;
|
||||
self.reveal_child = true;
|
||||
Utils.timeout(3000, () => {
|
||||
self.reveal_child = false;
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -75,10 +68,7 @@ export default ({ direction = "right" } = {}) => {
|
||||
box.children = [Indicator({ player, direction })];
|
||||
};
|
||||
|
||||
return Widget.Box({
|
||||
connections: [
|
||||
[options.mpris.preferred, update],
|
||||
[Mpris, update, "notify::players"],
|
||||
],
|
||||
});
|
||||
return Widget.Box()
|
||||
.hook(options.mpris.preferred, update)
|
||||
.hook(Mpris, update, "notify::players");
|
||||
};
|
||||
|
||||
@@ -12,52 +12,39 @@ import HoverRevealer from "../../misc/HoverRevealer.js";
|
||||
export default ({ direction = "left" } = {}) =>
|
||||
HoverRevealer({
|
||||
class_name: "notifications panel-button",
|
||||
eventboxConnections: [
|
||||
["button-press-event", () => App.openWindow("dashboard")],
|
||||
[
|
||||
Notifications,
|
||||
(box) =>
|
||||
(box.visible =
|
||||
Notifications.notifications.length > 0 || Notifications.dnd),
|
||||
],
|
||||
],
|
||||
connections: [
|
||||
[
|
||||
Notifications,
|
||||
(revealer) => {
|
||||
const title = Notifications.notifications[0]?.summary;
|
||||
if (revealer._title === title) return;
|
||||
setupEventBox: (box) =>
|
||||
box
|
||||
.on("button-press-event", () => App.openWindow("dashboard"))
|
||||
.hook(
|
||||
Notifications,
|
||||
() =>
|
||||
(box.visible =
|
||||
Notifications.notifications.length > 0 || Notifications.dnd),
|
||||
),
|
||||
|
||||
revealer._title = title;
|
||||
revealer.reveal_child = true;
|
||||
Utils.timeout(3000, () => {
|
||||
revealer.reveal_child = false;
|
||||
});
|
||||
},
|
||||
],
|
||||
],
|
||||
setupRevealer: (self) =>
|
||||
self.hook(Notifications, () => {
|
||||
let title = "";
|
||||
const summary = Notifications.notifications[0]?.summary;
|
||||
if (title === summary) return;
|
||||
|
||||
title = summary;
|
||||
self.reveal_child = true;
|
||||
Utils.timeout(3000, () => {
|
||||
self.reveal_child = false;
|
||||
});
|
||||
}),
|
||||
direction,
|
||||
indicator: Widget.Icon({
|
||||
binds: [
|
||||
[
|
||||
"icon",
|
||||
Notifications,
|
||||
"dnd",
|
||||
(dnd) =>
|
||||
dnd ? icons.notifications.silent : icons.notifications.noisy,
|
||||
],
|
||||
],
|
||||
icon: Notifications.bind("dnd").transform(
|
||||
(dnd) => icons.notifications[dnd ? "silent" : "noisy"],
|
||||
),
|
||||
}),
|
||||
child: Widget.Label({
|
||||
truncate: "end",
|
||||
max_width_chars: 40,
|
||||
binds: [
|
||||
[
|
||||
"label",
|
||||
Notifications,
|
||||
"notifications",
|
||||
(n) => n.reverse()[0]?.summary || "",
|
||||
],
|
||||
],
|
||||
label: Notifications.bind("notifications").transform(
|
||||
(n) => n.reverse()[0]?.summary || "",
|
||||
),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -10,15 +10,8 @@ export default () =>
|
||||
window: "overview",
|
||||
on_clicked: () => App.toggleWindow("overview"),
|
||||
content: FontIcon({
|
||||
binds: [
|
||||
[
|
||||
"icon",
|
||||
options.bar.icon,
|
||||
"value",
|
||||
(v) => {
|
||||
return v === "distro-icon" ? distroIcon : v;
|
||||
},
|
||||
],
|
||||
],
|
||||
label: options.bar.icon.bind("value").transform((v) => {
|
||||
return v === "distro-icon" ? distroIcon : v;
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -7,23 +7,16 @@ export default () =>
|
||||
PanelButton({
|
||||
class_name: "recorder",
|
||||
on_clicked: () => Recorder.stop(),
|
||||
binds: [["visible", Recorder, "recording"]],
|
||||
visible: Recorder.bind("recording"),
|
||||
content: Widget.Box({
|
||||
children: [
|
||||
Widget.Icon(icons.recorder.recording),
|
||||
Widget.Label({
|
||||
binds: [
|
||||
[
|
||||
"label",
|
||||
Recorder,
|
||||
"timer",
|
||||
(time) => {
|
||||
const sec = time % 60;
|
||||
const min = Math.floor(time / 60);
|
||||
return `${min}:${sec < 10 ? "0" + sec : sec}`;
|
||||
},
|
||||
],
|
||||
],
|
||||
label: Recorder.bind("timer").transform((time) => {
|
||||
const sec = time % 60;
|
||||
const min = Math.floor(time / 60);
|
||||
return `${min}:${sec < 10 ? "0" + sec : sec}`;
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
|
||||
@@ -7,7 +7,7 @@ import options from "../../options.js";
|
||||
/**
|
||||
* @param {import('types/widgets/revealer').default} revealer
|
||||
* @param {'left' | 'right' | 'up' | 'down'} direction
|
||||
* @param {import('types/variable').Variable} items
|
||||
* @param {import('types/variable').Variable<number>} items
|
||||
*/
|
||||
const Arrow = (revealer, direction, items) => {
|
||||
let deg = 0;
|
||||
@@ -29,14 +29,7 @@ const Arrow = (revealer, direction, items) => {
|
||||
|
||||
return Widget.Button({
|
||||
class_name: "panel-button sub-menu",
|
||||
connections: [
|
||||
[
|
||||
items,
|
||||
(btn) => {
|
||||
btn.tooltip_text = `${items.value} Items`;
|
||||
},
|
||||
],
|
||||
],
|
||||
tooltip_text: items.bind().transform((v) => `${v} Items`),
|
||||
on_clicked: () => {
|
||||
animate();
|
||||
revealer.reveal_child = !revealer.reveal_child;
|
||||
|
||||
@@ -13,23 +13,16 @@ const System = (type) => {
|
||||
const progress = Widget.Box({
|
||||
class_name: "progress",
|
||||
child: Widget.CircularProgress({
|
||||
binds: [["value", variables[type]]],
|
||||
value: variables[type].bind(),
|
||||
}),
|
||||
});
|
||||
|
||||
const revealer = Widget.Revealer({
|
||||
transition: "slide_right",
|
||||
child: Widget.Label({
|
||||
binds: [
|
||||
[
|
||||
"label",
|
||||
variables[type],
|
||||
"value",
|
||||
(v) => {
|
||||
return ` ${type}: ${Math.round(v * 100)}%`;
|
||||
},
|
||||
],
|
||||
],
|
||||
label: variables[type].bind("value").transform((v) => {
|
||||
return ` ${type}: ${Math.round(v * 100)}%`;
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ const MicrophoneIndicator = () =>
|
||||
if (!Audio.microphone) return;
|
||||
|
||||
const { muted, low, medium, high } = icons.audio.mic;
|
||||
if (Audio.microphone.is_muted) return (icon.icon = muted);
|
||||
|
||||
/** @type {Array<[number, string]>} */
|
||||
const cons = [
|
||||
@@ -42,7 +41,7 @@ const MicrophoneIndicator = () =>
|
||||
|
||||
icon.visible = Audio.recorders.length > 0 || Audio.microphone.is_muted;
|
||||
},
|
||||
"speaker-changed",
|
||||
"microphone-changed",
|
||||
);
|
||||
|
||||
const DNDIndicator = () =>
|
||||
|
||||
@@ -26,9 +26,6 @@ const setChildren = (box) =>
|
||||
}));
|
||||
|
||||
export default () =>
|
||||
Widget.Box({
|
||||
connections: [
|
||||
[Hyprland, setChildren, "notify::clients"],
|
||||
[Hyprland, setChildren, "notify::active"],
|
||||
],
|
||||
});
|
||||
Widget.Box()
|
||||
.hook(Hyprland, setChildren, "notify::clients")
|
||||
.hook(Hyprland, setChildren, "notify::active");
|
||||
|
||||
@@ -12,40 +12,34 @@ const Workspaces = () => {
|
||||
return Widget.Box({
|
||||
children: range(ws || 20).map((i) =>
|
||||
Widget.Button({
|
||||
setup: (btn) => (btn.id = i),
|
||||
attribute: i,
|
||||
on_clicked: () => dispatch(i),
|
||||
child: Widget.Label({
|
||||
label: `${i}`,
|
||||
class_name: "indicator",
|
||||
vpack: "center",
|
||||
}),
|
||||
connections: [
|
||||
[
|
||||
Hyprland,
|
||||
(btn) => {
|
||||
btn.toggleClassName("active", Hyprland.active.workspace.id === i);
|
||||
btn.toggleClassName(
|
||||
"occupied",
|
||||
Hyprland.getWorkspace(i)?.windows > 0,
|
||||
);
|
||||
},
|
||||
],
|
||||
],
|
||||
setup: (self) =>
|
||||
self.hook(Hyprland, () => {
|
||||
self.toggleClassName("active", Hyprland.active.workspace.id === i);
|
||||
self.toggleClassName(
|
||||
"occupied",
|
||||
(Hyprland.getWorkspace(i)?.windows || 0) > 0,
|
||||
);
|
||||
}),
|
||||
}),
|
||||
),
|
||||
connections: ws
|
||||
? []
|
||||
: [
|
||||
[
|
||||
Hyprland.active.workspace,
|
||||
(box) =>
|
||||
box.children.map((btn) => {
|
||||
btn.visible = Hyprland.workspaces.some(
|
||||
(ws) => ws.id === btn.id,
|
||||
);
|
||||
}),
|
||||
],
|
||||
],
|
||||
setup: (box) => {
|
||||
if (ws === 0) {
|
||||
box.hook(Hyprland.active.workspace, () =>
|
||||
box.children.map((btn) => {
|
||||
btn.visible = Hyprland.workspaces.some(
|
||||
(ws) => ws.id === btn.attribute,
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -58,7 +52,7 @@ export default () =>
|
||||
on_scroll_up: () => dispatch("m+1"),
|
||||
on_scroll_down: () => dispatch("m-1"),
|
||||
class_name: "eventbox",
|
||||
binds: [["child", options.workspaces, "value", Workspaces]],
|
||||
child: options.workspaces.bind("value").transform(Workspaces),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -7,19 +7,15 @@ import options from "../options.js";
|
||||
export default () =>
|
||||
PopupWindow({
|
||||
name: "dashboard",
|
||||
connections: [
|
||||
[
|
||||
options.bar.position,
|
||||
(self) => {
|
||||
self.anchor = [options.bar.position.value];
|
||||
if (options.bar.position.value === "top")
|
||||
self.transition = "slide_down";
|
||||
setup: (self) =>
|
||||
self.hook(options.bar.position, () => {
|
||||
self.anchor = [options.bar.position.value];
|
||||
if (options.bar.position.value === "top")
|
||||
self.transition = "slide_down";
|
||||
|
||||
if (options.bar.position.value === "bottom")
|
||||
self.transition = "slide_up";
|
||||
},
|
||||
],
|
||||
],
|
||||
if (options.bar.position.value === "bottom")
|
||||
self.transition = "slide_up";
|
||||
}),
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
NotificationColumn(),
|
||||
|
||||
@@ -13,23 +13,16 @@ const SysProgress = (type, title, unit) =>
|
||||
Widget.Box({
|
||||
class_name: `circular-progress-box ${type}`,
|
||||
hexpand: true,
|
||||
binds: [
|
||||
[
|
||||
"tooltipText",
|
||||
vars[type],
|
||||
"value",
|
||||
(v) => `${title}: ${Math.floor(v * 100)}${unit}`,
|
||||
],
|
||||
],
|
||||
tooltip_text: vars[type]
|
||||
.bind("value")
|
||||
.transform((v) => `${title}: ${Math.floor(v * 100)}${unit}`),
|
||||
child: Widget.CircularProgress({
|
||||
hexpand: true,
|
||||
class_name: `circular-progress ${type}`,
|
||||
child: Widget.Icon(icons.system[type]),
|
||||
start_at: 0.75,
|
||||
binds: [
|
||||
["value", vars[type]],
|
||||
["rounded", options.radii, "value", (v) => v > 0],
|
||||
],
|
||||
value: vars[type].bind(),
|
||||
rounded: options.radii.bind("value").transform((v) => v > 0),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -45,7 +38,7 @@ export default () =>
|
||||
Clock({ format: "%H:%M" }),
|
||||
Widget.Label({
|
||||
class_name: "uptime",
|
||||
binds: [["label", vars.uptime, "value", (t) => `uptime: ${t}`]],
|
||||
label: vars.uptime.bind("value").transform((t) => `uptime: ${t}`),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
|
||||
@@ -11,19 +11,16 @@ const ClearButton = () =>
|
||||
for (let i = 0; i < list.length; i++)
|
||||
timeout(50 * i, () => list[i]?.close());
|
||||
},
|
||||
binds: [["sensitive", Notifications, "notifications", (n) => n.length > 0]],
|
||||
sensitive: Notifications.bind("notifications").transform(
|
||||
(n) => n.length > 0,
|
||||
),
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
Widget.Label("Clear "),
|
||||
Widget.Icon({
|
||||
binds: [
|
||||
[
|
||||
"icon",
|
||||
Notifications,
|
||||
"notifications",
|
||||
(n) => (n.length > 0 ? icons.trash.full : icons.trash.empty),
|
||||
],
|
||||
],
|
||||
icon: Notifications.bind("notifications").transform(
|
||||
(n) => icons.trash[n.length > 0 ? "full" : "empty"],
|
||||
),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
@@ -42,18 +39,10 @@ const NotificationList = () =>
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
vexpand: true,
|
||||
connections: [
|
||||
[
|
||||
Notifications,
|
||||
(box) => {
|
||||
box.children = Notifications.notifications
|
||||
.reverse()
|
||||
.map(Notification);
|
||||
|
||||
box.visible = Notifications.notifications.length > 0;
|
||||
},
|
||||
],
|
||||
],
|
||||
children: Notifications.bind("notifications").transform((n) =>
|
||||
n.reverse().map(Notification),
|
||||
),
|
||||
visible: Notifications.bind("notifications").transform((n) => n.length > 0),
|
||||
});
|
||||
|
||||
const Placeholder = () =>
|
||||
@@ -64,11 +53,13 @@ const Placeholder = () =>
|
||||
hpack: "center",
|
||||
vexpand: true,
|
||||
hexpand: true,
|
||||
visible: Notifications.bind("notifications").transform(
|
||||
(n) => n.length === 0,
|
||||
),
|
||||
children: [
|
||||
Widget.Icon(icons.notifications.silent),
|
||||
Widget.Label("Your inbox is empty"),
|
||||
],
|
||||
binds: [["visible", Notifications, "notifications", (n) => n.length === 0]],
|
||||
});
|
||||
|
||||
export default () =>
|
||||
|
||||
@@ -8,28 +8,26 @@ const DesktopClock = () =>
|
||||
class_name: "clock-box-shadow",
|
||||
child: Widget.CenterBox({
|
||||
class_name: "clock-box",
|
||||
children: [
|
||||
Clock({
|
||||
class_name: "clock",
|
||||
hpack: "center",
|
||||
format: "%H",
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "separator-box",
|
||||
vertical: true,
|
||||
hexpand: true,
|
||||
hpack: "center",
|
||||
children: [
|
||||
Widget.Separator({ vpack: "center", vexpand: true }),
|
||||
Widget.Separator({ vpack: "center", vexpand: true }),
|
||||
],
|
||||
}),
|
||||
Clock({
|
||||
class_name: "clock",
|
||||
hpack: "center",
|
||||
format: "%M",
|
||||
}),
|
||||
],
|
||||
start_widget: Clock({
|
||||
class_name: "clock",
|
||||
hpack: "center",
|
||||
format: "%H",
|
||||
}),
|
||||
center_widget: Widget.Box({
|
||||
class_name: "separator-box",
|
||||
vertical: true,
|
||||
hexpand: true,
|
||||
hpack: "center",
|
||||
children: [
|
||||
Widget.Separator({ vpack: "center", vexpand: true }),
|
||||
Widget.Separator({ vpack: "center", vexpand: true }),
|
||||
],
|
||||
}),
|
||||
end_widget: Clock({
|
||||
class_name: "clock",
|
||||
hpack: "center",
|
||||
format: "%M",
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -40,21 +38,17 @@ const Desktop = () =>
|
||||
vertical: true,
|
||||
vexpand: true,
|
||||
hexpand: true,
|
||||
binds: [["visible", options.desktop.clock.enable]],
|
||||
connections: [
|
||||
[
|
||||
options.desktop.clock.position,
|
||||
(box) => {
|
||||
const [hpack = "center", vpack = "center", offset = 64] =
|
||||
options.desktop.clock.position.value.split(" ") || [];
|
||||
visible: options.desktop.clock.enable.bind("value"),
|
||||
setup: (self) =>
|
||||
self.hook(options.desktop.clock.position, () => {
|
||||
const [hpack = "center", vpack = "center", offset = 64] =
|
||||
options.desktop.clock.position.value.split(" ") || [];
|
||||
|
||||
// @ts-expect-error
|
||||
box.hpack = hpack;
|
||||
box.vpack = vpack;
|
||||
box.setCss(`margin: ${Number(offset)}px;`);
|
||||
},
|
||||
],
|
||||
],
|
||||
// @ts-expect-error
|
||||
self.hpack = hpack;
|
||||
self.vpack = vpack;
|
||||
self.setCss(`margin: ${Number(offset)}px;`);
|
||||
}),
|
||||
children: [
|
||||
DesktopClock(),
|
||||
Clock({ format: "%B %e. %A", class_name: "date" }),
|
||||
@@ -66,6 +60,7 @@ const Desktop = () =>
|
||||
export default (monitor) =>
|
||||
Widget.Window({
|
||||
monitor,
|
||||
keymode: "on-demand",
|
||||
name: `desktop${monitor}`,
|
||||
layer: "background",
|
||||
class_name: "desktop",
|
||||
|
||||
@@ -22,113 +22,95 @@ const AppButton = ({ icon, pinned = false, ...rest }) => {
|
||||
),
|
||||
});
|
||||
|
||||
const button = Widget.Button({
|
||||
return Widget.Button({
|
||||
...rest,
|
||||
attribute: indicators,
|
||||
child: Widget.Box({
|
||||
class_name: "box",
|
||||
child: Widget.Overlay({
|
||||
child: Widget.Icon({
|
||||
icon,
|
||||
binds: [["size", options.desktop.dock.icon_size]],
|
||||
size: options.desktop.dock.icon_size.bind("value"),
|
||||
}),
|
||||
pass_through: true,
|
||||
overlays: pinned ? [indicators] : [],
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
return Object.assign(button, { indicators });
|
||||
};
|
||||
|
||||
const Taskbar = () =>
|
||||
Widget.Box({
|
||||
binds: [
|
||||
[
|
||||
"children",
|
||||
Hyprland,
|
||||
"clients",
|
||||
(c) =>
|
||||
c.map((client) => {
|
||||
for (const appName of options.desktop.dock.pinned_apps.value) {
|
||||
if (client.class.toLowerCase().includes(appName.toLowerCase()))
|
||||
return null;
|
||||
}
|
||||
for (const app of Applications.list) {
|
||||
if (
|
||||
(client.title && app.match(client.title)) ||
|
||||
(client.class && app.match(client.class))
|
||||
) {
|
||||
return AppButton({
|
||||
icon: app.icon_name || "",
|
||||
tooltip_text: app.name,
|
||||
on_primary_click: () => focus(client),
|
||||
on_middle_click: () => launchApp(app),
|
||||
});
|
||||
}
|
||||
}
|
||||
}),
|
||||
],
|
||||
],
|
||||
children: Hyprland.bind("clients").transform((c) =>
|
||||
c.map((client) => {
|
||||
for (const appName of options.desktop.dock.pinned_apps.value) {
|
||||
if (client.class.toLowerCase().includes(appName.toLowerCase()))
|
||||
return null;
|
||||
}
|
||||
for (const app of Applications.list) {
|
||||
if (
|
||||
(client.title && app.match(client.title)) ||
|
||||
(client.class && app.match(client.class))
|
||||
) {
|
||||
return AppButton({
|
||||
icon: app.icon_name || "",
|
||||
tooltip_text: app.name,
|
||||
on_primary_click: () => focus(client),
|
||||
on_middle_click: () => launchApp(app),
|
||||
});
|
||||
}
|
||||
}
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
const PinnedApps = () =>
|
||||
Widget.Box({
|
||||
class_name: "pins",
|
||||
homogeneous: true,
|
||||
binds: [
|
||||
[
|
||||
"children",
|
||||
options.desktop.dock.pinned_apps,
|
||||
"value",
|
||||
(v) =>
|
||||
v
|
||||
.map((term) => ({ app: Applications.query(term)?.[0], term }))
|
||||
.filter(({ app }) => app)
|
||||
.map(({ app, term = true }) =>
|
||||
AppButton({
|
||||
pinned: true,
|
||||
icon: app.icon_name || "",
|
||||
on_primary_click: () => {
|
||||
for (const client of Hyprland.clients) {
|
||||
if (client.class.toLowerCase().includes(term))
|
||||
return focus(client);
|
||||
}
|
||||
children: options.desktop.dock.pinned_apps.bind("value").transform((v) =>
|
||||
v
|
||||
.map((term) => ({ app: Applications.query(term)?.[0], term }))
|
||||
.filter(({ app }) => app)
|
||||
.map(({ app, term }) =>
|
||||
AppButton({
|
||||
pinned: true,
|
||||
icon: app.icon_name || "",
|
||||
on_primary_click: () => {
|
||||
for (const client of Hyprland.clients) {
|
||||
if (client.class.toLowerCase().includes(term))
|
||||
return focus(client);
|
||||
}
|
||||
|
||||
launchApp(app);
|
||||
},
|
||||
on_middle_click: () => launchApp(app),
|
||||
tooltip_text: app.name,
|
||||
connections: [
|
||||
[
|
||||
Hyprland,
|
||||
(button) => {
|
||||
const running = Hyprland.clients.filter((client) =>
|
||||
client.class.toLowerCase().includes(term),
|
||||
);
|
||||
launchApp(app);
|
||||
},
|
||||
on_middle_click: () => launchApp(app),
|
||||
tooltip_text: app.name,
|
||||
setup: (button) =>
|
||||
button.hook(Hyprland, () => {
|
||||
const running = Hyprland.clients.filter((client) =>
|
||||
client.class.toLowerCase().includes(term),
|
||||
);
|
||||
|
||||
const focused = running.find(
|
||||
(client) =>
|
||||
client.address === Hyprland.active.client.address,
|
||||
);
|
||||
const focused = running.find(
|
||||
(client) => client.address === Hyprland.active.client.address,
|
||||
);
|
||||
|
||||
const index = running.findIndex((c) => c === focused);
|
||||
const index = running.findIndex((c) => c === focused);
|
||||
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
const indicator = button.indicators.children[i];
|
||||
indicator.visible = i < running.length;
|
||||
indicator.toggleClassName("focused", i === index);
|
||||
}
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
const indicator = button.attribute.children[i];
|
||||
indicator.visible = i < running.length;
|
||||
indicator.toggleClassName("focused", i === index);
|
||||
}
|
||||
|
||||
button.set_tooltip_text(
|
||||
running.length === 1 ? running[0].title : app.name,
|
||||
);
|
||||
},
|
||||
],
|
||||
],
|
||||
button.set_tooltip_text(
|
||||
running.length === 1 ? running[0].title : app.name,
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
],
|
||||
}),
|
||||
),
|
||||
),
|
||||
});
|
||||
|
||||
export default () => {
|
||||
@@ -144,9 +126,14 @@ export default () => {
|
||||
vpack: "center",
|
||||
hpack: "center",
|
||||
orientation: 1,
|
||||
connections: [
|
||||
[Hyprland, (box) => (box.visible = taskbar.children.length > 0)],
|
||||
],
|
||||
setup: (self) =>
|
||||
self.hook(
|
||||
taskbar,
|
||||
() => {
|
||||
self.visible = taskbar.children.length > 0;
|
||||
},
|
||||
"notify::children",
|
||||
),
|
||||
});
|
||||
return Widget.Box({
|
||||
class_name: "dock",
|
||||
|
||||
@@ -35,20 +35,10 @@ export default (monitor) => {
|
||||
}),
|
||||
],
|
||||
}),
|
||||
connections: [
|
||||
[
|
||||
"enter-notify-event",
|
||||
() => {
|
||||
revealer.reveal_child = true;
|
||||
},
|
||||
],
|
||||
[
|
||||
"leave-notify-event",
|
||||
() => {
|
||||
revealer.reveal_child = false;
|
||||
},
|
||||
],
|
||||
],
|
||||
binds: [["visible", options.bar.position, "value", (v) => v !== "bottom"]],
|
||||
setup: (self) =>
|
||||
self
|
||||
.on("enter-notify-event", () => (revealer.reveal_child = true))
|
||||
.on("leave-notify-event", () => (revealer.reveal_child = false))
|
||||
.bind("visible", options.bar.position, "value", (v) => v !== "bottom"),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -87,6 +87,7 @@ export default {
|
||||
next: "",
|
||||
},
|
||||
ui: {
|
||||
colorpicker: "color-select-symbolic",
|
||||
close: "window-close-symbolic",
|
||||
info: "info-symbolic",
|
||||
menu: "open-menu-symbolic",
|
||||
|
||||
@@ -7,7 +7,7 @@ const PasswordEntry = () =>
|
||||
Widget.Box({
|
||||
children: [
|
||||
Widget.Entry({
|
||||
connections: [[Lockscreen, (entry) => (entry.text = ""), "lock"]],
|
||||
setup: (self) => self.hook(Lockscreen, () => (self.text = ""), "lock"),
|
||||
visibility: false,
|
||||
placeholder_text: "Password",
|
||||
on_accept: ({ text }) => Lockscreen.auth(text || ""),
|
||||
@@ -17,9 +17,12 @@ const PasswordEntry = () =>
|
||||
Widget.Spinner({
|
||||
active: true,
|
||||
vpack: "center",
|
||||
connections: [
|
||||
[Lockscreen, (w, auth) => (w.visible = auth), "authenticating"],
|
||||
],
|
||||
setup: (self) =>
|
||||
self.hook(
|
||||
Lockscreen,
|
||||
(_, auth) => (self.visible = auth),
|
||||
"authenticating",
|
||||
),
|
||||
}),
|
||||
],
|
||||
});
|
||||
@@ -32,7 +35,8 @@ export default (monitor) => {
|
||||
monitor,
|
||||
layer: "overlay",
|
||||
visible: false,
|
||||
connections: [[Lockscreen, (w, lock) => (w.visible = lock), "lock"]],
|
||||
setup: (self) =>
|
||||
self.hook(Lockscreen, (_, lock) => (self.visible = lock), "lock"),
|
||||
child: Widget.Box({
|
||||
css: "min-width: 3000px; min-height: 2000px;",
|
||||
class_name: "shader",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Applauncher from "./applauncher/Applauncher.js";
|
||||
import Dashboard from "./dashboard/Dashboard.js";
|
||||
import Desktop from "./desktop/Desktop.js";
|
||||
import FloatingDock from "./dock/FloatingDock.js";
|
||||
import Lockscreen from "./lockscreen/Lockscreen.js";
|
||||
import Notifications from "./notifications/Notifications.js";
|
||||
import OSD from "./osd/OSD.js";
|
||||
@@ -15,20 +15,19 @@ import { init } from "./settings/setup.js";
|
||||
import { forMonitors } from "./utils.js";
|
||||
import { initWallpaper } from "./settings/wallpaper.js";
|
||||
import options from "./options.js";
|
||||
import Dock from "./dock/Dock.js";
|
||||
|
||||
initWallpaper();
|
||||
|
||||
const windows = () => [
|
||||
forMonitors(Desktop),
|
||||
forMonitors(FloatingDock),
|
||||
forMonitors(Lockscreen),
|
||||
forMonitors(Notifications),
|
||||
forMonitors(OSD),
|
||||
forMonitors(ScreenCorners),
|
||||
forMonitors(TopBar),
|
||||
Applauncher(),
|
||||
|
||||
Dashboard(),
|
||||
Dock(),
|
||||
Overview(),
|
||||
PowerMenu(),
|
||||
QuickSettings(),
|
||||
@@ -39,8 +38,6 @@ const windows = () => [
|
||||
export default {
|
||||
onConfigParsed: init,
|
||||
windows: windows().flat(1),
|
||||
maxStreamVolume: 1.05,
|
||||
cacheNotificationActions: false,
|
||||
closeWindowDelay: {
|
||||
quicksettings: options.transition.value,
|
||||
dashboard: options.transition.value,
|
||||
|
||||
@@ -4,15 +4,11 @@ import Battery from "resource:///com/github/Aylur/ags/service/battery.js";
|
||||
export default () =>
|
||||
Widget.Icon({
|
||||
class_name: "battery",
|
||||
binds: [["icon", Battery, "icon-name"]],
|
||||
connections: [
|
||||
[
|
||||
Battery,
|
||||
(icon) => {
|
||||
icon.toggleClassName("charging", Battery.charging);
|
||||
icon.toggleClassName("charged", Battery.charged);
|
||||
icon.toggleClassName("low", Battery.percent < 30);
|
||||
},
|
||||
],
|
||||
],
|
||||
icon: Battery.bind("icon_name"),
|
||||
setup: (icon) =>
|
||||
icon.hook(Battery, () => {
|
||||
icon.toggleClassName("charging", Battery.charging);
|
||||
icon.toggleClassName("charged", Battery.charged);
|
||||
icon.toggleClassName("low", Battery.percent < 30);
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { clock } from "../variables.js";
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import GLib from "gi://GLib";
|
||||
|
||||
/**
|
||||
* @param {import('types/widgets/label').Props & {
|
||||
@@ -7,20 +7,11 @@ import GLib from "gi://GLib";
|
||||
* interval?: number,
|
||||
* }} o
|
||||
*/
|
||||
export default ({
|
||||
format = "%H:%M:%S %B %e. %A",
|
||||
interval = 1000,
|
||||
...rest
|
||||
} = {}) =>
|
||||
export default ({ format = "%H:%M:%S %B %e. %A", ...rest } = {}) =>
|
||||
Widget.Label({
|
||||
class_name: "clock",
|
||||
label: clock.bind("value").transform((time) => {
|
||||
return time.format(format) || "wrong format";
|
||||
}),
|
||||
...rest,
|
||||
connections: [
|
||||
[
|
||||
interval,
|
||||
(label) =>
|
||||
(label.label =
|
||||
GLib.DateTime.new_now_local().format(format) || "wrong format"),
|
||||
],
|
||||
],
|
||||
});
|
||||
|
||||
@@ -1,49 +1,48 @@
|
||||
import Gtk from "gi://Gtk";
|
||||
import { createCtor } from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import Gtk from "gi://Gtk?version=3.0";
|
||||
import { subclass, register } from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import AgsLabel from "resource:///com/github/Aylur/ags/widgets/label.js";
|
||||
import GObject from "gi://GObject";
|
||||
|
||||
export default createCtor(
|
||||
class FontIcon extends AgsLabel {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
class FontIcon extends AgsLabel {
|
||||
static {
|
||||
register(this);
|
||||
}
|
||||
|
||||
/** @param {string | import('types/widgets/label').Props & { icon?: string }} params */
|
||||
constructor(params = "") {
|
||||
// @ts-expect-error
|
||||
const { icon = "", ...rest } = params;
|
||||
/** @param {string | import('types/widgets/label').Props<any> & { icon?: string }} params */
|
||||
constructor(params = "") {
|
||||
// @ts-expect-error
|
||||
const { icon = "", ...rest } = params;
|
||||
|
||||
super(typeof params === "string" ? {} : rest);
|
||||
this.toggleClassName("font-icon");
|
||||
super(typeof params === "string" ? {} : rest);
|
||||
this.toggleClassName("font-icon");
|
||||
|
||||
if (typeof params === "object") this.icon = icon;
|
||||
if (typeof params === "object") this.icon = icon;
|
||||
|
||||
if (typeof params === "string") this.icon = params;
|
||||
}
|
||||
if (typeof params === "string") this.icon = params;
|
||||
}
|
||||
|
||||
get icon() {
|
||||
return this.label;
|
||||
}
|
||||
set icon(icon) {
|
||||
this.label = icon;
|
||||
}
|
||||
get icon() {
|
||||
return this.label;
|
||||
}
|
||||
set icon(icon) {
|
||||
this.label = icon;
|
||||
}
|
||||
|
||||
get size() {
|
||||
return this.get_style_context().get_property(
|
||||
"font-size",
|
||||
Gtk.StateFlags.NORMAL,
|
||||
);
|
||||
}
|
||||
get size() {
|
||||
return this.get_style_context().get_property(
|
||||
"font-size",
|
||||
Gtk.StateFlags.NORMAL,
|
||||
);
|
||||
}
|
||||
|
||||
/** @returns {[number, number]} */
|
||||
vfunc_get_preferred_height() {
|
||||
return [this.size, this.size];
|
||||
}
|
||||
/** @returns {[number, number]} */
|
||||
vfunc_get_preferred_height() {
|
||||
return [this.size, this.size];
|
||||
}
|
||||
|
||||
/** @returns {[number, number]} */
|
||||
vfunc_get_preferred_width() {
|
||||
return [this.size, this.size];
|
||||
}
|
||||
},
|
||||
);
|
||||
/** @returns {[number, number]} */
|
||||
vfunc_get_preferred_width() {
|
||||
return [this.size, this.size];
|
||||
}
|
||||
}
|
||||
|
||||
export default subclass(FontIcon);
|
||||
|
||||
@@ -6,8 +6,8 @@ import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
* indicator?: import('types/widgets/box').BoxProps['child']
|
||||
* direction?: 'left' | 'right' | 'down' | 'up'
|
||||
* duration?: number
|
||||
* eventboxConnections?: import('types/widgets/box').BoxProps['connections']
|
||||
* connections?: import('types/widgets/revealer').RevealerProps['connections']
|
||||
* setupRevealer?: (rev: ReturnType<typeof Widget.Revealer>) => void
|
||||
* setupEventBox?: (rev: ReturnType<typeof Widget.EventBox>) => void
|
||||
* }} HoverRevealProps
|
||||
*/
|
||||
|
||||
@@ -19,9 +19,8 @@ export default ({
|
||||
child,
|
||||
direction = "left",
|
||||
duration = 300,
|
||||
connections = [],
|
||||
eventboxConnections = [],
|
||||
binds = [],
|
||||
setupEventBox,
|
||||
setupRevealer,
|
||||
...rest
|
||||
}) => {
|
||||
let open = false;
|
||||
@@ -31,15 +30,14 @@ export default ({
|
||||
|
||||
const revealer = Widget.Revealer({
|
||||
transition: `slide_${direction}`,
|
||||
connections,
|
||||
binds,
|
||||
setup: setupRevealer,
|
||||
transition_duration: duration,
|
||||
child,
|
||||
});
|
||||
|
||||
const eventbox = Widget.EventBox({
|
||||
...rest,
|
||||
connections: eventboxConnections,
|
||||
setup: setupEventBox,
|
||||
on_hover: () => {
|
||||
if (open) return;
|
||||
|
||||
|
||||
@@ -4,23 +4,42 @@ import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import options from "../options.js";
|
||||
import GObject from "gi://GObject";
|
||||
|
||||
class PopupWindow extends AgsWindow {
|
||||
const keyGrabber = Widget.Window({
|
||||
name: "key-grabber",
|
||||
popup: true,
|
||||
anchor: ["top", "left", "right", "bottom"],
|
||||
css: "background-color: transparent;",
|
||||
visible: false,
|
||||
exclusivity: "ignore",
|
||||
keymode: "on-demand",
|
||||
layer: "top",
|
||||
attribute: { list: [] },
|
||||
setup: (self) =>
|
||||
self.on("notify::visible", ({ visible }) => {
|
||||
if (!visible)
|
||||
self.attribute?.list.forEach((name) => App.closeWindow(name));
|
||||
}),
|
||||
child: Widget.EventBox({ vexpand: true }).on("button-press-event", () => {
|
||||
App.closeWindow("key-grabber");
|
||||
keyGrabber.attribute?.list.forEach((name) => App.closeWindow(name));
|
||||
}),
|
||||
});
|
||||
|
||||
// add before any PopupWindow is instantiated
|
||||
App.addWindow(keyGrabber);
|
||||
|
||||
export class PopupWindow extends AgsWindow {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
|
||||
/** @param {import('types/widgets/window').WindowProps & {
|
||||
* name: string
|
||||
* child: import('types/widgets/box').default
|
||||
* transition?: import('types/widgets/revealer').RevealerProps['transition']
|
||||
* }} o
|
||||
*/
|
||||
constructor({ name, child, transition = "none", visible = false, ...rest }) {
|
||||
super({
|
||||
...rest,
|
||||
name,
|
||||
popup: true,
|
||||
focusable: true,
|
||||
keymode: "exclusive",
|
||||
layer: "overlay",
|
||||
class_names: ["popup-window", name],
|
||||
});
|
||||
|
||||
@@ -28,15 +47,11 @@ class PopupWindow extends AgsWindow {
|
||||
this.revealer = Widget.Revealer({
|
||||
transition,
|
||||
child,
|
||||
transitionDuration: options.transition.value,
|
||||
connections: [
|
||||
[
|
||||
App,
|
||||
(_, wname, visible) => {
|
||||
if (wname === name) this.revealer.reveal_child = visible;
|
||||
},
|
||||
],
|
||||
],
|
||||
transition_duration: options.transition.value,
|
||||
setup: (self) =>
|
||||
self.hook(App, (_, wname, visible) => {
|
||||
if (wname === name) this.revealer.reveal_child = visible;
|
||||
}),
|
||||
});
|
||||
|
||||
this.child = Widget.Box({
|
||||
@@ -46,6 +61,9 @@ class PopupWindow extends AgsWindow {
|
||||
|
||||
this.show_all();
|
||||
this.visible = visible;
|
||||
|
||||
keyGrabber.bind("visible", this, "visible");
|
||||
keyGrabber.attribute?.list.push(name);
|
||||
}
|
||||
|
||||
set transition(dir) {
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
|
||||
/** @param {import('types/widgets/box').BoxProps & {
|
||||
* width: number
|
||||
* height: number
|
||||
* }} o */
|
||||
export default ({
|
||||
height = 18,
|
||||
width = 180,
|
||||
@@ -27,31 +31,30 @@ export default ({
|
||||
min-height: ${height}px;
|
||||
`,
|
||||
children: [fill],
|
||||
setup: (progress) =>
|
||||
(progress.setValue = (value) => {
|
||||
if (value < 0) return;
|
||||
attribute: (value) => {
|
||||
if (value < 0) return;
|
||||
|
||||
const axis = vertical ? "height" : "width";
|
||||
const axisv = vertical ? height : width;
|
||||
const min = vertical ? width : height;
|
||||
const preferred = (axisv - min) * value + min;
|
||||
const axis = vertical ? "height" : "width";
|
||||
const axisv = vertical ? height : width;
|
||||
const min = vertical ? width : height;
|
||||
const preferred = (axisv - min) * value + min;
|
||||
|
||||
if (!fill_size) {
|
||||
fill_size = preferred;
|
||||
fill.setCss(`min-${axis}: ${preferred}px;`);
|
||||
return;
|
||||
}
|
||||
if (!fill_size) {
|
||||
fill_size = preferred;
|
||||
fill.setCss(`min-${axis}: ${preferred}px;`);
|
||||
return;
|
||||
}
|
||||
|
||||
const frames = 10;
|
||||
const goal = preferred - fill_size;
|
||||
const step = goal / frames;
|
||||
const frames = 10;
|
||||
const goal = preferred - fill_size;
|
||||
const step = goal / frames;
|
||||
|
||||
for (let i = 0; i < frames; ++i) {
|
||||
Utils.timeout(5 * i, () => {
|
||||
fill_size += step;
|
||||
fill.setCss(`min-${axis}: ${fill_size}px`);
|
||||
});
|
||||
}
|
||||
}),
|
||||
for (let i = 0; i < frames; ++i) {
|
||||
Utils.timeout(5 * i, () => {
|
||||
fill_size += step;
|
||||
fill.setCss(`min-${axis}: ${fill_size}px`);
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,19 +1,4 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import { subclass } from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import Gtk from "gi://Gtk";
|
||||
import AgsWidget from "resource:///com/github/Aylur/ags/widgets/widget.js";
|
||||
|
||||
class RegularWindow extends AgsWidget(Gtk.Window, "RegularWindow") {
|
||||
static {
|
||||
AgsWidget.register(this);
|
||||
}
|
||||
/**
|
||||
* @param {import('types/widgets/widget').BaseProps<
|
||||
* RegularWindow, Gtk.Window.ConstructorProperties
|
||||
* >} params */
|
||||
constructor(params) {
|
||||
// @ts-expect-error
|
||||
super(params);
|
||||
}
|
||||
}
|
||||
|
||||
export default Widget.createCtor(RegularWindow);
|
||||
export default subclass(Gtk.Window, "RegularWindow");
|
||||
|
||||
@@ -11,14 +11,9 @@ export const CoverArt = (player, props) =>
|
||||
Widget.Box({
|
||||
...props,
|
||||
class_name: "cover",
|
||||
binds: [
|
||||
[
|
||||
"css",
|
||||
player,
|
||||
"cover-path",
|
||||
(path) => `background-image: url("${path}")`,
|
||||
],
|
||||
],
|
||||
css: player
|
||||
.bind("cover_path")
|
||||
.transform((p) => `background-image: url("${p}")`),
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -29,16 +24,15 @@ export const BlurredCoverArt = (player, props) =>
|
||||
Widget.Box({
|
||||
...props,
|
||||
class_name: "blurred-cover",
|
||||
connections: [
|
||||
[
|
||||
setup: (self) =>
|
||||
self.hook(
|
||||
player,
|
||||
(box) =>
|
||||
blurImg(player.cover_path).then((img) => {
|
||||
img && box.setCss(`background-image: url("${img}")`);
|
||||
}),
|
||||
"notify::cover-path",
|
||||
],
|
||||
],
|
||||
),
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -49,7 +43,7 @@ export const TitleLabel = (player, props) =>
|
||||
Widget.Label({
|
||||
...props,
|
||||
class_name: "title",
|
||||
binds: [["label", player, "track-title"]],
|
||||
label: player.bind("track_title"),
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -60,7 +54,7 @@ export const ArtistLabel = (player, props) =>
|
||||
Widget.Label({
|
||||
...props,
|
||||
class_name: "artist",
|
||||
binds: [["label", player, "track-artists", (a) => a.join(", ") || ""]],
|
||||
label: player.bind("track_artists").transform((a) => a.join(", ") || ""),
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -72,17 +66,13 @@ export const PlayerIcon = (player, { symbolic = true, ...props } = {}) =>
|
||||
...props,
|
||||
class_name: "player-icon",
|
||||
tooltip_text: player.identity || "",
|
||||
connections: [
|
||||
[
|
||||
player,
|
||||
(icon) => {
|
||||
const name = `${player.entry}${symbolic ? "-symbolic" : ""}`;
|
||||
Utils.lookUpIcon(name)
|
||||
? (icon.icon = name)
|
||||
: (icon.icon = icons.mpris.fallback);
|
||||
},
|
||||
],
|
||||
],
|
||||
setup: (self) =>
|
||||
self.hook(player, (icon) => {
|
||||
const name = `${player.entry}${symbolic ? "-symbolic" : ""}`;
|
||||
Utils.lookUpIcon(name)
|
||||
? (icon.icon = name)
|
||||
: (icon.icon = icons.mpris.fallback);
|
||||
}),
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -94,25 +84,18 @@ export const PositionSlider = (player, props) =>
|
||||
...props,
|
||||
class_name: "position-slider",
|
||||
draw_value: false,
|
||||
on_change: ({ value }) => {
|
||||
player.position = player.length * value;
|
||||
},
|
||||
properties: [
|
||||
[
|
||||
"update",
|
||||
(slider) => {
|
||||
if (slider.dragging) return;
|
||||
on_change: ({ value }) => (player.position = player.length * value),
|
||||
setup: (self) => {
|
||||
const update = () => {
|
||||
if (self.dragging) return;
|
||||
|
||||
slider.visible = player.length > 0;
|
||||
if (player.length > 0) slider.value = player.position / player.length;
|
||||
},
|
||||
],
|
||||
],
|
||||
connections: [
|
||||
[player, (s) => s._update(s)],
|
||||
[player, (s) => s._update(s), "position"],
|
||||
[1000, (s) => s._update(s)],
|
||||
],
|
||||
self.visible = player.length > 0;
|
||||
if (player.length > 0) self.value = player.position / player.length;
|
||||
};
|
||||
self.hook(player, update);
|
||||
self.hook(player, update, "position");
|
||||
self.poll(1000, update);
|
||||
},
|
||||
});
|
||||
|
||||
/** @param {number} length */
|
||||
@@ -126,90 +109,73 @@ function lengthStr(length) {
|
||||
/** @param {import('types/service/mpris').MprisPlayer} player */
|
||||
export const PositionLabel = (player) =>
|
||||
Widget.Label({
|
||||
properties: [
|
||||
[
|
||||
"update",
|
||||
(label, time) => {
|
||||
player.length > 0
|
||||
? (label.label = lengthStr(time || player.position))
|
||||
: (label.visible = !!player);
|
||||
},
|
||||
],
|
||||
],
|
||||
connections: [
|
||||
[player, (l, time) => l._update(l, time), "position"],
|
||||
[1000, (l) => l._update(l)],
|
||||
],
|
||||
setup: (self) => {
|
||||
const update = (_, time) => {
|
||||
player.length > 0
|
||||
? (self.label = lengthStr(time || player.position))
|
||||
: (self.visible = !!player);
|
||||
};
|
||||
self.hook(player, update, "position");
|
||||
self.poll(1000, update);
|
||||
},
|
||||
});
|
||||
|
||||
/** @param {import('types/service/mpris').MprisPlayer} player */
|
||||
export const LengthLabel = (player) =>
|
||||
Widget.Label({
|
||||
connections: [
|
||||
[
|
||||
player,
|
||||
(label) => {
|
||||
player.length > 0
|
||||
? (label.label = lengthStr(player.length))
|
||||
: (label.visible = !!player);
|
||||
},
|
||||
],
|
||||
],
|
||||
label: player.bind("length").transform((l) => lengthStr(l)),
|
||||
visible: player.bind("length").transform((l) => l > 0),
|
||||
});
|
||||
|
||||
/** @param {import('types/service/mpris').MprisPlayer} player */
|
||||
export const Slash = (player) =>
|
||||
Widget.Label({
|
||||
label: "/",
|
||||
connections: [
|
||||
[
|
||||
player,
|
||||
(label) => {
|
||||
label.visible = player.length > 0;
|
||||
},
|
||||
],
|
||||
],
|
||||
visible: player.bind("length").transform((l) => l > 0),
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {Object} o
|
||||
* @param {import('types/service/mpris').MprisPlayer} o.player
|
||||
* @param {import('types/widgets/stack').StackProps['items']} o.items
|
||||
* @param {import('types/widgets/stack').StackProps['children']} o.children
|
||||
* @param {'shuffle' | 'loop' | 'playPause' | 'previous' | 'next'} o.onClick
|
||||
* @param {string} o.prop
|
||||
* @param {string} o.canProp
|
||||
* @param {any} o.cantValue
|
||||
*/
|
||||
const PlayerButton = ({ player, items, onClick, prop, canProp, cantValue }) =>
|
||||
const PlayerButton = ({
|
||||
player,
|
||||
children,
|
||||
onClick,
|
||||
prop,
|
||||
canProp,
|
||||
cantValue,
|
||||
}) =>
|
||||
Widget.Button({
|
||||
child: Widget.Stack({
|
||||
items,
|
||||
binds: [["shown", player, prop, (p) => `${p}`]],
|
||||
}),
|
||||
on_clicked: player[onClick].bind(player),
|
||||
binds: [["visible", player, canProp, (c) => c !== cantValue]],
|
||||
child: Widget.Stack({ children }).bind(
|
||||
"shown",
|
||||
player,
|
||||
prop,
|
||||
(p) => `${p}`,
|
||||
),
|
||||
on_clicked: () => player[onClick](),
|
||||
visible: player.bind(canProp).transform((c) => c !== cantValue),
|
||||
});
|
||||
|
||||
/** @param {import('types/service/mpris').MprisPlayer} player */
|
||||
export const ShuffleButton = (player) =>
|
||||
PlayerButton({
|
||||
player,
|
||||
items: [
|
||||
[
|
||||
"true",
|
||||
Widget.Label({
|
||||
class_name: "shuffle enabled",
|
||||
label: icons.mpris.shuffle.enabled,
|
||||
}),
|
||||
],
|
||||
[
|
||||
"false",
|
||||
Widget.Label({
|
||||
class_name: "shuffle disabled",
|
||||
label: icons.mpris.shuffle.disabled,
|
||||
}),
|
||||
],
|
||||
],
|
||||
children: {
|
||||
true: Widget.Label({
|
||||
class_name: "shuffle enabled",
|
||||
label: icons.mpris.shuffle.enabled,
|
||||
}),
|
||||
false: Widget.Label({
|
||||
class_name: "shuffle disabled",
|
||||
label: icons.mpris.shuffle.disabled,
|
||||
}),
|
||||
},
|
||||
onClick: "shuffle",
|
||||
prop: "shuffle-status",
|
||||
canProp: "shuffle-status",
|
||||
@@ -220,29 +186,20 @@ export const ShuffleButton = (player) =>
|
||||
export const LoopButton = (player) =>
|
||||
PlayerButton({
|
||||
player,
|
||||
items: [
|
||||
[
|
||||
"None",
|
||||
Widget.Label({
|
||||
class_name: "loop none",
|
||||
label: icons.mpris.loop.none,
|
||||
}),
|
||||
],
|
||||
[
|
||||
"Track",
|
||||
Widget.Label({
|
||||
class_name: "loop track",
|
||||
label: icons.mpris.loop.track,
|
||||
}),
|
||||
],
|
||||
[
|
||||
"Playlist",
|
||||
Widget.Label({
|
||||
class_name: "loop playlist",
|
||||
label: icons.mpris.loop.playlist,
|
||||
}),
|
||||
],
|
||||
],
|
||||
children: {
|
||||
None: Widget.Label({
|
||||
class_name: "loop none",
|
||||
label: icons.mpris.loop.none,
|
||||
}),
|
||||
Track: Widget.Label({
|
||||
class_name: "loop track",
|
||||
label: icons.mpris.loop.track,
|
||||
}),
|
||||
Playlist: Widget.Label({
|
||||
class_name: "loop playlist",
|
||||
label: icons.mpris.loop.playlist,
|
||||
}),
|
||||
},
|
||||
onClick: "loop",
|
||||
prop: "loop-status",
|
||||
canProp: "loop-status",
|
||||
@@ -253,29 +210,20 @@ export const LoopButton = (player) =>
|
||||
export const PlayPauseButton = (player) =>
|
||||
PlayerButton({
|
||||
player,
|
||||
items: [
|
||||
[
|
||||
"Playing",
|
||||
Widget.Label({
|
||||
class_name: "playing",
|
||||
label: icons.mpris.playing,
|
||||
}),
|
||||
],
|
||||
[
|
||||
"Paused",
|
||||
Widget.Label({
|
||||
class_name: "paused",
|
||||
label: icons.mpris.paused,
|
||||
}),
|
||||
],
|
||||
[
|
||||
"Stopped",
|
||||
Widget.Label({
|
||||
class_name: "stopped",
|
||||
label: icons.mpris.stopped,
|
||||
}),
|
||||
],
|
||||
],
|
||||
children: {
|
||||
Playing: Widget.Label({
|
||||
class_name: "playing",
|
||||
label: icons.mpris.playing,
|
||||
}),
|
||||
Paused: Widget.Label({
|
||||
class_name: "paused",
|
||||
label: icons.mpris.paused,
|
||||
}),
|
||||
Stopped: Widget.Label({
|
||||
class_name: "stopped",
|
||||
label: icons.mpris.stopped,
|
||||
}),
|
||||
},
|
||||
onClick: "playPause",
|
||||
prop: "play-back-status",
|
||||
canProp: "can-play",
|
||||
@@ -286,15 +234,12 @@ export const PlayPauseButton = (player) =>
|
||||
export const PreviousButton = (player) =>
|
||||
PlayerButton({
|
||||
player,
|
||||
items: [
|
||||
[
|
||||
"true",
|
||||
Widget.Label({
|
||||
class_name: "previous",
|
||||
label: icons.mpris.prev,
|
||||
}),
|
||||
],
|
||||
],
|
||||
children: {
|
||||
true: Widget.Label({
|
||||
class_name: "previous",
|
||||
label: icons.mpris.prev,
|
||||
}),
|
||||
},
|
||||
onClick: "previous",
|
||||
prop: "can-go-prev",
|
||||
canProp: "can-go-prev",
|
||||
@@ -305,15 +250,12 @@ export const PreviousButton = (player) =>
|
||||
export const NextButton = (player) =>
|
||||
PlayerButton({
|
||||
player,
|
||||
items: [
|
||||
[
|
||||
"true",
|
||||
Widget.Label({
|
||||
class_name: "next",
|
||||
label: icons.mpris.next,
|
||||
}),
|
||||
],
|
||||
],
|
||||
children: {
|
||||
true: Widget.Label({
|
||||
class_name: "next",
|
||||
label: icons.mpris.next,
|
||||
}),
|
||||
},
|
||||
onClick: "next",
|
||||
prop: "can-go-next",
|
||||
canProp: "can-go-next",
|
||||
|
||||
@@ -39,14 +39,10 @@ const Popups = (parent) => {
|
||||
});
|
||||
};
|
||||
|
||||
return Widget.Box({
|
||||
vertical: true,
|
||||
connections: [
|
||||
[Notifications, onNotified, "notified"],
|
||||
[Notifications, onDismissed, "dismissed"],
|
||||
[Notifications, (box, id) => onDismissed(box, id, true), "closed"],
|
||||
],
|
||||
});
|
||||
return Widget.Box({ vertical: true })
|
||||
.hook(Notifications, onNotified, "notified")
|
||||
.hook(Notifications, onDismissed, "dismissed")
|
||||
.hook(Notifications, (box, id) => onDismissed(box, id, true), "closed");
|
||||
};
|
||||
|
||||
/** @param {import('types/widgets/revealer').RevealerProps['transition']} transition */
|
||||
@@ -67,6 +63,6 @@ export default (monitor) =>
|
||||
monitor,
|
||||
name: `notifications${monitor}`,
|
||||
class_name: "notifications",
|
||||
binds: [["anchor", options.notifications.position]],
|
||||
anchor: options.notifications.position.bind("value"),
|
||||
child: PopupList(),
|
||||
});
|
||||
|
||||
@@ -10,56 +10,39 @@ export const OnScreenIndicator = ({ height = 300, width = 48 } = {}) =>
|
||||
css: "padding: 1px;",
|
||||
child: Widget.Revealer({
|
||||
transition: "slide_left",
|
||||
connections: [
|
||||
[
|
||||
Indicator,
|
||||
(revealer, value) => {
|
||||
revealer.reveal_child = value > -1;
|
||||
},
|
||||
],
|
||||
],
|
||||
setup: (self) =>
|
||||
self.hook(Indicator, (_, value) => {
|
||||
self.reveal_child = value > -1;
|
||||
}),
|
||||
child: Progress({
|
||||
width,
|
||||
height,
|
||||
vertical: true,
|
||||
connections: [
|
||||
[Indicator, (progress, value) => progress.setValue(value)],
|
||||
],
|
||||
setup: (self) =>
|
||||
self.hook(Indicator, (_, value) => self.attribute(value)),
|
||||
child: Widget.Stack({
|
||||
vpack: "start",
|
||||
hpack: "center",
|
||||
hexpand: false,
|
||||
items: [
|
||||
[
|
||||
"true",
|
||||
Widget.Icon({
|
||||
hpack: "center",
|
||||
size: width,
|
||||
connections: [
|
||||
[Indicator, (icon, _v, name) => (icon.icon = name || "")],
|
||||
],
|
||||
}),
|
||||
],
|
||||
[
|
||||
"false",
|
||||
FontIcon({
|
||||
hpack: "center",
|
||||
hexpand: true,
|
||||
css: `font-size: ${width}px;`,
|
||||
connections: [
|
||||
[Indicator, (icon, _v, name) => (icon.icon = name || "")],
|
||||
],
|
||||
}),
|
||||
],
|
||||
],
|
||||
connections: [
|
||||
[
|
||||
Indicator,
|
||||
(stack, _v, name) => {
|
||||
stack.shown = `${!!Utils.lookUpIcon(name)}`;
|
||||
},
|
||||
],
|
||||
],
|
||||
children: {
|
||||
true: Widget.Icon({
|
||||
hpack: "center",
|
||||
size: width,
|
||||
setup: (w) =>
|
||||
w.hook(Indicator, (_, _v, name) => (w.icon = name || "")),
|
||||
}),
|
||||
false: FontIcon({
|
||||
hpack: "center",
|
||||
hexpand: true,
|
||||
css: `font-size: ${width}px;`,
|
||||
setup: (w) =>
|
||||
w.hook(Indicator, (_, _v, name) => (w.label = name || "")),
|
||||
}),
|
||||
},
|
||||
setup: (self) =>
|
||||
self.hook(Indicator, (_, _v, name) => {
|
||||
self.shown = `${!!Utils.lookUpIcon(name)}`;
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
||||
@@ -2,9 +2,10 @@ import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
import { createSurfaceFromWidget, substitute } from "../utils.js";
|
||||
import Gdk from "gi://Gdk";
|
||||
import Gtk from "gi://Gtk";
|
||||
import Gtk from "gi://Gtk?version=3.0";
|
||||
import options from "../options.js";
|
||||
import Hyprland from "resource:///com/github/Aylur/ags/service/hyprland.js";
|
||||
import icons from "../icons.js";
|
||||
|
||||
const SCALE = 0.08;
|
||||
const TARGET = [Gtk.TargetEntry.new("text/plain", Gtk.TargetFlags.SAME_APP, 0)];
|
||||
@@ -13,7 +14,13 @@ const TARGET = [Gtk.TargetEntry.new("text/plain", Gtk.TargetFlags.SAME_APP, 0)];
|
||||
const dispatch = (args) => Hyprland.sendMessage(`dispatch ${args}`);
|
||||
|
||||
/** @param {string} str */
|
||||
const icon = (str) => substitute(options.substitutions.icons, str);
|
||||
const icon = (str) => {
|
||||
const icon = substitute(options.substitutions.icons, str);
|
||||
if (Utils.lookUpIcon(icon)) return icon;
|
||||
|
||||
console.warn("no icon", icon);
|
||||
return icons.fallback.executable;
|
||||
};
|
||||
|
||||
export default ({ address, size: [w, h], class: c, title }) =>
|
||||
Widget.Button({
|
||||
@@ -32,19 +39,19 @@ export default ({ address, size: [w, h], class: c, title }) =>
|
||||
App.closeWindow("overview"),
|
||||
),
|
||||
|
||||
setup: (btn) => {
|
||||
btn.drag_source_set(
|
||||
Gdk.ModifierType.BUTTON1_MASK,
|
||||
TARGET,
|
||||
Gdk.DragAction.COPY,
|
||||
);
|
||||
btn.connect("drag-data-get", (_w, _c, data) =>
|
||||
data.set_text(address, address.length),
|
||||
);
|
||||
btn.connect("drag-begin", (_, context) => {
|
||||
Gtk.drag_set_icon_surface(context, createSurfaceFromWidget(btn));
|
||||
btn.toggleClassName("hidden", true);
|
||||
});
|
||||
btn.connect("drag-end", () => btn.toggleClassName("hidden", false));
|
||||
},
|
||||
setup: (btn) =>
|
||||
btn
|
||||
.on("drag-data-get", (_w, _c, data) =>
|
||||
data.set_text(address, address.length),
|
||||
)
|
||||
.on("drag-begin", (_, context) => {
|
||||
Gtk.drag_set_icon_surface(context, createSurfaceFromWidget(btn));
|
||||
btn.toggleClassName("hidden", true);
|
||||
})
|
||||
.on("drag-end", () => btn.toggleClassName("hidden", false))
|
||||
.drag_source_set(
|
||||
Gdk.ModifierType.BUTTON1_MASK,
|
||||
TARGET,
|
||||
Gdk.DragAction.COPY,
|
||||
),
|
||||
});
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
import Hyprland from "resource:///com/github/Aylur/ags/service/hyprland.js";
|
||||
import PopupWindow from "../misc/PopupWindow.js";
|
||||
import Workspace from "./Workspace.js";
|
||||
@@ -8,15 +6,30 @@ import { range } from "../utils.js";
|
||||
|
||||
const ws = options.workspaces;
|
||||
|
||||
/** @param {import('types/widgets/box').default} box */
|
||||
const Overview = () =>
|
||||
Widget.Box({
|
||||
children: [Workspace(0)], // for type infer
|
||||
setup: (self) =>
|
||||
Utils.idle(() => {
|
||||
self.hook(ws, () => {
|
||||
self.children = range(ws.value).map(Workspace);
|
||||
update(self);
|
||||
children(self);
|
||||
});
|
||||
self.hook(Hyprland, update);
|
||||
self.hook(Hyprland, children, "notify::workspaces");
|
||||
update(self);
|
||||
children(self);
|
||||
}),
|
||||
});
|
||||
|
||||
/** @param {ReturnType<typeof Overview>} box */
|
||||
const update = (box) => {
|
||||
if (App.windows.has("overview") && !App.getWindow("overview")?.visible)
|
||||
return;
|
||||
if (!box.get_parent()?.visible) return;
|
||||
|
||||
Hyprland.sendMessage("j/clients")
|
||||
.then((clients) => {
|
||||
box.children.forEach((ws) => {
|
||||
// @ts-expect-error
|
||||
ws.attribute(JSON.parse(clients));
|
||||
});
|
||||
})
|
||||
@@ -35,19 +48,5 @@ const children = (box) => {
|
||||
export default () =>
|
||||
PopupWindow({
|
||||
name: "overview",
|
||||
child: Widget.Box({
|
||||
setup: update,
|
||||
connections: [
|
||||
[
|
||||
ws,
|
||||
(box) => {
|
||||
box.children = range(ws.value).map(Workspace);
|
||||
update(box);
|
||||
children(box);
|
||||
},
|
||||
],
|
||||
[Hyprland, update],
|
||||
[Hyprland, children, "notify::workspaces"],
|
||||
],
|
||||
}),
|
||||
child: Overview(),
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import Hyprland from "resource:///com/github/Aylur/ags/service/hyprland.js";
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import Gdk from "gi://Gdk";
|
||||
import Gtk from "gi://Gtk";
|
||||
import Gtk from "gi://Gtk?version=3.0";
|
||||
import Client from "./Client.js";
|
||||
|
||||
const SCALE = 0.08;
|
||||
@@ -22,14 +22,10 @@ export default (index) => {
|
||||
min-width: ${3840 * SCALE}px;
|
||||
min-height: ${2160 * SCALE}px;
|
||||
`,
|
||||
connections: [
|
||||
[
|
||||
Hyprland,
|
||||
(box) => {
|
||||
box.toggleClassName("active", Hyprland.active.workspace.id === index);
|
||||
},
|
||||
],
|
||||
],
|
||||
setup: (box) =>
|
||||
box.hook(Hyprland, () => {
|
||||
box.toggleClassName("active", Hyprland.active.workspace.id === index);
|
||||
}),
|
||||
child: Widget.EventBox({
|
||||
hexpand: true,
|
||||
vexpand: true,
|
||||
|
||||
@@ -7,11 +7,13 @@ const Padding = (windowName) =>
|
||||
class_name: "padding",
|
||||
hexpand: true,
|
||||
vexpand: true,
|
||||
connections: [["button-press-event", () => App.toggleWindow(windowName)]],
|
||||
setup: (w) =>
|
||||
w.on("button-press-event", () => App.toggleWindow(windowName)),
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {import('types/widgets/window').WindowProps & {
|
||||
* @template {import('gi://Gtk?version=3.0').default.Widget} T
|
||||
* @param {import('types/widgets/window').WindowProps<T> & {
|
||||
* name: string
|
||||
* child: import('types/widgets/box').default
|
||||
* }} o
|
||||
@@ -23,20 +25,20 @@ export default ({ name, child, ...rest }) =>
|
||||
name,
|
||||
visible: false,
|
||||
popup: true,
|
||||
focusable: true,
|
||||
keymode: "on-demand",
|
||||
setup() {
|
||||
child.toggleClassName("window-content");
|
||||
},
|
||||
child: Widget.CenterBox({
|
||||
class_name: "shader",
|
||||
css: "min-width: 5000px; min-height: 3000px;",
|
||||
children: [
|
||||
Padding(name),
|
||||
Widget.CenterBox({
|
||||
vertical: true,
|
||||
children: [Padding(name), child, Padding(name)],
|
||||
}),
|
||||
Padding(name),
|
||||
],
|
||||
start_widget: Padding(name),
|
||||
end_widget: Padding(name),
|
||||
center_widget: Widget.CenterBox({
|
||||
vertical: true,
|
||||
start_widget: Padding(name),
|
||||
end_widget: Padding(name),
|
||||
center_widget: child,
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -17,7 +17,7 @@ export default () =>
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: "title",
|
||||
binds: [["label", PowerMenu, "title"]],
|
||||
label: PowerMenu.bind("title"),
|
||||
}),
|
||||
Widget.Label({
|
||||
class_name: "desc",
|
||||
|
||||
@@ -33,19 +33,15 @@ const Homogeneous = (toggles) =>
|
||||
export default () =>
|
||||
PopupWindow({
|
||||
name: "quicksettings",
|
||||
connections: [
|
||||
[
|
||||
options.bar.position,
|
||||
(self) => {
|
||||
self.anchor = ["right", options.bar.position.value];
|
||||
if (options.bar.position.value === "top")
|
||||
self.transition = "slide_down";
|
||||
setup: (self) =>
|
||||
self.hook(options.bar.position, () => {
|
||||
self.anchor = ["right", options.bar.position.value];
|
||||
if (options.bar.position.value === "top")
|
||||
self.transition = "slide_down";
|
||||
|
||||
if (options.bar.position.value === "bottom")
|
||||
self.transition = "slide_up";
|
||||
},
|
||||
],
|
||||
],
|
||||
if (options.bar.position.value === "bottom")
|
||||
self.transition = "slide_up";
|
||||
}),
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
children: [
|
||||
@@ -60,12 +56,8 @@ export default () =>
|
||||
],
|
||||
}),
|
||||
Row(
|
||||
[Homogeneous([/*NetworkToggle(),*/ BluetoothToggle()]), DND()],
|
||||
[/*WifiSelection()*/ BluetoothDevices()],
|
||||
),
|
||||
Row(
|
||||
[Homogeneous([/*ProfileToggle(),*/ ThemeToggle()]), MicMute()],
|
||||
[/*ProfileSelector(),*/ ThemeSelector()],
|
||||
[Homogeneous([ThemeToggle(), BluetoothToggle()]), MicMute()],
|
||||
[ThemeSelector(), BluetoothDevices()],
|
||||
),
|
||||
Media(),
|
||||
],
|
||||
|
||||
@@ -18,30 +18,23 @@ App.connect("window-toggled", (_, name, visible) => {
|
||||
export const Arrow = (name, activate) => {
|
||||
let deg = 0;
|
||||
let iconOpened = false;
|
||||
const icon = Widget.Icon(icons.ui.arrow.right).hook(opened, () => {
|
||||
if (
|
||||
(opened.value === name && !iconOpened) ||
|
||||
(opened.value !== name && iconOpened)
|
||||
) {
|
||||
const step = opened.value === name ? 10 : -10;
|
||||
iconOpened = !iconOpened;
|
||||
for (let i = 0; i < 9; ++i) {
|
||||
Utils.timeout(15 * i, () => {
|
||||
deg += step;
|
||||
icon.setCss(`-gtk-icon-transform: rotate(${deg}deg);`);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
return Widget.Button({
|
||||
child: Widget.Icon({
|
||||
icon: icons.ui.arrow.right,
|
||||
connections: [
|
||||
[
|
||||
opened,
|
||||
(icon) => {
|
||||
if (
|
||||
(opened.value === name && !iconOpened) ||
|
||||
(opened.value !== name && iconOpened)
|
||||
) {
|
||||
const step = opened.value === name ? 10 : -10;
|
||||
iconOpened = !iconOpened;
|
||||
for (let i = 0; i < 9; ++i) {
|
||||
Utils.timeout(15 * i, () => {
|
||||
deg += step;
|
||||
icon.setCss(`-gtk-icon-transform: rotate(${deg}deg);`);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
],
|
||||
],
|
||||
}),
|
||||
child: icon,
|
||||
on_clicked: () => {
|
||||
opened.value = opened.value === name ? "" : name;
|
||||
if (typeof activate === "function") activate();
|
||||
@@ -70,14 +63,10 @@ export const ArrowToggleButton = ({
|
||||
}) =>
|
||||
Widget.Box({
|
||||
class_name: "toggle-button",
|
||||
connections: [
|
||||
[
|
||||
service,
|
||||
(box) => {
|
||||
box.toggleClassName("active", condition());
|
||||
},
|
||||
],
|
||||
],
|
||||
setup: (self) =>
|
||||
self.hook(service, () => {
|
||||
self.toggleClassName("active", condition());
|
||||
}),
|
||||
children: [
|
||||
Widget.Button({
|
||||
child: Widget.Box({
|
||||
@@ -108,7 +97,7 @@ export const ArrowToggleButton = ({
|
||||
export const Menu = ({ name, icon, title, content }) =>
|
||||
Widget.Revealer({
|
||||
transition: "slide_down",
|
||||
binds: [["reveal-child", opened, "value", (v) => v === name]],
|
||||
reveal_child: opened.bind().transform((v) => v === name),
|
||||
child: Widget.Box({
|
||||
class_names: ["menu", name],
|
||||
vertical: true,
|
||||
@@ -136,14 +125,10 @@ export const SimpleToggleButton = ({
|
||||
}) =>
|
||||
Widget.Button({
|
||||
class_name: "simple-toggle",
|
||||
connections: [
|
||||
[
|
||||
service,
|
||||
(box) => {
|
||||
box.toggleClassName("active", condition());
|
||||
},
|
||||
],
|
||||
],
|
||||
setup: (self) =>
|
||||
self.hook(service, () => {
|
||||
self.toggleClassName("active", condition());
|
||||
}),
|
||||
child: icon,
|
||||
on_clicked: toggle,
|
||||
});
|
||||
|
||||
@@ -8,10 +8,10 @@ export const ProfileToggle = () =>
|
||||
ArrowToggleButton({
|
||||
name: "asusctl-profile",
|
||||
icon: Widget.Icon({
|
||||
binds: [["icon", Asusctl, "profile", (p) => icons.asusctl.profile[p]]],
|
||||
icon: Asusctl.bind("profile").transform((p) => icons.asusctl.profile[p]),
|
||||
}),
|
||||
label: Widget.Label({
|
||||
binds: [["label", Asusctl, "profile"]],
|
||||
label: Asusctl.bind("profile"),
|
||||
}),
|
||||
connection: [Asusctl, () => Asusctl.profile !== "Balanced"],
|
||||
activate: () => Asusctl.setProfile("Quiet"),
|
||||
@@ -23,7 +23,7 @@ export const ProfileSelector = () =>
|
||||
Menu({
|
||||
name: "asusctl-profile",
|
||||
icon: Widget.Icon({
|
||||
binds: [["icon", Asusctl, "profile", (p) => icons.asusctl.profile[p]]],
|
||||
icon: Asusctl.bind("profile").transform((p) => icons.asusctl.profile[p]),
|
||||
}),
|
||||
title: Widget.Label("Profile Selector"),
|
||||
content: [
|
||||
|
||||
@@ -7,41 +7,31 @@ export const BluetoothToggle = () =>
|
||||
ArrowToggleButton({
|
||||
name: "bluetooth",
|
||||
icon: Widget.Icon({
|
||||
connections: [
|
||||
[
|
||||
Bluetooth,
|
||||
(icon) => {
|
||||
icon.icon = Bluetooth.enabled
|
||||
? icons.bluetooth.enabled
|
||||
: icons.bluetooth.disabled;
|
||||
},
|
||||
],
|
||||
],
|
||||
icon: Bluetooth.bind("enabled").transform(
|
||||
(p) => icons.bluetooth[p ? "enabled" : "disabled"],
|
||||
),
|
||||
}),
|
||||
label: Widget.Label({
|
||||
truncate: "end",
|
||||
connections: [
|
||||
[
|
||||
Bluetooth,
|
||||
(label) => {
|
||||
if (!Bluetooth.enabled) return (label.label = "Disabled");
|
||||
setup: (self) =>
|
||||
self.hook(Bluetooth, () => {
|
||||
if (!Bluetooth.enabled) return (self.label = "Disabled");
|
||||
|
||||
if (Bluetooth.connectedDevices.length === 0)
|
||||
return (label.label = "Not Connected");
|
||||
if (Bluetooth.connected_devices.length === 0)
|
||||
return (self.label = "Not Connected");
|
||||
|
||||
if (Bluetooth.connectedDevices.length === 1)
|
||||
return (label.label = Bluetooth.connectedDevices[0].alias);
|
||||
if (Bluetooth.connected_devices.length === 1)
|
||||
return (self.label = Bluetooth.connected_devices[0].alias);
|
||||
|
||||
label.label = `${Bluetooth.connectedDevices.length} Connected`;
|
||||
},
|
||||
],
|
||||
],
|
||||
self.label = `${Bluetooth.connected_devices.length} Connected`;
|
||||
}),
|
||||
}),
|
||||
connection: [Bluetooth, () => Bluetooth.enabled],
|
||||
deactivate: () => (Bluetooth.enabled = false),
|
||||
activate: () => (Bluetooth.enabled = true),
|
||||
});
|
||||
|
||||
/** @param {import('types/service/bluetooth').BluetoothDevice} device */
|
||||
const DeviceItem = (device) =>
|
||||
Widget.Box({
|
||||
children: [
|
||||
@@ -49,26 +39,20 @@ const DeviceItem = (device) =>
|
||||
Widget.Label(device.name),
|
||||
Widget.Label({
|
||||
label: `${device.battery_percentage}%`,
|
||||
binds: [["visible", device, "battery-percentage", (p) => p > 0]],
|
||||
visible: device.bind("battery_percentage").transform((p) => p > 0),
|
||||
}),
|
||||
Widget.Box({ hexpand: true }),
|
||||
Widget.Spinner({
|
||||
binds: [
|
||||
["active", device, "connecting"],
|
||||
["visible", device, "connecting"],
|
||||
],
|
||||
active: device.bind("connecting"),
|
||||
visible: device.bind("connecting"),
|
||||
}),
|
||||
Widget.Switch({
|
||||
active: device.connected,
|
||||
binds: [["visible", device, "connecting", (c) => !c]],
|
||||
connections: [
|
||||
[
|
||||
"notify::active",
|
||||
({ active }) => {
|
||||
device.setConnection(active);
|
||||
},
|
||||
],
|
||||
],
|
||||
visible: device.bind("connecting").transform((p) => !p),
|
||||
setup: (self) =>
|
||||
self.on("notify::active", () => {
|
||||
device.setConnection(self.active);
|
||||
}),
|
||||
}),
|
||||
],
|
||||
});
|
||||
@@ -82,14 +66,9 @@ export const BluetoothDevices = () =>
|
||||
Widget.Box({
|
||||
hexpand: true,
|
||||
vertical: true,
|
||||
binds: [
|
||||
[
|
||||
"children",
|
||||
Bluetooth,
|
||||
"devices",
|
||||
(ds) => ds.filter((d) => d.name).map(DeviceItem),
|
||||
],
|
||||
],
|
||||
children: Bluetooth.bind("devices").transform((ds) =>
|
||||
ds.filter((d) => d.name).map(DeviceItem),
|
||||
),
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import icons from "../../icons.js";
|
||||
import Brightness from "../../services/brightness.js";
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
|
||||
const CanSupportBrightness = () => Utils.exec("which gbmonctl");
|
||||
|
||||
const BrightnessSlider = () =>
|
||||
Widget.Slider({
|
||||
draw_value: false,
|
||||
hexpand: true,
|
||||
binds: [["value", Brightness, "screen"]],
|
||||
value: Brightness.bind("screen"),
|
||||
on_change: ({ value }) => (Brightness.screen = value),
|
||||
});
|
||||
|
||||
@@ -18,14 +15,9 @@ export default () =>
|
||||
children: [
|
||||
Widget.Button({
|
||||
child: Widget.Icon(icons.brightness.indicator),
|
||||
binds: [
|
||||
[
|
||||
"tooltip-text",
|
||||
Brightness,
|
||||
"screen",
|
||||
(v) => `Screen Brightness: ${Math.floor(v * 100)}%`,
|
||||
],
|
||||
],
|
||||
tooltip_text: Brightness.bind("screen").transform(
|
||||
(v) => `Screen Brightness: ${Math.floor(v * 100)}%`,
|
||||
),
|
||||
}),
|
||||
BrightnessSlider(),
|
||||
],
|
||||
|
||||
@@ -6,17 +6,9 @@ import { SimpleToggleButton } from "../ToggleButton.js";
|
||||
export default () =>
|
||||
SimpleToggleButton({
|
||||
icon: Widget.Icon({
|
||||
connections: [
|
||||
[
|
||||
Notifications,
|
||||
(icon) => {
|
||||
icon.icon = Notifications.dnd
|
||||
? icons.notifications.silent
|
||||
: icons.notifications.noisy;
|
||||
},
|
||||
"notify::dnd",
|
||||
],
|
||||
],
|
||||
icon: Notifications.bind("dnd").transform(
|
||||
(dnd) => icons.notifications[dnd ? "silent" : "noisy"],
|
||||
),
|
||||
}),
|
||||
toggle: () => (Notifications.dnd = !Notifications.dnd),
|
||||
connection: [Notifications, () => Notifications.dnd],
|
||||
|
||||
@@ -6,6 +6,7 @@ import Avatar from "../../misc/Avatar.js";
|
||||
import icons from "../../icons.js";
|
||||
import { openSettings } from "../../settings/theme.js";
|
||||
import { uptime } from "../../variables.js";
|
||||
import DND from "./DND.js";
|
||||
|
||||
export default () =>
|
||||
Widget.Box({
|
||||
@@ -20,16 +21,17 @@ export default () =>
|
||||
/*Widget.Box({
|
||||
class_name: "battery horizontal",
|
||||
children: [
|
||||
Widget.Icon({ binds: [["icon", Battery, "icon-name"]] }),
|
||||
Widget.Icon({ icon: Battery.bind("icon_name") }),
|
||||
Widget.Label({
|
||||
binds: [["label", Battery, "percent", (p) => `${p}%`]],
|
||||
label: Battery.bind("percent").transform((p) => `${p}%`),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
}),*/
|
||||
DND(),
|
||||
Widget.Label({
|
||||
class_name: "uptime",
|
||||
binds: [["label", uptime, "value", (v) => `up: ${v}`]],
|
||||
}),*/
|
||||
label: uptime.bind().transform((v) => `up: ${v}`),
|
||||
}),
|
||||
Widget.Button({
|
||||
on_clicked: openSettings,
|
||||
child: Widget.Icon(icons.ui.settings),
|
||||
|
||||
@@ -7,31 +7,29 @@ import options from "../../options.js";
|
||||
const Footer = (player) =>
|
||||
Widget.CenterBox({
|
||||
class_name: "footer-box",
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "position",
|
||||
children: [
|
||||
mpris.PositionLabel(player),
|
||||
mpris.Slash(player),
|
||||
mpris.LengthLabel(player),
|
||||
],
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "controls",
|
||||
children: [
|
||||
mpris.ShuffleButton(player),
|
||||
mpris.PreviousButton(player),
|
||||
mpris.PlayPauseButton(player),
|
||||
mpris.NextButton(player),
|
||||
mpris.LoopButton(player),
|
||||
],
|
||||
}),
|
||||
mpris.PlayerIcon(player, {
|
||||
symbolic: false,
|
||||
hexpand: true,
|
||||
hpack: "end",
|
||||
}),
|
||||
],
|
||||
start_widget: Widget.Box({
|
||||
class_name: "position",
|
||||
children: [
|
||||
mpris.PositionLabel(player),
|
||||
mpris.Slash(player),
|
||||
mpris.LengthLabel(player),
|
||||
],
|
||||
}),
|
||||
center_widget: Widget.Box({
|
||||
class_name: "controls",
|
||||
children: [
|
||||
mpris.ShuffleButton(player),
|
||||
mpris.PreviousButton(player),
|
||||
mpris.PlayPauseButton(player),
|
||||
mpris.NextButton(player),
|
||||
mpris.LoopButton(player),
|
||||
],
|
||||
}),
|
||||
end_widget: mpris.PlayerIcon(player, {
|
||||
symbolic: false,
|
||||
hexpand: true,
|
||||
hpack: "end",
|
||||
}),
|
||||
});
|
||||
|
||||
/** @param {import('types/service/mpris').MprisPlayer} player */
|
||||
@@ -84,23 +82,10 @@ export default () =>
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
class_name: "media vertical",
|
||||
connections: [
|
||||
[
|
||||
"draw",
|
||||
(self) => {
|
||||
self.visible = Mpris.players.length > 0;
|
||||
},
|
||||
],
|
||||
],
|
||||
binds: [
|
||||
[
|
||||
"children",
|
||||
Mpris,
|
||||
"players",
|
||||
(ps) =>
|
||||
ps
|
||||
.filter((p) => !options.mpris.black_list.value.includes(p.identity))
|
||||
.map(PlayerBox),
|
||||
],
|
||||
],
|
||||
visible: Mpris.bind("players").transform((p) => p.length > 0),
|
||||
children: Mpris.bind("players").transform((ps) =>
|
||||
ps
|
||||
.filter((p) => !options.mpris.black_list.value.includes(p.identity))
|
||||
.map(PlayerBox),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -5,19 +5,15 @@ import { SimpleToggleButton } from "../ToggleButton.js";
|
||||
|
||||
export default () =>
|
||||
SimpleToggleButton({
|
||||
icon: Widget.Icon({
|
||||
connections: [
|
||||
[
|
||||
Audio,
|
||||
(icon) => {
|
||||
icon.icon = Audio.microphone?.is_muted
|
||||
? icons.audio.mic.muted
|
||||
: icons.audio.mic.high;
|
||||
},
|
||||
"microphone-changed",
|
||||
],
|
||||
],
|
||||
}),
|
||||
icon: Widget.Icon().hook(
|
||||
Audio,
|
||||
(self) => {
|
||||
self.icon = Audio.microphone?.is_muted
|
||||
? icons.audio.mic.muted
|
||||
: icons.audio.mic.high;
|
||||
},
|
||||
"microphone-changed",
|
||||
),
|
||||
toggle: () => (Audio.microphone.is_muted = !Audio.microphone.is_muted),
|
||||
connection: [Audio, () => Audio.microphone?.is_muted],
|
||||
connection: [Audio, () => Audio.microphone?.is_muted || false],
|
||||
});
|
||||
|
||||
@@ -9,25 +9,13 @@ export const NetworkToggle = () =>
|
||||
ArrowToggleButton({
|
||||
name: "network",
|
||||
icon: Widget.Icon({
|
||||
connections: [
|
||||
[
|
||||
Network,
|
||||
(icon) => {
|
||||
icon.icon = Network.wifi.icon_name || "";
|
||||
},
|
||||
],
|
||||
],
|
||||
icon: Network.wifi.bind("icon_name"),
|
||||
}),
|
||||
label: Widget.Label({
|
||||
truncate: "end",
|
||||
connections: [
|
||||
[
|
||||
Network,
|
||||
(label) => {
|
||||
label.label = Network.wifi.ssid || "Not Connected";
|
||||
},
|
||||
],
|
||||
],
|
||||
label: Network.wifi
|
||||
.bind("ssid")
|
||||
.transform((ssid) => ssid || "Not Connected"),
|
||||
}),
|
||||
connection: [Network, () => Network.wifi.enabled],
|
||||
deactivate: () => (Network.wifi.enabled = false),
|
||||
@@ -41,24 +29,17 @@ export const WifiSelection = () =>
|
||||
Menu({
|
||||
name: "network",
|
||||
icon: Widget.Icon({
|
||||
connections: [
|
||||
[
|
||||
Network,
|
||||
(icon) => {
|
||||
icon.icon = Network.wifi.icon_name;
|
||||
},
|
||||
],
|
||||
],
|
||||
icon: Network.wifi.bind("icon_name"),
|
||||
}),
|
||||
title: Widget.Label("Wifi Selection"),
|
||||
content: [
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
connections: [
|
||||
[
|
||||
setup: (self) =>
|
||||
self.hook(
|
||||
Network,
|
||||
(box) =>
|
||||
(box.children = Network.wifi?.access_points.map((ap) =>
|
||||
() =>
|
||||
(self.children = Network.wifi?.access_points.map((ap) =>
|
||||
Widget.Button({
|
||||
on_clicked: () =>
|
||||
Utils.execAsync(`nmcli device wifi connect ${ap.bssid}`),
|
||||
@@ -76,8 +57,7 @@ export const WifiSelection = () =>
|
||||
}),
|
||||
}),
|
||||
)),
|
||||
],
|
||||
],
|
||||
),
|
||||
}),
|
||||
Widget.Separator(),
|
||||
Widget.Button({
|
||||
|
||||
@@ -8,8 +8,8 @@ import { setTheme, openSettings } from "../../settings/theme.js";
|
||||
export const ThemeToggle = () =>
|
||||
ArrowToggleButton({
|
||||
name: "theme",
|
||||
icon: Widget.Label({ binds: [["label", options.theme.icon]] }),
|
||||
label: Widget.Label({ binds: [["label", options.theme.name]] }),
|
||||
icon: Widget.Label().bind("label", options.theme.icon),
|
||||
label: Widget.Label().bind("label", options.theme.name),
|
||||
connection: [opened, () => opened.value === "theme"],
|
||||
activate: () => opened.setValue("theme"),
|
||||
activateOnArrow: false,
|
||||
@@ -19,9 +19,7 @@ export const ThemeToggle = () =>
|
||||
export const ThemeSelector = () =>
|
||||
Menu({
|
||||
name: "theme",
|
||||
icon: Widget.Label({
|
||||
binds: [["label", options.theme.icon]],
|
||||
}),
|
||||
icon: Widget.Label().bind("label", options.theme.icon),
|
||||
title: Widget.Label("Theme Selector"),
|
||||
content: [
|
||||
...themes.map(({ name, icon }) =>
|
||||
@@ -35,9 +33,9 @@ export const ThemeSelector = () =>
|
||||
icon: icons.ui.tick,
|
||||
hexpand: true,
|
||||
hpack: "end",
|
||||
binds: [
|
||||
["visible", options.theme.name, "value", (v) => v === name],
|
||||
],
|
||||
visible: options.theme.name
|
||||
.bind("value")
|
||||
.transform((v) => v === name),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
|
||||
@@ -11,25 +11,13 @@ import { Menu } from "../ToggleButton.js";
|
||||
const VolumeIndicator = (type = "speaker") =>
|
||||
Widget.Button({
|
||||
on_clicked: () => (Audio[type].is_muted = !Audio[type].is_muted),
|
||||
child: Widget.Icon({
|
||||
connections: [
|
||||
[
|
||||
Audio,
|
||||
(icon) => {
|
||||
if (!Audio[type]) return;
|
||||
child: Widget.Icon().hook(Audio[type], (icon) => {
|
||||
icon.icon =
|
||||
type === "speaker"
|
||||
? getAudioTypeIcon(Audio[type].icon_name || "")
|
||||
: icons.audio.mic.high;
|
||||
|
||||
icon.icon =
|
||||
type === "speaker"
|
||||
? getAudioTypeIcon(Audio[type].icon_name || "")
|
||||
: icons.audio.mic.high;
|
||||
|
||||
icon.tooltip_text = `Volume ${Math.floor(
|
||||
Audio[type].volume * 100,
|
||||
)}%`;
|
||||
},
|
||||
`${type}-changed`,
|
||||
],
|
||||
],
|
||||
icon.tooltip_text = `Volume ${Math.floor(Audio[type].volume * 100)}%`;
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -39,15 +27,10 @@ const VolumeSlider = (type = "speaker") =>
|
||||
hexpand: true,
|
||||
draw_value: false,
|
||||
on_change: ({ value }) => (Audio[type].volume = value),
|
||||
connections: [
|
||||
[
|
||||
Audio,
|
||||
(slider) => {
|
||||
slider.value = Audio[type]?.volume;
|
||||
},
|
||||
`${type}-changed`,
|
||||
],
|
||||
],
|
||||
setup: (self) =>
|
||||
self.hook(Audio[type], () => {
|
||||
self.value = Audio[type].volume || 0;
|
||||
}),
|
||||
});
|
||||
|
||||
export const Volume = () =>
|
||||
@@ -62,14 +45,7 @@ export const Volume = () =>
|
||||
Widget.Box({
|
||||
vpack: "center",
|
||||
child: Arrow("app-mixer"),
|
||||
connections: [
|
||||
[
|
||||
Audio,
|
||||
(box) => {
|
||||
box.visible = Audio.apps.length > 0;
|
||||
},
|
||||
],
|
||||
],
|
||||
visible: Audio.bind("apps").transform((a) => a.length > 0),
|
||||
}),
|
||||
],
|
||||
});
|
||||
@@ -77,7 +53,7 @@ export const Volume = () =>
|
||||
export const Microhone = () =>
|
||||
Widget.Box({
|
||||
class_name: "slider horizontal",
|
||||
binds: [["visible", Audio, "recorders", (r) => r.length > 0]],
|
||||
visible: Audio.bind("recorders").transform((a) => a.length > 0),
|
||||
children: [VolumeIndicator("microphone"), VolumeSlider("microphone")],
|
||||
});
|
||||
|
||||
@@ -88,17 +64,10 @@ const MixerItem = (stream) =>
|
||||
class_name: "mixer-item horizontal",
|
||||
children: [
|
||||
Widget.Icon({
|
||||
binds: [["tooltipText", stream, "name"]],
|
||||
connections: [
|
||||
[
|
||||
stream,
|
||||
(icon) => {
|
||||
icon.icon = Utils.lookUpIcon(stream.name || "")
|
||||
? stream.name || ""
|
||||
: icons.mpris.fallback;
|
||||
},
|
||||
],
|
||||
],
|
||||
tooltip_text: stream.bind("name").transform((n) => n || ""),
|
||||
icon: stream.bind("name").transform((n) => {
|
||||
return Utils.lookUpIcon(n || "") ? n || "" : icons.mpris.fallback;
|
||||
}),
|
||||
}),
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
@@ -106,26 +75,19 @@ const MixerItem = (stream) =>
|
||||
Widget.Label({
|
||||
xalign: 0,
|
||||
truncate: "end",
|
||||
binds: [["label", stream, "description"]],
|
||||
label: stream.bind("description").transform((d) => d || ""),
|
||||
}),
|
||||
Widget.Slider({
|
||||
hexpand: true,
|
||||
draw_value: false,
|
||||
binds: [["value", stream, "volume"]],
|
||||
value: stream.bind("volume"),
|
||||
on_change: ({ value }) => (stream.volume = value),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Label({
|
||||
xalign: 1,
|
||||
connections: [
|
||||
[
|
||||
stream,
|
||||
(l) => {
|
||||
l.label = `${Math.floor(stream.volume * 100)}%`;
|
||||
},
|
||||
],
|
||||
],
|
||||
label: stream.bind("volume").transform((v) => `${Math.floor(v * 100)}`),
|
||||
}),
|
||||
],
|
||||
});
|
||||
@@ -148,7 +110,9 @@ const SinkItem = (stream) =>
|
||||
icon: icons.ui.tick,
|
||||
hexpand: true,
|
||||
hpack: "end",
|
||||
binds: [["visible", Audio, "speaker", (s) => s === stream]],
|
||||
visible: Audio.speaker
|
||||
.bind("stream")
|
||||
.transform((s) => s === stream.stream),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
@@ -171,7 +135,7 @@ export const AppMixer = () =>
|
||||
content: [
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
binds: [["children", Audio, "apps", (a) => a.map(MixerItem)]],
|
||||
children: Audio.bind("apps").transform((a) => a.map(MixerItem)),
|
||||
}),
|
||||
Widget.Separator(),
|
||||
SettingsButton(),
|
||||
@@ -186,7 +150,7 @@ export const SinkSelector = () =>
|
||||
content: [
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
binds: [["children", Audio, "speakers", (s) => s.map(SinkItem)]],
|
||||
children: Audio.bind("speakers").transform((a) => a.map(SinkItem)),
|
||||
}),
|
||||
Widget.Separator(),
|
||||
SettingsButton(),
|
||||
|
||||
@@ -10,50 +10,49 @@ const Corner = (place) =>
|
||||
vexpand: true,
|
||||
hpack: place.includes("left") ? "start" : "end",
|
||||
vpack: place.includes("top") ? "start" : "end",
|
||||
connections: [
|
||||
[
|
||||
options.radii,
|
||||
(self) => {
|
||||
setup: (self) =>
|
||||
self
|
||||
.hook(options.radii, () => {
|
||||
const r = options.radii.value * 2;
|
||||
self.set_size_request(r, r);
|
||||
},
|
||||
],
|
||||
],
|
||||
setup: (self) =>
|
||||
self.connect("draw", (self, cr) => {
|
||||
const context = self.get_style_context();
|
||||
const c = context.get_property(
|
||||
"background-color",
|
||||
Gtk.StateFlags.NORMAL,
|
||||
);
|
||||
const r = context.get_property("border-radius", Gtk.StateFlags.NORMAL);
|
||||
})
|
||||
.connect("draw", (self, cr) => {
|
||||
const context = self.get_style_context();
|
||||
const c = context.get_property(
|
||||
"background-color",
|
||||
Gtk.StateFlags.NORMAL,
|
||||
);
|
||||
const r = context.get_property(
|
||||
"border-radius",
|
||||
Gtk.StateFlags.NORMAL,
|
||||
);
|
||||
|
||||
switch (place) {
|
||||
case "topleft":
|
||||
cr.arc(r, r, r, Math.PI, (3 * Math.PI) / 2);
|
||||
cr.lineTo(0, 0);
|
||||
break;
|
||||
switch (place) {
|
||||
case "topleft":
|
||||
cr.arc(r, r, r, Math.PI, (3 * Math.PI) / 2);
|
||||
cr.lineTo(0, 0);
|
||||
break;
|
||||
|
||||
case "topright":
|
||||
cr.arc(0, r, r, (3 * Math.PI) / 2, 2 * Math.PI);
|
||||
cr.lineTo(r, 0);
|
||||
break;
|
||||
case "topright":
|
||||
cr.arc(0, r, r, (3 * Math.PI) / 2, 2 * Math.PI);
|
||||
cr.lineTo(r, 0);
|
||||
break;
|
||||
|
||||
case "bottomleft":
|
||||
cr.arc(r, 0, r, Math.PI / 2, Math.PI);
|
||||
cr.lineTo(0, r);
|
||||
break;
|
||||
case "bottomleft":
|
||||
cr.arc(r, 0, r, Math.PI / 2, Math.PI);
|
||||
cr.lineTo(0, r);
|
||||
break;
|
||||
|
||||
case "bottomright":
|
||||
cr.arc(0, 0, r, 0, Math.PI / 2);
|
||||
cr.lineTo(r, r);
|
||||
break;
|
||||
}
|
||||
case "bottomright":
|
||||
cr.arc(0, 0, r, 0, Math.PI / 2);
|
||||
cr.lineTo(r, r);
|
||||
break;
|
||||
}
|
||||
|
||||
cr.closePath();
|
||||
cr.setSourceRGBA(c.red, c.green, c.blue, c.alpha);
|
||||
cr.fill();
|
||||
}),
|
||||
cr.closePath();
|
||||
cr.setSourceRGBA(c.red, c.green, c.blue, c.alpha);
|
||||
cr.fill();
|
||||
}),
|
||||
});
|
||||
|
||||
/** @type {Array<'topleft' | 'topright' | 'bottomleft' | 'bottomright'>} */
|
||||
@@ -70,7 +69,7 @@ export default (monitor) =>
|
||||
place.includes("top") ? "top" : "bottom",
|
||||
place.includes("right") ? "right" : "left",
|
||||
],
|
||||
binds: [["visible", options.desktop.screen_corners]],
|
||||
visible: options.desktop.screen_corners.bind("value"),
|
||||
child: Corner(place),
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -13,7 +13,7 @@ class Asusctl extends Service {
|
||||
);
|
||||
}
|
||||
|
||||
profiles = Object.freeze(["Performance", "Balanced", "Quiet"]);
|
||||
profiles = /** @type {const} */ (["Performance", "Balanced", "Quiet"]);
|
||||
#profile = "Balanced";
|
||||
#mode = "Hyprid";
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import Notifications from "resource:///com/github/Aylur/ags/service/notifications.js";
|
||||
import { Variable } from "resource:///com/github/Aylur/ags/variable.js";
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import Service from "resource:///com/github/Aylur/ags/service.js";
|
||||
import { dependencies } from "../utils.js";
|
||||
import icons from "../icons.js";
|
||||
|
||||
const COLORS_CACHE = Utils.CACHE_DIR + "/colorpicker.json";
|
||||
|
||||
@@ -58,15 +58,15 @@ class Colors extends Service {
|
||||
);
|
||||
}
|
||||
|
||||
this.#notifID = Notifications.Notify(
|
||||
"Color Picker",
|
||||
this.#notifID,
|
||||
"color-select-symbolic",
|
||||
color,
|
||||
"",
|
||||
[],
|
||||
{},
|
||||
);
|
||||
const n = await Utils.notify({
|
||||
id: this.#notifID,
|
||||
iconName: icons.ui.colorpicker,
|
||||
summary: color,
|
||||
actions: {
|
||||
Copy: () => this.wlCopy(color),
|
||||
},
|
||||
});
|
||||
this.#notifID = n.id;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ showSearch.connect("changed", ({ value }) => {
|
||||
|
||||
/** @param {import('./option.js').Opt<string>} opt */
|
||||
const EnumSetter = (opt) => {
|
||||
const lbl = Widget.Label({ binds: [["label", opt]] });
|
||||
const lbl = Widget.Label().bind("label", opt);
|
||||
const step = (dir = 1) => {
|
||||
const i = opt.enums.findIndex((i) => i === lbl.label);
|
||||
opt.setValue(
|
||||
@@ -57,51 +57,42 @@ const Setter = (opt) => {
|
||||
setup(self) {
|
||||
self.set_range(0, 1000);
|
||||
self.set_increments(1, 5);
|
||||
self.on("value-changed", () => opt.setValue(self.value, true));
|
||||
self.hook(opt, () => (self.value = opt.value));
|
||||
},
|
||||
connections: [
|
||||
["value-changed", (self) => opt.setValue(self.value, true)],
|
||||
[opt, (self) => (self.value = opt.value)],
|
||||
],
|
||||
});
|
||||
case "float":
|
||||
case "object":
|
||||
return Widget.Entry({
|
||||
on_accept: (self) => opt.setValue(JSON.parse(self.text || ""), true),
|
||||
connections: [[opt, (self) => (self.text = JSON.stringify(opt.value))]],
|
||||
setup: (self) =>
|
||||
self.hook(opt, () => (self.text = JSON.stringify(opt.value))),
|
||||
});
|
||||
case "string":
|
||||
return Widget.Entry({
|
||||
on_accept: (self) => opt.setValue(self.text, true),
|
||||
connections: [[opt, (self) => (self.text = opt.value)]],
|
||||
setup: (self) => self.hook(opt, () => (self.text = opt.value)),
|
||||
});
|
||||
case "enum":
|
||||
return EnumSetter(opt);
|
||||
case "boolean":
|
||||
return Widget.Switch({
|
||||
connections: [
|
||||
["notify::active", (self) => opt.setValue(self.active, true)],
|
||||
[opt, (self) => (self.active = opt.value)],
|
||||
],
|
||||
});
|
||||
return Widget.Switch()
|
||||
.on("notify::active", (self) => opt.setValue(self.active, true))
|
||||
.hook(opt, (self) => (self.active = opt.value));
|
||||
|
||||
case "img":
|
||||
return Widget.FileChooserButton({
|
||||
connections: [
|
||||
[
|
||||
"selection-changed",
|
||||
(self) => {
|
||||
opt.setValue(self.get_uri()?.replace("file://", ""), true);
|
||||
},
|
||||
],
|
||||
],
|
||||
return Widget.FileChooserButton().on("selection-changed", (self) => {
|
||||
opt.setValue(self.get_uri()?.replace("file://", ""), true);
|
||||
});
|
||||
|
||||
case "font":
|
||||
return Widget.FontButton({
|
||||
show_size: false,
|
||||
use_size: false,
|
||||
connections: [
|
||||
["notify::font", ({ font }) => opt.setValue(font, true)],
|
||||
[opt, (self) => (self.font = opt.value)],
|
||||
],
|
||||
setup: (self) =>
|
||||
self
|
||||
.on("notify::font", ({ font }) => opt.setValue(font, true))
|
||||
.hook(opt, () => (self.font = opt.value)),
|
||||
});
|
||||
default:
|
||||
return Widget.Label({
|
||||
@@ -114,7 +105,7 @@ const Setter = (opt) => {
|
||||
const Row = (opt) =>
|
||||
Widget.Box({
|
||||
class_name: "row",
|
||||
setup: (self) => (self.opt = opt),
|
||||
attribute: opt,
|
||||
children: [
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
@@ -161,19 +152,15 @@ const Page = (category) =>
|
||||
child: Widget.Box({
|
||||
class_name: "page-content vertical",
|
||||
vertical: true,
|
||||
connections: [
|
||||
[
|
||||
search,
|
||||
(self) => {
|
||||
for (const child of self.children) {
|
||||
child.visible =
|
||||
child.opt.id.includes(search.value) ||
|
||||
child.opt.title.includes(search.value) ||
|
||||
child.opt.note.includes(search.value);
|
||||
}
|
||||
},
|
||||
],
|
||||
],
|
||||
setup: (self) =>
|
||||
self.hook(search, () => {
|
||||
for (const child of self.children) {
|
||||
child.visible =
|
||||
child.attribute.id.includes(search.value) ||
|
||||
child.attribute.title.includes(search.value) ||
|
||||
child.attribute.note.includes(search.value);
|
||||
}
|
||||
}),
|
||||
children: optionsList
|
||||
.filter((opt) => opt.category.includes(category))
|
||||
.map(Row),
|
||||
@@ -181,7 +168,7 @@ const Page = (category) =>
|
||||
});
|
||||
|
||||
const sidebar = Widget.Revealer({
|
||||
binds: [["reveal-child", search, "value", (v) => !v]],
|
||||
reveal_child: search.bind().transform((v) => !v),
|
||||
transition: "slide_right",
|
||||
child: Widget.Box({
|
||||
hexpand: false,
|
||||
@@ -213,14 +200,9 @@ const sidebar = Widget.Revealer({
|
||||
Widget.Button({
|
||||
label: (icons.dialog[name] || "") + " " + name,
|
||||
xalign: 0,
|
||||
binds: [
|
||||
[
|
||||
"class-name",
|
||||
currentPage,
|
||||
"value",
|
||||
(v) => (v === name ? "active" : ""),
|
||||
],
|
||||
],
|
||||
class_name: currentPage
|
||||
.bind()
|
||||
.transform((v) => `${v === name ? "active" : ""}`),
|
||||
on_clicked: () => currentPage.setValue(name),
|
||||
}),
|
||||
),
|
||||
@@ -254,21 +236,15 @@ const sidebar = Widget.Revealer({
|
||||
|
||||
const searchEntry = Widget.Revealer({
|
||||
transition: "slide_down",
|
||||
binds: [
|
||||
["reveal-child", showSearch],
|
||||
["transition-duration", options.transition],
|
||||
],
|
||||
reveal_child: showSearch.bind(),
|
||||
transition_duration: options.transition.bind("value"),
|
||||
child: Widget.Entry({
|
||||
connections: [
|
||||
[
|
||||
showSearch,
|
||||
(self) => {
|
||||
if (!showSearch.value) self.text = "";
|
||||
setup: (self) =>
|
||||
self.hook(showSearch, () => {
|
||||
if (!showSearch.value) self.text = "";
|
||||
|
||||
if (showSearch.value) self.grab_focus();
|
||||
},
|
||||
],
|
||||
],
|
||||
if (showSearch.value) self.grab_focus();
|
||||
}),
|
||||
hexpand: true,
|
||||
class_name: "search",
|
||||
placeholder_text: "Search Options",
|
||||
@@ -279,41 +255,36 @@ const searchEntry = Widget.Revealer({
|
||||
|
||||
const categoriesStack = Widget.Stack({
|
||||
transition: "slide_left_right",
|
||||
items: categories.map((name) => [name, Page(name)]),
|
||||
binds: [
|
||||
["shown", currentPage],
|
||||
["visible", search, "value", (v) => !v],
|
||||
],
|
||||
children: categories.reduce((obj, name) => {
|
||||
obj[name] = Page(name);
|
||||
return obj;
|
||||
}, {}),
|
||||
shown: currentPage.bind(),
|
||||
visible: search.bind().transform((v) => !v),
|
||||
});
|
||||
|
||||
const searchPage = Widget.Box({
|
||||
binds: [["visible", search, "value", (v) => !!v]],
|
||||
visible: search.bind().transform((v) => !!v),
|
||||
child: Page(""),
|
||||
});
|
||||
|
||||
export default RegularWindow({
|
||||
name: "settings-dialog",
|
||||
title: "Settings",
|
||||
setup: (win) => win.set_default_size(800, 500),
|
||||
connections: [
|
||||
[
|
||||
"delete-event",
|
||||
(win) => {
|
||||
setup: (win) =>
|
||||
win
|
||||
.on("delete-event", () => {
|
||||
win.hide();
|
||||
return true;
|
||||
},
|
||||
],
|
||||
[
|
||||
"key-press-event",
|
||||
(self, event) => {
|
||||
})
|
||||
.on("key-press-event", (_, event) => {
|
||||
if (event.get_keyval()[1] === imports.gi.Gdk.KEY_Escape) {
|
||||
self.text = "";
|
||||
showSearch.setValue(false);
|
||||
search.setValue("");
|
||||
}
|
||||
},
|
||||
],
|
||||
],
|
||||
})
|
||||
.set_default_size(800, 500),
|
||||
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
sidebar,
|
||||
|
||||
@@ -15,6 +15,9 @@ export async function globals() {
|
||||
globalThis.indicator = (
|
||||
await import("../services/onScreenIndicator.js")
|
||||
).default;
|
||||
globalThis.app = (
|
||||
await import("resource:///com/github/Aylur/ags/app.js")
|
||||
).default;
|
||||
|
||||
Mpris.players.forEach((player) => {
|
||||
player.connect("changed", (player) => {
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
} from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import { exec } from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import options from "../options.js";
|
||||
import Service, { Binding } from "resource:///com/github/Aylur/ags/service.js";
|
||||
import Service from "resource:///com/github/Aylur/ags/service.js";
|
||||
import { reloadScss } from "./scss.js";
|
||||
import { setupHyprland } from "./hyprland.js";
|
||||
const CACHE_FILE = CACHE_DIR + "/options.json";
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import { getOptions } from "./option.js";
|
||||
import { dependencies } from "../utils.js";
|
||||
|
||||
export function scssWatcher() {
|
||||
return Utils.subprocess(
|
||||
@@ -26,8 +25,6 @@ export function scssWatcher() {
|
||||
* options.bar.style.value => $bar-style
|
||||
*/
|
||||
export async function reloadScss() {
|
||||
if (!dependencies(["sassc"])) return;
|
||||
|
||||
const opts = getOptions();
|
||||
const vars = opts.map((opt) => {
|
||||
if (opt.scss === "exclude") return "";
|
||||
|
||||
@@ -8,7 +8,7 @@ import { wallpaper } from "./wallpaper.js";
|
||||
import { hyprlandInit, setupHyprland } from "./hyprland.js";
|
||||
import { globals } from "./globals.js";
|
||||
import { showAbout } from "../about/about.js";
|
||||
import Gtk from "gi://Gtk";
|
||||
import Gtk from "gi://Gtk?version=3.0";
|
||||
|
||||
export function init() {
|
||||
notificationBlacklist();
|
||||
|
||||
@@ -32,7 +32,7 @@ export function forMonitors(widget) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('gi://Gtk').Gtk.Widget} widget
|
||||
* @param {import('gi://Gtk?version=3.0').default.Widget} widget
|
||||
* @returns {any} - missing cairo type
|
||||
*/
|
||||
export function createSurfaceFromWidget(widget) {
|
||||
|
||||
@@ -4,6 +4,10 @@ import options from "./options.js";
|
||||
|
||||
const intval = options.systemFetchInterval;
|
||||
|
||||
export const clock = Variable(GLib.DateTime.new_now_local(), {
|
||||
poll: [1000, () => GLib.DateTime.new_now_local()],
|
||||
});
|
||||
|
||||
export const uptime = Variable("", {
|
||||
poll: [
|
||||
60_000,
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
{
|
||||
"name": "ags-dotfiles",
|
||||
"version": "1.6.4",
|
||||
"version": "1.7.5",
|
||||
"description": "My config files for AGS",
|
||||
"main": "config.js",
|
||||
"scripts": {
|
||||
"check": "tsc --noEmit",
|
||||
"lint": "eslint . --fix",
|
||||
"stylelint": "stylelint ./scss --fix"
|
||||
"stylelint": "stylelint ./scss --fix",
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -18,10 +20,16 @@
|
||||
"homepage": "https://github.com/Aylur/dotfiles#readme",
|
||||
"kofi": "https://ko-fi.com/aylur",
|
||||
"devDependencies": {
|
||||
"@girs/dbusmenugtk3-0.4": "^0.4.0-3.2.7",
|
||||
"@girs/gobject-2.0": "^2.78.0-3.2.7",
|
||||
"@girs/gtk-3.0": "^3.24.38-3.2.7",
|
||||
"@girs/gvc-1.0": "^1.0.0-3.2.7",
|
||||
"@girs/nm-1.0": "^1.43.1-3.1.0"
|
||||
"@girs/dbusmenugtk3-0.4": "^0.4.0-3.2.0",
|
||||
"@girs/gobject-2.0": "^2.76.1-3.2.3",
|
||||
"@girs/gtk-3.0": "^3.24.39-3.2.2",
|
||||
"@girs/gvc-1.0": "^1.0.0-3.1.0",
|
||||
"@girs/nm-1.0": "^1.43.1-3.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.33.0",
|
||||
"@typescript-eslint/parser": "^5.33.0",
|
||||
"eslint": "^8.44.0",
|
||||
"prettier": "^3.2.5",
|
||||
"stylelint-config-standard-scss": "^10.0.0",
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"strict": true,
|
||||
"noImplicitAny": false,
|
||||
"baseUrl": ".",
|
||||
"typeRoots": ["./types/ags.d.ts", "./node_modules/@girs"],
|
||||
"typeRoots": ["./types"],
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
}
|
||||
|
||||
@@ -160,6 +160,7 @@
|
||||
brightnessctl
|
||||
ydotool
|
||||
kitty
|
||||
hyprpicker
|
||||
]}";
|
||||
};
|
||||
Install = {
|
||||
|
||||
Reference in New Issue
Block a user