nixos-configs/modules/ags/config/ts/misc/cursorbox.ts
matt1432 4f4b1e5140
Some checks are pending
Discord / discord commits (push) Waiting to run
fix(ags cursorbox): make release more reliable
2024-05-15 15:13:24 -04:00

269 lines
7.7 KiB
TypeScript

import Gtk from 'gi://Gtk?version=3.0';
import Gdk from 'gi://Gdk?version=3.0';
// Types
import { BaseProps, Widget as AgsWidget } from 'types/widgets/widget';
type EventHandler<Self> = (self: Self, event: Gdk.Event) => boolean | unknown;
export type CursorBoxProps<
Child extends Gtk.Widget,
Attr = unknown,
Self = CursorBox<Child, Attr>,
> = BaseProps<Self, Gtk.EventBox.ConstructorProperties & {
child?: Child
on_hover?: EventHandler<Self>
on_hover_lost?: EventHandler<Self>
on_scroll_up?: EventHandler<Self>
on_scroll_down?: EventHandler<Self>
on_primary_click?: EventHandler<Self>
on_middle_click?: EventHandler<Self>
on_secondary_click?: EventHandler<Self>
on_primary_click_release?: EventHandler<Self>
on_middle_click_release?: EventHandler<Self>
on_secondary_click_release?: EventHandler<Self>
}, Attr>;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export interface CursorBox<Child, Attr> extends AgsWidget<Attr> { }
export class CursorBox<Child extends Gtk.Widget, Attr> extends Gtk.EventBox {
static {
Widget.register(this, {
properties: {
'on-clicked': ['jsobject', 'rw'],
'on-hover': ['jsobject', 'rw'],
'on-hover-lost': ['jsobject', 'rw'],
'on-scroll-up': ['jsobject', 'rw'],
'on-scroll-down': ['jsobject', 'rw'],
'on-primary-click': ['jsobject', 'rw'],
'on-secondary-click': ['jsobject', 'rw'],
'on-middle-click': ['jsobject', 'rw'],
'on-primary-click-release': ['jsobject', 'rw'],
'on-secondary-click-release': ['jsobject', 'rw'],
'on-middle-click-release': ['jsobject', 'rw'],
},
});
}
constructor(props: CursorBoxProps<Child, Attr> = {}) {
super(props as Gtk.EventBox.ConstructorProperties);
this.add_events(Gdk.EventMask.SCROLL_MASK);
this.add_events(Gdk.EventMask.SMOOTH_SCROLL_MASK);
// Gesture stuff
const gesture = Gtk.GestureMultiPress.new(this);
this.hook(gesture, (_, _n, x, y) => {
if (!x || !y) {
return;
}
this.#canRun.setValue(!(
x > this.get_allocated_width() ||
x <= 0 ||
y > this.get_allocated_height() ||
y <= 0
));
}, 'released');
this.connect('enter-notify-event', (_, event: Gdk.Event) => {
this.set_state_flags(Gtk.StateFlags.PRELIGHT, false);
if (!this.#display) {
return;
}
this.window.set_cursor(Gdk.Cursor.new_from_name(
this.#display,
this.#disabled.value ?
'not-allowed' :
'pointer',
));
return this.on_hover?.(this, event);
});
this.connect('leave-notify-event', (_, event: Gdk.Event) => {
this.unset_state_flags(Gtk.StateFlags.PRELIGHT);
this.window.set_cursor(null);
return this.on_hover_lost?.(this, event);
});
this.connect('button-press-event', (_, event: Gdk.Event) => {
this.set_state_flags(Gtk.StateFlags.ACTIVE, false);
if (this.#disabled.value) {
return;
}
if (event.get_button()[1] === Gdk.BUTTON_PRIMARY) {
return this.on_primary_click?.(this, event);
}
else if (event.get_button()[1] === Gdk.BUTTON_MIDDLE) {
return this.on_middle_click?.(this, event);
}
else if (event.get_button()[1] === Gdk.BUTTON_SECONDARY) {
return this.on_secondary_click?.(this, event);
}
});
this.connect('button-release-event', (_, event: Gdk.Event) => {
this.unset_state_flags(Gtk.StateFlags.ACTIVE);
if (this.#disabled.value) {
return;
}
if (event.get_button()[1] === Gdk.BUTTON_PRIMARY) {
// Every click, do a one shot connect to
// CanRun to wait for location of click
const id = this.#canRun.connect('changed', () => {
if (this.#canRun.value) {
this.on_primary_click_release?.(this, event);
}
this.#canRun.disconnect(id);
});
}
else if (event.get_button()[1] === Gdk.BUTTON_MIDDLE) {
return this.on_middle_click_release?.(this, event);
}
else if (event.get_button()[1] === Gdk.BUTTON_SECONDARY) {
return this.on_secondary_click_release?.(this, event);
}
});
this.connect('scroll-event', (_, event: Gdk.Event) => {
if (event.get_scroll_deltas()[2] < 0) {
return this.on_scroll_up?.(this, event);
}
else if (event.get_scroll_deltas()[2] > 0) {
return this.on_scroll_down?.(this, event);
}
});
this.hook(this.#disabled, () => {
this.toggleClassName('disabled', this.#disabled.value);
});
}
#display = Gdk.Display.get_default();
// Make this variable to know if the function should
// be executed depending on where the click is released
#canRun = Variable(true);
#disabled = Variable(false);
get disabled() {
return this.#disabled.value;
}
set disabled(value: boolean) {
this.#disabled.setValue(value);
}
get child() {
return super.child as Child;
}
set child(child: Child) {
super.child = child;
}
get on_hover() {
return this._get('on-hover');
}
set on_hover(callback: EventHandler<this>) {
this._set('on-hover', callback);
}
get on_hover_lost() {
return this._get('on-hover-lost');
}
set on_hover_lost(callback: EventHandler<this>) {
this._set('on-hover-lost', callback);
}
get on_scroll_up() {
return this._get('on-scroll-up');
}
set on_scroll_up(callback: EventHandler<this>) {
this._set('on-scroll-up', callback);
}
get on_scroll_down() {
return this._get('on-scroll-down');
}
set on_scroll_down(callback: EventHandler<this>) {
this._set('on-scroll-down', callback);
}
get on_primary_click() {
return this._get('on-primary-click');
}
set on_primary_click(callback: EventHandler<this>) {
this._set('on-primary-click', callback);
}
get on_middle_click() {
return this._get('on-middle-click');
}
set on_middle_click(callback: EventHandler<this>) {
this._set('on-middle-click', callback);
}
get on_secondary_click() {
return this._get('on-secondary-click');
}
set on_secondary_click(callback: EventHandler<this>) {
this._set('on-secondary-click', callback);
}
get on_primary_click_release() {
return this._get('on-primary-click-release');
}
set on_primary_click_release(callback: EventHandler<this>) {
this._set('on-primary-click-release', callback);
}
get on_middle_click_release() {
return this._get('on-middle-click-release');
}
set on_middle_click_release(callback: EventHandler<this>) {
this._set('on-middle-click-release', callback);
}
get on_secondary_click_release() {
return this._get('on-secondary-click-release');
}
set on_secondary_click_release(callback: EventHandler<this>) {
this._set('on-secondary-click-release', callback);
}
}
export default <Child extends Gtk.Widget, Attr>(
props?: CursorBoxProps<Child, Attr>,
) => new CursorBox(props ?? {});