refactor: make the whole thing more generic

This commit is contained in:
2024-04-02 16:28:57 +02:00
parent 7b648e1955
commit 651f3ad808
193 changed files with 763 additions and 521 deletions

View File

@@ -0,0 +1,61 @@
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
import Widget from "resource:///com/github/Aylur/ags/widget.js";
import icons from "../../icons.js";
import Asusctl from "../../services/asusctl.js";
import { ArrowToggleButton, Menu } from "../ToggleButton.js";
export const ProfileToggle = () =>
ArrowToggleButton({
name: "asusctl-profile",
icon: Widget.Icon({
icon: Asusctl.bind("profile").transform((p) => icons.asusctl.profile[p]),
}),
label: Widget.Label({
label: Asusctl.bind("profile"),
}),
connection: [Asusctl, () => Asusctl.profile !== "Balanced"],
activate: () => Asusctl.setProfile("Quiet"),
deactivate: () => Asusctl.setProfile("Balanced"),
activateOnArrow: false,
});
export const ProfileSelector = () =>
Menu({
name: "asusctl-profile",
icon: Widget.Icon({
icon: Asusctl.bind("profile").transform((p) => icons.asusctl.profile[p]),
}),
title: Widget.Label("Profile Selector"),
content: [
Widget.Box({
vertical: true,
hexpand: true,
children: [
Widget.Box({
vertical: true,
children: Asusctl.profiles.map((prof) =>
Widget.Button({
on_clicked: () => Asusctl.setProfile(prof),
child: Widget.Box({
children: [
Widget.Icon(icons.asusctl.profile[prof]),
Widget.Label(prof),
],
}),
}),
),
}),
],
}),
Widget.Separator(),
Widget.Button({
on_clicked: () => Utils.execAsync("rog-control-center"),
child: Widget.Box({
children: [
Widget.Icon(icons.ui.settings),
Widget.Label("Rog Control Center"),
],
}),
}),
],
});

View File

