fix(ags): move get_default calls inside functions
All checks were successful
Discord / discord commits (push) Has been skipped

This commit is contained in:
matt1432 2024-11-20 23:38:41 -05:00
parent d223d18842
commit 4604dcb6fd
24 changed files with 438 additions and 382 deletions

View file

@ -33,7 +33,7 @@ export default () => {
respond('closed all windows'); respond('closed all windows');
} }
else if (request.startsWith('fetchCapsState')) { else if (request.startsWith('fetchCapsState')) {
Brightness.fetchCapsState(); Brightness.get_default().fetchCapsState();
respond('fetched caps_lock state'); respond('fetched caps_lock state');
} }
else if (request.startsWith('popup')) { else if (request.startsWith('popup')) {
@ -41,7 +41,7 @@ export default () => {
respond('osd popped up'); respond('osd popped up');
} }
else if (request.startsWith('save-replay')) { else if (request.startsWith('save-replay')) {
GSR.saveReplay(); GSR.get_default().saveReplay();
respond('saving replay'); respond('saving replay');
} }
}, },
@ -61,9 +61,9 @@ export default () => {
PowerMenu(); PowerMenu();
Screenshot(); Screenshot();
Brightness.initService({ caps: 'input2::capslock' }); Brightness.get_default({ caps: 'input2::capslock' });
GSR.initService(); GSR.get_default();
new MonitorClicks(); MonitorClicks.get_default();
}, },
}); });
}; };

View file

@ -34,11 +34,11 @@ export default () => {
respond('closed all windows'); respond('closed all windows');
} }
else if (request.startsWith('fetchCapsState')) { else if (request.startsWith('fetchCapsState')) {
Brightness.fetchCapsState(); Brightness.get_default().fetchCapsState();
respond('fetched caps_lock state'); respond('fetched caps_lock state');
} }
else if (request.startsWith('Brightness.screen')) { else if (request.startsWith('Brightness.screen')) {
Brightness.screen += parseFloat(request.replace('Brightness.screen ', '')); Brightness.get_default().screen += parseFloat(request.replace('Brightness.screen ', ''));
respond('screen brightness changed'); respond('screen brightness changed');
} }
else if (request.startsWith('popup')) { else if (request.startsWith('popup')) {
@ -68,11 +68,11 @@ export default () => {
PowerMenu(); PowerMenu();
Screenshot(); Screenshot();
Brightness.initService({ Brightness.get_default({
kbd: 'tpacpi::kbd_backlight', kbd: 'tpacpi::kbd_backlight',
caps: 'input1::capslock', caps: 'input1::capslock',
}); });
new MonitorClicks(); MonitorClicks.get_default();
}, },
}); });
}; };

View file

