---
// cSpell: ignore Backtab
title: Key Handling Overview
description: Key Handling Overview
---

import Link from '@slint/common-files/src/components/Link.astro';
import SpecialKeys from "../../../collections/enums/keys.md"

To handle keyboard input in Slint, use the `FocusScope` with `KeyBinding` elements or individual `key-pressed` and `key-released` callbacks in various elements.
Keyboard input is delivered via [`KeyEvent`](#keyevent) data structures.
The primary field of this data structure is the `text` property, which holds all affected keys
encoded in a string.
Use the [`Key` namespace](#key-namespace) to identify known named keys.

See the [Key Bindings](#key-bindings) section for how to handle key bindings declaratively with
Slint's <Link type="KeyBinding" label="KeyBinding"/> elements and the built-in <Link type="keys" label="keys" /> type, which is constructed from the `@keys` macro.

## KeyEvent

 This structure is generated and passed to the key press and release callbacks of the `FocusScope` element.

- **`text`** (_string_):  The unicode representation of the key pressed.
- **`modifiers`** (_KeyboardModifiers_):  The keyboard modifiers active at the time of the key press event.
- **`repeat`** (_bool_):  This field is set to true for key press events that are repeated, i.e. the key is held down. It's always false for key release events.

## Key Bindings

Use the <Link type="KeyBinding" label="KeyBinding element"/> inside a `FocusScope` to declare key bindings (or keyboard shortcuts).
The `KeyBinding`'s `keys` property takes an instance of the <Link label="keys type" type="keys"/> and invokes the `activated` callback when the key combination is detected.

The `keys` type represents a key, combined with modifiers and is constructed with the `@keys` macro.

Key bindings in Slint are based on **logical keys** — the character a keypress produces on the current keyboard layout — not the physical position of a key.
This means, for example, that `@keys(Control + Z)` activates when the user presses the key that produces `z` on their layout, regardless of where that key sits on the keyboard.
As a consequence, numpad digit keys are not distinguished from the main row digit keys, and `AltGr` isn't available as a modifier (it is consumed by the OS to produce characters).

The `@keys(..)` macro accepts a key from the [`Key` namespace](#key-namespace), combined with any of these modifiers:

- `Meta` (⌃ control on macOS and Windows key on PC)
- `Control` (⌘ command on macOS)
- `Shift`
- `Alt` (⌥ option on macOS)

```slint playground
export component Example inherits Window {
    forward-focus: scope;

    scope := FocusScope {
        KeyBinding {
            keys: @keys(Control + Q);
            activated => { debug("Quit shortcut activated"); }
        }

        KeyBinding {
            keys: @keys(Control + S);
            activated => { debug("Save shortcut activated"); }
        }

        Text {
            text: scope.has-focus ? "Press Control + Q or Control + S" : "Click to focus";
        }
    }
}
```

:::caution[Caution]
The behavior of ambiguous KeyBinding elements is currently undefined,
for example when two or more KeyBindings declare the same `keys`.

If possible, conflicts are detected at compile-time. Ambiguous key bindings that are detected at runtime will log a warning.
:::

### Different Keyboard Layouts

Different keyboard layouts are handled automatically by the `@keys(..)` macro for keys from the [`Key` namespace](#key-namespace).
For example, `@keys(Control + Plus)` fires whenever the user presses the key that produces `+` on their layout — on a US layout that requires `Shift` + `=`, while on a German layout `+` has its own key and no `Shift` is needed.

Named keys inside `@keys(..)` are logical: `A` represents the character `a`, not a specific physical key location. This means `@keys(Control + A)` will activate on whichever key produces `a` on the user's layout, regardless of where that key sits on the keyboard.

If you need to define a key binding that involves a key outside of the [`Key` namespace](#key-namespace), continue reading the next section which covers advanced key definitions.

### Advanced Key Bindings

If possible, prefer to use a key from the [`Key` namespace](#key-namespace) inside `@keys(..)`.
If you absolutely need to define a binding to a different key, you can use a string literal inside the `@keys(..)` macro.
For example: `@keys(Control + "ä")`.

Note though that the `@keys(..)` macro no longer automatically handles different keyboard layouts and Shift behavior in this case.
The provided string is simply matched against the **lowercased** `text`, plus the modifiers of the given `KeyEvent`

To prevent issues with characters that entirely change their keycode when `Shift` (or sometimes `Alt` on macOS) is applied, you can mark these modifiers as optional with a trailing `?`, e.g.:

- `Shift?`
- `Alt?`

When a modifier is marked as optional, the `keys` will ignore the state of the corresponding modifier when matching.

For example, to support key bindings that produce a Euro-Sign (€), add both `Shift?` and `Alt?`: `@keys(Control + Shift? + Alt? + "€")`.
This will allow your users to reach the binding on almost all keyboard layouts that have a key with this label.

As another example, `@keys(Control + Plus)` is equivalent to `@keys(Control + Shift? + "+")`.

## Key Namespace

The `Key` namespace contains a list of well-known logical key codes (including any non-printable characters).
Key names in this namespace identify keys by the **character they produce**, not by their physical position on the keyboard.
For example, `Key.A` represents the character `a`, not the top-left letter key of a QWERTY keyboard.

Note that alphabetic keys (e.g. Key.A-Key.Z) are represented as lowercase (e.g. "a"-"z").
Make sure to case-convert the [`KeyEvent`](#keyevent) `text` or the Key as necessary.

<SpecialKeys />
