feat(agsV2): add notif-button in bar
All checks were successful
Discord / discord commits (push) Has been skipped
All checks were successful
Discord / discord commits (push) Has been skipped
This commit is contained in:
parent
e3ca8dc85e
commit
50fc642f03
11 changed files with 228 additions and 97 deletions
|
@ -5,7 +5,7 @@ import style from './style.scss';
|
||||||
import Bar from './widgets/bar/wim';
|
import Bar from './widgets/bar/wim';
|
||||||
import BgFade from './widgets/bg-fade/main';
|
import BgFade from './widgets/bg-fade/main';
|
||||||
import Corners from './widgets/corners/main';
|
import Corners from './widgets/corners/main';
|
||||||
import { NotifPopups } from './widgets/notifs/main';
|
import { NotifPopups, NotifCenter } from './widgets/notifs/main';
|
||||||
|
|
||||||
import MonitorClicks from './services/monitor-clicks';
|
import MonitorClicks from './services/monitor-clicks';
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ App.start({
|
||||||
BgFade();
|
BgFade();
|
||||||
Corners();
|
Corners();
|
||||||
NotifPopups();
|
NotifPopups();
|
||||||
|
NotifCenter();
|
||||||
|
|
||||||
new MonitorClicks();
|
new MonitorClicks();
|
||||||
},
|
},
|
||||||
|
|
|
@ -113,26 +113,27 @@ export default class MonitorClicks extends GObject.Object {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const layers = JSON.parse(await hyprMessage('j/layers')) as LayerResult;
|
try {
|
||||||
const pos = JSON.parse(await hyprMessage('j/cursorpos')) as CursorPos;
|
const layers = JSON.parse(await hyprMessage('j/layers')) as LayerResult;
|
||||||
|
const pos = JSON.parse(await hyprMessage('j/cursorpos')) as CursorPos;
|
||||||
|
|
||||||
Object.values(layers).forEach((key) => {
|
Object.values(layers).forEach((key) => {
|
||||||
const overlayLayer = key.levels['3'];
|
const overlayLayer = key.levels['3'];
|
||||||
|
|
||||||
if (overlayLayer) {
|
if (overlayLayer) {
|
||||||
const noCloseWidgetsNames = [
|
const noCloseWidgetsNames = [
|
||||||
'bar-',
|
'bar-',
|
||||||
'osk',
|
'osk',
|
||||||
];
|
];
|
||||||
|
|
||||||
const getNoCloseWidgets = (names: string[]) => {
|
const getNoCloseWidgets = (names: string[]) => {
|
||||||
const arr = [] as Layer[];
|
const arr = [] as Layer[];
|
||||||
|
|
||||||
names.forEach((name) => {
|
names.forEach((name) => {
|
||||||
arr.push(
|
arr.push(
|
||||||
overlayLayer.find(
|
overlayLayer.find(
|
||||||
(n) => n.namespace.startsWith(name),
|
(n) => n.namespace.startsWith(name),
|
||||||
) ||
|
) ||
|
||||||
// Return an empty Layer if widget doesn't exist
|
// Return an empty Layer if widget doesn't exist
|
||||||
{
|
{
|
||||||
address: '',
|
address: '',
|
||||||
|
@ -142,44 +143,48 @@ export default class MonitorClicks extends GObject.Object {
|
||||||
h: 0,
|
h: 0,
|
||||||
namespace: '',
|
namespace: '',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return arr;
|
return arr;
|
||||||
};
|
};
|
||||||
const clickIsOnWidget = (w: Layer) => {
|
const clickIsOnWidget = (w: Layer) => {
|
||||||
return pos.x > w.x && pos.x < w.x + w.w &&
|
return pos.x > w.x && pos.x < w.x + w.w &&
|
||||||
pos.y > w.y && pos.y < w.y + w.h;
|
pos.y > w.y && pos.y < w.y + w.h;
|
||||||
};
|
};
|
||||||
|
|
||||||
const noCloseWidgets = getNoCloseWidgets(noCloseWidgetsNames);
|
const noCloseWidgets = getNoCloseWidgets(noCloseWidgetsNames);
|
||||||
|
|
||||||
const widgets = overlayLayer.filter((n) => {
|
const widgets = overlayLayer.filter((n) => {
|
||||||
let window = null as null | PopupWindow;
|
let window = null as null | PopupWindow;
|
||||||
|
|
||||||
if (App.get_windows().some((win) =>
|
if (App.get_windows().some((win) =>
|
||||||
win.name === n.namespace)) {
|
win.name === n.namespace)) {
|
||||||
window = (App.get_window(n.namespace) as PopupWindow);
|
window = (App.get_window(n.namespace) as PopupWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
return window &&
|
return window &&
|
||||||
window.close_on_unfocus &&
|
window.close_on_unfocus &&
|
||||||
window.close_on_unfocus ===
|
window.close_on_unfocus ===
|
||||||
clickStage;
|
clickStage;
|
||||||
});
|
|
||||||
|
|
||||||
if (noCloseWidgets.some(clickIsOnWidget)) {
|
|
||||||
// Don't handle clicks when on certain widgets
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
widgets.forEach((w) => {
|
|
||||||
if (!(pos.x > w.x && pos.x < w.x + w.w &&
|
|
||||||
pos.y > w.y && pos.y < w.y + w.h)) {
|
|
||||||
App.get_window(w.namespace)?.set_visible(false);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (noCloseWidgets.some(clickIsOnWidget)) {
|
||||||
|
// Don't handle clicks when on certain widgets
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
widgets.forEach((w) => {
|
||||||
|
if (!(pos.x > w.x && pos.x < w.x + w.w &&
|
||||||
|
pos.y > w.y && pos.y < w.y + w.h)) {
|
||||||
|
App.get_window(w.namespace)?.set_visible(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,33 +20,38 @@ Hyprland.connect('event', async() => {
|
||||||
m1.size === m2.size &&
|
m1.size === m2.size &&
|
||||||
Array.from(m1.keys()).every((key) => m1.get(key) === m2.get(key));
|
Array.from(m1.keys()).every((key) => m1.get(key) === m2.get(key));
|
||||||
|
|
||||||
const newMonitors = JSON.parse(await hyprMessage('j/monitors')) as AstalHyprland.Monitor[];
|
try {
|
||||||
|
const newMonitors = JSON.parse(await hyprMessage('j/monitors')) as AstalHyprland.Monitor[];
|
||||||
|
|
||||||
const fs = FullscreenState.get();
|
const fs = FullscreenState.get();
|
||||||
const fsClients = Hyprland.get_clients().filter((c) => {
|
const fsClients = Hyprland.get_clients().filter((c) => {
|
||||||
const mon = newMonitors.find((monitor) => monitor.id === c.get_monitor().id);
|
const mon = newMonitors.find((monitor) => monitor.id === c.get_monitor()?.id);
|
||||||
|
|
||||||
return c.fullscreenClient !== 0 &&
|
return c.fullscreenClient !== 0 &&
|
||||||
c.workspace.id === mon?.activeWorkspace.id;
|
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,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,12 @@ export default () => {
|
||||||
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() => {
|
||||||
updateVars(Hyprland.get_client(JSON.parse(await hyprMessage('j/activewindow')).address));
|
try {
|
||||||
|
updateVars(Hyprland.get_client(JSON.parse(await hyprMessage('j/activewindow')).address));
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
59
nixosModules/ags/v2/widgets/bar/items/notif-button.tsx
Normal file
59
nixosModules/ags/v2/widgets/bar/items/notif-button.tsx
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import { bind } from 'astal';
|
||||||
|
import { App } from 'astal/gtk3';
|
||||||
|
|
||||||
|
import AstalNotifd from 'gi://AstalNotifd?version=0.1';
|
||||||
|
const Notifications = AstalNotifd.get_default();
|
||||||
|
|
||||||
|
import Separator from '../../misc/separator';
|
||||||
|
|
||||||
|
const SPACING = 4;
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import { PopupWindow } from '../../misc/popup-window';
|
||||||
|
|
||||||
|
|
||||||
|
export default () => (
|
||||||
|
<button
|
||||||
|
className="bar-item"
|
||||||
|
cursor="pointer"
|
||||||
|
|
||||||
|
onButtonReleaseEvent={(self) => {
|
||||||
|
const win = App.get_window('win-notif-center') as PopupWindow;
|
||||||
|
|
||||||
|
win.set_x_pos(
|
||||||
|
self.get_allocation(),
|
||||||
|
'right',
|
||||||
|
);
|
||||||
|
|
||||||
|
win.visible = !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 {
|
||||||
|
return 'notification-symbolic';
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Separator size={SPACING} />
|
||||||
|
|
||||||
|
<label label={bind(Notifications, 'notifications').as((n) => String(n.length))} />
|
||||||
|
</box>
|
||||||
|
</button>
|
||||||
|
);
|
|
@ -19,7 +19,7 @@ const Workspace = ({ id = 0 }) => (
|
||||||
tooltip_text={id.toString()}
|
tooltip_text={id.toString()}
|
||||||
|
|
||||||
onClickRelease={() => {
|
onClickRelease={() => {
|
||||||
hyprMessage(`dispatch workspace ${id}`);
|
hyprMessage(`dispatch workspace ${id}`).catch(console.log);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<box
|
<box
|
||||||
|
|
|
@ -8,6 +8,12 @@
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
min-height: 35px;
|
min-height: 35px;
|
||||||
|
|
||||||
|
transition: background-color 300ms;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: lighten($window_bg_color, 3%);
|
||||||
|
}
|
||||||
|
|
||||||
&.battery icon {
|
&.battery icon {
|
||||||
&.charging {
|
&.charging {
|
||||||
color: green;
|
color: green;
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { Astal, Gtk } from 'astal/gtk3';
|
||||||
import Battery from './items/battery';
|
import Battery from './items/battery';
|
||||||
import Clock from './items/clock';
|
import Clock from './items/clock';
|
||||||
import CurrentClient from './items/current-client';
|
import CurrentClient from './items/current-client';
|
||||||
|
import NotifButton from './items/notif-button';
|
||||||
import SysTray from './items/tray';
|
import SysTray from './items/tray';
|
||||||
import Workspaces from './items/workspaces';
|
import Workspaces from './items/workspaces';
|
||||||
|
|
||||||
|
@ -39,6 +40,10 @@ export default () => (
|
||||||
</box>
|
</box>
|
||||||
|
|
||||||
<box hexpand halign={Gtk.Align.END}>
|
<box hexpand halign={Gtk.Align.END}>
|
||||||
|
<NotifButton />
|
||||||
|
|
||||||
|
<Separator size={8} />
|
||||||
|
|
||||||
<Battery />
|
<Battery />
|
||||||
|
|
||||||
<Separator size={2} />
|
<Separator size={2} />
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { App, Astal, Widget } from 'astal/gtk3';
|
import { App, Astal, Gtk, Widget } from 'astal/gtk3';
|
||||||
import { register, property } from 'astal/gobject';
|
import { register, property } from 'astal/gobject';
|
||||||
import { Binding, idle } from 'astal';
|
import { Binding, idle } from 'astal';
|
||||||
|
|
||||||
import { hyprMessage } from '../../lib';
|
import { get_hyprland_monitor, hyprMessage } from '../../lib';
|
||||||
|
|
||||||
/* Types */
|
/* Types */
|
||||||
type CloseType = 'none' | 'stay' | 'released' | 'clicked';
|
type CloseType = 'none' | 'stay' | 'released' | 'clicked';
|
||||||
|
@ -60,7 +60,7 @@ export class PopupWindow extends Widget.Window {
|
||||||
App.add_window(this);
|
App.add_window(this);
|
||||||
|
|
||||||
const setTransition = (_: PopupWindow, t: HyprTransition | Binding<HyprTransition>) => {
|
const setTransition = (_: PopupWindow, t: HyprTransition | Binding<HyprTransition>) => {
|
||||||
hyprMessage(`keyword layerrule animation ${t}, ${this.name}`);
|
hyprMessage(`keyword layerrule animation ${t}, ${this.name}`).catch(console.log);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.connect('notify::transition', setTransition);
|
this.connect('notify::transition', setTransition);
|
||||||
|
@ -82,6 +82,34 @@ export class PopupWindow extends Widget.Window {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
set_x_pos(
|
||||||
|
alloc: Gtk.Allocation,
|
||||||
|
side = 'right' as 'left' | 'right',
|
||||||
|
) {
|
||||||
|
const monitor = this.gdkmonitor ??
|
||||||
|
this.get_display().get_monitor_at_point(alloc.x, alloc.y);
|
||||||
|
|
||||||
|
// @ts-expect-error this should exist
|
||||||
|
const transform = get_hyprland_monitor(monitor)?.transform;
|
||||||
|
|
||||||
|
let width: number;
|
||||||
|
|
||||||
|
if (transform && (transform === 1 || transform === 3)) {
|
||||||
|
width = monitor.get_geometry().height;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
width = monitor.get_geometry().width;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.margin_right = side === 'right' ?
|
||||||
|
(width - alloc.x - alloc.width) :
|
||||||
|
this.margin_right;
|
||||||
|
|
||||||
|
this.margin_left = side === 'right' ?
|
||||||
|
this.margin_left :
|
||||||
|
(alloc.x - alloc.width);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (props: PopupWindowProps) => new PopupWindow(props);
|
export default (props: PopupWindowProps) => new PopupWindow(props);
|
||||||
|
|
|
@ -63,41 +63,47 @@ export class NotifGestureWrapper extends Widget.EventBox {
|
||||||
public dragging: boolean;
|
public dragging: boolean;
|
||||||
|
|
||||||
private async get_hovered(): Promise<boolean> {
|
private async get_hovered(): Promise<boolean> {
|
||||||
const layers = JSON.parse(await hyprMessage('j/layers')) as LayerResult;
|
try {
|
||||||
const cursorPos = JSON.parse(await hyprMessage('j/cursorpos')) as CursorPos;
|
const layers = JSON.parse(await hyprMessage('j/layers')) as LayerResult;
|
||||||
|
const cursorPos = JSON.parse(await hyprMessage('j/cursorpos')) as CursorPos;
|
||||||
|
|
||||||
const window = this.get_window();
|
const window = this.get_window();
|
||||||
|
|
||||||
if (window) {
|
if (window) {
|
||||||
const monitor = display?.get_monitor_at_window(window);
|
const monitor = display?.get_monitor_at_window(window);
|
||||||
|
|
||||||
if (monitor) {
|
if (monitor) {
|
||||||
const plugName = get_hyprland_monitor(monitor)?.name;
|
const plugName = get_hyprland_monitor(monitor)?.name;
|
||||||
const notifLayer = layers[plugName ?? '']?.levels['3']
|
const notifLayer = layers[plugName ?? '']?.levels['3']
|
||||||
?.find((n) => n.namespace === 'notifications');
|
?.find((n) => n.namespace === 'notifications');
|
||||||
|
|
||||||
if (notifLayer) {
|
if (notifLayer) {
|
||||||
const index = [...NotifGestureWrapper.popups.keys()]
|
const index = [...NotifGestureWrapper.popups.keys()]
|
||||||
.sort((a, b) => b - a)
|
.sort((a, b) => b - a)
|
||||||
.indexOf(this.id);
|
.indexOf(this.id);
|
||||||
|
|
||||||
const popups = [...NotifGestureWrapper.popups.entries()]
|
const popups = [...NotifGestureWrapper.popups.entries()]
|
||||||
.sort((a, b) => b[0] - a[0])
|
.sort((a, b) => b[0] - a[0])
|
||||||
.map(([key, val]) => [key, val.get_allocated_height()]);
|
.map(([key, val]) => [key, val.get_allocated_height()]);
|
||||||
|
|
||||||
const thisY = notifLayer.y + popups
|
const thisY = notifLayer.y + popups
|
||||||
.map((v) => v[1])
|
.map((v) => v[1])
|
||||||
.slice(0, index)
|
.slice(0, index)
|
||||||
.reduce((prev, curr) => prev + curr, 0);
|
.reduce((prev, curr) => prev + curr, 0);
|
||||||
|
|
||||||
if (cursorPos.y >= thisY && cursorPos.y <= thisY + (popups[index][1] ?? 0)) {
|
if (cursorPos.y >= thisY && cursorPos.y <= thisY + (popups[index][1] ?? 0)) {
|
||||||
if (cursorPos.x >= notifLayer.x && cursorPos.x <= notifLayer.x + notifLayer.w) {
|
if (cursorPos.x >= notifLayer.x &&
|
||||||
return true;
|
cursorPos.x <= notifLayer.x + notifLayer.w) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import { Astal } from 'astal/gtk3';
|
import { Astal } from 'astal/gtk3';
|
||||||
|
|
||||||
|
import PopupWindow from '../misc/popup-window';
|
||||||
|
|
||||||
import Popups from './popups';
|
import Popups from './popups';
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,3 +15,12 @@ export const NotifPopups = () => (
|
||||||
<Popups />
|
<Popups />
|
||||||
</window>
|
</window>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const NotifCenter = () => (
|
||||||
|
<PopupWindow
|
||||||
|
name="notif-center"
|
||||||
|
anchor={Astal.WindowAnchor.TOP | Astal.WindowAnchor.RIGHT}
|
||||||
|
>
|
||||||
|
<box css="min-height: 100px; min-width: 100px; background: black;" />
|
||||||
|
</PopupWindow>
|
||||||
|
);
|
||||||
|
|
Loading…
Reference in a new issue