@@ -0,0 +1,74 @@
import Bluetooth from "resource:///com/github/Aylur/ags/service/bluetooth.js";
import Widget from "resource:///com/github/Aylur/ags/widget.js";
import icons from "../../icons.js";
import { Menu, ArrowToggleButton } from "../ToggleButton.js";
export const BluetoothToggle = () =>
ArrowToggleButton({
name: "bluetooth",
icon: Widget.Icon({
icon: Bluetooth.bind("enabled").transform(
(p) => icons.bluetooth[p ? "enabled" : "disabled"],
),
}),
label: Widget.Label({
truncate: "end",
setup: (self) =>
self.hook(Bluetooth, () => {
if (!Bluetooth.enabled) return (self.label = "Disabled");
if (Bluetooth.connected_devices.length === 0)
return (self.label = "Not Connected");
if (Bluetooth.connected_devices.length === 1)
return (self.label = Bluetooth.connected_devices[0].alias);
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: [
Widget.Icon(device.icon_name + "-symbolic"),
Widget.Label(device.name),
Widget.Label({
label: `${device.battery_percentage}%`,
visible: device.bind("battery_percentage").transform((p) => p > 0),
}),
Widget.Box({ hexpand: true }),
Widget.Spinner({
active: device.bind("connecting"),
visible: device.bind("connecting"),
}),
Widget.Switch({
active: device.connected,
visible: device.bind("connecting").transform((p) => !p),
setup: (self) =>
self.on("notify::active", () => {
device.setConnection(self.active);
}),
}),
],
});
export const BluetoothDevices = () =>
Menu({
name: "bluetooth",
icon: Widget.Icon(icons.bluetooth.disabled),
title: Widget.Label("Bluetooth"),
content: [
Widget.Box({
hexpand: true,
vertical: true,
children: Bluetooth.bind("devices").transform((ds) =>
ds.filter((d) => d.name).map(DeviceItem),
),
}),
],
});

View File

@@ -0,0 +1,24 @@
import Widget from "resource:///com/github/Aylur/ags/widget.js";
import icons from "../../icons.js";
import Brightness from "../../services/brightness.js";
const BrightnessSlider = () =>
Widget.Slider({
draw_value: false,
hexpand: true,
value: Brightness.bind("screen"),
on_change: ({ value }) => (Brightness.screen = value),
});
export default () =>
Widget.Box({
children: [
Widget.Button({
child: Widget.Icon(icons.brightness.indicator),
tooltip_text: Brightness.bind("screen").transform(
(v) => `Screen Brightness: ${Math.floor(v * 100)}%`,
),
}),
BrightnessSlider(),
],
});

View File

@@ -0,0 +1,15 @@
import Widget from "resource:///com/github/Aylur/ags/widget.js";
import Notifications from "resource:///com/github/Aylur/ags/service/notifications.js";
import icons from "../../icons.js";
import { SimpleToggleButton } from "../ToggleButton.js";
export default () =>
SimpleToggleButton({
icon: Widget.Icon({
icon: Notifications.bind("dnd").transform(
(dnd) => icons.notifications[dnd ? "silent" : "noisy"],
),
}),
toggle: () => (Notifications.dnd = !Notifications.dnd),
connection: [Notifications, () => Notifications.dnd],
});

View File

@@ -0,0 +1,50 @@
import Widget from "resource:///com/github/Aylur/ags/widget.js";
import Battery from "resource:///com/github/Aylur/ags/service/battery.js";
import PowerMenu from "../../services/powermenu.js";
import Lockscreen from "../../services/lockscreen.js";
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({
class_name: "header horizontal",
children: [
Avatar(),
Widget.Box({
hpack: "end",
vpack: "center",
hexpand: true,
children: [
/*Widget.Box({
class_name: "battery horizontal",
children: [
Widget.Icon({ icon: Battery.bind("icon_name") }),
Widget.Label({
label: Battery.bind("percent").transform((p) => `${p}%`),
}),
],
}),*/
DND(),
Widget.Label({
class_name: "uptime",
label: uptime.bind().transform((v) => `up: ${v}`),
}),
Widget.Button({
on_clicked: openSettings,
child: Widget.Icon(icons.ui.settings),
}),
Widget.Button({
on_clicked: () => Lockscreen.lockscreen(),
child: Widget.Icon(icons.lock),
}),
Widget.Button({
on_clicked: () => PowerMenu.action("shutdown"),
child: Widget.Icon(icons.powermenu.shutdown),
}),
],
}),
],
});

View File

@@ -0,0 +1,91 @@
import Mpris from "resource:///com/github/Aylur/ags/service/mpris.js";
import Widget from "resource:///com/github/Aylur/ags/widget.js";
import * as mpris from "../../misc/mpris.js";
import options from "../../options.js";
/** @param {import('types/service/mpris').MprisPlayer} player */
const Footer = (player) =>
Widget.CenterBox({
class_name: "footer-box",
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 */
const TextBox = (player) =>
Widget.Box({
children: [
mpris.CoverArt(player, {
hpack: "end",
hexpand: false,
}),
Widget.Box({
hexpand: true,
vertical: true,
class_name: "labels",
children: [
mpris.TitleLabel(player, {
xalign: 0,
justification: "left",
wrap: true,
}),
mpris.ArtistLabel(player, {
xalign: 0,
justification: "left",
wrap: true,
}),
],
}),
],
});
/** @param {import('types/service/mpris').MprisPlayer} player */
const PlayerBox = (player) =>
Widget.Box({
class_name: `player ${player.name}`,
child: mpris.BlurredCoverArt(player, {
hexpand: true,
child: Widget.Box({
hexpand: true,
vertical: true,
children: [
TextBox(player),
mpris.PositionSlider(player),
Footer(player),
],
}),
}),
});
export default () =>
Widget.Box({
vertical: true,
class_name: "media vertical",
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),
),
});

View File

@@ -0,0 +1,19 @@
import Audio from "resource:///com/github/Aylur/ags/service/audio.js";
import Widget from "resource:///com/github/Aylur/ags/widget.js";
import icons from "../../icons.js";
import { SimpleToggleButton } from "../ToggleButton.js";
export default () =>
SimpleToggleButton({
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 || false],
});

View File

@@ -0,0 +1,71 @@
import Widget from "resource:///com/github/Aylur/ags/widget.js";
import Network from "resource:///com/github/Aylur/ags/service/network.js";
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
import icons from "../../icons.js";
import { Menu, ArrowToggleButton } from "../ToggleButton.js";
import Applications from "resource:///com/github/Aylur/ags/service/applications.js";
export const NetworkToggle = () =>
ArrowToggleButton({
name: "network",
icon: Widget.Icon({
icon: Network.wifi.bind("icon_name"),
}),
label: Widget.Label({
truncate: "end",
label: Network.wifi
.bind("ssid")
.transform((ssid) => ssid || "Not Connected"),
}),
connection: [Network, () => Network.wifi.enabled],
deactivate: () => (Network.wifi.enabled = false),
activate: () => {
Network.wifi.enabled = true;
Network.wifi.scan();
},
});
export const WifiSelection = () =>
Menu({
name: "network",
icon: Widget.Icon({
icon: Network.wifi.bind("icon_name"),
}),
title: Widget.Label("Wifi Selection"),
content: [
Widget.Box({
vertical: true,
setup: (self) =>
self.hook(
Network,
() =>
(self.children = Network.wifi?.access_points.map((ap) =>
Widget.Button({
on_clicked: () =>
Utils.execAsync(`nmcli device wifi connect ${ap.bssid}`),
child: Widget.Box({
children: [
Widget.Icon(ap.iconName),
Widget.Label(ap.ssid || ""),
ap.active &&
Widget.Icon({
icon: icons.ui.tick,
hexpand: true,
hpack: "end",
}),
],
}),
}),
)),
),
}),
Widget.Separator(),
Widget.Button({
on_clicked: () =>
Applications.query("gnome-control-center")?.[0].launch(),
child: Widget.Box({
children: [Widget.Icon(icons.ui.settings), Widget.Label("Network")],
}),
}),
],
});

View File

@@ -0,0 +1,55 @@
import Widget from "resource:///com/github/Aylur/ags/widget.js";
import { ArrowToggleButton, Menu, opened } from "../ToggleButton.js";
import themes from "../../themes.js";
import icons from "../../icons.js";
import options from "../../options.js";
import { setTheme, openSettings } from "../../settings/theme.js";
export const ThemeToggle = () =>
ArrowToggleButton({
name: "theme",
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,
deactivate: () => {},
});
export const ThemeSelector = () =>
Menu({
name: "theme",
icon: Widget.Label().bind("label", options.theme.icon),
title: Widget.Label("Theme Selector"),
content: [
...themes.map(({ name, icon }) =>
Widget.Button({
on_clicked: () => setTheme(name),
child: Widget.Box({
children: [
Widget.Label(icon),
Widget.Label(name),
Widget.Icon({
icon: icons.ui.tick,
hexpand: true,
hpack: "end",
visible: options.theme.name
.bind("value")
.transform((v) => v === name),
}),
],
}),
}),
),
Widget.Separator(),
Widget.Button({
on_clicked: openSettings,
child: Widget.Box({
children: [
Widget.Icon(icons.ui.settings),
Widget.Label("Theme Settings"),
],
}),
}),
],
});

View File

@@ -0,0 +1,158 @@
import Audio from "resource:///com/github/Aylur/ags/service/audio.js";
import Widget from "resource:///com/github/Aylur/ags/widget.js";
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
import icons from "../../icons.js";
import FontIcon from "../../misc/FontIcon.js";
import { getAudioTypeIcon } from "../../utils.js";
import { Arrow } from "../ToggleButton.js";
import { Menu } from "../ToggleButton.js";
/** @param {'speaker' | 'microphone'=} type */
const VolumeIndicator = (type = "speaker") =>
Widget.Button({
on_clicked: () => (Audio[type].is_muted = !Audio[type].is_muted),
child: Widget.Icon().hook(Audio[type], (icon) => {
icon.icon =
type === "speaker"
? getAudioTypeIcon(Audio[type].icon_name || "")
: icons.audio.mic.high;
icon.tooltip_text = `Volume ${Math.floor(Audio[type].volume * 100)}%`;
}),
});
/** @param {'speaker' | 'microphone'=} type */
const VolumeSlider = (type = "speaker") =>
Widget.Slider({
hexpand: true,
draw_value: false,
on_change: ({ value }) => (Audio[type].volume = value),
setup: (self) =>
self.hook(Audio[type], () => {
self.value = Audio[type].volume || 0;
}),
});
export const Volume = () =>
Widget.Box({
children: [
VolumeIndicator("speaker"),
VolumeSlider("speaker"),
Widget.Box({
vpack: "center",
child: Arrow("sink-selector"),
}),
Widget.Box({
vpack: "center",
child: Arrow("app-mixer"),
visible: Audio.bind("apps").transform((a) => a.length > 0),
}),
],
});
export const Microhone = () =>
Widget.Box({
class_name: "slider horizontal",
visible: Audio.bind("recorders").transform((a) => a.length > 0),
children: [VolumeIndicator("microphone"), VolumeSlider("microphone")],
});
/** @param {import('types/service/audio').Stream} stream */
const MixerItem = (stream) =>
Widget.Box({
hexpand: true,
class_name: "mixer-item horizontal",
children: [
Widget.Icon({
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,
children: [
Widget.Label({
xalign: 0,
truncate: "end",
label: stream.bind("description").transform((d) => d || ""),
}),
Widget.Slider({
hexpand: true,
draw_value: false,
value: stream.bind("volume"),
on_change: ({ value }) => (stream.volume = value),
}),
],
}),
Widget.Label({
xalign: 1,
label: stream.bind("volume").transform((v) => `${Math.floor(v * 100)}`),
}),
],
});
/** @param {import('types/service/audio').Stream} stream */
const SinkItem = (stream) =>
Widget.Button({
hexpand: true,
on_clicked: () => (Audio.speaker = stream),
child: Widget.Box({
children: [
Widget.Icon({
icon: getAudioTypeIcon(stream.icon_name || ""),
tooltip_text: stream.icon_name,
}),
Widget.Label(
(stream.description || "").split(" ").slice(0, 4).join(" "),
),
Widget.Icon({
icon: icons.ui.tick,
hexpand: true,
hpack: "end",
visible: Audio.speaker
.bind("stream")
.transform((s) => s === stream.stream),
}),
],
}),
});
const SettingsButton = () =>
Widget.Button({
on_clicked: () => Utils.execAsync("pavucontrol"),
hexpand: true,
child: Widget.Box({
children: [Widget.Icon(icons.ui.settings), Widget.Label("Settings")],
}),
});
export const AppMixer = () =>
Menu({
name: "app-mixer",
icon: FontIcon(icons.audio.mixer),
title: Widget.Label("App Mixer"),
content: [
Widget.Box({
vertical: true,
children: Audio.bind("apps").transform((a) => a.map(MixerItem)),
}),
Widget.Separator(),
SettingsButton(),
],
});
export const SinkSelector = () =>
Menu({
name: "sink-selector",
icon: Widget.Icon(icons.audio.type.headset),
title: Widget.Label("Sink Selector"),
content: [
Widget.Box({
vertical: true,
children: Audio.bind("speakers").transform((a) => a.map(SinkItem)),
}),
Widget.Separator(),
SettingsButton(),
],
});