feat(agsV2): get notif cursors right
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
531ec67e0e
commit
2ee60de237
4 changed files with 123 additions and 53 deletions
|
@ -5,7 +5,35 @@ import { idle } from 'astal';
|
||||||
import AstalNotifd from 'gi://AstalNotifd?version=0.1';
|
import AstalNotifd from 'gi://AstalNotifd?version=0.1';
|
||||||
const Notifications = AstalNotifd.get_default();
|
const Notifications = AstalNotifd.get_default();
|
||||||
|
|
||||||
|
import AstalHyprland from 'gi://AstalHyprland?version=0.1';
|
||||||
|
const Hyprland = AstalHyprland.get_default();
|
||||||
|
|
||||||
import { HasNotifs } from './notification';
|
import { HasNotifs } from './notification';
|
||||||
|
import { get_hyprland_monitor } from '../../lib';
|
||||||
|
|
||||||
|
/* Types */
|
||||||
|
interface Layer {
|
||||||
|
address: string
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
w: number
|
||||||
|
h: number
|
||||||
|
namespace: string
|
||||||
|
}
|
||||||
|
interface Levels {
|
||||||
|
0?: Layer[] | null
|
||||||
|
1?: Layer[] | null
|
||||||
|
2?: Layer[] | null
|
||||||
|
3?: Layer[] | null
|
||||||
|
}
|
||||||
|
interface Layers {
|
||||||
|
levels: Levels
|
||||||
|
}
|
||||||
|
type LayerResult = Record<string, Layers>;
|
||||||
|
interface CursorPos {
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const display = Gdk.Display.get_default();
|
const display = Gdk.Display.get_default();
|
||||||
|
@ -36,19 +64,77 @@ type NotifGestureWrapperProps = Widget.BoxProps & {
|
||||||
};
|
};
|
||||||
|
|
||||||
@register()
|
@register()
|
||||||
class NotifGestureWrapper extends Widget.EventBox {
|
export class NotifGestureWrapper extends Widget.EventBox {
|
||||||
|
static popups = new Map<number, NotifGestureWrapper>();
|
||||||
|
|
||||||
readonly id: number;
|
readonly id: number;
|
||||||
|
|
||||||
readonly slide_in_from: 'Left' | 'Right';
|
readonly slide_in_from: 'Left' | 'Right';
|
||||||
|
|
||||||
readonly slideAway: (side: 'Left' | 'Right') => void;
|
|
||||||
|
|
||||||
@property(Boolean)
|
|
||||||
declare hovered: boolean;
|
|
||||||
|
|
||||||
@property(Boolean)
|
@property(Boolean)
|
||||||
declare dragging: boolean;
|
declare dragging: boolean;
|
||||||
|
|
||||||
|
get hovered(): boolean {
|
||||||
|
const layers = JSON.parse(Hyprland.message('j/layers')) as LayerResult;
|
||||||
|
const cursorPos = JSON.parse(Hyprland.message('j/cursorpos')) as CursorPos;
|
||||||
|
|
||||||
|
const window = this.get_window();
|
||||||
|
|
||||||
|
if (window) {
|
||||||
|
const monitor = display?.get_monitor_at_window(window);
|
||||||
|
|
||||||
|
if (monitor) {
|
||||||
|
const plugName = get_hyprland_monitor(monitor)?.name;
|
||||||
|
const notifLayer = layers[plugName ?? '']?.levels['3']
|
||||||
|
?.find((n) => n.namespace === 'notifications');
|
||||||
|
|
||||||
|
if (notifLayer) {
|
||||||
|
const index = [...NotifGestureWrapper.popups.keys()]
|
||||||
|
.sort((a, b) => b - a)
|
||||||
|
.indexOf(this.id);
|
||||||
|
|
||||||
|
const popups = [...NotifGestureWrapper.popups.entries()]
|
||||||
|
.sort((a, b) => b[0] - a[0])
|
||||||
|
.map(([key, val]) => [key, val.get_allocated_height()]);
|
||||||
|
|
||||||
|
const thisY = notifLayer.y + popups
|
||||||
|
.map((v) => v[1])
|
||||||
|
.slice(0, index)
|
||||||
|
.reduce((prev, curr) => prev + curr, 0);
|
||||||
|
|
||||||
|
if (cursorPos.y >= thisY && cursorPos.y <= thisY + (popups[index][1] ?? 0)) {
|
||||||
|
if (cursorPos.x >= notifLayer.x && cursorPos.x <= notifLayer.x + notifLayer.w) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public slideAway(side: 'Left' | 'Right', force = false) {
|
||||||
|
((this.get_child() as Widget.Revealer).get_child() as Widget.Box)
|
||||||
|
.css = side === 'Left' ? slideLeft : slideRight;
|
||||||
|
|
||||||
|
// Make it uninteractable
|
||||||
|
this.sensitive = false;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
(this.get_child() as Widget.Revealer).revealChild = false;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
// Kill notif and update HasNotifs after anim is done
|
||||||
|
if (force) {
|
||||||
|
Notifications.get_notification(this.id)?.dismiss();
|
||||||
|
}
|
||||||
|
HasNotifs.set(Notifications.get_notifications().length > 0);
|
||||||
|
this.destroy();
|
||||||
|
}, ANIM_DURATION);
|
||||||
|
}, ANIM_DURATION - 100);
|
||||||
|
}
|
||||||
|
|
||||||
constructor({
|
constructor({
|
||||||
id,
|
id,
|
||||||
slide_in_from = 'Left',
|
slide_in_from = 'Left',
|
||||||
|
@ -57,36 +143,8 @@ class NotifGestureWrapper extends Widget.EventBox {
|
||||||
super();
|
super();
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.dragging = false;
|
this.dragging = false;
|
||||||
this.hovered = false;
|
|
||||||
this.slide_in_from = slide_in_from;
|
this.slide_in_from = slide_in_from;
|
||||||
|
|
||||||
this.slideAway = (side: 'Left' | 'Right', force = false) => {
|
|
||||||
((this.get_child() as Widget.Revealer).get_child() as Widget.Box)
|
|
||||||
.css = side === 'Left' ? slideLeft : slideRight;
|
|
||||||
|
|
||||||
// Make it uninteractable
|
|
||||||
this.sensitive = false;
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
(this.get_child() as Widget.Revealer).revealChild = false;
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
// Kill notif and update HasNotifs after anim is done
|
|
||||||
if (force) {
|
|
||||||
Notifications.get_notification(this.id)?.dismiss();
|
|
||||||
}
|
|
||||||
HasNotifs.set(Notifications.get_notifications().length > 0);
|
|
||||||
this.destroy();
|
|
||||||
}, ANIM_DURATION);
|
|
||||||
}, ANIM_DURATION - 100);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.connect('notify::dragging', () => {
|
|
||||||
if (!this.hovered && this.dragging) {
|
|
||||||
this.hovered = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// OnClick
|
// OnClick
|
||||||
this.connect('button-press-event', () => {
|
this.connect('button-press-event', () => {
|
||||||
if (!display) {
|
if (!display) {
|
||||||
|
@ -96,9 +154,6 @@ class NotifGestureWrapper extends Widget.EventBox {
|
||||||
display,
|
display,
|
||||||
'grabbing',
|
'grabbing',
|
||||||
));
|
));
|
||||||
if (!this.hovered) {
|
|
||||||
this.hovered = true;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// OnRelease
|
// OnRelease
|
||||||
|
@ -110,9 +165,6 @@ class NotifGestureWrapper extends Widget.EventBox {
|
||||||
display,
|
display,
|
||||||
'grab',
|
'grab',
|
||||||
));
|
));
|
||||||
if (!this.hovered) {
|
|
||||||
this.hovered = true;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// OnHover
|
// OnHover
|
||||||
|
@ -124,9 +176,6 @@ class NotifGestureWrapper extends Widget.EventBox {
|
||||||
display,
|
display,
|
||||||
'grab',
|
'grab',
|
||||||
));
|
));
|
||||||
if (!this.hovered) {
|
|
||||||
this.hovered = true;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// OnHoverLost
|
// OnHoverLost
|
||||||
|
@ -138,10 +187,6 @@ class NotifGestureWrapper extends Widget.EventBox {
|
||||||
display,
|
display,
|
||||||
'grab',
|
'grab',
|
||||||
));
|
));
|
||||||
|
|
||||||
if (this.hovered) {
|
|
||||||
this.hovered = false;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const gesture = Gtk.GestureDrag.new(this);
|
const gesture = Gtk.GestureDrag.new(this);
|
||||||
|
|
|
@ -6,6 +6,7 @@ import Popups from './popups';
|
||||||
export const NotifPopups = () => (
|
export const NotifPopups = () => (
|
||||||
<window
|
<window
|
||||||
name="notifications"
|
name="notifications"
|
||||||
|
namespace="notifications"
|
||||||
layer={Astal.Layer.OVERLAY}
|
layer={Astal.Layer.OVERLAY}
|
||||||
anchor={Astal.WindowAnchor.TOP | Astal.WindowAnchor.LEFT}
|
anchor={Astal.WindowAnchor.TOP | Astal.WindowAnchor.LEFT}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { App, Gtk } from 'astal/gtk3';
|
import { App, Gtk, Gdk } from 'astal/gtk3';
|
||||||
import { Variable } from 'astal';
|
import { Variable } from 'astal';
|
||||||
|
|
||||||
import GLib from 'gi://GLib?version=2.0';
|
import GLib from 'gi://GLib?version=2.0';
|
||||||
|
@ -60,6 +60,29 @@ const NotifIcon = ({ notifObj }: { notifObj: AstalNotifd.Notification }) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const setupButton = (self: Gtk.Widget) => {
|
||||||
|
const display = Gdk.Display.get_default();
|
||||||
|
|
||||||
|
// OnHover
|
||||||
|
self.connect('enter-notify-event', () => {
|
||||||
|
if (!display) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.window.set_cursor(Gdk.Cursor.new_from_name(
|
||||||
|
display,
|
||||||
|
'pointer',
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
// OnHoverLost
|
||||||
|
self.connect('leave-notify-event', () => {
|
||||||
|
if (!display) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.window.set_cursor(null);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const BlockedApps = [
|
const BlockedApps = [
|
||||||
'Spotify',
|
'Spotify',
|
||||||
];
|
];
|
||||||
|
@ -119,6 +142,7 @@ export const Notification = ({
|
||||||
className="close-button"
|
className="close-button"
|
||||||
valign={Gtk.Align.START}
|
valign={Gtk.Align.START}
|
||||||
halign={Gtk.Align.END}
|
halign={Gtk.Align.END}
|
||||||
|
setup={setupButton}
|
||||||
|
|
||||||
onButtonReleaseEvent={() => {
|
onButtonReleaseEvent={() => {
|
||||||
notifObj.dismiss();
|
notifObj.dismiss();
|
||||||
|
@ -147,6 +171,7 @@ export const Notification = ({
|
||||||
<button
|
<button
|
||||||
className="action-button"
|
className="action-button"
|
||||||
hexpand
|
hexpand
|
||||||
|
setup={setupButton}
|
||||||
|
|
||||||
onButtonReleaseEvent={() => notifObj.invoke(action.id)}
|
onButtonReleaseEvent={() => notifObj.invoke(action.id)}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import AstalNotifd from 'gi://AstalNotifd?version=0.1';
|
import AstalNotifd from 'gi://AstalNotifd?version=0.1';
|
||||||
const Notifications = AstalNotifd.get_default();
|
const Notifications = AstalNotifd.get_default();
|
||||||
|
|
||||||
|
import { NotifGestureWrapper } from './gesture';
|
||||||
import { Notification } from './notification';
|
import { Notification } from './notification';
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,8 +12,6 @@ export default () => (
|
||||||
vertical
|
vertical
|
||||||
|
|
||||||
setup={(self) => {
|
setup={(self) => {
|
||||||
const NotifsMap = new Map<number, ReturnType<typeof Notification>>();
|
|
||||||
|
|
||||||
const addPopup = (id: number) => {
|
const addPopup = (id: number) => {
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return;
|
return;
|
||||||
|
@ -25,19 +24,19 @@ export default () => (
|
||||||
self.pack_end(NewNotif, false, false, 0);
|
self.pack_end(NewNotif, false, false, 0);
|
||||||
self.show_all();
|
self.show_all();
|
||||||
|
|
||||||
NotifsMap.set(id, NewNotif);
|
NotifGestureWrapper.popups.set(id, NewNotif);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleResolved = (id: number) => {
|
const handleResolved = (id: number) => {
|
||||||
const notif = NotifsMap.get(id);
|
const notif = NotifGestureWrapper.popups.get(id);
|
||||||
|
|
||||||
if (!notif) {
|
if (!notif) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
notif.slideAway('Left');
|
notif.slideAway('Left');
|
||||||
NotifsMap.delete(id);
|
NotifGestureWrapper.popups.delete(id);
|
||||||
};
|
};
|
||||||
|
|
||||||
self
|
self
|
||||||
|
|
Loading…
Reference in a new issue