feat(ags osk): add spam typing on long press
All checks were successful
Discord / discord commits (push) Has been skipped

This commit is contained in:
matt1432 2024-11-25 17:25:27 -05:00
parent f445db75c1
commit 85a9b91aad
6 changed files with 96 additions and 108 deletions

View file

@ -32,8 +32,7 @@
border-radius: 0.7rem; border-radius: 0.7rem;
min-height: 3rem; min-height: 3rem;
transition: background-color 0.2s ease-in-out, transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out;
border-color 0.2s ease-in-out;
&.normal, &.normal,
&.Super { &.Super {

View file

@ -1,6 +1,7 @@
import { execAsync } from 'astal'; import { execAsync, idle } from 'astal';
import { Gtk } from 'astal/gtk3'; import { Gtk } from 'astal/gtk3';
import Tablet from '../../services/tablet';
import { hyprMessage } from '../../lib'; import { hyprMessage } from '../../lib';
import OskWindow from './osk-window'; import OskWindow from './osk-window';
@ -19,14 +20,18 @@ const releaseAllKeys = () => {
}; };
export default (window: OskWindow) => { export default (window: OskWindow) => {
const gesture = Gtk.GestureDrag.new(window); const tablet = Tablet.get_default();
window.get_child().css = `margin-bottom: -${HIDDEN_MARGIN}px;`;
let signals = [] as number[]; let signals = [] as number[];
window.setVisible = (state: boolean) => { const gesture = Gtk.GestureDrag.new(window);
if (state) {
window.get_child().css = `
margin-bottom: -${HIDDEN_MARGIN}px;
`;
window.hook(tablet, 'notify::osk-state', () => {
if (tablet.oskState) {
window.setSlideDown(); window.setSlideDown();
window.get_child().css = ` window.get_child().css = `
@ -36,6 +41,7 @@ export default (window: OskWindow) => {
} }
else { else {
releaseAllKeys(); releaseAllKeys();
window.setSlideUp(); window.setSlideUp();
window.get_child().css = ` window.get_child().css = `
@ -43,7 +49,11 @@ export default (window: OskWindow) => {
margin-bottom: -${HIDDEN_MARGIN}px; margin-bottom: -${HIDDEN_MARGIN}px;
`; `;
} }
}; });
idle(() => {
tablet.oskState = false;
});
window.killGestureSigs = () => { window.killGestureSigs = () => {
signals.forEach((id) => { signals.forEach((id) => {
@ -115,7 +125,7 @@ export default (window: OskWindow) => {
transition: margin-bottom 0.5s ease-in-out; transition: margin-bottom 0.5s ease-in-out;
margin-bottom: 0px; margin-bottom: 0px;
`; `;
window.setVisible(true); tablet.oskState = true;
} }
else { else {
window.get_child().css = ` window.get_child().css = `
@ -184,7 +194,7 @@ export default (window: OskWindow) => {
margin-bottom: -${HIDDEN_MARGIN}px; margin-bottom: -${HIDDEN_MARGIN}px;
`; `;
window.setVisible(false); tablet.oskState = false;
} }
else { else {
window.get_child().css = ` window.get_child().css = `

View file

@ -115,8 +115,8 @@ export default () => (
return ( return (
<box vertical> <box vertical>
<box <box
halign={Gtk.Align.END}
className="row" className="row"
halign={Gtk.Align.END}
> >
{...keys} {...keys}
</box> </box>

View file

@ -1,11 +1,13 @@
import { execAsync, Variable } from 'astal'; import { bind, execAsync, interval, Variable } from 'astal';
import { Gdk, Gtk, Widget } from 'astal/gtk3'; import { Gtk, Widget } from 'astal/gtk3';
import Brightness from '../../services/brightness'; import Brightness from '../../services/brightness';
import Separator from '../misc/separator'; import Separator from '../misc/separator';
/* Types */ /* Types */
import AstalIO from 'gi://AstalIO';
interface Key { interface Key {
keytype: string keytype: string
label: string label: string
@ -16,7 +18,6 @@ interface Key {
} }
const display = Gdk.Display.get_default();
const brightness = Brightness.get_default(); const brightness = Brightness.get_default();
const SPACING = 4; const SPACING = 4;
@ -24,10 +25,6 @@ const LSHIFT_CODE = 42;
const LALT_CODE = 56; const LALT_CODE = 56;
const LCTRL_CODE = 29; const LCTRL_CODE = 29;
// Keep track of when a non modifier key
// is clicked to release all modifiers
const NormalClick = Variable(false);
// Keep track of modifier statuses // Keep track of modifier statuses
const Super = Variable(false); const Super = Variable(false);
const LAlt = Variable(false); const LAlt = Variable(false);
@ -97,42 +94,14 @@ const ModKey = (key: Key) => {
const button = ( const button = (
<eventbox <eventbox
className="key" className="key"
cursor="pointer"
onButtonReleaseEvent={() => { onButtonPressEvent={() => {
console.log('mod toggled');
execAsync(`ydotool key ${key.keycode}:${Mod.get() ? 0 : 1}`); execAsync(`ydotool key ${key.keycode}:${Mod.get() ? 0 : 1}`);
label.toggleClassName('active', !Mod.get()); label.toggleClassName('active', !Mod.get());
Mod.set(!Mod.get()); Mod.set(!Mod.get());
}} }}
setup={(self) => {
self.hook(NormalClick, () => {
Mod.set(false);
label.toggleClassName('active', false);
execAsync(`ydotool key ${key.keycode}:0`);
});
// OnHover
self.connect('enter-notify-event', () => {
if (!display) {
return;
}
self.window.set_cursor(Gdk.Cursor.new_from_name(
display,
'pointer',
));
self.toggleClassName('hover', true);
});
// OnHoverLost
self.connect('leave-notify-event', () => {
self.window.set_cursor(null);
self.toggleClassName('hover', false);
});
}}
> >
{label} {label}
</eventbox> </eventbox>
@ -147,66 +116,63 @@ const ModKey = (key: Key) => {
}; };
const RegularKey = (key: Key) => { const RegularKey = (key: Key) => {
const IsActive = Variable(false);
const IsLongPressing = Variable(false);
IsLongPressing.subscribe((v) => {
console.log(v);
});
const widget = ( const widget = (
<eventbox <eventbox
className="key" className="key"
cursor="pointer"
onButtonReleaseEvent={() => {
IsLongPressing.set(false);
IsActive.set(false);
}}
> >
<label <label
className={`normal ${key.label}`} className={bind(IsActive).as((v) => [
'normal',
key.label,
(v ? 'active' : ''),
].join(' '))}
label={key.label} label={key.label}
setup={(self) => { setup={(self) => {
self self
.hook(Shift, () => { .hook(Shift, () => {
if (!key.labelShift) { if (key.labelShift) {
return; self.label = Shift.get() ?
key.labelShift :
key.label;
} }
self.label = Shift.get() ? key.labelShift : key.label;
}) })
.hook(Caps, () => { .hook(Caps, () => {
if (key.label === 'Caps') { if (key.label === 'Caps') {
self.toggleClassName('active', Caps.get()); IsActive.set(Caps.get());
return; return;
} }
if (!key.labelShift) { if (key.labelShift && key.label.match(/[A-Za-z]/)) {
return;
}
if (key.label.match(/[A-Za-z]/)) {
self.label = Caps.get() ? self.label = Caps.get() ?
key.labelShift : key.labelShift :
key.label; key.label;
} }
}) })
.hook(AltGr, () => { .hook(AltGr, () => {
if (!key.labelAltGr) { if (key.labelAltGr) {
return; self.toggleClassName('altgr', AltGr.get());
self.label = AltGr.get() ?
key.labelAltGr :
key.label;
} }
self.toggleClassName('altgr', AltGr.get());
self.label = AltGr.get() ? key.labelAltGr : key.label;
}); });
// OnHover
self.connect('enter-notify-event', () => {
if (!display) {
return;
}
self.window.set_cursor(Gdk.Cursor.new_from_name(
display,
'pointer',
));
self.toggleClassName('hover', true);
});
// OnHoverLost
self.connect('leave-notify-event', () => {
self.window.set_cursor(null);
self.toggleClassName('hover', false);
});
}} }}
/> />
</eventbox> </eventbox>
@ -216,8 +182,7 @@ const RegularKey = (key: Key) => {
gesture.delay_factor = 1.0; gesture.delay_factor = 1.0;
// OnPrimaryClickRelease const onClick = (callback: () => void) => {
widget.hook(gesture, 'cancelled', () => {
const pointer = gesture.get_point(null); const pointer = gesture.get_point(null);
const x = pointer[1]; const x = pointer[1];
const y = pointer[2]; const y = pointer[2];
@ -226,11 +191,41 @@ const RegularKey = (key: Key) => {
return; return;
} }
console.log('key clicked'); callback();
};
execAsync(`ydotool key ${key.keycode}:1`); widget.hook(gesture, 'begin', () => {
execAsync(`ydotool key ${key.keycode}:0`); IsActive.set(true);
NormalClick.set(true); });
widget.hook(gesture, 'cancelled', () => {
onClick(() => {
execAsync(`ydotool key ${key.keycode}:1`);
execAsync(`ydotool key ${key.keycode}:0`);
IsActive.set(false);
});
});
// Long Press
widget.hook(gesture, 'pressed', () => {
onClick(() => {
IsLongPressing.set(true);
});
});
let spamClick: AstalIO.Time | undefined;
IsLongPressing.subscribe((v) => {
if (v) {
spamClick = interval(100, () => {
execAsync(`ydotool key ${key.keycode}:1`);
execAsync(`ydotool key ${key.keycode}:0`);
});
}
else {
spamClick?.cancel();
}
}); });
return ( return (

View file

@ -1,20 +1,15 @@
import { execAsync, idle } from 'astal'; import { execAsync } from 'astal';
import { Astal } from 'astal/gtk3'; import { Astal } from 'astal/gtk3';
import Tablet from '../../services/tablet';
import OskWindow from './osk-window'; import OskWindow from './osk-window';
import Gesture from './gesture'; import Gesture from './gesture';
import Keyboard from './keyboard'; import Keyboard from './keyboard';
export default () => { export default () => {
// Start ydotool daemon
execAsync('ydotoold').catch(print); execAsync('ydotoold').catch(print);
const tablet = Tablet.get_default(); return Gesture((
const window = (
<OskWindow <OskWindow
name="osk" name="osk"
namespace="noanim-osk" namespace="noanim-osk"
@ -29,15 +24,5 @@ export default () => {
> >
<Keyboard /> <Keyboard />
</OskWindow> </OskWindow>
) as OskWindow; ) as OskWindow);
window.hook(tablet, 'notify::osk-state', (self, state) => {
self.setVisible(state);
});
idle(() => {
window.setVisible(false);
});
return Gesture(window);
}; };

View file

@ -6,7 +6,6 @@ import { register } from 'astal/gobject';
export default class OskWindow extends Widget.Window { export default class OskWindow extends Widget.Window {
public startY: number | null = null; public startY: number | null = null;
declare public setVisible: (state: boolean) => void;
declare public killGestureSigs: () => void; declare public killGestureSigs: () => void;
declare public setSlideUp: () => void; declare public setSlideUp: () => void;
declare public setSlideDown: () => void; declare public setSlideDown: () => void;