feat(control): bind arrow keys to arrow buttons

This commit is contained in:
E44
2026-06-13 22:00:09 +02:00
parent a3d444df20
commit a49b842a4c
2 changed files with 87 additions and 22 deletions
@@ -36,7 +36,7 @@
{#if content.open} {#if content.open}
<div <div
class="absolute inset-0 backdrop-blur flex justify-center items-center z-50 {className}" class="popup absolute inset-0 backdrop-blur flex justify-center items-center z-50 {className}"
transition:fade={{ duration: 100 }} transition:fade={{ duration: 100 }}
> >
<div <div
+83 -18
View File
@@ -34,6 +34,7 @@
import { liveQuery, type Observable } from 'dexie'; import { liveQuery, type Observable } from 'dexie';
import TextInput from '$lib/components/TextInput.svelte'; import TextInput from '$lib/components/TextInput.svelte';
import { add_to_keyboard_queue } from '$lib/ts/utils'; import { add_to_keyboard_queue } from '$lib/ts/utils';
import { onMount } from 'svelte';
let all_display_states: Observable<'on' | 'off' | 'mixed'> | undefined = $state(); let all_display_states: Observable<'on' | 'off' | 'mixed'> | undefined = $state();
$effect(() => { $effect(() => {
@@ -44,11 +45,16 @@
let popup_content: PopupContent = $state({ let popup_content: PopupContent = $state({
open: false, open: false,
snippet: null, snippet: null,
title: '', title: ''
}); });
let current_text = $state(''); let current_text = $state('');
const key_pressed = $state({
ArrowRight: false,
ArrowLeft: false
});
function popup_close_function() { function popup_close_function() {
popup_content.open = false; popup_content.open = false;
} }
@@ -79,7 +85,7 @@
snippet: website_popup, snippet: website_popup,
title: 'Webseite Anzeigen', title: 'Webseite Anzeigen',
window_class: 'w-xl', window_class: 'w-xl',
title_icon: Globe, title_icon: Globe
}; };
}; };
@@ -102,7 +108,7 @@
open: true, open: true,
snippet: ask_shutdown_popup, snippet: ask_shutdown_popup,
title: 'Bildschirm Herunterfahren', title: 'Bildschirm Herunterfahren',
title_icon: PowerOff, title_icon: PowerOff
}; };
} }
@@ -110,7 +116,10 @@
popup_content.open = false; popup_content.open = false;
await run_on_all_selected_displays((d) => { await run_on_all_selected_displays((d) => {
shutdown(d.ip); // no await here because we want to be fast shutdown(d.ip); // no await here because we want to be fast
db.displays.update(d.id, { status: 'app_offline', preview: { currently_updating: false, url: null} }); db.displays.update(d.id, {
status: 'app_offline',
preview: { currently_updating: false, url: null }
});
}, false); }, false);
} }
@@ -143,10 +152,58 @@
async function send_website() { async function send_website() {
popup_content.open = false; popup_content.open = false;
await run_on_all_selected_displays((d) => await run_on_all_selected_displays((d) => open_website(d.ip, website_url));
open_website(d.ip, website_url)
);
} }
function has_open_popup(): boolean {
return document.querySelector('.popup') !== null;
}
function handle_key(
event: KeyboardEvent,
key: 'ArrowRight' | 'ArrowLeft',
action: 'press' | 'release'
) {
if (event.key === key) {
const current_press_state: boolean = key_pressed[key];
if (
(action === 'press' && (current_press_state === true || has_open_popup())) ||
(action === 'release' && current_press_state === false)
)
return;
key_pressed[key] = !current_press_state;
add_to_keyboard_queue(async () => {
await run_on_all_selected_displays(
(d) => send_keyboard_input(d.ip, [{ key, action }]),
true
);
});
}
}
function add_remove_event_listeners(
add_remove_function: (type: string, listener: (e: KeyboardEvent) => void) => void
) {
const actions = {
keydown: 'press',
keyup: 'release'
} as const;
const keys = ['ArrowRight', 'ArrowLeft'] as const;
for (const [action_type, action_value] of Object.entries(actions)) {
for (const key of keys) {
add_remove_function(action_type, (e: KeyboardEvent) => handle_key(e, key, action_value));
}
}
}
onMount(() => {
add_remove_event_listeners(window.addEventListener);
return () => {
add_remove_event_listeners(window.removeEventListener);
};
});
</script> </script>
{#snippet website_popup()} {#snippet website_popup()}
@@ -202,15 +259,19 @@
<div class="flex flex-row gap-2 w-75 justify-normal"> <div class="flex flex-row gap-2 w-75 justify-normal">
<button <button
title="Vorherige Folie (Pfeil nach Links) [gedrückt halten möglich]" title="Vorherige Folie (Pfeil nach Links) [gedrückt halten möglich]"
class="px-9 bg-stone-700 {$selected_online_display_ids.length === 0 class="px-9 {key_pressed.ArrowLeft
? 'bg-stone-500'
: 'bg-stone-700'} {$selected_online_display_ids.length === 0
? 'text-stone-500 cursor-not-allowed' ? 'text-stone-500 cursor-not-allowed'
: 'hover:bg-stone-600 active:bg-stone-500 cursor-pointer'} py-2 rounded-xl flex justify-center items-center transition-colors duration-200" : 'hover:bg-stone-600 active:bg-stone-500 cursor-pointer'} py-2 rounded-xl flex justify-center items-center transition-colors duration-200"
disabled={$selected_online_display_ids.length === 0} disabled={$selected_online_display_ids.length === 0 || key_pressed.ArrowLeft}
onmousedown={() => { onmousedown={() => {
add_to_keyboard_queue(async () => await send_single_key_press('ArrowLeft', 'press')); add_to_keyboard_queue(async () => await send_single_key_press('ArrowLeft', 'press'));
}} }}
onmouseup={() => { onmouseup={() => {
add_to_keyboard_queue(async () => await send_single_key_press('ArrowLeft', 'release')); add_to_keyboard_queue(
async () => await send_single_key_press('ArrowLeft', 'release')
);
}} }}
> >
<ArrowBigLeft /> <ArrowBigLeft />
@@ -218,15 +279,21 @@
<button <button
title="Nächste Folie (Pfeil nach Rechts) [gedrückt halten möglich]" title="Nächste Folie (Pfeil nach Rechts) [gedrückt halten möglich]"
class="px-9 bg-stone-700 {$selected_online_display_ids.length === 0 class="px-9 {key_pressed.ArrowRight
? 'bg-stone-500 cursor-not-allowed'
: `bg-stone-700 ${
$selected_online_display_ids.length === 0
? 'text-stone-500 cursor-not-allowed' ? 'text-stone-500 cursor-not-allowed'
: 'hover:bg-stone-600 active:bg-stone-500 cursor-pointer'} py-2 rounded-xl flex justify-center items-center transition-colors duration-200" : 'hover:bg-stone-600 active:bg-stone-500 cursor-pointer'
disabled={$selected_online_display_ids.length === 0} }`} py-2 rounded-xl flex justify-center items-center transition-colors duration-200"
disabled={$selected_online_display_ids.length === 0 || key_pressed.ArrowRight}
onmousedown={() => { onmousedown={() => {
add_to_keyboard_queue(async () => await send_single_key_press('ArrowRight', 'press')); add_to_keyboard_queue(async () => await send_single_key_press('ArrowRight', 'press'));
}} }}
onmouseup={() => { onmouseup={() => {
add_to_keyboard_queue(async () => await send_single_key_press('ArrowRight', 'release')); add_to_keyboard_queue(
async () => await send_single_key_press('ArrowRight', 'release')
);
}} }}
> >
<ArrowBigRight /> <ArrowBigRight />
@@ -271,8 +338,7 @@
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<Button <Button
className="px-3 flex gap-3 w-full xl:w-75 justify-normal" className="px-3 flex gap-3 w-full xl:w-75 justify-normal"
disabled={$all_display_states === 'on' || disabled={$all_display_states === 'on' || $selected_display_ids.length === 0}
$selected_display_ids.length === 0}
click_function={startup_action} click_function={startup_action}
> >
<Power /> Bildschirm Hochfahren <Power /> Bildschirm Hochfahren
@@ -280,8 +346,7 @@
<Button <Button
className="px-3 flex gap-3 w-full xl:w-75 justify-normal" className="px-3 flex gap-3 w-full xl:w-75 justify-normal"
disabled={$all_display_states === 'off' || disabled={$all_display_states === 'off' || $selected_online_display_ids.length === 0}
$selected_online_display_ids.length === 0}
click_function={ask_shutdown} click_function={ask_shutdown}
> >
<PowerOff /> Bildschirm Herunterfahren</Button <PowerOff /> Bildschirm Herunterfahren</Button