refactor(ags): typecheck popupwins and make cursorbox its own thing

This commit is contained in:
matt1432 2023-12-18 23:20:32 -05:00
parent 065cb0ce5f
commit e1e38fc18f
27 changed files with 215 additions and 176 deletions

View file

@ -4,7 +4,7 @@ import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
import { Box, Icon, Label } from 'resource:///com/github/Aylur/ags/widget.js';
import { lookUpIcon } from 'resource:///com/github/Aylur/ags/utils.js';
import EventBox from '../misc/cursorbox.js';
import CursorBox from '../misc/cursorbox.js';
/**
@ -50,14 +50,13 @@ export default (app) => {
],
});
return EventBox({
return CursorBox({
hexpand: true,
class_name: 'app',
attribute: { app },
onPrimaryClickRelease: (self) => {
on_primary_click_release: (self) => {
App.closeWindow('applauncher');
Hyprland.sendMessage(`dispatch exec sh -c
${self.attribute.app.executable}`);

View file

@ -78,10 +78,9 @@ const Applauncher = ({ window_name = 'applauncher' } = {}) => {
},
on_change: ({ text }) => {
if (!text) {
if (text === null) {
return;
}
setSort(text);
let visibleApps = 0;

View file

@ -4,13 +4,13 @@ import { Label } from 'resource:///com/github/Aylur/ags/widget.js';
const { DateTime } = imports.gi.GLib;
import EventBox from '../../misc/cursorbox.js';
import CursorBox from '../../misc/cursorbox.js';
export default () => EventBox({
className: 'toggle-off',
export default () => CursorBox({
class_name: 'toggle-off',
onPrimaryClickRelease: () => App.toggleWindow('calendar'),
on_primary_click_release: () => App.toggleWindow('calendar'),
setup: (self) => {
self.hook(App, (_, windowName, visible) => {

View file

@ -2,7 +2,7 @@ import { Label } from 'resource:///com/github/Aylur/ags/widget.js';
import Variable from 'resource:///com/github/Aylur/ags/variable.js';
import EventBox from '../../misc/cursorbox.js';
import CursorBox from '../../misc/cursorbox.js';
import Persist from '../../misc/persist.js';
const HeartState = Variable('');
@ -16,8 +16,8 @@ Persist({
});
export default () => EventBox({
onPrimaryClickRelease: () => {
export default () => CursorBox({
on_primary_click_release: () => {
HeartState.value = HeartState.value === '' ? '󰣐' : '';
},

View file

@ -3,18 +3,18 @@ import Notifications from 'resource:///com/github/Aylur/ags/service/notification
import { Box, CenterBox, Icon, Label } from 'resource:///com/github/Aylur/ags/widget.js';
import EventBox from '../../misc/cursorbox.js';
import CursorBox from '../../misc/cursorbox.js';
import Separator from '../../misc/separator.js';
const SPACING = 4;
export default () => EventBox({
className: 'toggle-off',
export default () => CursorBox({
class_name: 'toggle-off',
onPrimaryClickRelease: (self) => {
on_primary_click_release: (self) => {
// @ts-expect-error
App.getWindow('notification-center')?.setXPos(
App.getWindow('notification-center')?.attribute.set_x_pos(
self.get_allocation(),
'right',
);

View file

@ -1,13 +1,13 @@
import { Label } from 'resource:///com/github/Aylur/ags/widget.js';
import Tablet from '../../../services/tablet.js';
import EventBox from '../../misc/cursorbox.js';
import CursorBox from '../../misc/cursorbox.js';
export default () => EventBox({
className: 'toggle-off',
export default () => CursorBox({
class_name: 'toggle-off',
onPrimaryClickRelease: () => Tablet.toggleOsk(),
on_primary_click_release: () => Tablet.toggleOsk(),
child: Label({
class_name: 'osk-toggle',

View file

@ -8,20 +8,18 @@ import Brightness from './brightness.js';
import KeyboardLayout from './keyboard-layout.js';
import Network from './network.js';
import EventBox from '../../misc/cursorbox.js';
import CursorBox from '../../misc/cursorbox.js';
import Separator from '../../misc/separator.js';
const SPACING = 4;
export default () => EventBox({
className: 'toggle-off',
export default () => CursorBox({
class_name: 'toggle-off',
onHoverLost: () => { /**/ },
onPrimaryClickRelease: (self) => {
on_primary_click_release: (self) => {
// @ts-expect-error
App.getWindow('notification-center').setXPos(
App.getWindow('notification-center').attribute.set_x_pos(
self.get_allocation(),
'right',
);

View file

@ -1,13 +1,13 @@
import { Box, Label } from 'resource:///com/github/Aylur/ags/widget.js';
import Tablet from '../../../services/tablet.js';
import EventBox from '../../misc/cursorbox.js';
import CursorBox from '../../misc/cursorbox.js';
export default () => EventBox({
export default () => CursorBox({
class_name: 'toggle-off',
onPrimaryClickRelease: () => Tablet.toggleMode(),
on_primary_click_release: () => Tablet.toggleMode(),
child: Box({
class_name: 'tablet-toggle',

View file

@ -3,7 +3,7 @@ import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
import { timeout } from 'resource:///com/github/Aylur/ags/utils.js';
import { Box, Overlay, Revealer } from 'resource:///com/github/Aylur/ags/widget.js';
import EventBox from '../../misc/cursorbox.js';
import CursorBox from '../../misc/cursorbox.js';
const URGENT_DURATION = 1000;
@ -16,10 +16,10 @@ const Workspace = ({ id }) => {
transition: 'slide_right',
attribute: { id },
child: EventBox({
child: CursorBox({
tooltipText: `${id}`,
onPrimaryClickRelease: () => {
on_primary_click_release: () => {
Hyprland.sendMessage(`dispatch workspace ${id}`);
},
@ -84,7 +84,7 @@ export default () => {
const updateHighlight = (self) => {
const currentId = Hyprland.active.workspace.id;
// @ts-expect-error
const indicators = self.get_parent().get_children()[0].child.children;
const indicators = self.get_parent().get_children()[0].child.child.children;
const currentIndex = Array.from(indicators)
.findIndex((w) => w.attribute.id === currentId);
@ -105,7 +105,7 @@ export default () => {
const widget = Overlay({
pass_through: true,
overlays: [highlight],
child: EventBox({
child: CursorBox({
child: Box({
class_name: 'workspaces',

View file

@ -1,7 +1,7 @@
import { timeout } from 'resource:///com/github/Aylur/ags/utils.js';
import { Box, EventBox, Overlay } from 'resource:///com/github/Aylur/ags/widget.js';
import Gtk from 'gi://Gtk';
const { Gtk } = imports.gi;
const MAX_OFFSET = 200;
const OFFSCREEN = 500;
@ -61,10 +61,9 @@ export default ({
setup(self);
self
// @ts-expect-error
.hook(gesture, (overlay, realGesture) => {
if (realGesture) {
overlay.attribute.list().forEach((over) => {
Array.from(overlay.attribute.list()).forEach((over) => {
over.visible = true;
});
}

View file

@ -4,7 +4,7 @@ import { Button, Icon, Label, Stack, Slider, CenterBox, Box } from 'resource:///
import { execAsync, lookUpIcon, readFileAsync } from 'resource:///com/github/Aylur/ags/utils.js';
import Separator from '../misc/separator.js';
import EventBox from '../misc/cursorbox.js';
import CursorBox from '../misc/cursorbox.js';
/**
* @typedef {import('types/service/mpris').MprisPlayer} Player
@ -137,10 +137,10 @@ export const PlayerIcon = (player, overlay) => {
* @param {Overlay=} widget
* @param {Overlay=} over
*/
const playerIcon = (p, widget, over) => EventBox({
const playerIcon = (p, widget, over) => CursorBox({
tooltip_text: p.identity || '',
onPrimaryClickRelease: () => {
on_primary_click_release: () => {
widget?.attribute.moveToTop(over);
},
@ -223,7 +223,13 @@ export const PositionSlider = (player, colors) => Slider({
},
});
const PlayerButton = ({ player, colors, items, onClick, prop }) => EventBox({
const PlayerButton = ({
player,
colors,
items,
onClick,
prop,
}) => CursorBox({
child: Button({
attribute: { hovered: false },
child: Stack({ items }),

View file

@ -17,13 +17,13 @@ const micIcons = {
};
export const SpeakerIcon = Variable();
export const SpeakerIcon = Variable('');
Audio.connect('speaker-changed', () => {
if (!Audio.speaker) {
return;
}
if (Audio.speaker.stream.isMuted) {
if (Audio.speaker.stream.is_muted) {
SpeakerIcon.value = speakerIcons[0];
}
else {
@ -37,13 +37,13 @@ Audio.connect('speaker-changed', () => {
}
});
export const MicIcon = Variable();
export const MicIcon = Variable('');
Audio.connect('microphone-changed', () => {
if (!Audio.microphone) {
return;
}
if (Audio.microphone.stream.isMuted) {
if (Audio.microphone.stream.is_muted) {
MicIcon.value = micIcons[0];
}
else {

View file

@ -3,7 +3,10 @@ import App from 'resource:///com/github/Aylur/ags/app.js';
export default () => {
Array.from(App.windows)
.filter((w) => w[1].closeOnUnfocus && w[1].closeOnUnfocus !== 'stay')
// @ts-expect-error
.filter((w) => w[1].attribute?.close_on_unfocus &&
// @ts-expect-error
w[1].attribute?.close_on_unfocus !== 'stay')
.forEach((w) => {
App.closeWindow(w[0]);
});

View file

@ -1,49 +1,38 @@
import Variable from 'resource:///com/github/Aylur/ags/variable.js';
import { Button, EventBox } from 'resource:///com/github/Aylur/ags/widget.js';
import { EventBox } from 'resource:///com/github/Aylur/ags/widget.js';
import Gtk from 'gi://Gtk';
const { Gtk } = imports.gi;
// TODO: wrap in another EventBox for disabled cursor
/**
* @typedef {import('types/widget.js').Widget} Widget
* @typedef {Widget & Object} CursorProps
* @property {boolean=} isButton
* @property {function(Widget):void=} onPrimaryClickRelease
* @typedef {import('types/widgets/eventbox').EventBoxProps} EventBox
*
* @param {CursorProps} obj
* @param {EventBox & {
* on_primary_click_release?: function(EventBox):void
* }} o
*/
export default ({
isButton = false,
onPrimaryClickRelease = (self) => {
self;
},
attribute,
on_primary_click_release = () => {/**/},
...props
}) => {
// Make this variable to know if the function should
// be executed depending on where the click is released
const CanRun = Variable(true);
const Disabled = Variable(false);
let widgetFunc;
if (isButton) {
widgetFunc = Button;
}
else {
widgetFunc = EventBox;
}
const widget = widgetFunc({
const widget = EventBox({
...props,
cursor: 'pointer',
sensitive: Disabled.bind().transform((v) => !v),
on_primary_click_release: (self) => {
// Every click, do a one shot connect to
// CanRun to wait for location of click
const id = CanRun.connect('changed', () => {
if (CanRun.value) {
onPrimaryClickRelease(self);
on_primary_click_release(self);
}
CanRun.disconnect(id);
@ -51,9 +40,31 @@ export default ({
},
});
const wrapper = EventBox({
cursor: 'pointer',
attribute: {
...attribute,
disabled: Disabled,
get_child: () => widget.child,
set_child: (new_child) => {
widget.child = new_child;
},
},
child: widget,
}).hook(Disabled, (self) => {
self.cursor = Disabled.value ?
'not-allowed' :
'pointer';
});
const gesture = Gtk.GestureLongPress.new(widget);
// @ts-expect-error
widget.hook(gesture, () => {
const pointer = gesture.get_point(null);
const x = pointer[1];
@ -69,5 +80,5 @@ export default ({
);
}, 'end');
return widget;
return wrapper;
};

View file

@ -4,24 +4,35 @@ import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
import { Revealer, Box, Window } from 'resource:///com/github/Aylur/ags/widget.js';
import { timeout } from 'resource:///com/github/Aylur/ags/utils.js';
/**
* @typedef {import('types/widgets/revealer').RevealerProps} RevProp
* @typedef {import('types/widgets/window').WindowProps} WinProp
*/
/**
* @param {WinProp & {
* transition?: RevProp['transition']
* transition_duration?: RevProp['transition_duration']
* onOpen?: function
* onClose?: function
* blur?: boolean
* close_on_unfocus?: 'none'|'stay'|'released'|'clicked'
* }} o
*/
export default ({
// Revealer props
transition = 'slide_down',
transitionDuration = 500,
// Optional: execute a function whenever
// the window pops up or goes away
transition_duration = 500,
onOpen = () => { /**/ },
onClose = () => { /**/ },
// Window props
name,
child,
blur = false,
closeOnUnfocus = 'released',
visible = false,
layer = 'overlay',
blur = false,
close_on_unfocus = 'released',
...props
}) => {
const window = Window({
@ -30,11 +41,49 @@ export default ({
visible: false,
...props,
attribute: {
set_x_pos: (
alloc = {},
side = 'right',
) => {
const width = window.get_display()
.get_monitor_at_point(alloc.x, alloc.y)
.get_geometry().width;
window.margins = [
window.margins[0],
side === 'right' ?
(width - alloc.x - alloc.width) :
window.margins[1],
window.margins[2],
side === 'right' ?
window.margins[3] :
(alloc.x - alloc.width),
];
},
// @ts-expect-error
get_child: () => window.child.children[0].child,
set_child: (newChild) => {
// @ts-expect-error
window.child.children[0].child = newChild;
// @ts-expect-error
window.child.children[0].show_all();
},
// This is for my custom pointers.js
close_on_unfocus,
},
setup: () => {
// Add way to make window open on startup
const id = App.connect('config-parsed', () => {
if (visible) {
App.openWindow(name);
App.openWindow(String(name));
}
App.disconnect(id);
});
@ -56,18 +105,18 @@ export default ({
`,
child: Revealer({
transition,
transitionDuration,
transition_duration,
setup: (self) => {
self.hook(App, (_, currentName, isOpen) => {
if (currentName === name) {
self.revealChild = isOpen;
self.reveal_child = isOpen;
if (isOpen) {
onOpen(window);
}
else {
timeout(transitionDuration, () => {
timeout(Number(transition_duration), () => {
onClose(window);
});
}
@ -80,39 +129,5 @@ export default ({
}),
});
window.setXPos = (
alloc,
side = 'right',
) => {
const width = window.get_display()
.get_monitor_at_point(alloc.x, alloc.y)
.get_geometry().width;
window.margins = [
window.margins[0],
side === 'right' ?
(width - alloc.x - alloc.width) :
window.margins[1],
window.margins[2],
side === 'right' ?
window.margins[3] :
(alloc.x - alloc.width),
];
};
// Make getting the original child passed in this
// function easier when making more code for the widget
window.getChild = () => window.child.children[0].child;
window.setChild = (newChild) => {
window.child.children[0].child = newChild;
window.child.children[0].show_all();
};
// This is for my custom pointers.js
window.closeOnUnfocus = closeOnUnfocus;
return window;
};

View file

@ -1,6 +1,14 @@
import { Box } from 'resource:///com/github/Aylur/ags/widget.js';
/**
* @param {number} size
* @param {{
* vertical?: boolean
* css?: string
* props?: import('types/widgets/box').BoxProps
* }} o
*/
export default (size, {
vertical = false,
css = '',

View file

@ -18,7 +18,7 @@ const getDragState = (box) => box.get_parent().get_parent()
.get_parent().get_parent().get_parent()._dragging;
import Gesture from './gesture.js';
import EventBox from '../misc/cursorbox.js';
import CursorBox from '../misc/cursorbox.js';
const NotificationIcon = (notif) => {
@ -75,8 +75,8 @@ const NotificationIcon = (notif) => {
}
if (notif.image) {
return EventBox({
onPrimaryClickRelease: iconCmd,
return CursorBox({
on_primary_click_release: iconCmd,
child: Box({
vpack: 'start',
@ -106,8 +106,8 @@ const NotificationIcon = (notif) => {
}
return EventBox({
onPrimaryClickRelease: iconCmd,
return CursorBox({
on_primary_click_release: iconCmd,
child: Box({
vpack: 'start',
@ -205,7 +205,7 @@ export const Notification = ({
}),
// Close button
EventBox({
CursorBox({
child: Button({
className: 'close-button',
vpack: 'start',

View file

@ -5,7 +5,7 @@ import { Button, Label, Box, Icon, Scrollable, Revealer } from 'resource:///com/
import { timeout } from 'resource:///com/github/Aylur/ags/utils.js';
import { Notification, HasNotifs } from './base.js';
import EventBox from '../misc/cursorbox.js';
import CursorBox from '../misc/cursorbox.js';
const addNotif = (box, notif) => {
@ -55,7 +55,7 @@ const NotificationList = () => Box({
});
// Needs to be wrapped to still have onHover when disabled
const ClearButton = () => EventBox({
const ClearButton = () => CursorBox({
child: Button({
onPrimaryClickRelease: () => {
Notifications.clear();

View file

@ -8,7 +8,7 @@ export const NotifPopups = () => PopupWindow({
anchor: ['top', 'left'],
visible: true,
transition: 'none',
closeOnUnfocus: 'stay',
close_on_unfocus: 'stay',
child: PopUpsWidget(),
});

View file

@ -53,8 +53,8 @@ export default () => PopupWindow({
name: 'osd',
anchor: ['bottom'],
exclusivity: 'ignore',
closeOnUnfocus: 'stay',
close_on_unfocus: 'stay',
transition: 'slide_up',
transitionDuration: 200,
transition_duration: 200,
child: OSDs(),
});

View file

@ -1,12 +1,10 @@
import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
import { EventBox } from 'resource:///com/github/Aylur/ags/widget.js';
import { Button, EventBox } from 'resource:///com/github/Aylur/ags/widget.js';
import Gtk from 'gi://Gtk';
import Gdk from 'gi://Gdk';
import Cairo from 'cairo';
const { Gtk, Gdk } = imports.gi;
import Button from '../misc/cursorbox.js';
import { updateClients } from './clients.js';
const TARGET = [Gtk.TargetEntry.new('text/plain', Gtk.TargetFlags.SAME_APP, 0)];
@ -58,10 +56,15 @@ export const WorkspaceDrop = (props) => EventBox({
},
});
export const WindowButton = ({ address, mainBox, ...props } = {}) => Button({
isButton: true,
export const WindowButton = ({
address,
mainBox,
...props
} = {}) => Button({
...props,
cursor: 'pointer',
setup: (self) => {
self.drag_source_set(
Gdk.ModifierType.BUTTON1_MASK,

View file

@ -93,15 +93,15 @@ export const Overview = () => {
};
export default () => {
const window = PopupWindow({
const win = PopupWindow({
name: 'overview',
blur: true,
closeOnUnfocus: 'none',
onOpen: (win) => {
win.setChild(Overview());
win.getChild().getChild().update();
close_on_unfocus: 'none',
onOpen: () => {
win.attribute.set_child(Overview());
win.attribute.get_child().getChild().update();
},
});
return window;
return win;
};

View file

@ -4,17 +4,17 @@ import { CenterBox, Label } from 'resource:///com/github/Aylur/ags/widget.js';
import { execAsync } from 'resource:///com/github/Aylur/ags/utils.js';
import PopupWindow from './misc/popup.js';
import Button from './misc/cursorbox.js';
import CursorBox from './misc/cursorbox.js';
const PowermenuWidget = () => CenterBox({
className: 'powermenu',
class_name: 'powermenu',
// @ts-expect-error
vertical: false,
startWidget: Button({
isButton: true,
className: 'shutdown',
onPrimaryClickRelease: () => execAsync(['systemctl', 'poweroff'])
startWidget: CursorBox({
class_name: 'shutdown',
on_primary_click_release: () => execAsync(['systemctl', 'poweroff'])
.catch(print),
child: Label({
@ -22,10 +22,9 @@ const PowermenuWidget = () => CenterBox({
}),
}),
centerWidget: Button({
isButton: true,
className: 'reboot',
onPrimaryClickRelease: () => execAsync(['systemctl', 'reboot'])
centerWidget: CursorBox({
class_name: 'reboot',
on_primary_click_release: () => execAsync(['systemctl', 'reboot'])
.catch(print),
child: Label({
@ -33,10 +32,9 @@ const PowermenuWidget = () => CenterBox({
}),
}),
endWidget: Button({
isButton: true,
className: 'logout',
onPrimaryClickRelease: () => Hyprland.sendMessage('dispatch exit')
endWidget: CursorBox({
class_name: 'logout',
on_primary_click_release: () => Hyprland.sendMessage('dispatch exit')
.catch(print),
child: Label({

View file

@ -3,7 +3,7 @@ import Bluetooth from 'resource:///com/github/Aylur/ags/service/bluetooth.js';
import { Box, Icon, Label, ListBox, Overlay, Revealer, Scrollable } from 'resource:///com/github/Aylur/ags/widget.js';
import EventBox from '../misc/cursorbox.js';
import CursorBox from '../misc/cursorbox.js';
const SCROLL_THRESH_H = 200;
const SCROLL_THRESH_N = 7;
@ -42,8 +42,8 @@ const BluetoothDevice = (dev) => {
widget.add(Revealer({
revealChild: true,
transition: 'slide_down',
child: EventBox({
onPrimaryClickRelease: () => dev.setConnection(true),
child: CursorBox({
on_primary_click_release: () => dev.setConnection(true),
child,
}),
}));

View file

@ -7,7 +7,7 @@ import { Box, Icon, Label, Revealer } from 'resource:///com/github/Aylur/ags/wid
import { execAsync } from 'resource:///com/github/Aylur/ags/utils.js';
import { SpeakerIcon, MicIcon } from '../misc/audio-icons.js';
import EventBox from '../misc/cursorbox.js';
import CursorBox from '../misc/cursorbox.js';
import Separator from '../misc/separator.js';
import { NetworkMenu } from './network.js';
@ -85,10 +85,10 @@ const GridButton = ({
className: 'grid-button',
children: [
EventBox({
CursorBox({
className: 'left-part',
onPrimaryClickRelease: () => {
on_primary_click_release: () => {
if (Activated.value) {
secondaryCommand();
}
@ -100,10 +100,10 @@ const GridButton = ({
child: icon,
}),
EventBox({
CursorBox({
className: 'right-part',
onPrimaryClickRelease: () => {
on_primary_click_release: () => {
ButtonStates.forEach((state) => {
if (state !== Activated) {
state.value = false;

View file

@ -5,7 +5,7 @@ import Variable from 'resource:///com/github/Aylur/ags/variable.js';
import { Box, Icon, Label, ListBox, Overlay, Revealer, Scrollable } from 'resource:///com/github/Aylur/ags/widget.js';
import { execAsync } from 'resource:///com/github/Aylur/ags/utils.js';
import EventBox from '../misc/cursorbox.js';
import CursorBox from '../misc/cursorbox.js';
const SCROLL_THRESH_H = 200;
const SCROLL_THRESH_N = 7;
@ -60,8 +60,8 @@ const AccessPoint = (ap) => {
widget.add(Revealer({
revealChild: true,
transition: 'slide_down',
child: EventBox({
onPrimaryClickRelease: () => {
child: CursorBox({
on_primary_click_release: () => {
execAsync(`nmcli device wifi connect
${widget.ap.value.bssid}`).catch(print);
},

View file

@ -62,7 +62,7 @@ class Pointers extends Service {
this.#getDevices();
this.#udevClient.connect('uevent', (_, action) => {
if (action === 'add' || action === 'remove') {
this.getDevices();
this.#getDevices();
if (this.#process) {
this.killProc();
this.startProc();
@ -139,9 +139,9 @@ class Pointers extends Service {
#initAppConnection() {
App.connect('window-toggled', () => {
const anyVisibleAndClosable = Array.from(App.windows).some((w) => {
const closable = w[1].closeOnUnfocus &&
!(w[1].closeOnUnfocus === 'none' ||
w[1].closeOnUnfocus === 'stay');
const closable = w[1].attribute?.close_on_unfocus &&
!(w[1].attribute?.close_on_unfocus === 'none' ||
w[1].attribute?.close_on_unfocus === 'stay');
return w[1].visible && closable;
});
@ -158,8 +158,8 @@ class Pointers extends Service {
static detectClickedOutside(clickStage) {
const toClose = Array.from(App.windows).some((w) => {
const closable = (w[1].closeOnUnfocus &&
w[1].closeOnUnfocus === clickStage);
const closable = (w[1].attribute?.close_on_unfocus &&
w[1].attribute?.close_on_unfocus === clickStage);
return w[1].visible && closable;
});
@ -181,8 +181,8 @@ class Pointers extends Service {
const widgets = key.levels['3'].filter((n) => {
const window = App.getWindow(n.namespace);
return window?.closeOnUnfocus &&
window?.closeOnUnfocus === clickStage;
return window?.attribute?.close_on_unfocus &&
window?.attribute?.close_on_unfocus === clickStage;
});
if (pos.x > bar?.x && pos.x < bar?.x + bar?.w &&