@ -2,7 +2,6 @@ import { idle } from 'astal';
import { App, Gdk, Gtk } from 'astal/gtk3'; import { App, Gdk, Gtk } from 'astal/gtk3';
import AstalHyprland from 'gi://AstalHyprland'; import AstalHyprland from 'gi://AstalHyprland';
const Hyprland = AstalHyprland.get_default();
/* Types */ /* Types */
import PopupWindow from './widgets/misc/popup-window'; import PopupWindow from './widgets/misc/popup-window';
@ -32,19 +31,23 @@ export interface CursorPos {
export const get_hyprland_monitor = (monitor: Gdk.Monitor): AstalHyprland.Monitor | undefined => { export const get_hyprland_monitor = (monitor: Gdk.Monitor): AstalHyprland.Monitor | undefined => {
const hyprland = AstalHyprland.get_default();
const manufacturer = monitor.manufacturer?.replace(',', ''); const manufacturer = monitor.manufacturer?.replace(',', '');
const model = monitor.model?.replace(',', ''); const model = monitor.model?.replace(',', '');
const start = `${manufacturer} ${model}`; const start = `${manufacturer} ${model}`;
return Hyprland.get_monitors().find((m) => m.description?.startsWith(start)); return hyprland.get_monitors().find((m) => m.description?.startsWith(start));
}; };
export const get_hyprland_monitor_desc = (monitor: Gdk.Monitor): string => { export const get_hyprland_monitor_desc = (monitor: Gdk.Monitor): string => {
const hyprland = AstalHyprland.get_default();
const manufacturer = monitor.manufacturer?.replace(',', ''); const manufacturer = monitor.manufacturer?.replace(',', '');
const model = monitor.model?.replace(',', ''); const model = monitor.model?.replace(',', '');
const start = `${manufacturer} ${model}`; const start = `${manufacturer} ${model}`;
return `desc:${Hyprland.get_monitors().find((m) => m.description?.startsWith(start))?.description}`; return `desc:${hyprland.get_monitors().find((m) => m.description?.startsWith(start))?.description}`;
}; };
export const get_gdkmonitor_from_desc = (desc: string): Gdk.Monitor => { export const get_gdkmonitor_from_desc = (desc: string): Gdk.Monitor => {
@ -69,9 +72,11 @@ export const hyprMessage = (message: string) => new Promise<string>((
resolution = () => { /**/ }, resolution = () => { /**/ },
rejection = () => { /**/ }, rejection = () => { /**/ },
) => { ) => {
const hyprland = AstalHyprland.get_default();
try { try {
Hyprland.message_async(message, (_, asyncResult) => { hyprland.message_async(message, (_, asyncResult) => {
const result = Hyprland.message_finish(asyncResult); const result = hyprland.message_finish(asyncResult);
resolution(result); resolution(result);
}); });
@ -82,9 +87,11 @@ export const hyprMessage = (message: string) => new Promise<string>((
}); });
export const centerCursor = (): void => { export const centerCursor = (): void => {
const hyprland = AstalHyprland.get_default();
let x: number; let x: number;
let y: number; let y: number;
const monitor = Hyprland.get_focused_monitor(); const monitor = hyprland.get_focused_monitor();
switch (monitor.transform) { switch (monitor.transform) {
case 1: case 1:

View file

@ -12,7 +12,7 @@ const SCREEN_ICONS: Record<number, string> = {
const INTERVAL = 500; const INTERVAL = 500;
@register() @register()
class Brightness extends GObject.Object { export default class Brightness extends GObject.Object {
declare private _kbd: string | undefined; declare private _kbd: string | undefined;
declare private _caps: string | undefined; declare private _caps: string | undefined;
@ -98,33 +98,39 @@ class Brightness extends GObject.Object {
return this._capsIcon; return this._capsIcon;
} }
/** public constructor({ kbd, caps }: { kbd?: string, caps?: string } = {}) {
* This is to basically have the constructor run when I want and super();
* still export this to wherever I need to.
*
* @param o params
* @param o.kbd name of kbd in brightnessctl
* @param o.caps name of caps_lock in brightnessctl
*/
public async initService({ kbd, caps }: { kbd?: string, caps?: string }) {
try {
if (kbd) {
this.hasKbd = true;
this._kbd = kbd;
this._monitorKbdState();
this._kbdMax = Number(await execAsync(`brightnessctl -d ${this._kbd} m`));
}
this._caps = caps; try {
this._screen = Number(await execAsync('brightnessctl g')) / (async() => {
Number(await execAsync('brightnessctl m')); if (kbd) {
this.notify('screen'); this.hasKbd = true;
this._kbd = kbd;
this._monitorKbdState();
this._kbdMax = Number(await execAsync(`brightnessctl -d ${this._kbd} m`));
}
this._caps = caps;
this._screen = Number(await execAsync('brightnessctl g')) /
Number(await execAsync('brightnessctl m'));
this.notify('screen');
})();
} }
catch (_e) { catch (_e) {
console.error('missing dependency: brightnessctl'); console.error('missing dependency: brightnessctl');
} }
} }
private static _default: InstanceType<typeof Brightness> | undefined;
public static get_default({ kbd, caps }: { kbd?: string, caps?: string } = {}) {
if (!Brightness._default) {
Brightness._default = new Brightness({ kbd, caps });
}
return Brightness._default;
}
private _getScreenIcon() { private _getScreenIcon() {
const brightness = this._screen * 100; const brightness = this._screen * 100;
@ -168,7 +174,3 @@ class Brightness extends GObject.Object {
.catch(logError); .catch(logError);
} }
} }
const brightnessService = new Brightness();
export default brightnessService;

View file

@ -72,10 +72,12 @@ const notifySend = ({
}); });
@register() @register()
class GSR extends GObject.Object { export default class GSR extends GObject.Object {
private _lastNotifID: number | undefined; private _lastNotifID: number | undefined;
public initService() { public constructor() {
super();
try { try {
subprocess( subprocess(
['gsr-start'], ['gsr-start'],
@ -99,6 +101,16 @@ class GSR extends GObject.Object {
} }
} }
private static _default: InstanceType<typeof GSR> | undefined;
public static get_default() {
if (!GSR._default) {
GSR._default = new GSR();
}
return GSR._default;
}
public saveReplay() { public saveReplay() {
execAsync(['gpu-save-replay']) execAsync(['gpu-save-replay'])
.then(async() => { .then(async() => {
@ -155,7 +167,3 @@ class GSR extends GObject.Object {
}); });
} }
} }
const gsrService = new GSR();
export default gsrService;

View file

@ -39,10 +39,10 @@ export default class MonitorClicks extends GObject.Object {
constructor() { constructor() {
super(); super();
this.#initAppConnection(); this._initAppConnection();
} }
startProc() { private _startProc() {
if (this.process) { if (this.process) {
return; return;
} }
@ -68,7 +68,7 @@ export default class MonitorClicks extends GObject.Object {
this.emit('proc-started', true); this.emit('proc-started', true);
} }
killProc() { private _killProc() {
if (this.process) { if (this.process) {
this.process.kill(); this.process.kill();
this.process = null; this.process = null;
@ -76,7 +76,7 @@ export default class MonitorClicks extends GObject.Object {
} }
} }
#initAppConnection() { private _initAppConnection() {
App.connect('window-toggled', () => { App.connect('window-toggled', () => {
const anyVisibleAndClosable = const anyVisibleAndClosable =
(App.get_windows() as PopupWindow[]).some((w) => { (App.get_windows() as PopupWindow[]).some((w) => {
@ -90,16 +90,26 @@ export default class MonitorClicks extends GObject.Object {
}); });
if (anyVisibleAndClosable) { if (anyVisibleAndClosable) {
this.startProc(); this._startProc();
} }
else { else {
this.killProc(); this._killProc();
} }
}); });
} }
static async detectClickedOutside(clickStage: string) { private static _default: InstanceType<typeof MonitorClicks> | undefined;
public static get_default() {
if (!MonitorClicks._default) {
MonitorClicks._default = new MonitorClicks();
}
return MonitorClicks._default;
}
public static async detectClickedOutside(clickStage: string) {
const toClose = ((App.get_windows() as PopupWindow[])).some((w) => { const toClose = ((App.get_windows() as PopupWindow[])).some((w) => {
const closable = ( const closable = (
w.close_on_unfocus && w.close_on_unfocus &&
@ -124,6 +134,7 @@ export default class MonitorClicks extends GObject.Object {
const noCloseWidgetsNames = [ const noCloseWidgetsNames = [
'bar-', 'bar-',
'osk', 'osk',
'noanim-',
]; ];
const getNoCloseWidgets = (names: string[]) => { const getNoCloseWidgets = (names: string[]) => {

View file

@ -33,9 +33,7 @@ export class AppItem extends Widget.Box {
); );
const textBox = ( const textBox = (
<box <box vertical>
vertical
>
<label <label
className="title" className="title"
label={app.name} label={app.name}

View file

@ -2,7 +2,6 @@ import { App, Astal, Gdk, Gtk, Widget } from 'astal/gtk3';
import { bind, idle, Variable } from 'astal'; import { bind, idle, Variable } from 'astal';
import AstalHyprland from 'gi://AstalHyprland'; import AstalHyprland from 'gi://AstalHyprland';
const Hyprland = AstalHyprland.get_default();
import { get_hyprland_monitor_desc, get_monitor_desc, hyprMessage } from '../../lib'; import { get_hyprland_monitor_desc, get_monitor_desc, hyprMessage } from '../../lib';
@ -12,49 +11,6 @@ const FullscreenState = Variable({
clientAddrs: new Map() as Map<string, string>, clientAddrs: new Map() as Map<string, string>,
}); });
Hyprland.connect('event', async() => {
const arrayEquals = (a1: unknown[], a2: unknown[]) =>
a1.sort().toString() === a2.sort().toString();
const mapEquals = (m1: Map<string, string>, m2: Map<string, string>) =>
m1.size === m2.size &&
Array.from(m1.keys()).every((key) => m1.get(key) === m2.get(key));
try {
const newMonitors = JSON.parse(await hyprMessage('j/monitors')) as AstalHyprland.Monitor[];
const fs = FullscreenState.get();
const fsClients = Hyprland.get_clients().filter((c) => {
const mon = newMonitors.find((monitor) => monitor.id === c.get_monitor()?.id);
return c.fullscreenClient !== 0 &&
c.workspace.id === mon?.activeWorkspace.id;
});
const monitors = fsClients.map((c) =>
get_monitor_desc(c.monitor));
const clientAddrs = new Map(fsClients.map((c) => [
get_monitor_desc(c.monitor),
c.address ?? '',
]));
const hasChanged =
!arrayEquals(monitors, fs.monitors) ||
!mapEquals(clientAddrs, fs.clientAddrs);
if (hasChanged) {
FullscreenState.set({
monitors,
clientAddrs,
});
}
}
catch (e) {
console.log(e);
}
});
export default ({ export default ({
anchor, anchor,
gdkmonitor = Gdk.Display.get_default()?.get_monitor(0) as Gdk.Monitor, gdkmonitor = Gdk.Display.get_default()?.get_monitor(0) as Gdk.Monitor,
@ -64,16 +20,62 @@ export default ({
anchor: Astal.WindowAnchor anchor: Astal.WindowAnchor
gdkmonitor?: Gdk.Monitor gdkmonitor?: Gdk.Monitor
} & Widget.WindowProps) => { } & Widget.WindowProps) => {
const hyprland = AstalHyprland.get_default();
const monitor = get_hyprland_monitor_desc(gdkmonitor); const monitor = get_hyprland_monitor_desc(gdkmonitor);
const BarVisible = Variable(false); const BarVisible = Variable(false);
hyprland.connect('event', async() => {
const arrayEquals = (a1: unknown[], a2: unknown[]) =>
a1.sort().toString() === a2.sort().toString();
const mapEquals = (m1: Map<string, string>, m2: Map<string, string>) =>
m1.size === m2.size &&
Array.from(m1.keys()).every((key) => m1.get(key) === m2.get(key));
try {
const newMonitors = JSON.parse(await hyprMessage('j/monitors')) as AstalHyprland.Monitor[];
const fs = FullscreenState.get();
const fsClients = hyprland.get_clients().filter((c) => {
const mon = newMonitors.find((m) => m.id === c.get_monitor()?.id);
return c.fullscreenClient !== 0 &&
c.workspace.id === mon?.activeWorkspace.id;
});
const monitors = fsClients.map((c) =>
get_monitor_desc(c.monitor));
const clientAddrs = new Map(fsClients.map((c) => [
get_monitor_desc(c.monitor),
c.address ?? '',
]));
const hasChanged =
!arrayEquals(monitors, fs.monitors) ||
!mapEquals(clientAddrs, fs.clientAddrs);
if (hasChanged) {
FullscreenState.set({
monitors,
clientAddrs,
});
}
}
catch (e) {
console.log(e);
}
});
FullscreenState.subscribe((v) => { FullscreenState.subscribe((v) => {
BarVisible.set(!v.monitors.includes(monitor)); BarVisible.set(!v.monitors.includes(monitor));
}); });
const barCloser = ( const barCloser = (
<window <window
name={`bar-${monitor}-closer`} name={`noanim-bar-${monitor}-closer`}
namespace={`noanim-bar-${monitor}-closer`}
css="all: unset;" css="all: unset;"
visible={false} visible={false}
gdkmonitor={gdkmonitor} gdkmonitor={gdkmonitor}
@ -97,13 +99,13 @@ export default ({
); );
// Hide bar instantly when out of focus // Hide bar instantly when out of focus
Hyprland.connect('notify::focused-workspace', () => { hyprland.connect('notify::focused-workspace', () => {
const addr = FullscreenState.get().clientAddrs.get(monitor); const addr = FullscreenState.get().clientAddrs.get(monitor);
if (addr) { if (addr) {
const client = Hyprland.get_client(addr); const client = hyprland.get_client(addr);
if (client?.workspace.id !== Hyprland.get_focused_workspace().get_id()) { if (client?.workspace.id !== hyprland.get_focused_workspace().get_id()) {
BarVisible.set(true); BarVisible.set(true);
barCloser.visible = false; barCloser.visible = false;
} }
@ -152,8 +154,8 @@ export default ({
const win = ( const win = (
<window <window
name={`bar-${monitor}`} name={`noanim-bar-${monitor}`}
namespace={`bar-${monitor}`} namespace={`noanim-bar-${monitor}`}
layer={Astal.Layer.OVERLAY} layer={Astal.Layer.OVERLAY}
gdkmonitor={gdkmonitor} gdkmonitor={gdkmonitor}
anchor={anchor} anchor={anchor}

View file

@ -1,45 +1,48 @@
import { bind } from 'astal'; import { bind } from 'astal';
import AstalBattery from 'gi://AstalBattery'; import AstalBattery from 'gi://AstalBattery';
const Battery = AstalBattery.get_default();
import Separator from '../../misc/separator'; import Separator from '../../misc/separator';
const LOW_BATT = 20; const LOW_BATT = 20;
export default () => ( export default () => {
<box className="bar-item battery"> const battery = AstalBattery.get_default();
<icon
setup={(self) => { return (
const update = () => { <box className="bar-item battery">
const percent = Math.round(Battery.get_percentage() * 100); <icon
const level = Math.floor(percent / 10) * 10; setup={(self) => {
const isCharging = Battery.get_charging(); const update = () => {
const charged = percent === 100 && isCharging; const percent = Math.round(battery.get_percentage() * 100);
const iconName = charged ? const level = Math.floor(percent / 10) * 10;
'battery-level-100-charged-symbolic' : const isCharging = battery.get_charging();
const charged = percent === 100 && isCharging;
const iconName = charged ?
'battery-level-100-charged-symbolic' :
`battery-level-${level}${isCharging ? `battery-level-${level}${isCharging ?
'-charging' : '-charging' :
''}-symbolic`; ''}-symbolic`;
self.set_icon(iconName); self.set_icon(iconName);
self.toggleClassName('charging', isCharging); self.toggleClassName('charging', isCharging);
self.toggleClassName('charged', charged); self.toggleClassName('charged', charged);
self.toggleClassName('low', percent < LOW_BATT); self.toggleClassName('low', percent < LOW_BATT);
}; };
update(); update();
Battery.connect('notify::percentage', () => update()); battery.connect('notify::percentage', () => update());
Battery.connect('notify::icon-name', () => update()); battery.connect('notify::icon-name', () => update());
Battery.connect('notify::battery-icon-name', () => update()); battery.connect('notify::battery-icon-name', () => update());
}} }}
/> />
<Separator size={8} /> <Separator size={8} />
<label label={bind(Battery, 'percentage').as((v) => `${Math.round(v * 100)}%`)} /> <label label={bind(battery, 'percentage').as((v) => `${Math.round(v * 100)}%`)} />
</box> </box>
); );
};

View file

@ -3,17 +3,19 @@ import { bind } from 'astal';
import Brightness from '../../../services/brightness'; import Brightness from '../../../services/brightness';
export default () => { export default () => {
const brightness = Brightness.get_default();
return ( return (
<box className="bar-item brightness"> <box className="bar-item brightness">
<overlay> <overlay>
<circularprogress <circularprogress
startAt={0.75} startAt={0.75}
endAt={0.75} endAt={0.75}
value={bind(Brightness, 'screen')} value={bind(brightness, 'screen')}
rounded rounded
/> />
<icon icon={bind(Brightness, 'screenIcon')} /> <icon icon={bind(brightness, 'screenIcon')} />
</overlay> </overlay>
</box> </box>
); );

View file

@ -1,16 +1,16 @@
import { bind, Variable } from 'astal'; import { bind, Variable } from 'astal';
import AstalApps from 'gi://AstalApps'; import AstalApps from 'gi://AstalApps';
const Applications = AstalApps.Apps.new();
import AstalHyprland from 'gi://AstalHyprland'; import AstalHyprland from 'gi://AstalHyprland';
const Hyprland = AstalHyprland.get_default();
import Separator from '../../misc/separator'; import Separator from '../../misc/separator';
import { hyprMessage } from '../../../lib'; import { hyprMessage } from '../../../lib';
export default () => { export default () => {
const applications = AstalApps.Apps.new();
const hyprland = AstalHyprland.get_default();
const visibleIcon = Variable<boolean>(false); const visibleIcon = Variable<boolean>(false);
const focusedIcon = Variable<string>(''); const focusedIcon = Variable<string>('');
const focusedTitle = Variable<string>(''); const focusedTitle = Variable<string>('');
@ -18,10 +18,10 @@ export default () => {
let lastFocused: string | undefined; let lastFocused: string | undefined;
const updateVars = ( const updateVars = (
client: AstalHyprland.Client | null = Hyprland.get_focused_client(), client: AstalHyprland.Client | null = hyprland.get_focused_client(),
) => { ) => {
lastFocused = client?.get_address(); lastFocused = client?.get_address();
const app = Applications.fuzzy_query( const app = applications.fuzzy_query(
client?.get_class() ?? '', client?.get_class() ?? '',
)[0]; )[0];
@ -45,11 +45,11 @@ export default () => {
}; };
updateVars(); updateVars();
Hyprland.connect('notify::focused-client', () => updateVars()); hyprland.connect('notify::focused-client', () => updateVars());
Hyprland.connect('client-removed', () => updateVars()); hyprland.connect('client-removed', () => updateVars());
Hyprland.connect('client-added', async() => { hyprland.connect('client-added', async() => {
try { try {
updateVars(Hyprland.get_client(JSON.parse(await hyprMessage('j/activewindow')).address)); updateVars(hyprland.get_client(JSON.parse(await hyprMessage('j/activewindow')).address));
} }
catch (e) { catch (e) {
console.log(e); console.log(e);

View file

@ -1,25 +1,25 @@
import { bind, Variable } from 'astal'; import { bind, Variable } from 'astal';
import AstalApps from 'gi://AstalApps'; import AstalApps from 'gi://AstalApps';
const Applications = AstalApps.Apps.new();
import AstalHyprland from 'gi://AstalHyprland'; import AstalHyprland from 'gi://AstalHyprland';
const Hyprland = AstalHyprland.get_default();
import { hyprMessage } from '../../../lib'; import { hyprMessage } from '../../../lib';
export default () => { export default () => {
const applications = AstalApps.Apps.new();
const hyprland = AstalHyprland.get_default();
const visibleIcon = Variable<boolean>(false); const visibleIcon = Variable<boolean>(false);
const focusedIcon = Variable<string>(''); const focusedIcon = Variable<string>('');
let lastFocused: string | undefined; let lastFocused: string | undefined;
const updateVars = ( const updateVars = (
client: AstalHyprland.Client | null = Hyprland.get_focused_client(), client: AstalHyprland.Client | null = hyprland.get_focused_client(),
) => { ) => {
lastFocused = client?.get_address(); lastFocused = client?.get_address();
const app = Applications.fuzzy_query( const app = applications.fuzzy_query(
client?.get_class() ?? '', client?.get_class() ?? '',
)[0]; )[0];
@ -41,11 +41,11 @@ export default () => {
}; };
updateVars(); updateVars();
Hyprland.connect('notify::focused-client', () => updateVars()); hyprland.connect('notify::focused-client', () => updateVars());
Hyprland.connect('client-removed', () => updateVars()); hyprland.connect('client-removed', () => updateVars());
Hyprland.connect('client-added', async() => { hyprland.connect('client-added', async() => {
try { try {
updateVars(Hyprland.get_client(JSON.parse(await hyprMessage('j/activewindow')).address)); updateVars(hyprland.get_client(JSON.parse(await hyprMessage('j/activewindow')).address));
} }
catch (e) { catch (e) {
console.log(e); console.log(e);

View file

@ -5,10 +5,10 @@ import AstalNetwork from 'gi://AstalNetwork';
export default () => { export default () => {
const Hovered = Variable(false);
// TODO: do this everywhere else
const network = AstalNetwork.get_default(); const network = AstalNetwork.get_default();
const Hovered = Variable(false);
return ( return (
<button <button
className="bar-item network" className="bar-item network"

View file

@ -2,7 +2,6 @@ import { bind } from 'astal';
import { App } from 'astal/gtk3'; import { App } from 'astal/gtk3';
import AstalNotifd from 'gi://AstalNotifd'; import AstalNotifd from 'gi://AstalNotifd';
const Notifications = AstalNotifd.get_default();
import Separator from '../../misc/separator'; import Separator from '../../misc/separator';
@ -12,48 +11,52 @@ const SPACING = 4;
import PopupWindow from '../../misc/popup-window'; import PopupWindow from '../../misc/popup-window';
export default () => ( export default () => {
<button const notifications = AstalNotifd.get_default();
className="bar-item"
cursor="pointer"
onButtonReleaseEvent={(self) => { return (
const win = App.get_window('win-notif-center') as PopupWindow; <button
className="bar-item"
cursor="pointer"
win.set_x_pos( onButtonReleaseEvent={(self) => {
self.get_allocation(), const win = App.get_window('win-notif-center') as PopupWindow;
'right',
);
win.visible = !win.visible; win.set_x_pos(
}} self.get_allocation(),
'right',
);
setup={(self) => { win.visible = !win.visible;
App.connect('window-toggled', (_, win) => { }}
if (win.name === 'win-notif-center') {
self.toggleClassName('toggle-on', win.visible); setup={(self) => {
} App.connect('window-toggled', (_, win) => {
}); if (win.name === 'win-notif-center') {
}} self.toggleClassName('toggle-on', win.visible);
>
<box>
<icon
icon={bind(Notifications, 'notifications').as((notifs) => {
if (Notifications.dontDisturb) {
return 'notification-disabled-symbolic';
} }
else if (notifs.length > 0) { });
return 'notification-new-symbolic'; }}
} >
else { <box>
return 'notification-symbolic'; <icon
} icon={bind(notifications, 'notifications').as((notifs) => {
})} if (notifications.dontDisturb) {
/> return 'notification-disabled-symbolic';
}
else if (notifs.length > 0) {
return 'notification-new-symbolic';
}
else {
return 'notification-symbolic';
}
})}
/>
<Separator size={SPACING} /> <Separator size={SPACING} />
<label label={bind(Notifications, 'notifications').as((n) => String(n.length))} /> <label label={bind(notifications, 'notifications').as((n) => String(n.length))} />
</box> </box>
</button> </button>
); );
};

View file

@ -2,7 +2,6 @@ import { App, Gdk, Gtk, Widget } from 'astal/gtk3';
import { bind, idle } from 'astal'; import { bind, idle } from 'astal';
import AstalTray from 'gi://AstalTray'; import AstalTray from 'gi://AstalTray';
const Tray = AstalTray.get_default();
const SKIP_ITEMS = ['.spotify-wrapped']; const SKIP_ITEMS = ['.spotify-wrapped'];
@ -35,20 +34,22 @@ const TrayItem = (item: AstalTray.TrayItem) => {
}; };
export default () => { export default () => {
const tray = AstalTray.get_default();
const itemMap = new Map<string, Widget.Revealer>(); const itemMap = new Map<string, Widget.Revealer>();
return ( return (
<box <box
className="bar-item system-tray" className="bar-item system-tray"
visible={bind(Tray, 'items').as((items) => items.length !== 0)} visible={bind(tray, 'items').as((items) => items.length !== 0)}
setup={(self) => { setup={(self) => {
self self
.hook(Tray, 'item-added', (_, item: string) => { .hook(tray, 'item-added', (_, item: string) => {
if (itemMap.has(item) || SKIP_ITEMS.includes(Tray.get_item(item).title)) { if (itemMap.has(item) || SKIP_ITEMS.includes(tray.get_item(item).title)) {
return; return;
} }
const widget = TrayItem(Tray.get_item(item)) as Widget.Revealer; const widget = TrayItem(tray.get_item(item)) as Widget.Revealer;
itemMap.set(item, widget); itemMap.set(item, widget);
@ -59,7 +60,7 @@ export default () => {
}); });
}) })
.hook(Tray, 'item-removed', (_, item: string) => { .hook(tray, 'item-removed', (_, item: string) => {
if (!itemMap.has(item)) { if (!itemMap.has(item)) {
return; return;
} }

View file

@ -2,78 +2,83 @@ import { Gtk, Widget } from 'astal/gtk3';
import { timeout } from 'astal'; import { timeout } from 'astal';
import AstalHyprland from 'gi://AstalHyprland'; import AstalHyprland from 'gi://AstalHyprland';
const Hyprland = AstalHyprland.get_default();
import { hyprMessage } from '../../../lib'; import { hyprMessage } from '../../../lib';
const URGENT_DURATION = 1000; const URGENT_DURATION = 1000;
const Workspace = ({ id = 0 }) => ( const Workspace = ({ id = 0 }) => {
<revealer const hyprland = AstalHyprland.get_default();
name={id.toString()}
transitionType={Gtk.RevealerTransitionType.SLIDE_RIGHT}
>
<eventbox
cursor="pointer"
tooltip_text={id.toString()}
onClickRelease={() => { return (
hyprMessage(`dispatch workspace ${id}`).catch(console.log); <revealer
}} name={id.toString()}
transitionType={Gtk.RevealerTransitionType.SLIDE_RIGHT}
> >
<box <eventbox
valign={Gtk.Align.CENTER} cursor="pointer"
className="button" tooltip_text={id.toString()}
setup={(self) => { onClickRelease={() => {
const update = ( hyprMessage(`dispatch workspace ${id}`).catch(console.log);
_: Widget.Box, }}
client?: AstalHyprland.Client, >
) => { <box
const workspace = Hyprland.get_workspace(id); valign={Gtk.Align.CENTER}
const occupied = workspace && workspace.get_clients().length > 0; className="button"
self.toggleClassName('occupied', occupied); setup={(self) => {
const update = (
_: Widget.Box,
client?: AstalHyprland.Client,
) => {
const workspace = hyprland.get_workspace(id);
const occupied = workspace && workspace.get_clients().length > 0;
if (!client) { self.toggleClassName('occupied', occupied);
return;
}
const isUrgent = client && if (!client) {
return;
}
const isUrgent = client &&
client.get_workspace().get_id() === id; client.get_workspace().get_id() === id;
if (isUrgent) { if (isUrgent) {
self.toggleClassName('urgent', true); self.toggleClassName('urgent', true);
// Only show for a sec when urgent is current workspace // Only show for a sec when urgent is current workspace
if (Hyprland.get_focused_workspace().get_id() === id) { if (hyprland.get_focused_workspace().get_id() === id) {
timeout(URGENT_DURATION, () => { timeout(URGENT_DURATION, () => {
self.toggleClassName('urgent', false); self.toggleClassName('urgent', false);
}); });
}
} }
} };
};
update(self); update(self);
self self
.hook(Hyprland, 'event', () => update(self)) .hook(hyprland, 'event', () => update(self))
// Deal with urgent windows // Deal with urgent windows
.hook(Hyprland, 'urgent', update) .hook(hyprland, 'urgent', update)
.hook(Hyprland, 'notify::focused-workspace', () => { .hook(hyprland, 'notify::focused-workspace', () => {
if (Hyprland.get_focused_workspace().get_id() === id) { if (hyprland.get_focused_workspace().get_id() === id) {
self.toggleClassName('urgent', false); self.toggleClassName('urgent', false);
} }
}); });
}} }}
/> />
</eventbox> </eventbox>
</revealer> </revealer>
); );
};
export default () => { export default () => {
const Hyprland = AstalHyprland.get_default();
const L_PADDING = 2; const L_PADDING = 2;
const WS_WIDTH = 30; const WS_WIDTH = 30;

View file

@ -94,5 +94,3 @@ export class ClipItem extends Widget.Box {
} }
} }
} }
export default ClipItem;

View file

@ -10,8 +10,8 @@ import Center from './center';
export const NotifPopups = () => ( export const NotifPopups = () => (
<window <window
name="notifications" name="notifications"
namespace="noanim-notifications"
gdkmonitor={get_gdkmonitor_from_desc('desc:Acer Technologies Acer K212HQL T3EAA0014201')} gdkmonitor={get_gdkmonitor_from_desc('desc:Acer Technologies Acer K212HQL T3EAA0014201')}
namespace="notifications"
layer={Astal.Layer.OVERLAY} layer={Astal.Layer.OVERLAY}
anchor={Astal.WindowAnchor.BOTTOM | Astal.WindowAnchor.LEFT} anchor={Astal.WindowAnchor.BOTTOM | Astal.WindowAnchor.LEFT}
> >

View file

@ -2,7 +2,6 @@ import { bind, timeout } from 'astal';
import { App, Gtk, Widget } from 'astal/gtk3'; import { App, Gtk, Widget } from 'astal/gtk3';
import AstalNotifd from 'gi://AstalNotifd'; import AstalNotifd from 'gi://AstalNotifd';
const Notifications = AstalNotifd.get_default();
import { Notification, HasNotifs } from './notification'; import { Notification, HasNotifs } from './notification';
import NotifGestureWrapper from './gesture'; import NotifGestureWrapper from './gesture';
@ -22,71 +21,79 @@ const addNotif = (box: Widget.Box, notifObj: AstalNotifd.Notification) => {
} }
}; };
const NotificationList = () => ( const NotificationList = () => {
<box const notifications = AstalNotifd.get_default();
vertical
vexpand
valign={Gtk.Align.START}
visible={bind(HasNotifs)}
// It needs to be bigger than the notifs to not jiggle
css="min-width: 550px;"
setup={(self) => { return (
Notifications.get_notifications().forEach((n) => { <box
addNotif(self, n); vertical
}); vexpand
valign={Gtk.Align.START}
visible={bind(HasNotifs)}
// It needs to be bigger than the notifs to not jiggle
css="min-width: 550px;"
self setup={(self) => {
.hook(Notifications, 'notified', (_, id) => { notifications.get_notifications().forEach((n) => {
if (id) { addNotif(self, n);
const notifObj = Notifications.get_notification(id); });
if (notifObj) { self
addNotif(self, notifObj); .hook(notifications, 'notified', (_, id) => {
if (id) {
const notifObj = notifications.get_notification(id);
if (notifObj) {
addNotif(self, notifObj);
}
} }
} })
}) .hook(notifications, 'resolved', (_, id) => {
.hook(Notifications, 'resolved', (_, id) => { const notif = (self.get_children() as NotifGestureWrapper[])
const notif = (self.get_children() as NotifGestureWrapper[]) .find((ch) => ch.id === id);
.find((ch) => ch.id === id);
if (notif?.sensitive) { if (notif?.sensitive) {
notif.slideAway('Right'); notif.slideAway('Right');
} }
}); });
}}
/>
);
const ClearButton = () => (
<eventbox
cursor={bind(HasNotifs).as((hasNotifs) => hasNotifs ? 'pointer' : 'not-allowed')}
>
<button
className="clear"
sensitive={bind(HasNotifs)}
onButtonReleaseEvent={() => {
Notifications.get_notifications().forEach((notif) => {
notif.dismiss();
});
timeout(1000, () => {
App.get_window('win-notif-center')?.set_visible(false);
});
}} }}
> />
<box> );
<label label="Clear " /> };
<icon icon={bind(Notifications, 'notifications') const ClearButton = () => {
.as((notifs) => notifs.length > 0 ? const notifications = AstalNotifd.get_default();
'user-trash-full-symbolic' :
'user-trash-symbolic')} return (
/> <eventbox
</box> cursor={bind(HasNotifs).as((hasNotifs) => hasNotifs ? 'pointer' : 'not-allowed')}
</button> >
</eventbox> <button
); className="clear"
sensitive={bind(HasNotifs)}
onButtonReleaseEvent={() => {
notifications.get_notifications().forEach((notif) => {
notif.dismiss();
});
timeout(1000, () => {
App.get_window('win-notif-center')?.set_visible(false);
});
}}
>
<box>
<label label="Clear " />
<icon icon={bind(notifications, 'notifications')
.as((notifs) => notifs.length > 0 ?
'user-trash-full-symbolic' :
'user-trash-symbolic')}
/>
</box>
</button>
</eventbox>
);
};
const Header = () => ( const Header = () => (
<box className="header"> <box className="header">

View file

@ -5,7 +5,6 @@ import { idle, interval, timeout } from 'astal';
import AstalIO from 'gi://AstalIO'; import AstalIO from 'gi://AstalIO';
import AstalNotifd from 'gi://AstalNotifd'; import AstalNotifd from 'gi://AstalNotifd';
const Notifications = AstalNotifd.get_default();
import { hyprMessage } from '../../lib'; import { hyprMessage } from '../../lib';
@ -159,10 +158,12 @@ export class NotifGestureWrapper extends Widget.EventBox {
if (!duplicate) { if (!duplicate) {
// Kill notif if specified // Kill notif if specified
if (!this.is_popup) { if (!this.is_popup) {
Notifications.get_notification(this.id)?.dismiss(); const notifications = AstalNotifd.get_default();
notifications.get_notification(this.id)?.dismiss();
// Update HasNotifs // Update HasNotifs
HasNotifs.set(Notifications.get_notifications().length > 0); HasNotifs.set(notifications.get_notifications().length > 0);
} }
else { else {
// Make sure we cleanup any references to this instance // Make sure we cleanup any references to this instance
@ -183,6 +184,8 @@ export class NotifGestureWrapper extends Widget.EventBox {
setup_notif = () => { /**/ }, setup_notif = () => { /**/ },
...rest ...rest
}: NotifGestureWrapperProps) { }: NotifGestureWrapperProps) {
const notifications = AstalNotifd.get_default();
super({ super({
on_button_press_event: () => { on_button_press_event: () => {
this.setCursor('grabbing'); this.setCursor('grabbing');
@ -234,7 +237,7 @@ export class NotifGestureWrapper extends Widget.EventBox {
}); });
} }
this.hook(Notifications, 'notified', (_, notifId) => { this.hook(notifications, 'notified', (_, notifId) => {
if (notifId === this.id) { if (notifId === this.id) {
this.slideAway(this.is_popup ? 'Left' : 'Right', true); this.slideAway(this.is_popup ? 'Left' : 'Right', true);
} }
@ -315,7 +318,7 @@ export class NotifGestureWrapper extends Widget.EventBox {
slideRight; slideRight;
idle(() => { idle(() => {
if (!Notifications.get_notification(id)) { if (!notifications.get_notification(id)) {
return; return;
} }
@ -328,7 +331,7 @@ export class NotifGestureWrapper extends Widget.EventBox {
rev.revealChild = true; rev.revealChild = true;
timeout(ANIM_DURATION, () => { timeout(ANIM_DURATION, () => {
if (!Notifications.get_notification(id)) { if (!notifications.get_notification(id)) {
return; return;
} }

View file

@ -7,7 +7,6 @@ import AstalApps from 'gi://AstalApps';
const Applications = AstalApps.Apps.new(); const Applications = AstalApps.Apps.new();
import AstalNotifd from 'gi://AstalNotifd'; import AstalNotifd from 'gi://AstalNotifd';
const Notifications = AstalNotifd.get_default();
import NotifGestureWrapper from './gesture'; import NotifGestureWrapper from './gesture';
// import SmoothProgress from '../misc/smooth-progress'; // import SmoothProgress from '../misc/smooth-progress';
@ -94,7 +93,9 @@ export const Notification = ({
popup_timer = 0, popup_timer = 0,
slide_in_from = 'Left' as 'Left' | 'Right', slide_in_from = 'Left' as 'Left' | 'Right',
}): NotifGestureWrapper | undefined => { }): NotifGestureWrapper | undefined => {
const notifObj = Notifications.get_notification(id); const notifications = AstalNotifd.get_default();
const notifObj = notifications.get_notification(id);
if (!notifObj) { if (!notifObj) {
return; return;
@ -106,7 +107,7 @@ export const Notification = ({
return; return;
} }
HasNotifs.set(Notifications.get_notifications().length > 0); HasNotifs.set(notifications.get_notifications().length > 0);
// const progress = SmoothProgress({ className: 'smooth-progress' }); // const progress = SmoothProgress({ className: 'smooth-progress' });

View file

@ -1,65 +1,68 @@
import AstalNotifd from 'gi://AstalNotifd'; import AstalNotifd from 'gi://AstalNotifd';
const Notifications = AstalNotifd.get_default();
import NotifGestureWrapper from './gesture'; import NotifGestureWrapper from './gesture';
import { Notification } from './notification'; import { Notification } from './notification';
export default () => ( export default () => {
<box const notifications = AstalNotifd.get_default();
return (
<box
// Needed so it occupies space at the start // Needed so it occupies space at the start
// It needs to be bigger than the notifs to not jiggle // It needs to be bigger than the notifs to not jiggle
css="min-width: 550px;" css="min-width: 550px;"
vertical vertical
setup={(self) => { setup={(self) => {
const notifQueue: number[] = []; const notifQueue: number[] = [];
const addPopup = (id: number) => { const addPopup = (id: number) => {
if (!id || !Notifications.get_notification(id)) { if (!id || !notifications.get_notification(id)) {
return; return;
} }
if (NotifGestureWrapper.sliding_in === 0) { if (NotifGestureWrapper.sliding_in === 0) {
const NewNotif = Notification({ id, popup_timer: 5 }); const NewNotif = Notification({ id, popup_timer: 5 });
if (NewNotif) { if (NewNotif) {
// Use this instead of add to put it at the top // Use this instead of add to put it at the top
self.pack_end(NewNotif, false, false, 0); self.pack_end(NewNotif, false, false, 0);
self.show_all(); self.show_all();
NotifGestureWrapper.popups.set(id, NewNotif); NotifGestureWrapper.popups.set(id, NewNotif);
}
} }
} else {
else { notifQueue.push(id);
notifQueue.push(id);
}
};
NotifGestureWrapper.on_sliding_in = (n) => {
if (n === 0) {
const id = notifQueue.shift();
if (id) {
addPopup(id);
} }
} };
};
const handleResolved = (id: number) => { NotifGestureWrapper.on_sliding_in = (n) => {
const notif = NotifGestureWrapper.popups.get(id); if (n === 0) {
const id = notifQueue.shift();
if (!notif) { if (id) {
return; addPopup(id);
} }
}
};
notif.slideAway('Left'); const handleResolved = (id: number) => {
NotifGestureWrapper.popups.delete(id); const notif = NotifGestureWrapper.popups.get(id);
};
self if (!notif) {
.hook(Notifications, 'notified', (_, id) => addPopup(id)) return;
.hook(Notifications, 'resolved', (_, id) => handleResolved(id)); }
}}
/> notif.slideAway('Left');
); NotifGestureWrapper.popups.delete(id);
};
self
.hook(notifications, 'notified', (_, id) => addPopup(id))
.hook(notifications, 'resolved', (_, id) => handleResolved(id));
}}
/>
);
};

View file

@ -51,6 +51,7 @@ export default () => {
globalThis.popup_osd = popup; globalThis.popup_osd = popup;
const brightness = Brightness.get_default();
const speaker = AstalWp.get_default()?.audio.default_speaker; const speaker = AstalWp.get_default()?.audio.default_speaker;
const microphone = AstalWp.get_default()?.audio.default_microphone; const microphone = AstalWp.get_default()?.audio.default_microphone;
@ -123,29 +124,29 @@ export default () => {
css="margin-bottom: 80px;" css="margin-bottom: 80px;"
setup={(self) => { setup={(self) => {
self.hook(Brightness, 'notify::screen-icon', () => { self.hook(brightness, 'notify::screen-icon', () => {
popup('brightness'); popup('brightness');
}); });
}} }}
> >
<box className="osd-item widget"> <box className="osd-item widget">
<icon icon={bind(Brightness, 'screenIcon')} /> <icon icon={bind(brightness, 'screenIcon')} />
<ProgressBar <ProgressBar
fraction={bind(Brightness, 'screen')} fraction={bind(brightness, 'screen')}
valign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER}
/> />
</box> </box>
</box> </box>
{ {
Brightness.hasKbd && ( brightness.hasKbd && (
<box <box
name="keyboard" name="keyboard"
css="margin-bottom: 80px;" css="margin-bottom: 80px;"
setup={(self) => { setup={(self) => {
self.hook(Brightness, 'notify::kbd-level', () => { self.hook(brightness, 'notify::kbd-level', () => {
popup('keyboard'); popup('keyboard');
}); });
}} }}
@ -154,8 +155,8 @@ export default () => {
<icon icon="keyboard-brightness-symbolic" /> <icon icon="keyboard-brightness-symbolic" />
<ProgressBar <ProgressBar
fraction={bind(Brightness, 'kbdLevel').as((v) => (v ?? 0) / 2)} fraction={bind(brightness, 'kbdLevel').as((v) => (v ?? 0) / 2)}
sensitive={bind(Brightness, 'kbdLevel').as((v) => v !== 0)} sensitive={bind(brightness, 'kbdLevel').as((v) => v !== 0)}
valign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER}
/> />
</box> </box>
@ -168,13 +169,13 @@ export default () => {
css="margin-bottom: 80px;" css="margin-bottom: 80px;"
setup={(self) => { setup={(self) => {
self.hook(Brightness, 'notify::caps-icon', () => { self.hook(brightness, 'notify::caps-icon', () => {
popup('caps'); popup('caps');
}); });
}} }}
> >
<box className="osd-item widget"> <box className="osd-item widget">
<icon icon={bind(Brightness, 'capsIcon')} /> <icon icon={bind(brightness, 'capsIcon')} />
<label label="Caps Lock" /> <label label="Caps Lock" />
</box> </box>

View file

@ -2,10 +2,7 @@ import { bind, execAsync, Variable } from 'astal';
import { App, Gtk, Widget } from 'astal/gtk3'; import { App, Gtk, Widget } from 'astal/gtk3';
import AstalApps from 'gi://AstalApps'; import AstalApps from 'gi://AstalApps';
const Applications = AstalApps.Apps.new();
import AstalHyprland from 'gi://AstalHyprland'; import AstalHyprland from 'gi://AstalHyprland';
const Hyprland = AstalHyprland.get_default();
import PopupWindow from '../misc/popup-window'; import PopupWindow from '../misc/popup-window';
import Separator from '../misc/separator'; import Separator from '../misc/separator';
@ -25,6 +22,8 @@ const takeScreenshot = (selector: string[], delay = 1000): void => {
}; };
export default () => { export default () => {
const hyprland = AstalHyprland.get_default();
const windowList = <box vertical /> as Widget.Box; const windowList = <box vertical /> as Widget.Box;
const updateWindows = async() => { const updateWindows = async() => {
@ -32,8 +31,10 @@ export default () => {
return; return;
} }
const applications = AstalApps.Apps.new();
windowList.children = (JSON.parse(await hyprMessage('j/clients')) as AstalHyprland.Client[]) windowList.children = (JSON.parse(await hyprMessage('j/clients')) as AstalHyprland.Client[])
.filter((client) => client.workspace.id === Hyprland.get_focused_workspace().get_id()) .filter((client) => client.workspace.id === hyprland.get_focused_workspace().get_id())
.map((client) => ( .map((client) => (
<button <button
className="item-btn" className="item-btn"
@ -44,7 +45,7 @@ export default () => {
}} }}
> >
<box halign={Gtk.Align.CENTER}> <box halign={Gtk.Align.CENTER}>
<icon icon={Applications.fuzzy_query(client.class)[0].iconName} /> <icon icon={applications.fuzzy_query(client.class)[0].iconName} />
<Separator size={ICON_SEP} /> <Separator size={ICON_SEP} />
@ -58,8 +59,8 @@ export default () => {
)); ));
}; };
Hyprland.connect('notify::clients', updateWindows); hyprland.connect('notify::clients', updateWindows);
Hyprland.connect('notify::focused-workspace', updateWindows); hyprland.connect('notify::focused-workspace', updateWindows);
const Shown = Variable<string>('monitors'); const Shown = Variable<string>('monitors');
@ -70,7 +71,7 @@ export default () => {
> >
<scrollable name="monitors"> <scrollable name="monitors">
<box vertical> <box vertical>
{bind(Hyprland, 'monitors').as((monitors) => monitors.map((monitor) => ( {bind(hyprland, 'monitors').as((monitors) => monitors.map((monitor) => (
<button <button
className="item-btn" className="item-btn"
cursor="pointer" cursor="pointer"