Skip to main content

denise_keyboard/
lib.rs

1//! An on-screen keyboard for panels that have no other one.
2//!
3//! # It is not a special case
4//!
5//! The point of this crate is that nothing downstream of it can tell the
6//! difference between a key tapped here and a key pressed on a keyboard plugged
7//! into the machine. [`denise`] splits keyboard input into two events —
8//! [`InputEvent::Key`], a physical position, and [`InputEvent::Text`], a
9//! character somebody meant to insert — and the hardware path in `denise-evdev`
10//! emits the first followed by whatever the second turns out to be.
11//!
12//! [`Keyboard::press`] emits the same two events, in the same order, produced by
13//! the same [`Composer`] from the same [`Layout`] tables. The application hands
14//! them to [`Ui::handle`], which is the call the hardware path's events arrive
15//! through as well. So a [`TextInput`] inserts them without knowing, a key
16//! binding on Enter fires exactly as it would, and every widget that already
17//! handles keys handles these.
18//!
19//! That is worth stating because the alternative is so tempting: a method on the
20//! text field that inserts a character directly. It would work, and then Enter
21//! would do nothing, Escape would do nothing, and every widget that is not a
22//! text field would be deaf to the keyboard.
23//!
24//! # It is built from widgets
25//!
26//! Every key is a [`Button`] — [`Button::no_focus`], so pressing one does not
27//! move the caret out of the field being typed into. They sit on a
28//! [`Ui::push_shelf`], which slides up from the bottom without pushing a scene,
29//! so the field keeps focus while the keyboard is up. Neither of those is a
30//! keyboard feature; they are toolkit features this is the first user of.
31//!
32//! # Modifiers
33//!
34//! Shift is a one-shot: armed by a tap and spent by the next key. There is no
35//! clock in the press path, so no double-tap window could latch it — and none
36//! is wanted, because Caps Lock has a key of its own where Caps Lock goes.
37//!
38//! Caps is a latch and not a held Shift: it applies to letters and leaves the
39//! digit row alone, which is the difference between a locked keyboard typing
40//! `1` and typing `!`, and caps over shift gives lower case the way a hand
41//! expects. The [`Composer`] models that already, so it is latched with a
42//! `CapsLock` key rather than reimplemented here. Ctrl is a one-shot too, and
43//! reaches the events it modifies.
44//!
45//! Every key that changes what the *next* press means says which state it is
46//! in, and the number and punctuation keys carry what Shift would give in a
47//! small second legend — the `!` over the `1`. Letters do not: a capital `Q`
48//! over a `q` is not news.
49//!
50//! The keys whose meaning is a *name* are drawn rather than lettered — see
51//! [`icons`] — which is why they look the same on a machine with no fonts
52//! installed as on one with DejaVu.
53//!
54//! The third level is the layout's own `AltGr` rather than a page of symbols
55//! chosen here, because there is no such page to choose: `@` is `AltGr`+`2` on a
56//! Norwegian keyboard and `Shift`+`2` on a US one, and a fixed grid would be
57//! wrong on one of them. It latches, since a finger cannot hold one key and
58//! press another.
59//!
60//! # Layouts
61//!
62//! [`Keyboard::from_system`] starts from whatever the machine is configured
63//! for, which is the answer the hardware path starts from too — so a panel with
64//! a keyboard plugged into it and one without agree about what the `;` position
65//! types. It hands back a [`LayoutSource`], and
66//! [`LayoutSource::Unknown`] is the one
67//! worth showing somebody: the system asked for a layout there is no table for
68//! and got US.
69//!
70//! The layout key walks the built-ins. Switching **reletters the keys where
71//! they stand** rather than rebuilding them, because a position does not move
72//! when the layout changes — `KeyCode::Semicolon` is where `ø` lives on
73//! Norwegian and `ö` on German, and it is the same key.
74//!
75//! Switching the keyboard does not switch a physical keyboard attached to the
76//! same machine. An application that wants both in step calls
77//! `InputBackend::set_layout` as well; the toolkit does not couple them,
78//! because it does not know the two are meant to agree.
79//!
80//! # The shape of it
81//!
82//! A compact physical keyboard rather than a phone one: fourteen columns,
83//! Backspace top right, Tab opening the second row, Enter closing the home row,
84//! Shift at both ends of the bottom one. A panel is something somebody stands
85//! in front of and types an address into, so the digits stay on screen instead
86//! of going behind a `123` page, and Tab is how a form gets crossed.
87//!
88//! The width is what makes the layouts complete — see [`ROWS`] for which
89//! positions carry what, and why a narrower grid could not type `å`.
90//!
91//! # Holding a key
92//!
93//! Backspace repeats while it is held and nothing else does, which is what a
94//! phone does and what stops a slow finger typing `aaaaaa`. [`Keyboard::tick`]
95//! collects what a held key has earned, once a frame; it costs nothing when
96//! nobody is touching one, because a repeating key asks the tree to wake it
97//! only between its press and its release.
98//!
99//! Holding a *letter* offers its alternates instead — `é è ê ë` over the `e`,
100//! in a framed strip above the key, chosen by where the finger lifts. The
101//! characters come from the layout, so they change when it does. That gesture
102//! needs the application's help for one call: see [`Keyboard::handle`].
103//!
104//! A field focused under the keyboard is scrolled clear of it where it sits in
105//! something that scrolls; where it does not, [`Keyboard::occluded`] says what
106//! to move it clear of.
107//!
108//! [`InputEvent::Key`]: denise::InputEvent::Key
109//! [`InputEvent::Text`]: denise::InputEvent::Text
110//! [`Ui::handle`]: denise_ui::Ui::handle
111//! [`Ui::push_shelf`]: denise_ui::Ui::push_shelf
112//! [`Button`]: denise_ui::widgets::Button
113//! [`Button::no_focus`]: denise_ui::widgets::Button::no_focus
114//! [`TextInput`]: denise_ui::widgets::TextInput
115
116use denise::{ElementState, InputEvent, KeyCode, Modifiers, Point, Rect, Role};
117use denise_layout::{Composer, Layout, LayoutSource, Output};
118use denise_text::TextStyle;
119use denise_ui::widgets::{Button, Panel, TextInput};
120use denise_ui::{NodeId, Side, Ui};
121
122mod grid;
123pub mod icons;
124
125pub use grid::{Key, ROWS, Row};
126
127/// What the third-level key says.
128const LEVEL3_LEGEND: &str = "alt";
129
130/// The position the layout key borrows.
131///
132/// A key in the grid has to *be* a position, and no real position means "change
133/// layout". `Unidentified` is what the tree already uses for a key it cannot
134/// name, and no layout table letters it — so nothing can be pressed by accident
135/// and nothing else will ever claim it.
136pub(crate) const LAYOUT_KEY: KeyCode = KeyCode::Unidentified(u32::MAX);
137
138/// Height of one key, in logical pixels.
139///
140/// A finger, not a mouse: this is the smallest target that is comfortable on a
141/// panel somebody is standing in front of.
142pub const KEY_HEIGHT: i32 = 48;
143
144/// Space between keys, and around the edge of the shelf.
145pub const KEY_GAP: i32 = 6;
146
147/// Legend size, in logical pixels, when the application names no style.
148const KEY_TEXT: u16 = 16;
149
150/// How long Backspace waits before it starts deleting on its own.
151///
152/// Long enough that no ordinary tap reaches it, short enough that somebody who
153/// meant to hold does not first wonder whether it is broken.
154pub const REPEAT_DELAY_MS: u64 = 450;
155
156/// The margin of frame showing around a strip of alternates, in logical pixels.
157///
158/// Wide enough that the border reads as the edge of something floating rather
159/// than as a seam between keys.
160const STRIP_PAD: i32 = 8;
161
162/// How thick that frame is drawn.
163const STRIP_BORDER: i32 = 2;
164
165/// How long a letter is held before it offers its alternates.
166///
167/// Longer than the pause before Backspace starts repeating, and deliberately:
168/// a repeat that starts a shade early costs one character, and a strip of
169/// letters that opens under a finger that meant to type `e` costs the gesture.
170pub const HOLD_MS: u64 = 500;
171
172/// How often it deletes after that.
173///
174/// Roughly fifteen a second: fast enough to clear a URL bar in a moment, slow
175/// enough to stop where you meant to.
176pub const REPEAT_INTERVAL_MS: u64 = 65;
177
178/// What the Shift key is currently doing.
179///
180/// Three states rather than a shift that latches on a double tap, and the
181/// reason is that [`Keyboard::press`] is not given a clock. A double-tap window
182/// needs one, and threading a timestamp through every key press to serve one key
183/// is a poor trade against a cycle a user can see: the key says which state it
184/// is in, and tapping it moves to the next.
185#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
186pub enum Shift {
187    /// Lower case, and the next character is not shifted.
188    #[default]
189    Off,
190    /// The next character is shifted, and then this releases.
191    Once,
192}
193
194impl Shift {
195    /// What the key says.
196    #[inline]
197    pub const fn legend(self) -> &'static str {
198        match self {
199            Shift::Off => "shift",
200            Shift::Once => "SHIFT",
201        }
202    }
203
204    /// The state after a tap.
205    #[inline]
206    const fn next(self) -> Self {
207        match self {
208            Shift::Off => Shift::Once,
209            Shift::Once => Shift::Off,
210        }
211    }
212
213    /// Whether Shift itself is held for the next press.
214    ///
215    /// Caps Lock is deliberately not part of this. It is not a held Shift —
216    /// treating it as one is the bug that makes a locked keyboard type `!` for
217    /// `1` — and it now has a key of its own, latched in the composer, applying
218    /// to letters only.
219    #[inline]
220    const fn holds_shift(self) -> bool {
221        matches!(self, Shift::Once)
222    }
223}
224
225/// An on-screen keyboard, and the composition state that goes with it.
226///
227/// Holds no widgets of its own: [`Keyboard::open`] builds them into a shelf and
228/// [`Keyboard::close`] takes them away with it. What it keeps between those is
229/// the part that has to survive a key press — the layout and the composer's
230/// half-finished dead keys.
231pub struct Keyboard {
232    layout: &'static Layout,
233    composer: Composer,
234    modifiers: Modifiers,
235    shift: Shift,
236    level3: bool,
237    /// Ctrl armed for the next key. One-shot, like Shift.
238    ctrl: bool,
239    /// The alternates on screen, if a key is being held: the key they came
240    /// from, and one node per character offered.
241    offering: Option<Offering>,
242    shelf: Option<NodeId>,
243    keys: Vec<(KeyCode, NodeId)>,
244    scale: f32,
245    style: TextStyle,
246}
247
248impl Keyboard {
249    /// A keyboard in `layout`.
250    ///
251    /// `denise_layout::from_system()` is the argument that makes it agree with
252    /// whatever the machine is configured for.
253    pub fn new(layout: &'static Layout) -> Self {
254        Self {
255            layout,
256            composer: Composer::new(layout),
257            modifiers: Modifiers::NONE,
258            shift: Shift::Off,
259            level3: false,
260            ctrl: false,
261            offering: None,
262            shelf: None,
263            keys: Vec::new(),
264            scale: 1.0,
265            style: TextStyle::built_in(KEY_TEXT),
266        }
267    }
268
269    /// The same keyboard at a display scale.
270    ///
271    /// The grid is written in logical pixels — [`KEY_HEIGHT`] is a fingertip,
272    /// not a count of device pixels — and this is what turns them into the ones
273    /// the surface has. The same `scale` the application scales its own layout
274    /// by, and the same one [`Theme::scaled`](denise::Theme::scaled) takes.
275    ///
276    /// Set it before opening. A keyboard already on screen is not relaid out,
277    /// because the surface it is sitting on has not changed size either.
278    #[must_use]
279    pub fn with_scale(mut self, scale: f32) -> Self {
280        self.scale = scale;
281        self
282    }
283
284    /// Changes the face the legends are drawn in, keys already up included.
285    ///
286    /// [`with_style`](Self::with_style) is the one to reach for; this is for the
287    /// application that cannot yet know its own font — the table editor builds
288    /// its tree with the built-in face so that it has *a* face whether or not a
289    /// font file turned up, and restyles everything once one has.
290    pub fn set_style<M: Clone + 'static>(&mut self, ui: &mut Ui<M>, style: TextStyle) {
291        self.style = style;
292        for &(_, node) in &self.keys {
293            if let Some(button) = ui.widget_mut::<Button<M>>(node) {
294                button.set_style(style);
295            }
296        }
297    }
298
299    /// The face and size the legends are drawn in.
300    ///
301    /// Worth setting, and the reason is what the default has to be: a widget
302    /// cannot know which fonts the application loaded, so [`Button`] falls back
303    /// to the built-in 8x8 bitmap face and so does this. On a panel that has a
304    /// real font that fallback is visible — the one widget somebody is touching
305    /// is the one drawn in a different typeface — and on a layout with `ß` or a
306    /// composed `ü` on it, the built-in face has no such glyph to draw.
307    ///
308    /// Already scaled, like every other style an application builds: this does
309    /// not multiply `size_px` by [`with_scale`](Self::with_scale).
310    #[must_use]
311    pub fn with_style(mut self, style: TextStyle) -> Self {
312        self.style = style;
313        self
314    }
315
316    /// A keyboard in whatever layout the machine is configured for.
317    ///
318    /// The same answer the hardware path starts from, so a panel with a
319    /// keyboard plugged in and one without agree about what the `;` position
320    /// types. Returns the [`LayoutSource`] alongside, which is worth showing
321    /// somebody: [`LayoutSource::Unknown`] means the system asked for a layout
322    /// there is no table for and got US, and a keyboard silently in the wrong
323    /// language is a bad afternoon.
324    pub fn from_system() -> (Self, LayoutSource) {
325        let (layout, source) = denise_layout::from_system();
326        (Self::new(layout), source)
327    }
328
329    /// Changes layout, relettering the keys where they stand.
330    ///
331    /// A position does not move when the layout changes — `KeyCode::Semicolon`
332    /// is where `ø` lives on Norwegian and `ö` on German — so this replaces
333    /// legends rather than rebuilding the grid.
334    ///
335    /// Any half-typed dead key is dropped: a mark waiting for a base character
336    /// means nothing once the layout that was going to supply it has gone.
337    /// Shift, Caps Lock and the third level survive, because they are facts
338    /// about the keyboard rather than about the layout.
339    pub fn set_layout<M: Clone + 'static>(&mut self, ui: &mut Ui<M>, layout: &'static Layout) {
340        if core::ptr::eq(self.layout, layout) {
341            return;
342        }
343        self.layout = layout;
344        // `Composer::set_layout` drops the pending dead key and keeps Caps Lock
345        // and the third level, which is exactly the split wanted: a half-typed
346        // mark belonged to the old layout, and the user's hands have not moved.
347        self.composer.set_layout(layout);
348        self.relabel(ui);
349    }
350
351    /// Moves to the next built-in layout, wrapping.
352    ///
353    /// What the layout key does. Returns the layout it moved to, whose `name`
354    /// is what the key then says.
355    pub fn cycle_layout<M: Clone + 'static>(&mut self, ui: &mut Ui<M>) -> &'static Layout {
356        let next = denise_layout::BUILT_IN
357            .iter()
358            .position(|l| core::ptr::eq(*l, self.layout))
359            .map_or(0, |i| (i + 1) % denise_layout::BUILT_IN.len());
360        let layout = denise_layout::BUILT_IN[next];
361        self.set_layout(ui, layout);
362        layout
363    }
364
365    /// The layout its keys are lettered from.
366    #[inline]
367    pub const fn layout(&self) -> &'static Layout {
368        self.layout
369    }
370
371    /// The node each key was added as, in grid order.
372    ///
373    /// Kept because a layout switch relabels these rather than rebuilding them:
374    /// a position does not move when the layout changes, only its legend does.
375    #[inline]
376    pub fn keys(&self) -> &[(KeyCode, NodeId)] {
377        &self.keys
378    }
379
380    /// Whether the keyboard is on screen.
381    #[inline]
382    pub const fn is_open(&self) -> bool {
383        self.shelf.is_some()
384    }
385
386    /// The height a shelf needs for the whole keyboard, in logical pixels.
387    ///
388    /// [`Keyboard::height`] is this at the keyboard's scale, and is the one an
389    /// application wants; this is the constant it is derived from.
390    pub const LOGICAL_HEIGHT: i32 = ROWS.len() as i32 * (KEY_HEIGHT + KEY_GAP) + KEY_GAP;
391
392    /// The height the whole keyboard occupies, in the surface's own pixels.
393    ///
394    /// [`Self::LOGICAL_HEIGHT`] through [`with_scale`](Self::with_scale). What
395    /// the shelf is pushed at, and what an application subtracts when it wants
396    /// to know how much screen it has left.
397    #[inline]
398    pub fn height(&self) -> i32 {
399        self.scaled(Self::LOGICAL_HEIGHT)
400    }
401
402    /// One logical length in surface pixels.
403    ///
404    /// Through [`Rect::scaled`] rather than a multiplication written again here,
405    /// so a key's edges and the shelf's height round the same way and the bottom
406    /// row does not end a pixel short of the shelf it sits in.
407    #[inline]
408    fn scaled(&self, logical: i32) -> i32 {
409        Rect::new(0, 0, 0, logical).scaled(self.scale).height
410    }
411
412    /// Slides the keyboard up and letters its keys from the current layout.
413    ///
414    /// `on_key` is how a key press reaches the application, the same shape every
415    /// other widget uses to carry a value into a message. The application answers
416    /// by calling [`Keyboard::press`] and handing the result to
417    /// [`Ui::handle`](denise_ui::Ui::handle).
418    ///
419    /// Returns the shelf, or `None` when one is already up — the tree allows one
420    /// at a time.
421    pub fn open<M: Clone + 'static>(
422        &mut self,
423        ui: &mut Ui<M>,
424        on_key: fn(KeyCode) -> M,
425    ) -> Option<NodeId> {
426        if self.shelf.is_some() {
427            return None;
428        }
429        let shelf = ui.push_shelf(Side::Below, self.height())?;
430        let width = ui.size().width as i32;
431        self.build(ui, shelf, width, on_key);
432        self.shelf = Some(shelf);
433        Some(shelf)
434    }
435
436    /// Slides the keyboard out. The keys go with it.
437    ///
438    /// Any half-typed dead key goes too: a mark waiting for a base character
439    /// means nothing once the keyboard that was going to supply it is gone.
440    pub fn close<M: Clone + 'static>(&mut self, ui: &mut Ui<M>) {
441        if self.shelf.take().is_some() {
442            self.keys.clear();
443            self.composer.set_layout(self.layout);
444            ui.close_shelf();
445        }
446    }
447
448    /// Opens or closes the keyboard to follow the focus, once a frame.
449    ///
450    /// The ordinary policy, and the one a panel wants: focus lands on a
451    /// [`TextInput`] and the keyboard appears;
452    /// focus goes anywhere else, or nowhere, and it leaves. Call it in the
453    /// application's turn, beside [`Ui::drain_messages`](denise_ui::Ui::drain_messages).
454    ///
455    /// It answers *is this a text field* by asking the tree for the node as one,
456    /// so there is no list of fields to keep in step with the tree.
457    ///
458    /// A press on a key moves no focus, so the keyboard does not close itself
459    /// mid-word. Escape is the application's to bind: a shelf pushes no scene,
460    /// so the tree does not claim the key and will not close the keyboard for
461    /// you.
462    ///
463    /// An application wanting a different rule — a search box that already has a
464    /// hardware keyboard, a field that should never summon one — ignores this and
465    /// reads [`Ui::focus_changed`](denise_ui::Ui::focus_changed) itself. That is
466    /// the whole of what this does.
467    pub fn follow_focus<M: Clone + 'static>(&mut self, ui: &mut Ui<M>, on_key: fn(KeyCode) -> M) {
468        let Some(focus) = ui.focus_changed() else {
469            return;
470        };
471        let wants = focus.is_some_and(|id| ui.widget::<TextInput<M>>(id).is_some());
472        if wants {
473            self.open(ui, on_key);
474        } else {
475            self.close(ui);
476        }
477    }
478
479    /// One key from the grid, tapped — modifier keys included.
480    ///
481    /// The call an application makes when a key's message arrives, and the one
482    /// that does the right thing whichever key it was: Shift and the third-level
483    /// key change state and relabel the keyboard, everything else types.
484    ///
485    /// Returns the events to hand to [`Ui::handle`](denise_ui::Ui::handle);
486    /// empty for a modifier key, which changes what the *next* press means and
487    /// sends nothing itself.
488    pub fn press_key<M: Clone + 'static>(
489        &mut self,
490        ui: &mut Ui<M>,
491        code: KeyCode,
492    ) -> Vec<InputEvent> {
493        match code {
494            KeyCode::ShiftLeft => {
495                self.tap_shift();
496                self.relabel(ui);
497                Vec::new()
498            }
499            KeyCode::CapsLock => {
500                self.tap_caps();
501                self.relabel(ui);
502                Vec::new()
503            }
504            KeyCode::ControlLeft => {
505                self.tap_ctrl();
506                self.relabel(ui);
507                Vec::new()
508            }
509            KeyCode::AltRight => {
510                self.tap_level3();
511                self.relabel(ui);
512                Vec::new()
513            }
514            LAYOUT_KEY => {
515                self.cycle_layout(ui);
516                Vec::new()
517            }
518            _ => {
519                let spent = self.shift == Shift::Once || self.ctrl;
520                let events = self.press(code);
521                // A one-shot modifier has just been spent, so the keys have to
522                // stop claiming it is still armed.
523                if spent {
524                    self.relabel(ui);
525                }
526                events
527            }
528        }
529    }
530
531    /// Collects whatever a held key has earned, once a frame.
532    ///
533    /// Call it beside [`follow_focus`](Self::follow_focus), and hand the result
534    /// to [`Ui::handle`](denise_ui::Ui::handle) the way a key press is handed
535    /// over. Empty on nearly every frame: only Backspace repeats, and only while
536    /// a finger is actually on it.
537    ///
538    /// The events are the ones a real keyboard sends for an auto-repeat —
539    /// [`InputEvent::Key`] with `repeat: true`, and whatever that types — which
540    /// is what lets a widget tell a repeat from a deliberate second press. A
541    /// `TextInput` inserts both; something that must not act twice on one
542    /// gesture can look.
543    ///
544    /// **Nothing is polled.** A repeating key asks the tree to wake it while it
545    /// is held and stops asking the moment it is released, so a panel with
546    /// nobody touching it schedules nothing — this call simply finds a tally of
547    /// nought and returns.
548    ///
549    /// [`InputEvent::Key`]: denise::InputEvent::Key
550    pub fn tick<M: Clone + 'static>(&mut self, ui: &mut Ui<M>, now_ms: u64) -> Vec<InputEvent> {
551        let _ = now_ms;
552        let mut out = Vec::new();
553        // Collected by position rather than from one remembered key, because
554        // "which key is held" is the tree's fact and not this crate's.
555        //
556        // Read first, take second, and the split is not tidiness: `widget_mut`
557        // damages the node it hands out, because it cannot know whether the
558        // caller changed anything. Asking sixty keys mutably once a frame
559        // therefore repainted the whole keyboard on every frame anything else
560        // woke the tree for — which on a panel is a keyboard that flickers.
561        let owed: Vec<(KeyCode, NodeId)> = self
562            .keys
563            .iter()
564            .copied()
565            .filter(|&(_, node)| {
566                ui.widget::<Button<M>>(node)
567                    .is_some_and(|button| button.repeats_pending() > 0)
568            })
569            .collect();
570        let held: Vec<(KeyCode, u32)> = owed
571            .into_iter()
572            .filter_map(|(code, node)| {
573                let repeats = ui.widget_mut::<Button<M>>(node)?.take_repeats();
574                (repeats > 0).then_some((code, repeats))
575            })
576            .collect();
577        for (code, repeats) in held {
578            for _ in 0..repeats {
579                out.extend(self.press_repeat(code));
580            }
581        }
582        self.offer_alternates(ui);
583        out
584    }
585
586    /// Opens a held key's alternates, once it has been held long enough.
587    ///
588    /// A strip of characters above the key, drawn as ordinary nodes at the top
589    /// of the shelf rather than as a popup. That is not a shortcut: a popup
590    /// pushes a scene, a pushed scene cancels whatever press it covers, and the
591    /// press it would cancel here is the one holding the key that opened it.
592    fn offer_alternates<M: Clone + 'static>(&mut self, ui: &mut Ui<M>) {
593        if self.offering.is_some() || self.shelf.is_none() {
594            return;
595        }
596        // The one key held past the threshold, read without writing.
597        let ready = self.keys.iter().copied().find(|&(_, node)| {
598            ui.widget::<Button<M>>(node)
599                .and_then(Button::held_ms)
600                .is_some_and(|held| held >= HOLD_MS)
601        });
602        let Some((code, key)) = ready else {
603            return;
604        };
605        self.open_alternates(ui, code, key);
606    }
607
608    /// Builds the strip for a key, wherever the decision to came from.
609    fn open_alternates<M: Clone + 'static>(&mut self, ui: &mut Ui<M>, code: KeyCode, key: NodeId) {
610        if self.offering.is_some() {
611            return;
612        }
613        let Some(shelf) = self.shelf else {
614            return;
615        };
616        let Some(base) = self.legend(code) else {
617            return;
618        };
619        let choices: Vec<char> = self.layout.alternates_for(base).collect();
620        if choices.is_empty() {
621            return;
622        }
623        let Some(bounds) = ui.layout(key) else {
624            return;
625        };
626
627        // Centred over the key, and kept on screen: a strip that ran off the
628        // edge would put half its choices where no finger can reach them.
629        //
630        // The frame is not decoration. Drawn flush and in the keys' own colours
631        // this landed as *another row of the keyboard* — five accented letters
632        // sitting on the digits, indistinguishable from them, so the one thing
633        // the gesture has to say (these five, right now, and not the sixty
634        // behind them) was the one thing it did not. So: a border in the accent
635        // colour, a margin wide enough to read as an edge rather than a seam,
636        // and a clear gap between the strip and the row it floats over.
637        let cell = bounds.height;
638        let pad = self.scaled(STRIP_PAD);
639        let width = cell * choices.len() as i32 + pad * 2;
640        let height = cell + pad * 2;
641        let x = (bounds.x + bounds.width / 2 - width / 2)
642            .max(0)
643            .min((ui.size().width as i32 - width).max(0));
644        let y = (bounds.y - height - self.scaled(KEY_GAP) * 2).max(0);
645
646        let Some(strip) = ui.add(
647            shelf,
648            Panel::filled(Role::Base300)
649                .backdrop()
650                .with_radius(denise::theme::Radius::Box)
651                .with_border(Role::Primary, self.scaled(STRIP_BORDER).max(1)),
652            Rect::new(x, y, width, height),
653        ) else {
654            return;
655        };
656        let mut nodes = Vec::with_capacity(choices.len());
657        for (i, ch) in choices.iter().enumerate() {
658            // Under the strip rather than beside it, so that taking the strip
659            // away takes the choices with it: `Ui::remove` drops a subtree, and
660            // a choice parented to the shelf would outlive the gesture that
661            // made it and sit there being pressable.
662            let at = Rect::new(pad + cell * i as i32, pad, cell, cell);
663            if let Some(node) = ui.add(
664                strip,
665                // Inert, and that is the design rather than an omission: the
666                // press that opened this strip is still down on the key, so the
667                // tree never presses one of these and they would never emit.
668                // The choice is made by where the finger lifts, which
669                // `Keyboard::handle` answers.
670                Button::<M>::inert(ch.to_string())
671                    .no_focus()
672                    .with_role(Role::Base100)
673                    .with_style(self.style),
674                at,
675            ) {
676                nodes.push((*ch, node));
677            }
678        }
679        self.offering = Some(Offering {
680            strip,
681            choices: nodes,
682            over: None,
683        });
684    }
685
686    /// Input the keyboard answers itself, before the tree sees it.
687    ///
688    /// Only the alternates gesture needs this, and it needs it because the
689    /// gesture has no precedent in the tree: the press that opened the strip is
690    /// still down on the *key*, so the tree quite correctly keeps sending
691    /// everything there, and the choice is made by where the finger lifts
692    /// instead. The keyboard therefore does its own hit test against the strip
693    /// it drew.
694    ///
695    /// Call it beside [`Ui::handle`](denise_ui::Ui::handle), with the same
696    /// events. Returns what to type, which is empty on nearly every call —
697    /// nothing happens here unless a strip is open.
698    ///
699    /// A character chosen this way arrives as [`InputEvent::Text`] alone, with
700    /// no [`InputEvent::Key`] around it. That is the honest shape: `é` is not at
701    /// a position on this keyboard, nothing pressed a key to get it, and a
702    /// binding watching for keys should not think one was pressed.
703    ///
704    /// Lifting anywhere else ends the gesture and types nothing — except on
705    /// the key itself, which types what it always types. That is why the strip
706    /// does not repeat the base character among its choices: the key is still
707    /// there underneath it, still where the finger already is, so a hold opened
708    /// by accident is undone by not moving.
709    ///
710    /// [`InputEvent::Text`]: denise::InputEvent::Text
711    /// [`InputEvent::Key`]: denise::InputEvent::Key
712    pub fn handle<M: Clone + 'static>(
713        &mut self,
714        ui: &mut Ui<M>,
715        events: &[InputEvent],
716    ) -> Vec<InputEvent> {
717        let mut out = Vec::new();
718        for event in events {
719            let Some(offering) = self.offering.as_ref() else {
720                return out;
721            };
722            match event {
723                InputEvent::PointerMoved { position } | InputEvent::TouchMoved { position, .. } => {
724                    let at = self.choice_at(ui, *position);
725                    if let Some(offering) = self.offering.as_mut()
726                        && offering.over != at
727                    {
728                        offering.over = at;
729                        self.highlight(ui);
730                    }
731                }
732                InputEvent::PointerButton {
733                    state: ElementState::Up,
734                    position,
735                    ..
736                }
737                | InputEvent::TouchUp {
738                    position,
739                    cancelled: false,
740                    ..
741                } => {
742                    let chosen = self
743                        .choice_at(ui, *position)
744                        .and_then(|i| offering.choices.get(i))
745                        .map(|(ch, _)| *ch);
746                    self.close_offering(ui);
747                    if let Some(ch) = chosen {
748                        out.push(InputEvent::Text { ch });
749                    }
750                }
751                // A sequence the system took away is not a choice, wherever the
752                // last position happened to land. The strip goes, and nothing
753                // is typed — the same answer as lifting off it, because that is
754                // what a cancelled gesture is.
755                InputEvent::TouchUp {
756                    cancelled: true, ..
757                } => self.close_offering(ui),
758                _ => {}
759            }
760        }
761        out
762    }
763
764    /// Which choice a point is over, if any.
765    fn choice_at<M: Clone + 'static>(&self, ui: &Ui<M>, at: Point) -> Option<usize> {
766        let offering = self.offering.as_ref()?;
767        offering
768            .choices
769            .iter()
770            .position(|&(_, node)| ui.bounds(node).is_some_and(|b| b.contains(at)))
771    }
772
773    /// Paints the choice under the finger differently from the rest.
774    fn highlight<M: Clone + 'static>(&mut self, ui: &mut Ui<M>) {
775        let Some(offering) = self.offering.as_ref() else {
776            return;
777        };
778        let over = offering.over;
779        let nodes: Vec<(usize, NodeId)> = offering
780            .choices
781            .iter()
782            .enumerate()
783            .map(|(i, &(_, node))| (i, node))
784            .collect();
785        for (i, node) in nodes {
786            let role = if Some(i) == over {
787                Role::Primary
788            } else {
789                Role::Base100
790            };
791            // Read before writing: `widget_mut` repaints whatever it hands out,
792            // and a strip that repainted every key on every pointer move is the
793            // flicker bug in miniature.
794            let same = ui
795                .widget::<Button<M>>(node)
796                .is_some_and(|button| button.role() == role);
797            if !same && let Some(button) = ui.widget_mut::<Button<M>>(node) {
798                button.set_role(role);
799            }
800        }
801    }
802
803    /// Takes the strip away, leaving the key it came from alone.
804    fn close_offering<M: Clone + 'static>(&mut self, ui: &mut Ui<M>) {
805        if let Some(offering) = self.offering.take() {
806            ui.remove(offering.strip);
807        }
808    }
809
810    /// Opens a key's alternates without waiting for a finger.
811    ///
812    /// For a headless run — a snapshot, a test — which has no finger to hold
813    /// anything with and no clock running while it does. The gesture itself
814    /// goes through [`tick`](Self::tick) and a real press.
815    pub fn offer_for_test<M: Clone + 'static>(&mut self, ui: &mut Ui<M>, code: KeyCode) {
816        let Some(&(_, key)) = self.keys.iter().find(|&&(c, _)| c == code) else {
817            return;
818        };
819        self.open_alternates(ui, code, key);
820    }
821
822    /// The characters currently on offer, and the nodes showing them.
823    ///
824    /// Empty unless a key is being held past [`HOLD_MS`]. Given out so a test
825    /// can aim at one, and so an application that wants to drive the gesture
826    /// some other way can.
827    pub fn choices(&self) -> &[(char, NodeId)] {
828        self.offering.as_ref().map_or(&[], |o| &o.choices)
829    }
830
831    /// Whether a held key is offering its alternates.
832    #[inline]
833    pub const fn offering(&self) -> bool {
834        self.offering.is_some()
835    }
836
837    /// One auto-repeat of a key already down.
838    ///
839    /// [`press`](Self::press) with `repeat: true`, and without the one-shot
840    /// Shift bookkeeping: a repeat is the *same* press arriving again, so it
841    /// cannot spend a shift that the first press already spent.
842    pub fn press_repeat(&mut self, code: KeyCode) -> Vec<InputEvent> {
843        let mut out = Vec::with_capacity(3);
844        let modifiers = self.modifiers();
845        for state in [ElementState::Down, ElementState::Up] {
846            out.push(InputEvent::Key {
847                code,
848                state,
849                repeat: true,
850                modifiers,
851            });
852            let composed = self.composer.feed(code, state, modifiers);
853            for &ch in composed.as_slice() {
854                out.push(InputEvent::Text { ch });
855            }
856        }
857        out
858    }
859
860    /// One key, tapped: the events a real keyboard would have sent.
861    ///
862    /// [`InputEvent::Key`] down, then whatever that typed as
863    /// [`InputEvent::Text`], then [`InputEvent::Key`] up — the order the
864    /// hardware path uses, so that a binding on the key runs before any text
865    /// arrives and a field can insert every character it sees without filtering.
866    ///
867    /// A dead key types nothing and returns just the two `Key` events; the mark
868    /// arrives folded into the next character, or beside it when the two cannot
869    /// combine.
870    ///
871    /// [`InputEvent::Key`]: denise::InputEvent::Key
872    /// [`InputEvent::Text`]: denise::InputEvent::Text
873    pub fn press(&mut self, code: KeyCode) -> Vec<InputEvent> {
874        let mut out = Vec::with_capacity(3);
875        let modifiers = self.modifiers();
876        for state in [ElementState::Down, ElementState::Up] {
877            out.push(InputEvent::Key {
878                code,
879                state,
880                repeat: false,
881                modifiers,
882            });
883            let composed = self.composer.feed(code, state, modifiers);
884            for &ch in composed.as_slice() {
885                out.push(InputEvent::Text { ch });
886            }
887        }
888        // A one-shot modifier is spent on the character it modified. Doing this
889        // after the feed rather than before is what makes it apply to exactly
890        // one key.
891        if self.shift == Shift::Once {
892            self.shift = Shift::Off;
893        }
894        self.ctrl = false;
895        out
896    }
897
898    /// Taps the Shift key: off, then once, then locked, then off again.
899    ///
900    /// Returns the state it moved to. The caller relabels with
901    /// [`Keyboard::relabel`] — or lets [`Keyboard::press_key`] do both.
902    pub fn tap_shift(&mut self) -> Shift {
903        self.shift = self.shift.next();
904        self.shift
905    }
906
907    /// Caps Lock on or off. Returns the state it moved to.
908    ///
909    /// A latch of its own rather than a third state of Shift, which is what a
910    /// real keyboard does and what the composer already modelled: it applies to
911    /// letters and spares the digit row, so a locked keyboard types `1` and not
912    /// `!`. Told through the key stream, exactly as a real Caps Lock reaches it.
913    pub fn tap_caps(&mut self) -> bool {
914        let modifiers = self.modifiers();
915        self.composer
916            .feed(KeyCode::CapsLock, ElementState::Down, modifiers);
917        self.composer
918            .feed(KeyCode::CapsLock, ElementState::Up, modifiers);
919        self.composer.caps_lock()
920    }
921
922    /// Whether Caps Lock is on.
923    #[inline]
924    pub fn caps(&self) -> bool {
925        self.composer.caps_lock()
926    }
927
928    /// Ctrl for the next key, on or off. Returns the state it moved to.
929    ///
930    /// One-shot like Shift, and spent by the next key that types: a modifier
931    /// that stayed on would be a keyboard that could not type a plain letter
932    /// again without somebody noticing why.
933    pub fn tap_ctrl(&mut self) -> bool {
934        self.ctrl = !self.ctrl;
935        self.ctrl
936    }
937
938    /// Whether Ctrl is armed for the next key.
939    #[inline]
940    pub const fn ctrl(&self) -> bool {
941        self.ctrl
942    }
943
944    /// Taps the third-level key, the one a physical keyboard spells `AltGr`.
945    ///
946    /// This is the symbol page, and it is the layout's own third level rather
947    /// than a grid of symbols chosen here: `@` is `AltGr`+`2` on a Norwegian
948    /// keyboard and `Shift`+`2` on a US one, and a fixed grid would be wrong on
949    /// one of them. It latches rather than being held, because a finger cannot
950    /// hold one key and press another.
951    ///
952    /// Returns whether the third level is now on.
953    pub fn tap_level3(&mut self) -> bool {
954        self.level3 = !self.level3;
955        // The composer tracks this from the key stream exactly as it does for a
956        // real AltGr, so it is told the same way.
957        let state = if self.level3 {
958            ElementState::Down
959        } else {
960            ElementState::Up
961        };
962        self.composer
963            .feed(KeyCode::AltRight, state, self.modifiers());
964        self.level3
965    }
966
967    /// The screen rectangle the keyboard is covering, or `None` when it is not
968    /// up.
969    ///
970    /// Focusing a field already scrolls it clear of this, where the field is in
971    /// something that scrolls and has somewhere to scroll to. This is for when
972    /// it is not: a form at fixed rectangles has no scroll to give, and getting
973    /// a field out from under the keyboard means the application moving
974    /// something — shrinking a viewport by this height, or sliding a panel up.
975    ///
976    /// The keyboard's resting place from the moment it is opened, so an
977    /// application acting on it during the slide aims where the keyboard is
978    /// going.
979    #[inline]
980    pub fn occluded<M: Clone + 'static>(&self, ui: &Ui<M>) -> Option<Rect> {
981        self.shelf.and(ui.occluded())
982    }
983
984    /// The state of the Shift key.
985    #[inline]
986    pub const fn shift(&self) -> Shift {
987        self.shift
988    }
989
990    /// Whether the third level — the layout's `AltGr` — is showing.
991    #[inline]
992    pub const fn level3(&self) -> bool {
993        self.level3
994    }
995
996    /// The modifiers a key press reports right now.
997    fn modifiers(&self) -> Modifiers {
998        let mut modifiers = self.modifiers;
999        if self.shift.holds_shift() {
1000            modifiers |= Modifiers::SHIFT;
1001        }
1002        if self.ctrl {
1003            modifiers |= Modifiers::CTRL;
1004        }
1005        modifiers
1006    }
1007
1008    /// What to print on a key, at the level the keyboard is currently showing.
1009    ///
1010    /// A dead key shows its mark, which is what the user is about to be holding.
1011    /// What to print on a key: exactly what pressing it would produce.
1012    ///
1013    /// Asked of the composer rather than worked out here, so Caps Lock sparing
1014    /// the digit row and the third level are right by construction instead of by
1015    /// a rule repeated in two places.
1016    fn legend(&self, code: KeyCode) -> Option<char> {
1017        match self.composer.output_for(code, self.shift.holds_shift()) {
1018            Output::Char(ch) | Output::Dead(ch) => Some(ch),
1019            Output::None => None,
1020        }
1021    }
1022
1023    /// What a key says right now.
1024    ///
1025    /// The three keys whose legends come from the keyboard's own state rather
1026    /// than from the layout, then the layout's answer. One function because
1027    /// [`build`](Self::build) and [`relabel`](Self::relabel) must agree: when
1028    /// they did not, the layout key came up blank and only found its name after
1029    /// something else had caused a relabel.
1030    fn label_for(&self, code: KeyCode) -> String {
1031        match code {
1032            KeyCode::ShiftLeft => self.shift.legend().to_string(),
1033            KeyCode::CapsLock => if self.caps() { "CAPS" } else { "caps" }.to_string(),
1034            KeyCode::ControlLeft => if self.ctrl { "CTRL" } else { "ctrl" }.to_string(),
1035            KeyCode::AltRight => LEVEL3_LEGEND.to_string(),
1036            LAYOUT_KEY => self.layout.name.to_string(),
1037            _ => grid::legend_of(code)
1038                .map(str::to_string)
1039                .or_else(|| self.legend(code).map(|ch| ch.to_string()))
1040                .unwrap_or_default(),
1041        }
1042    }
1043
1044    /// What is printed small in a key's top-right corner, if anything.
1045    ///
1046    /// What the key would type with Shift held — the `!` over the `1`, the `?`
1047    /// over the `+` — which is the whole reason a real keyboard prints it: you
1048    /// cannot discover Shift by pressing Shift, because pressing it is what
1049    /// changes the legend.
1050    ///
1051    /// **Numbers and punctuation only.** A letter's shifted form is its own
1052    /// capital and tells nobody anything, and forty keys each carrying a second
1053    /// glyph is a keyboard that reads as noise. This is the rule the keyboards
1054    /// it is shaped after use, and the reason they use it.
1055    ///
1056    /// Empty while Shift is held, because the main legend has already become
1057    /// the shifted character and printing it twice on one key says nothing.
1058    fn corner_for(&self, code: KeyCode) -> String {
1059        if code == LAYOUT_KEY {
1060            // The one corner that is not a shifted character. The key wears a
1061            // globe, which says what it is for and cannot say which layout is
1062            // live — so the name it used to carry as its legend moves here
1063            // rather than being dropped. On a panel with no other keyboard,
1064            // "am I typing Norwegian?" is a question the keyboard itself has to
1065            // answer.
1066            return self.layout.name.to_string();
1067        }
1068        if grid::legend_of(code).is_some() {
1069            // A key with a fixed word on it — back, enter, tab — types nothing
1070            // and has no other state to advertise.
1071            return String::new();
1072        }
1073        let Some(base) = self.legend(code) else {
1074            return String::new();
1075        };
1076        if base.is_alphabetic() {
1077            return String::new();
1078        }
1079        let shifted = match self.composer.output_for(code, true) {
1080            Output::Char(ch) | Output::Dead(ch) => ch,
1081            Output::None => return String::new(),
1082        };
1083        if shifted == base {
1084            return String::new();
1085        }
1086        shifted.to_string()
1087    }
1088
1089    /// Rewrites every key's legend for the current shift and level.
1090    ///
1091    /// A position does not move when a modifier changes — only what it types —
1092    /// so this replaces labels on the keys that are already there.
1093    pub fn relabel<M: Clone + 'static>(&mut self, ui: &mut Ui<M>) {
1094        for &(code, node) in &self.keys {
1095            let label = self.label_for(code);
1096            let corner = self.corner_for(code);
1097            if let Some(button) = ui.widget_mut::<Button<M>>(node) {
1098                button.set_label(label);
1099                button.set_corner(corner);
1100            }
1101        }
1102    }
1103
1104    /// Lays the rows out and adds a button per key.
1105    ///
1106    /// Explicit rectangles, because the toolkit has no layout engine and a
1107    /// keyboard is the case that wants none: a fixed grid, measured once.
1108    fn build<M: Clone + 'static>(
1109        &mut self,
1110        ui: &mut Ui<M>,
1111        shelf: NodeId,
1112        width: i32,
1113        on_key: fn(KeyCode) -> M,
1114    ) {
1115        // A backdrop first, and it is not decoration: a shelf is a bare
1116        // container, so without one the page underneath shows through the gaps
1117        // between the keys — which on a browser is a paragraph of text running
1118        // between the rows. Added before the keys so it paints behind them.
1119        //
1120        // A *backdrop* and not an ordinary panel, which is what stops a finger
1121        // landing in the gap between two keys from falling through to the page
1122        // and taking the focus with it — a near-miss used to dismiss the whole
1123        // keyboard.
1124        //
1125        // `Base300` rather than `Base200`, which is two steps from the keys
1126        // rather than one: at one step a light theme's keys barely lift off the
1127        // deck and the whole thing reads as a flat grey slab. The dark themes
1128        // were always fine; this is the light one catching up.
1129        ui.add(
1130            shelf,
1131            Panel::filled(Role::Base300).backdrop(),
1132            Rect::new(0, 0, width, self.height()),
1133        );
1134
1135        // The surface's width, back in the units the grid below is written in.
1136        // Laying out logically and scaling each rectangle at the end is what
1137        // keeps a row reaching both edges at 1.5x: `Rect::scaled` scales
1138        // *edges*, so keys that were a gap apart still are.
1139        let width = if self.scale > 0.0 {
1140            Rect::new(0, 0, width, 0).scaled(1.0 / self.scale).width
1141        } else {
1142            width
1143        };
1144        for (r, row) in ROWS.iter().enumerate() {
1145            let y = KEY_GAP + r as i32 * (KEY_HEIGHT + KEY_GAP);
1146            // Every key in a row shares the leftover width, so a row of ten and
1147            // a row of three both reach both edges.
1148            //
1149            // Each key's edges are placed from its running share of the row
1150            // rather than from a rounded width per unit, and the difference is
1151            // the whole point: a width of `leftover / units` throws away the
1152            // remainder once per key, and eleven keys of it leaves the row
1153            // visibly short of the edge it was supposed to reach. Placing edges
1154            // spends the remainder across the row instead, a pixel at a time.
1155            let count = row.keys.len() as i32;
1156            let units: i32 = row.keys.iter().map(|k| k.units).sum::<i32>().max(1);
1157            let leftover = (width - KEY_GAP * (count + 1)).max(count);
1158            let mut done = 0;
1159            for (i, key) in row.keys.iter().enumerate() {
1160                let gaps = KEY_GAP * (i as i32 + 1);
1161                let x = gaps + leftover * done / units;
1162                done += key.units;
1163                let w = gaps + leftover * done / units - x;
1164                let mut button = Button::new(self.label_for(key.code), on_key(key.code))
1165                    .no_focus()
1166                    .with_role(role_of(key.code))
1167                    .with_style(self.style)
1168                    .with_corner(self.corner_for(key.code));
1169                // The word stays the button's label even where a picture is
1170                // drawn over it: that is what the key still reports, and what a
1171                // test reads.
1172                if let Some(icon) = grid::icon_of(key.code) {
1173                    button = button.with_icon(icon);
1174                }
1175                if key.repeats {
1176                    button = button.with_repeat(REPEAT_DELAY_MS, REPEAT_INTERVAL_MS);
1177                } else if grid::legend_of(key.code).is_none() {
1178                    // Only the keys that type a character can offer alternates,
1179                    // so only they need to be asked how long they have been
1180                    // held. A key with a word on it has nothing to offer and
1181                    // stays free.
1182                    button = button.watching_hold();
1183                }
1184                if let Some(node) = ui.add(
1185                    shelf,
1186                    button,
1187                    Rect::new(x, y, w, KEY_HEIGHT).scaled(self.scale),
1188                ) {
1189                    self.keys.push((key.code, node));
1190                }
1191            }
1192        }
1193    }
1194}
1195
1196/// What colour a key wears.
1197///
1198/// Two kinds, and the split is what the key does rather than what it looks
1199/// like: keys that type a character are the neutral field the eye skims over,
1200/// and keys that change what the *next* press means stand out from them,
1201/// because those are the ones a user has to find deliberately. Enter is with
1202/// the second group for the same reason — it is the key with a consequence.
1203///
1204/// [`Role::Primary`] is the toolkit's default for a button and is wrong for
1205/// every key here: forty of them shouting at once is not emphasis.
1206fn role_of(code: KeyCode) -> Role {
1207    match code {
1208        KeyCode::ShiftLeft
1209        | KeyCode::CapsLock
1210        | KeyCode::ControlLeft
1211        | KeyCode::AltRight
1212        | KeyCode::Backspace
1213        | KeyCode::Tab
1214        | KeyCode::Escape
1215        | KeyCode::ArrowLeft
1216        | KeyCode::ArrowRight
1217        | LAYOUT_KEY => Role::Neutral,
1218        KeyCode::Enter => Role::Primary,
1219        _ => Role::Base100,
1220    }
1221}
1222
1223/// Compiles the examples in this crate's README, so they cannot drift from the API
1224/// they claim to demonstrate. Never built except under `cargo test --doc`.
1225#[cfg(doctest)]
1226#[doc = include_str!("../README.md")]
1227struct Readme;
1228
1229/// The alternates a held key is offering, while it offers them.
1230struct Offering {
1231    /// The strip's backdrop, removed with the rest.
1232    strip: NodeId,
1233    /// One node per character, in the order they are shown.
1234    choices: Vec<(char, NodeId)>,
1235    /// Which one the finger is over, if any.
1236    over: Option<usize>,
1237}