feat(ags pointers): move everything to service and add onClickUnfocus param to popups
This commit is contained in:
parent
e6cbee084f
commit
fda39b75c1
5 changed files with 128 additions and 120 deletions
|
@ -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(),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}]],
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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({
|
||||||
|
|
|
@ -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'],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,31 +36,17 @@ class Pointers extends Service {
|
||||||
devices = [];
|
devices = [];
|
||||||
udevClient = GUdev.Client.new(['input']);
|
udevClient = GUdev.Client.new(['input']);
|
||||||
|
|
||||||
get process() { return this.proc; }
|
get process() { return this.proc; }
|
||||||
get lastLine() { return this.output; }
|
get lastLine() { return this.output; }
|
||||||
get pointers() { return this.devices; }
|
get pointers() { return this.devices; }
|
||||||
|
|
||||||
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
|
||||||
getDevices() {
|
|
||||||
this.devices = [];
|
|
||||||
this.udevClient.query_by_subsystem('input').forEach(dev => {
|
|
||||||
const isPointer = UDEV_POINTERS.some(p => dev.has_property(p));
|
|
||||||
if (isPointer) {
|
|
||||||
const hasEventFile = dev.has_property('DEVNAME') &&
|
|
||||||
dev.get_property('DEVNAME').includes('event');
|
|
||||||
if (hasEventFile)
|
|
||||||
this.devices.push(dev.get_property('DEVNAME'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.emit('device-fetched', true);
|
|
||||||
}
|
|
||||||
|
|
||||||
initUdevConnection() {
|
initUdevConnection() {
|
||||||
this.getDevices();
|
this.getDevices();
|
||||||
this.udevClient.connect('uevent', (_, action) => {
|
this.udevClient.connect('uevent', (_, action) => {
|
||||||
|
@ -69,6 +60,21 @@ class Pointers extends Service {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDevices() {
|
||||||
|
this.devices = [];
|
||||||
|
this.udevClient.query_by_subsystem('input').forEach(dev => {
|
||||||
|
const isPointer = UDEV_POINTERS.some(p => dev.has_property(p));
|
||||||
|
if (isPointer) {
|
||||||
|
const hasEventFile = dev.has_property('DEVNAME') &&
|
||||||
|
dev.get_property('DEVNAME').includes('event');
|
||||||
|
if (hasEventFile)
|
||||||
|
this.devices.push(dev.get_property('DEVNAME'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.emit('device-fetched', true);
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
this.output = output;
|
|
||||||
this.emit('new-line', output);
|
if (ON_RELEASE_TRIGGERS.some(p => output.includes(p))) {
|
||||||
}
|
this.output = output;
|
||||||
|
this.detectClickedOutside('released');
|
||||||
|
this.emit('released', 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();
|
||||||
|
|
Loading…
Reference in a new issue