nixos-configs/modules/ags/config/ts/quick-settings/button-grid.ts

346 lines
9.5 KiB
TypeScript
Raw Normal View History

const Bluetooth = await Service.import('bluetooth');
const Network = await Service.import('network');
const { Box, Icon, Label, Revealer } = Widget;
const { execAsync } = Utils;
import { SpeakerIcon, MicIcon } from '../misc/audio-icons.ts';
import CursorBox from '../misc/cursorbox.ts';
import Separator from '../misc/separator.ts';
import { NetworkMenu } from './network.ts';
import { BluetoothMenu } from './bluetooth.ts';
2023-12-04 15:39:12 -05:00
2024-01-13 11:15:08 -05:00
// Types
2024-01-22 10:23:32 -05:00
import GObject from 'types/@girs/gobject-2.0/gobject-2.0';
import { Variable as Var } from 'types/variable.ts';
2024-02-21 19:08:55 -05:00
import {
BoxGeneric,
IconGeneric,
LabelGeneric,
RevealerGeneric,
} from 'global-types';
2024-01-13 11:15:08 -05:00
type IconTuple = [
GObject.Object,
2024-02-21 19:08:55 -05:00
(self: IconGeneric) => void,
2024-01-13 11:15:08 -05:00
signal?: string,
];
type IndicatorTuple = [
GObject.Object,
2024-02-21 19:08:55 -05:00
(self: LabelGeneric) => void,
2024-01-13 11:15:08 -05:00
signal?: string,
];
type GridButtonType = {
command?(): void
secondary_command?(): void
2024-02-21 19:08:55 -05:00
on_open?(menu: RevealerGeneric): void
2024-01-13 11:15:08 -05:00
icon: string | IconTuple
indicator?: IndicatorTuple
2024-02-21 19:08:55 -05:00
// @ts-expect-error me is lazy
menu?: Widget
2024-01-13 11:15:08 -05:00
};
const SPACING = 28;
2024-02-21 19:08:55 -05:00
const ButtonStates = [] as Array<Var<boolean>>;
2024-01-13 11:15:08 -05:00
const GridButton = ({
2023-12-20 03:45:05 -05:00
command = () => {/**/},
secondary_command = () => {/**/},
on_open = () => {/**/},
icon,
indicator,
menu,
2024-01-13 11:15:08 -05:00
}: GridButtonType) => {
const Activated = Variable(false);
ButtonStates.push(Activated);
2024-01-13 11:15:08 -05:00
let iconWidget = Icon();
2023-12-23 01:14:21 -05:00
let indicatorWidget = Label();
// Allow setting icon dynamically or statically
if (typeof icon === 'string') {
2023-12-23 01:14:21 -05:00
iconWidget = Icon({
2023-12-20 03:45:05 -05:00
class_name: 'grid-label',
icon,
setup: (self) => {
self.hook(Activated, () => {
self.setCss(`color: ${Activated.value ?
'rgba(189, 147, 249, 0.8)' :
'unset'};`);
});
},
});
}
2023-12-23 01:14:21 -05:00
else if (Array.isArray(icon)) {
iconWidget = Icon({
2023-12-20 03:45:05 -05:00
class_name: 'grid-label',
setup: (self) => {
self
.hook(...icon)
.hook(Activated, () => {
self.setCss(`color: ${Activated.value ?
'rgba(189, 147, 249, 0.8)' :
'unset'};`);
});
},
});
}
if (indicator) {
2023-12-23 01:14:21 -05:00
indicatorWidget = Label({
2023-12-20 03:45:05 -05:00
class_name: 'sub-label',
justification: 'left',
truncate: 'end',
2023-12-23 01:14:21 -05:00
max_width_chars: 12,
setup: (self) => {
self.hook(...indicator);
},
});
}
if (menu) {
menu = Revealer({
transition: 'slide_down',
child: menu,
2023-12-23 01:14:21 -05:00
reveal_child: Activated.bind(),
});
}
const widget = Box({
vertical: true,
children: [
Box({
2023-12-20 03:45:05 -05:00
class_name: 'grid-button',
children: [
CursorBox({
2023-12-20 03:45:05 -05:00
class_name: 'left-part',
on_primary_click_release: () => {
if (Activated.value) {
2023-12-20 03:45:05 -05:00
secondary_command();
}
else {
command();
}
},
2023-12-23 01:14:21 -05:00
child: iconWidget,
}),
CursorBox({
2023-12-20 03:45:05 -05:00
class_name: 'right-part',
on_primary_click_release: () => {
ButtonStates.forEach((state) => {
if (state !== Activated) {
2024-02-11 02:18:59 -05:00
state.setValue(false);
}
});
2024-02-11 02:18:59 -05:00
Activated.setValue(!Activated.value);
},
2023-12-20 03:45:05 -05:00
on_hover: (self) => {
if (menu) {
2023-12-20 03:45:05 -05:00
const rowMenu =
2024-02-21 19:08:55 -05:00
((((self.get_parent() as BoxGeneric)
?.get_parent() as BoxGeneric)
?.get_parent() as BoxGeneric)
?.get_parent() as BoxGeneric)
?.children[1] as BoxGeneric;
2024-01-13 11:15:08 -05:00
const isSetup = (rowMenu
2024-02-21 19:08:55 -05:00
.get_children() as Array<BoxGeneric>)
2024-01-13 11:15:08 -05:00
.find((ch) => ch === menu);
if (!isSetup) {
rowMenu.add(menu);
rowMenu.show_all();
}
}
},
child: Icon({
icon: 'down-large-symbolic',
2023-12-20 03:45:05 -05:00
class_name: 'grid-chev',
setup: (self) => {
self.hook(Activated, () => {
let deg = 270;
if (Activated.value) {
deg = menu ? 360 : 450;
on_open(menu);
}
self.setCss(`
-gtk-icon-transform: rotate(${deg}deg);
`);
});
},
}),
}),
],
}),
2023-12-23 01:14:21 -05:00
indicatorWidget,
],
});
return widget;
};
2023-12-23 01:14:21 -05:00
const Row = ({ buttons }) => {
2024-01-13 11:15:08 -05:00
const child = Box({
class_name: 'button-row',
hpack: 'center',
});
const widget = Box({
vertical: true,
children: [
2024-01-13 11:15:08 -05:00
child,
Box({ vertical: true }),
],
});
for (let i = 0; i < buttons.length; ++i) {
if (i === buttons.length - 1) {
2024-01-13 11:15:08 -05:00
child.add(buttons[i]);
}
else {
2024-01-13 11:15:08 -05:00
child.add(buttons[i]);
child.add(Separator(SPACING));
}
}
return widget;
};
const FirstRow = () => Row({
buttons: [
GridButton({
command: () => Network.toggleWifi(),
2023-12-20 03:45:05 -05:00
secondary_command: () => {
// TODO: connection editor
},
2024-01-13 11:15:08 -05:00
icon: [Network, (self) => {
self.icon = Network.wifi?.icon_name;
}],
2024-01-13 11:15:08 -05:00
indicator: [Network, (self) => {
self.label = Network.wifi?.ssid || Network.wired?.internet;
}],
2023-12-04 15:39:12 -05:00
menu: NetworkMenu(),
on_open: () => Network.wifi.scan(),
}),
// TODO: do vpn
GridButton({
command: () => {
//
},
2023-12-20 03:45:05 -05:00
secondary_command: () => {
//
},
icon: 'airplane-mode-disabled-symbolic',
}),
GridButton({
command: () => Bluetooth.toggle(),
2023-12-20 03:45:05 -05:00
secondary_command: () => {
// TODO: bluetooth connection editor
},
2024-01-13 11:15:08 -05:00
icon: [Bluetooth, (self) => {
if (Bluetooth.enabled) {
self.icon = Bluetooth.connected_devices[0] ?
Bluetooth.connected_devices[0].icon_name :
'bluetooth-active-symbolic';
}
else {
self.icon = 'bluetooth-disabled-symbolic';
}
}],
indicator: [Bluetooth, (self) => {
self.label = Bluetooth.connected_devices[0] ?
`${Bluetooth.connected_devices[0]}` :
'Disconnected';
}, 'notify::connected-devices'],
menu: BluetoothMenu(),
on_open: (menu) => {
2023-12-23 01:14:21 -05:00
execAsync(`bluetoothctl scan ${menu.reveal_child ?
'on' :
2023-12-08 00:01:43 -05:00
'off'}`).catch(print);
},
}),
],
});
const SecondRow = () => Row({
buttons: [
GridButton({
command: () => {
execAsync(['pactl', 'set-sink-mute',
'@DEFAULT_SINK@', 'toggle']).catch(print);
},
2023-12-20 03:45:05 -05:00
secondary_command: () => {
execAsync(['bash', '-c', 'pavucontrol'])
.catch(print);
},
2024-01-13 11:15:08 -05:00
icon: [SpeakerIcon, (self) => {
self.icon = SpeakerIcon.value;
}],
}),
GridButton({
command: () => {
execAsync(['pactl', 'set-source-mute',
'@DEFAULT_SOURCE@', 'toggle']).catch(print);
},
2023-12-20 03:45:05 -05:00
secondary_command: () => {
execAsync(['bash', '-c', 'pavucontrol'])
.catch(print);
},
2024-01-13 11:15:08 -05:00
icon: [MicIcon, (self) => {
self.icon = MicIcon.value;
}],
}),
2024-01-17 20:16:26 -05:00
// TODO: replace this with Rotation Toggle and move lock to a separate section
GridButton({
command: () => {
execAsync(['lock']).catch(print);
},
secondary_command: () => App.openWindow('win-powermenu'),
icon: 'system-lock-screen-symbolic',
}),
],
});
export default () => Box({
2023-12-20 03:45:05 -05:00
class_name: 'button-grid',
vertical: true,
2023-11-06 18:37:23 -05:00
hpack: 'center',
children: [
FirstRow(),
Separator(10, { vertical: true }),
SecondRow(),
],
});