feat(ags pointers): move everything to service and add onClickUnfocus param to popups

This commit is contained in:
matt1432 2023-10-24 17:26:38 -04:00
parent e6cbee084f
commit fda39b75c1
5 changed files with 128 additions and 120 deletions

View file

@ -1,6 +1,8 @@
import { App, Utils } from './imports.js'; import { App, Utils } from './imports.js';
import Closer from './js/misc/closer.js'; import closeAll from './js/misc/closer.js';
globalThis.closeAll = closeAll;
import Powermenu from './js/powermenu.js'; import Powermenu from './js/powermenu.js';
import * as Bar from './js/bar/main.js'; import * as Bar from './js/bar/main.js';
import NotifCenter from './js/notifications/center.js'; import NotifCenter from './js/notifications/center.js';
@ -25,25 +27,25 @@ export default {
notificationPopupTimeout: 5000, notificationPopupTimeout: 5000,
cacheNotificationActions: true, cacheNotificationActions: true,
closeWindowDelay: { closeWindowDelay: {
'quick-settings': 500,
'notification-center': 500,
'calendar': 500,
'powermenu': 500,
'overview': 500,
'applauncher': 500, 'applauncher': 500,
'calendar': 500,
'notification-center': 500,
'overview': 500,
'powermenu': 500,
'quick-settings': 500,
}, },
windows: [ windows: [
AppLauncher(),
Calendar(),
NotifCenter(),
Overview(),
Powermenu(), Powermenu(),
QuickSettings(),
Bar.Bar(), Bar.Bar(),
Bar.BgGradient(), Bar.BgGradient(),
Closer(),
NotifCenter(),
NotifPopups(),
Calendar(),
QuickSettings(),
Overview(),
AppLauncher(),
Corners.Bottomleft(), Corners.Bottomleft(),
Corners.Bottomright(), Corners.Bottomright(),
NotifPopups(),
], ],
}; };

View file

@ -1,86 +1,15 @@
import { App, Utils, Widget } from '../../imports.js'; import { App } from '../../imports.js';
const { Window, Revealer } = Widget;
// TODO: find a way to not need this?
import Pointers from '../../services/pointers.js'; import Pointers from '../../services/pointers.js';
const ALWAYS_OPEN = [
'closer',
'bar',
'bg-gradient',
'notifications',
'cornertl',
'cornertr',
'cornerbl',
'cornerbr',
];
export default () => {
const closeAll = () => { const closableWindows = Array.from(App.windows).filter(w => {
App.windows.forEach(w => { return w[1].closeOnUnfocus &&
if (!ALWAYS_OPEN.some(window => window === w.name)) w[1].closeOnUnfocus !== 'none';
App.closeWindow(w.name); });
closableWindows.forEach(w => {
App.closeWindow(w[0]);
}); });
App.closeWindow('closer');
}; };
globalThis.closeAll = closeAll;
Pointers.connect('new-line', (_, out) => {
if (out) {
Utils.execAsync('hyprctl layers -j').then(layers => {
layers = JSON.parse(layers);
Utils.execAsync('hyprctl cursorpos -j').then(pos => {
pos = JSON.parse(pos);
Object.values(layers).forEach(key => {
const bar = key['levels']['3']
.find(n => n.namespace === 'bar');
const widgets = key['levels']['3']
.filter(n => !ALWAYS_OPEN.includes(n.namespace));
if (pos.x > bar.x && pos.x < bar.x + bar.w &&
pos.y > bar.y && pos.y < bar.y + bar.h) {
// don't handle clicks when on bar
}
else {
widgets.forEach(l => {
if (!(pos.x > l.x && pos.x < l.x + l.w &&
pos.y > l.y && pos.y < l.y + l.h)) {
closeAll();
return;
}
});
}
});
}).catch(print);
}).catch(print);
}
});
export default () => Window({
name: 'closer',
popup: true,
layer: 'top',
child: Revealer({
connections: [[App, (_, windowName, visible) => {
const anyVisibleAndClosable = Array.from(App.windows).some(w => {
const isAlwaysOpen = ALWAYS_OPEN.some(window => window === w[0]);
return w[1].visible && !isAlwaysOpen;
});
if (!anyVisibleAndClosable)
App.closeWindow('closer');
if (windowName === 'closer') {
if (visible)
Pointers.startProc();
else
Pointers.killProc();
}
}]],
}),
});

View file

@ -5,6 +5,7 @@ const { Revealer, Box, Window } = Widget;
export default ({ export default ({
name, name,
child, child,
closeOnUnfocus = 'released',
transition = 'slide_down', transition = 'slide_down',
onOpen = () => {}, onOpen = () => {},
...props ...props
@ -27,9 +28,6 @@ export default ({
if (currentName === name) { if (currentName === name) {
rev.reveal_child = visible; rev.reveal_child = visible;
onOpen(child); onOpen(child);
if (visible && name !== 'overview')
App.openWindow('closer');
} }
}]], }]],
child: child, child: child,
@ -37,5 +35,6 @@ export default ({
}), }),
}); });
window.getChild = () => child; window.getChild = () => child;
window.closeOnUnfocus = closeOnUnfocus;
return window; return window;
}; };

View file

