ltk/input/keyboard/
mod.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>

//! Keyboard → ltk dispatch.
//!
//! Translates [`KeyInput`] events into focus / text-insertion /
//! widget-submit actions. Does not share state with the gesture state
//! machine — keyboard tracks its own focus (`AppData::keyboard_focus`)
//! and modifier flags (`shift_pressed`, `ctrl_pressed`, `alt_pressed`).
//!
//! The only cross-cutting state it reads is `SurfaceState::focused_idx`
//! (set by pointer / touch focus updates) so keyboard navigation can
//! branch on "is a widget focused?" — Return submits / Space presses /
//! typing inserts all funnel through that check.

use crate::app::App;
use crate::event_loop::{ AppData, SurfaceFocus };
use crate::Keysym;
use crate::platform::Platform;

use super::event::KeyInput;

pub( super ) mod back;
pub( super ) mod dispatch;
pub( super ) mod text_keys;
pub( super ) mod shortcuts;
pub( super ) mod nav;

impl<A: App, P: Platform> AppData<A, P>
{
	/// The keyboard focus moved onto `focus`.
	pub( crate ) fn keyboard_enter( &mut self, focus: SurfaceFocus )
	{
		self.keyboard_focus = focus;
		self.surface_mut( focus ).request_redraw();
		if let Some( ref mut a ) = self.a11y { a.set_window_focus( true ); }
	}

	/// The keyboard focus left every surface of the app.
	pub( crate ) fn keyboard_leave( &mut self )
	{
		self.stop_key_repeat();
		self.keyboard_focus = SurfaceFocus::Main;
		self.app.on_keyboard_leave();
		if let Some( ref mut a ) = self.a11y { a.set_window_focus( false ); }
	}

	/// A key went down on the surface with the keyboard focus. Starts the runtime's own repeat for it.
	pub( crate ) fn key_press( &mut self, key: KeyInput )
	{
		let focus = self.keyboard_focus;
		// Back is a navigation request, not input for an embedded system.
		if key.keysym != Keysym::XF86_Back
		{
			let raw = key.raw( true, self.ctrl_pressed, self.shift_pressed, self.alt_pressed );
			self.app.on_raw_key_event( &raw );
		}
		self.stop_key_repeat();
		if key.keysym != Keysym::XF86_Back && self.app.claims_keyboard() { return; }
		self.dispatch_key( focus, key.clone() );
		self.start_key_repeat( focus, key );
	}

	pub( crate ) fn key_release( &mut self, key: &KeyInput )
	{
		if key.keysym != Keysym::XF86_Back
		{
			let raw = key.raw( false, self.ctrl_pressed, self.shift_pressed, self.alt_pressed );
			self.app.on_raw_key_event( &raw );
		}
		// Releasing a key other than the repeating one, a modifier for
		// instance, leaves the repeat running.
		if self.key_repeat.as_ref().is_some_and( |state| state.event.keysym == key.keysym )
		{
			self.stop_key_repeat();
		}
	}

	/// A repeat the platform generated itself. The runtime's own repeat stops so the key does not fire twice.
	pub( crate ) fn key_repeated( &mut self, key: KeyInput )
	{
		self.stop_key_repeat();
		if key.keysym != Keysym::XF86_Back && self.app.claims_keyboard() { return; }
		let focus = self.keyboard_focus;
		self.dispatch_key( focus, key );
	}

	pub( crate ) fn modifiers_changed( &mut self, ctrl: bool, shift: bool, alt: bool )
	{
		self.shift_pressed = shift;
		self.ctrl_pressed  = ctrl;
		self.alt_pressed   = alt;
		self.app.on_modifiers( ctrl, shift, alt );
	}

	/// The platform's key repeat: `rate` keys a second after `delay` milliseconds. A `rate` of 0 means the platform asked for no repeat.
	pub( crate ) fn set_repeat_info( &mut self, rate: u32, delay: u32 )
	{
		self.compositor_repeat_rate  = rate;
		self.compositor_repeat_delay = delay;
		if rate == 0 { self.stop_key_repeat(); }
	}
}