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, event: Gdk.Event) => boolean | unknown; // eslint-disable-next-line export interface CursorBox extends AgsWidget { } export type CursorBoxProps< Child extends Gtk.Widget, Attr = unknown, Self = CursorBox, > = BaseProps on_hover_lost?: EventHandler on_scroll_up?: EventHandler on_scroll_down?: EventHandler on_primary_click?: EventHandler on_middle_click?: EventHandler on_secondary_click?: EventHandler on_primary_click_release?: EventHandler on_middle_click_release?: EventHandler on_secondary_click_release?: EventHandler }, Attr>; // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging export class CursorBox 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 = {}) { 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._set('on-hover', callback); } get on_hover_lost() { return this._get('on-hover-lost'); } set on_hover_lost(callback: EventHandler) { this._set('on-hover-lost', callback); } get on_scroll_up() { return this._get('on-scroll-up'); } set on_scroll_up(callback: EventHandler) { this._set('on-scroll-up', callback); } get on_scroll_down() { return this._get('on-scroll-down'); } set on_scroll_down(callback: EventHandler) { this._set('on-scroll-down', callback); } get on_primary_click() { return this._get('on-primary-click'); } set on_primary_click(callback: EventHandler) { this._set('on-primary-click', callback); } get on_middle_click() { return this._get('on-middle-click'); } set on_middle_click(callback: EventHandler) { this._set('on-middle-click', callback); } get on_secondary_click() { return this._get('on-secondary-click'); } set on_secondary_click(callback: EventHandler) { 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._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._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._set('on-secondary-click-release', callback); } } export default ( props?: CursorBoxProps, ) => new CursorBox(props ?? {});