@ -15,6 +15,7 @@ function update(box) {
export default () => PopupWindow({ export default () => PopupWindow({
name: 'overview', name: 'overview',
transition: 'crossfade', transition: 'crossfade',
closeOnUnfocus: 'none',
onOpen: child => update(child), onOpen: child => update(child),
child: Box({ child: Box({

View file

@ -1,4 +1,4 @@
import { Service, Utils } from '../imports.js'; import { App, Service, Utils } from '../imports.js';
import GUdev from 'gi://GUdev'; import GUdev from 'gi://GUdev';
const UDEV_POINTERS = [ const UDEV_POINTERS = [
@ -8,12 +8,15 @@ const UDEV_POINTERS = [
'ID_INPUT_TOUCHSCREEN', 'ID_INPUT_TOUCHSCREEN',
'ID_INPUT_TABLET', 'ID_INPUT_TABLET',
]; ];
const LIB_POINTERS = [ const ON_RELEASE_TRIGGERS = [
'BTN',
'released', 'released',
'TOUCH_UP', 'TOUCH_UP',
'HOLD_END', 'HOLD_END',
]; ];
const ON_CLICK_TRIGGERS = [
'pressed',
'TOUCH_DOWN',
];
class Pointers extends Service { class Pointers extends Service {
@ -23,6 +26,8 @@ class Pointers extends Service {
'proc-destroyed': ['boolean'], 'proc-destroyed': ['boolean'],
'device-fetched': ['boolean'], 'device-fetched': ['boolean'],
'new-line': ['string'], 'new-line': ['string'],
'released': ['string'],
'clicked': ['string'],
}); });
} }
@ -38,9 +43,23 @@ class Pointers extends Service {
constructor() { constructor() {
super(); super();
this.initUdevConnection(); this.initUdevConnection();
this.initAppConnection();
} }
// FIXME: logitech mouse screws everything up on disconnect // FIXME: logitech mouse screws everything up on disconnect
initUdevConnection() {
this.getDevices();
this.udevClient.connect('uevent', (_, action) => {
if (action === 'add' || action === 'remove') {
this.getDevices();
if (this.proc) {
this.killProc();
this.startProc();
}
}
});
}
getDevices() { getDevices() {
this.devices = []; this.devices = [];
this.udevClient.query_by_subsystem('input').forEach(dev => { this.udevClient.query_by_subsystem('input').forEach(dev => {
@ -56,19 +75,6 @@ class Pointers extends Service {
this.emit('device-fetched', true); this.emit('device-fetched', true);
} }
initUdevConnection() {
this.getDevices();
this.udevClient.connect('uevent', (_, action) => {
if (action === 'add' || action === 'remove') {
this.getDevices();
if (this.proc) {
this.killProc();
this.startProc();
}
}
});
}
startProc() { startProc() {
if (this.proc) if (this.proc)
return; return;
@ -82,11 +88,21 @@ class Pointers extends Service {
this.proc = Utils.subprocess( this.proc = Utils.subprocess(
['libinput', 'debug-events', ...args], ['libinput', 'debug-events', ...args],
output => { output => {
if (!output.includes('cancelled')) { if (output.includes('cancelled'))
if (LIB_POINTERS.some(p => output.includes(p))) { return;
if (ON_RELEASE_TRIGGERS.some(p => output.includes(p))) {
this.output = output; this.output = output;
this.detectClickedOutside('released');
this.emit('released', output);
this.emit('new-line', output); this.emit('new-line', output);
} }
if (ON_CLICK_TRIGGERS.some(p => output.includes(p))) {
this.output = output;
this.detectClickedOutside('clicked');
this.emit('clicked', output);
this.emit('new-line', output);
} }
}, },
err => logError(err), err => logError(err),
@ -101,6 +117,67 @@ class Pointers extends Service {
this.emit('proc-destroyed', true); this.emit('proc-destroyed', true);
} }
} }
initAppConnection() {
App.connect('window-toggled', () => {
const anyVisibleAndClosable = Array.from(App.windows).some(w => {
const closable = (w[1].closeOnUnfocus &&
w[1].closeOnUnfocus !== 'none');
return w[1].visible && closable;
});
if (anyVisibleAndClosable)
this.startProc();
else
this.killProc();
});
}
detectClickedOutside(clickStage) {
const toClose = Array.from(App.windows).some(w => {
const closable = (w[1].closeOnUnfocus &&
w[1].closeOnUnfocus === clickStage);
return w[1].visible && closable;
});
if (!toClose)
return;
Utils.execAsync('hyprctl layers -j').then(layers => {
layers = JSON.parse(layers);
Utils.execAsync('hyprctl cursorpos -j').then(pos => {
pos = JSON.parse(pos);
Object.values(layers).forEach(key => {
const bar = key['levels']['3']
.find(n => n.namespace === 'bar');
const widgets = key['levels']['3'].filter(n => {
const window = App.getWindow(n.namespace);
return window.closeOnUnfocus &&
window.closeOnUnfocus === clickStage;
});
if (pos.x > bar.x && pos.x < bar.x + bar.w &&
pos.y > bar.y && pos.y < bar.y + bar.h) {
// don't handle clicks when on bar
// TODO: make this configurable
}
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.closeWindow(w.namespace);
});
}
});
}).catch(print);
}).catch(print);
}
} }
const pointersService = new Pointers(); const pointersService = new Pointers();