ltk

Trait App

Source
pub trait App: 'static {
    type Message: Clone + 'static;

Show 109 methods // Required methods fn view(&self) -> Element<Self::Message>; fn update(&mut self, msg: Self::Message); fn app_id(&self) -> &str; fn save_state(&self) -> Option<Vec<u8>>; fn restore_state(&mut self, state: Vec<u8>); // Provided methods fn restore_on_launch(&self) -> bool { ... } fn invalidate_after(&self, _msg: &Self::Message) -> InvalidationScope { ... } fn overlays(&self) -> Vec<OverlaySpec<Self::Message>> { ... } fn overlay_title(&self, _id: OverlayId) -> Option<String> { ... } fn overlay_namespace(&self, _id: OverlayId) -> Option<&'static str> { ... } fn subsurfaces(&self) -> Vec<SubsurfaceSpec<Self::Message>> { ... } fn poll_external(&mut self) -> Vec<Self::Message> { ... } fn take_activation_requests(&mut self) -> Vec<String> { ... } fn on_activation_token( &mut self, _tag: String, _token: String, ) -> Option<Self::Message> { ... } fn on_first_frame_committed(&mut self) { ... } fn on_main_frame_committed(&mut self) { ... } fn on_overlay_frame_committed(&mut self, _id: OverlayId) { ... } fn on_subsurface_committed(&mut self, _id: SubsurfaceId) { ... } fn on_close_requested(&mut self) -> bool { ... } fn on_tap(&mut self) -> Option<Self::Message> { ... } fn on_key(&mut self, _keysym: Keysym) -> Option<Self::Message> { ... } fn on_key_with_modifiers( &mut self, keysym: Keysym, _ctrl: bool, _shift: bool, ) -> Option<Self::Message> { ... } fn on_back(&mut self) -> BackResponse<Self::Message> { ... } fn back_at_root(&self) -> BackAtRoot { ... } fn on_swipe_up(&mut self) -> Option<Self::Message> { ... } fn on_swipe_progress(&mut self, _progress: f32) { ... } fn on_swipe_down(&mut self) -> Option<Self::Message> { ... } fn on_swipe_down_progress(&mut self, _progress: f32) { ... } fn swipe_threshold(&self) -> f32 { ... } fn swipe_down_threshold(&self) -> f32 { ... } fn swipe_down_edge(&self) -> f32 { ... } fn on_overscroll_down_progress(&mut self, _progress: f32) { ... } fn on_overscroll_down(&mut self, _committed: bool) -> Option<Self::Message> { ... } fn overscroll_dismiss_threshold(&self) -> f32 { ... } fn on_swipe_left(&mut self) -> Option<Self::Message> { ... } fn on_swipe_right(&mut self) -> Option<Self::Message> { ... } fn on_swipe_horizontal_progress(&mut self, _progress: f32) { ... } fn swipe_horizontal_threshold(&self) -> f32 { ... } fn long_press_duration(&self) -> Duration { ... } fn on_drag_move(&mut self, _x: f32, _y: f32) { ... } fn on_drop(&mut self, _x: f32, _y: f32) -> Option<Self::Message> { ... } fn on_text_input_focused(&mut self, _active: bool) { ... } fn external_text_input(&self) -> Option<u64> { ... } fn external_text_purpose(&self) -> Purpose { ... } fn on_external_text_commit(&mut self, _text: &str) { ... } fn on_external_text_delete(&mut self, _before: u32, _after: u32) { ... } fn builtin_keyboard(&self) -> bool { ... } fn on_toplevel_event(&self, _event: ToplevelEvent) -> Option<Self::Message> { ... } fn on_files_changed(&self, _dir: &Path) -> Option<Self::Message> { ... } fn on_mounts_changed(&self, _mounts: &[Mount]) -> Option<Self::Message> { ... } fn on_open_files(&self, _paths: Vec<PathBuf>) -> Option<Self::Message> { ... } fn take_focus_request(&mut self) -> Option<WidgetId> { ... } fn take_blur_request(&mut self) -> bool { ... } fn take_clipboard_request(&mut self) -> Option<String> { ... } fn take_primary_request(&mut self) -> Option<String> { ... } fn take_paste_request(&mut self) -> Option<SelectionKind> { ... } fn on_paste(&mut self, _text: &str) { ... } fn take_text_command(&mut self) -> Option<(WidgetId, TextCommand)> { ... } fn on_pointer_move(&mut self, _x: f32, _y: f32) { ... } fn on_pointer_hover( &mut self, _target: SurfaceTarget, _position: Option<(f32, f32)>, ) { ... } fn on_blur_regions( &mut self, _target: SurfaceTarget, _regions: &[(Rect, f32)], ) { ... } fn on_pointer_axis(&mut self, _x: f32, _y: f32, _dx: f32, _dy: f32) { ... } fn on_pointer_wheel( &mut self, _x: f32, _y: f32, _notches_x: f32, _notches_y: f32, ) -> bool { ... } fn on_touch_down(&mut self, _id: i64, _x: f32, _y: f32) { ... } fn on_touch_move(&mut self, _id: i64, _x: f32, _y: f32) { ... } fn on_touch_up(&mut self, _id: i64, _x: f32, _y: f32) { ... } fn claims_raw_touch(&self) -> bool { ... } fn claims_raw_touch_at(&self, _x: f32, _y: f32) -> bool { ... } fn on_drop_motion(&mut self, _x: f32, _y: f32) { ... } fn on_drop_leave(&mut self) { ... } fn on_drop_received(&mut self, _x: f32, _y: f32, _mime: &str, _text: &str) { ... } fn on_pointer_button(&mut self, _x: f32, _y: f32, _pressed: bool) { ... } fn on_pointer_secondary(&mut self, _x: f32, _y: f32) { ... } fn on_pointer_middle(&mut self, _x: f32, _y: f32) { ... } fn on_mouse_button(&mut self, _event: &MouseButtonEvent) { ... } fn on_modifiers(&mut self, _ctrl: bool, _shift: bool, _alt: bool) { ... } fn on_keyboard_leave(&mut self) { ... } fn on_raw_key( &mut self, _keysym: Keysym, _keycode: u32, _pressed: bool, _ctrl: bool, _shift: bool, ) { ... } fn on_raw_key_event(&mut self, key: &RawKey) { ... } fn claims_keyboard(&self) -> bool { ... } fn on_resize(&mut self, _width: u32, _height: u32) { ... } fn on_scale_changed(&mut self, _scale: u32) { ... } fn set_channel_sender(&mut self, _sender: ChannelSender<Self::Message>) { ... } fn poll_interval(&self) -> Option<Duration> { ... } fn key_repeat_delay(&self) -> Option<Duration> { ... } fn key_repeat_interval(&self) -> Option<Duration> { ... } fn cursor_override(&self) -> Option<CursorShape> { ... } fn cursor_at(&self, _x: f32, _y: f32) -> Option<CursorShape> { ... } fn key_repeats(&self, keysym: Keysym) -> bool { ... } fn is_animating(&self) -> bool { ... } fn needs_repaint(&mut self) -> bool { ... } fn cap_software_animation(&self) -> bool { ... } fn subsurface_motion_only(&self) -> bool { ... } fn background_color(&self) -> Color { ... } fn input_region(&self) -> Option<Vec<Rect>> { ... } fn overlay_opaque_region(&self, _id: OverlayId) -> Option<Vec<Rect>> { ... } fn window_config(&self) -> Option<(&str, &str)> { ... } fn shell_mode(&self) -> ShellMode { ... } fn secondary_view( &self, _width: u32, _height: u32, ) -> Option<Element<Self::Message>> { ... } fn requested_exit(&self) -> bool { ... } fn window_size_hint(&self) -> Option<(u32, u32)> { ... } fn window_title(&self) -> String { ... } fn window_resizable(&self) -> bool { ... } fn start_fullscreen(&self) -> bool { ... } fn fullscreen(&self) -> Option<bool> { ... } fn exclusive_zone(&self) -> i32 { ... } fn layer_anchor(&self) -> Anchor { ... } fn layer_size(&self) -> (u32, u32) { ... } fn keyboard_exclusive(&self) -> bool { ... }
}
Expand description

Trait that application types must implement to integrate with ltk.

Required Associated Types§

Source

type Message: Clone + 'static

The message type produced by this application.

Required Methods§

Source

fn view(&self) -> Element<Self::Message>

Build the widget tree for the current frame.

Source

fn update(&mut self, msg: Self::Message)

Apply a message to the application state.

Source

fn app_id(&self) -> &str

Stable application identifier, reverse-DNS style ("net.liberux.settings", "io.eydos.Calls"), constant for the life of the process.

The runtime uses it in three places:

  • xdg_toplevel.set_app_id on the main window, so the compositor, the dock and the task switcher can match the window to its .desktop entry (use the desktop file’s name minus .desktop, or its StartupWMClass);
  • the accessibility tree — AccessKit / AT-SPI2 report it as the application name;
  • the on-disk session directory $XDG_STATE_HOME/<app_id>/ where save_state is persisted.

It must not be empty and must not contain /; the runtime then disables session persistence and logs once. Layer-shell and session-lock components still return one (it names them in the a11y tree), even though they never get a toplevel. The app_id element of the deprecated window_config tuple is ignored in favour of this method — keep both pointing at the same const.

const APP_ID: &str = "net.example.Notes";

impl App for Notes
{
    fn app_id( &self ) -> &str { APP_ID }
}
Source

fn save_state(&self) -> Option<Vec<u8>>

Serialize the part of the state worth surviving a restart.

Return the bytes the runtime should persist, or None when there is nothing to bring back (a shell panel, a lock screen, a stateless tool). The encoding is the app’s own — JSON, CBOR, a hand-rolled line format — ltk never inspects it and puts no serde bound on the trait. Keep it small and cheap: the runtime calls this

  • every ~30 s while the app runs, touching the disk only when the bytes differ from the last save (a UI-only change that round-trips to identical bytes costs one comparison);
  • when the window closes — after on_close_requested allowed it or requested_exit returned true;
  • on SIGTERM / SIGINT, which the runtime turns into a clean exit of the event loop instead of letting the process die.

The bytes are written atomically (temp file + rename, mode 0600) to $XDG_STATE_HOME/<app_id>/state.bin (~/.local/state/<app_id>/state.bin when the variable is unset) next to a session.json holding the compositor’s xdg-session-management-v1 session id and a clean-exit marker. Returning None removes a stale state.bin. Only ShellMode::Window apps (and the deprecated window_config path) are persisted; layer and session-lock surfaces are skipped silently, so shell components simply return None.

The file is plain on disk: never put secrets in it (passwords belong to the secure-mode text_edit and its wiped buffers), and keep large blobs (images, caches) elsewhere. The signal handler is installed process-wide when run is entered, so the clean exit holds no matter which thread receives the signal or when it was started.

restore_state explains when — and when not — the bytes come back.

use ltk::{ App, Element, text };
use serde::{ Deserialize, Serialize };

#[ derive( Clone ) ]
enum Msg { Tab( usize ) }

#[ derive( Serialize, Deserialize ) ]
struct Persisted { version: u32, tab: usize, draft: String }

struct Editor { tab: usize, draft: String, cursor_blink: bool }

impl App for Editor
{
    type Message = Msg;

    fn app_id( &self ) -> &str { "net.example.Editor" }

    fn save_state( &self ) -> Option<Vec<u8>>
    {
        // Only what a relaunch needs — the blink phase stays out.
        let p = Persisted { version: 1, tab: self.tab, draft: self.draft.clone() };
        serde_json::to_vec( &p ).ok()
    }

    fn restore_state( &mut self, state: Vec<u8> )
    {
        // Tolerate an older or corrupt file: keep the defaults.
        if let Ok( p ) = serde_json::from_slice::<Persisted>( &state )
        {
            self.tab   = p.tab;
            self.draft = p.draft;
        }
    }
}
Source

fn restore_state(&mut self, state: Vec<u8>)

Re-apply bytes previously returned by save_state.

Called at most once per process, synchronously inside run / try_run, before the window is created and before the first view — the first frame already shows the restored state. It is not called on an ordinary launch unless restore_on_launch says so. The runtime restores app state only when

  • the process is relaunched as part of a session restore: the shell sets LTK_SESSION_RESTORE=1 in the environment (the runtime removes the variable before the app can spawn children); or
  • the previous run of this app_id did not exit cleanly (crash, SIGKILL, power loss): its session.json still says clean_exit: false.

This mirrors Android’s saved-instance-state contract: opening the app from the launcher gives a fresh instance, coming back after the system killed it lands where the user left off. Window geometry (size, position, workspace) is separate — the compositor restores it through xdg-session-management-v1 on every launch, plain ones included, using the session id ltk stores.

state is exactly what save_state produced, possibly by an older build: validate, version, and fall back to defaults instead of panicking. Nothing is called when no state file exists or the app returned None last time. Shell components never receive this call and implement it as a no-op.

See save_state for the file layout and a worked example.

Provided Methods§

Source

fn restore_on_launch(&self) -> bool

Whether an ordinary launch also restores the saved state.

By default restore_state follows Android’s saved-instance-state contract: the state comes back after a crash or a session restore, and a launch from the launcher starts fresh. Some apps keep state that the user expects to find on every launch, however the last run ended, as Android browsers keep their tabs in their own storage even when closed from the app switcher. Such an app returns true, and the runtime then hands the saved bytes to restore_state on a plain launch too. Nothing else changes: saving, the file layout and the window geometry are the same either way.

Asked once per process, inside run / try_run, right before restore_state would be called and before the window exists, so the answer can only come from the state the app was constructed with, for instance a user setting read in main. It is not asked, and nothing is restored, when persistence is off: no usable app_id, another live instance holding the state directory, or a layer or session-lock surface. With no state file, or after save_state returned None, restore_state is not called either.

Since restore_state now runs on launches that carry their own intent, such as a file or URL on the command line, it should merge that intent into the restored state rather than let the restore replace it. What save_state returns then outlives every exit, so leave out what the user would expect to be forgotten on closing the app (a private window, a half-typed password).

Default false.

struct Browser { tabs: Vec<String>, keep_tabs: bool, opened: Option<String> }

impl App for Browser
{
    fn save_state( &self ) -> Option<Vec<u8>>
    {
        Some( self.tabs.join( "\n" ).into_bytes() )
    }

    fn restore_state( &mut self, state: Vec<u8> )
    {
        let Ok( text ) = String::from_utf8( state ) else { return };
        self.tabs = text.lines().map( String::from ).collect();
        // The URL this launch was asked to open goes after the restored tabs.
        self.tabs.extend( self.opened.take() );
    }

    fn restore_on_launch( &self ) -> bool { self.keep_tabs }
}
Source

fn invalidate_after(&self, _msg: &Self::Message) -> InvalidationScope

Tell the runtime which surfaces could have changed visibly as a result of update being called with this message.

Default is InvalidationScope::All — every surface is redrawn. A shell that knows, for example, that a SetVolume message only affects its quick-settings overlay can return InvalidationScope::Only( vec![ SurfaceTarget::Overlay( quick_id ) ] ) to skip pointless work on other surfaces.

Called before update (so it sees the message but not the post-update state). Side-effect free: must not mutate self.

Source

fn overlays(&self) -> Vec<OverlaySpec<Self::Message>>

Describe the auxiliary overlay surfaces that should be active this frame. Each entry becomes its own Wayland layer-shell surface independent from the main surface returned by view.

Return an empty Vec (the default) to disable multi-surface support — the app then renders only onto its main surface.

The runtime diffs the list across frames using OverlaySpec::id: stable IDs are kept alive, new IDs cause a surface to be created, and IDs that disappear cause the surface to be destroyed.

Source

fn overlay_title(&self, _id: OverlayId) -> Option<String>

Title of a Layer::Window overlay, shown in its decoration and in the compositor’s window lists. Default: the app_id.

Source

fn overlay_namespace(&self, _id: OverlayId) -> Option<&'static str>

wlr-layer-shell namespace of a layer overlay, so the compositor can tell it apart from the rest. Read when the surface is created. Default: "ltk-overlay".

Source

fn subsurfaces(&self) -> Vec<SubsurfaceSpec<Self::Message>>

Describe the input-transparent child surfaces composited over the main surface this frame. Each becomes a wl_subsurface the compositor moves by position; see SubsurfaceSpec. Diffed across frames by SubsurfaceSpec::id. Default empty.

Source

fn poll_external(&mut self) -> Vec<Self::Message>

Return any pending messages from external sources (timers, async, etc.).

Source

fn take_activation_requests(&mut self) -> Vec<String>

Tags for which the app wants a fresh xdg-activation-v1 token this iteration. Drained once per loop; each tag is echoed back verbatim through App::on_activation_token once the compositor issues the token, so the app can hand it to a child it is about to spawn via the $XDG_ACTIVATION_TOKEN launch convention. Default none.

Source

fn on_activation_token( &mut self, _tag: String, _token: String, ) -> Option<Self::Message>

Deliver a token issued for an earlier App::take_activation_requests tag. tag is the string the app handed out; token is the opaque activation token to place in the launched child’s environment.

Source

fn on_first_frame_committed(&mut self)

Called once, immediately after the first buffer for the main surface has been rendered and committed to the compositor. Note: this is the client-side commit, not the compositor’s present — under VT switching the actual present can be deferred (no DRM master yet), so a handoff signal that fires here is enough to tell a parent “ready to be presented as soon as my VT becomes active”.

Source

fn on_main_frame_committed(&mut self)

Called every time a buffer for the main surface has been rendered and committed, before the subsurfaces are reconciled for the same iteration. Lets a host sequence a subsurface change after the main frame it depends on: a subsurface retired here goes away only once the main frame that replaces it has been sent.

Source

fn on_overlay_frame_committed(&mut self, _id: OverlayId)

Called every time a buffer for the overlay id has been rendered and committed.

Source

fn on_subsurface_committed(&mut self, _id: SubsurfaceId)

Called every time the content of the subsurface id has been rasterised and committed (first raster, resize or a new content_version), before the parent commit that maps it.

Source

fn on_close_requested(&mut self) -> bool

Called when the surface is asked to close (compositor request, titlebar button, layer-shell closed event). Return true to allow the application to exit, or false to cancel.

Default: true (always close).

Source

fn on_tap(&mut self) -> Option<Self::Message>

Called when tapping/clicking outside any widget.

Source

fn on_key(&mut self, _keysym: Keysym) -> Option<Self::Message>

Called on key press when no text input is focused.

Source

fn on_key_with_modifiers( &mut self, keysym: Keysym, _ctrl: bool, _shift: bool, ) -> Option<Self::Message>

Called on key press with modifier state. Override this instead of on_key when you need Ctrl/Shift/Alt awareness.

Source

fn on_back(&mut self) -> BackResponse<Self::Message>

Called on a back request — the shell’s back button or gesture (delivered as the XF86Back key) or a mouse’s back button — once the runtime has nothing of its own left to close.

Back peels one layer per request, top-most first:

  1. transient UI the runtime owns — the built-in context menu, open xdg-popup overlays, a text selection, the top-most pressable carrying an on_escape (a dialog’s cancel);
  2. text-input focus — dropping it also retires the on-screen keyboard on compositors that did not close it themselves;
  3. this hook — the app’s own navigation state;
  4. Self::on_key_with_modifiers with Keysym::XF86_Back, for apps that predate this hook;
  5. Self::back_at_root.

Return BackResponse::Message with the message that undoes the top-most piece of navigation state — the part of the model that says where the user is: the open panel, the current screen, the position in a history. Derive the answer from the model every time instead of keeping a separate list of things to undo: a list drifts the first time the user leaves a screen some other way (its own close button, a link), while the model cannot disagree with itself.

Navigation state is also what Self::save_state should persist. Keeping the two the same is what makes back behave identically after a session restore or a crash: the restored model yields the same answers here, with nothing extra to serialise. NavStack packages that pattern for the common multi-screen app. docs/navigation.md covers the model at length.

Escape is a different request: it only runs step 1, drops the focus and reaches Self::on_key_with_modifiers. It never navigates and never leaves the app.

#[ derive( Clone ) ]
enum Msg { CloseSearch, Show( Screen ) }

struct Notes { screen: Screen, searching: bool }

impl App for Notes
{
    fn on_back( &mut self ) -> BackResponse<Msg>
    {
        if self.searching { return BackResponse::Message( Msg::CloseSearch ); }
        match self.screen
        {
            Screen::Detail => BackResponse::Message( Msg::Show( Screen::List ) ),
            Screen::List   => BackResponse::Unhandled,
        }
    }
}
Source

fn back_at_root(&self) -> BackAtRoot

What a back request does when Self::on_back had nothing to go back to. Default: BackAtRoot::Minimize — the window leaves the screen and the app keeps its state, the way a root activity behaves on Android.

Only applies to ShellMode::Window surfaces; layer-shell and session-lock surfaces never react to back at the root.

Source

fn on_swipe_up(&mut self) -> Option<Self::Message>

Called when a sufficient upward swipe gesture is detected.

Return a message to handle the swipe (e.g., opening an overview). The gesture is recognized when the user drags upward by at least swipe_threshold() × screen height and then releases.

Source

fn on_swipe_progress(&mut self, _progress: f32)

Called during an upward swipe gesture with progress ≥ 0.0.

Use this to create follow-the-finger animations. progress starts at 0.0 when the drag begins and increases as the finger moves upward, reaching 1.0 when the drag distance equals swipe_threshold() × screen height. It is not clamped at 1.0 — the value keeps growing if the finger drags further so that follow-the-finger panels can continue tracking the finger all the way up the screen. Clamp inside the handler if you need a bounded [0, 1] range for a visual indicator.

When the user releases without completing the gesture, this method is called once more with progress = 0.0 to signal cancellation.

Source

fn on_swipe_down(&mut self) -> Option<Self::Message>

Called when a sufficient downward swipe gesture is detected.

Return a message to handle the swipe (e.g., toggling quick settings). The gesture is recognized when the user drags downward by at least swipe_down_threshold() × screen height and then releases.

Source

fn on_swipe_down_progress(&mut self, _progress: f32)

Called during a downward swipe gesture with progress ≥ 0.0.

Use this to create follow-the-finger animations. progress starts at 0.0 when the drag begins and increases as the finger moves downward, reaching 1.0 when the drag distance equals swipe_down_threshold() × screen height. It is not clamped at 1.0 — the value keeps growing if the finger drags further so that follow-the-finger panels can continue tracking the finger all the way down the screen. Clamp inside the handler if you need a bounded [0, 1] range for a visual indicator.

When the user releases without completing the gesture, this method is called once more with progress = 0.0 to signal cancellation.

Source

fn swipe_threshold(&self) -> f32

Fraction of screen height required to trigger on_swipe_up.

Default: 0.6 (60% of screen height). Lower values make the gesture easier to trigger; higher values require more vertical travel.

Source

fn swipe_down_threshold(&self) -> f32

Fraction of screen height required to trigger on_swipe_down.

Default: 0.15 (15% of screen height). Typically lower than swipe_threshold() because downward swipes are often initiated from a small UI element like a top bar.

Source

fn swipe_down_edge(&self) -> f32

Top-edge band where a downward swipe may originate, as a fraction of screen height. Presses below this band never fire on_swipe_down nor on_swipe_down_progress.

Default: 1.0 (entire screen — any starting point is accepted). Set to a small value like 0.05 to restrict the gesture to the top edge, matching the usual system-panel pull-down UX.

Source

fn on_overscroll_down_progress(&mut self, _progress: f32)

Called while a vertical scroll is pinned at its top and the finger keeps pulling down. progress is the accumulated overscroll as a fraction of the surface height (≥ 0.0, unbounded). Use it to drive a pull-from-top-to-dismiss panel that tracks the finger.

Source

fn on_overscroll_down(&mut self, _committed: bool) -> Option<Self::Message>

Called when the finger releases after a top overscroll pull-down. committed is true when the pull passed overscroll_dismiss_threshold.

Source

fn overscroll_dismiss_threshold(&self) -> f32

Fraction of surface height a top overscroll pull-down must exceed to commit a dismiss on release. Default: 0.25.

Source

fn on_swipe_left(&mut self) -> Option<Self::Message>

Called when a sufficient leftward swipe gesture is detected.

Return a message to handle the swipe (e.g., paging to the next homescreen). Fires on release when the user dragged left by at least swipe_horizontal_threshold() × screen width. Unlike vertical swipes, horizontal swipes survive starting the press on an interactive widget: once the drag distance crosses an 8 px threshold the press is promoted to a horizontal drag and no tap will fire at release.

Source

fn on_swipe_right(&mut self) -> Option<Self::Message>

Called when a sufficient rightward swipe gesture is detected. Mirror of on_swipe_left for the other direction.

Source

fn on_swipe_horizontal_progress(&mut self, _progress: f32)

Called during a horizontal swipe with signed progress. Negative values mean the finger has moved left, positive values mean right. The value is dx / (threshold * width), so ±1.0 marks the commit threshold; it is not clamped so follow-the-finger panels can keep tracking past the threshold. When the user releases without completing the gesture this method is called once more with 0.0 to signal cancellation.

Source

fn swipe_horizontal_threshold(&self) -> f32

Fraction of screen width required to commit App::on_swipe_left / App::on_swipe_right. Default: 0.5 (half the screen). A shell that wants easier paging can lower this; one that wants to avoid accidental pages can raise it.

Source

fn long_press_duration(&self) -> Duration

Duration a press must remain stationary (within tolerance) before the widget’s long-press message fires and the gesture transitions into drag mode. Default: 500 ms.

Source

fn on_drag_move(&mut self, _x: f32, _y: f32)

Called on every pointer motion once a long-press has fired and the gesture has entered drag mode. x, y are in physical pixels of the surface that owned the original press.

Source

fn on_drop(&mut self, _x: f32, _y: f32) -> Option<Self::Message>

Called on release once a long-press has fired. Return a message to handle the drop (e.g., commit the drop target). x, y are in physical pixels of the surface that owned the original press. The regular tap / swipe handling is suppressed for this gesture.

Source

fn on_text_input_focused(&mut self, _active: bool)

Called when a text-input widget gains or loses focus.

Source

fn external_text_input(&self) -> Option<u64>

Some( serial ) holds the input method for a text field ltk does not own (an editable in an embedded web view) while no ltk text widget is focused; a new serial renews the request, e.g. on a press on the field.

Source

fn external_text_purpose(&self) -> Purpose

What the field held by Self::external_text_input is for; Terminal gives the on-screen keyboard its row of terminal keys.

Source

fn on_external_text_commit(&mut self, _text: &str)

Text committed by the input method while Self::external_text_input holds it.

Source

fn on_external_text_delete(&mut self, _before: u32, _after: u32)

Delete around the external field’s cursor, in UTF-8 bytes as zwp_text_input_v3 counts.

Source

fn builtin_keyboard(&self) -> bool

true to draw ltk’s own on-screen keyboard under a focused text field even when the compositor offers an input method; it is drawn regardless when none is offered, or with LTK_BUILTIN_OSK=1.

Source

fn on_toplevel_event(&self, _event: ToplevelEvent) -> Option<Self::Message>

Translate a Wayland ext-foreign-toplevel-list-v1 event into an app message. The runtime binds the protocol globally and forwards every open / close it sees here; apps that ignore window state leave the default (return None) and pay nothing.

The id field is the Wayland object id of the toplevel handle — unique within the session, stable for the handle’s lifetime, the same value coming in on Closed as the matching Opened. app_id on Opened is the toplevel’s app_id event (may be empty if the compositor never sent one before the first done).

Source

fn on_files_changed(&self, _dir: &Path) -> Option<Self::Message>

A directory the app listed through crate::files::list changed: in a browser, through another client of the desktop. Natively not reported yet.

Source

fn on_mounts_changed(&self, _mounts: &[Mount]) -> Option<Self::Message>

The folders of the user’s computer mounted in a browser, when the app connects and whenever one comes or goes. Natively there are none.

Source

fn on_open_files(&self, _paths: Vec<PathBuf>) -> Option<Self::Message>

The files a browser’s desktop launched the app to open. Natively they come in the app’s arguments instead.

Source

fn take_focus_request(&mut self) -> Option<WidgetId>

Return Some(id) once to focus the widget with that WidgetId. The app must return None after the first call.

The runtime waits a few drawn frames for the widget to appear, so a request made from the update that adds the widget to the view is honoured, and a target scrolled out of a viewport is brought into view before it is focused. A widget that never appears is dropped.

Source

fn take_blur_request(&mut self) -> bool

Return true once to drop the keyboard focus, which folds the on-screen keyboard; a take_focus_request in the same cycle wins.

Source

fn take_clipboard_request(&mut self) -> Option<String>

Return Some( text ) once to put it in the clipboard, for other applications as well.

Source

fn take_primary_request(&mut self) -> Option<String>

Return Some( text ) once to make it the primary selection, which the middle button pastes, here and in other applications.

Source

fn take_paste_request(&mut self) -> Option<SelectionKind>

Return Some( kind ) once to have Self::on_paste called with the text that selection holds.

Source

fn on_paste(&mut self, _text: &str)

The text of the selection Self::take_paste_request asked for; not called when it is empty.

Source

fn take_text_command(&mut self) -> Option<(WidgetId, TextCommand)>

Return Some( ( id, command ) ) once to have the text field with that WidgetId take the focus and do command, after the frame that lays it out is drawn. A field that is not on screen then drops the command.

Source

fn on_pointer_move(&mut self, _x: f32, _y: f32)

Called on every pointer motion (mouse move, button press, button release) and on every primary-finger touch down and motion. x, y are in physical pixels of the main surface, the same coordinate space as widget rects. Default: no-op.

Useful for embedding components that need to know the live pointer position without going through a full crate::widget::external::ExternalSource / WidgetHandlers dispatch path — for example forwarding clicks into an embedded WPE WebView.

Source

fn on_pointer_hover( &mut self, _target: SurfaceTarget, _position: Option<(f32, f32)>, )

Mouse motion over target, in physical pixels of that surface, or None when the pointer leaves it. Touch never reports here.

Source

fn on_blur_regions(&mut self, _target: SurfaceTarget, _regions: &[(Rect, f32)])

The ( rect, corner radius ) of every Container::blur_behind drawn on target, in its physical pixels, after a frame where they changed: for a shell to tell its compositor what to blur.

Source

fn on_pointer_axis(&mut self, _x: f32, _y: f32, _dx: f32, _dy: f32)

Called on a wheel / touchpad scroll event whose position does not fall inside any LTK scroll viewport. x, y are in physical pixels (same coordinate space as Self::on_pointer_move); dx, dy are scroll deltas in the raw axis units the compositor delivered (AxisSource::Wheel gives ~10 px ticks, touchpads give continuous values). Default: no-op.

Useful for embeddings that take over scrolling for their own content — for example forwarding the event to a WPE view that owns a scrollable web page — or for wheel-driven view state such as a stepped carousel. The runtime rebuilds and repaints after the hook returns, so state mutated here shows immediately.

Source

fn on_pointer_wheel( &mut self, _x: f32, _y: f32, _notches_x: f32, _notches_y: f32, ) -> bool

Called before Self::on_pointer_axis for a scroll event from a wheel or any device that reports discrete detents, with the number of detents on each axis (fractional on high-resolution wheels; positive is right / down). Return true to consume it; false, the default, lets the same event reach on_pointer_axis in raw units.

Source

fn on_touch_down(&mut self, _id: i64, _x: f32, _y: f32)

Raw multi-touch callbacks. Default: no-op.

The first finger to land becomes the primary slot and is fed through the regular gesture machine — on_pointer_*, swipe, scroll, long-press, drag-and-drop all run from that slot. Every additional finger fires on_touch_down / on_touch_move / on_touch_up instead, with id matching the wl_touch.id the compositor delivered.

Use this for app-defined multi-finger gestures (pinch-zoom, two-finger pan in a canvas, parallel keyboard keys) that the built-in single-slot machine cannot model. (x, y) are in physical pixels, matching the coordinate space of Self::on_pointer_move.

When Self::claims_raw_touch returns true the primary finger arrives here as well, instead of driving the gesture machine.

Source

fn on_touch_move(&mut self, _id: i64, _x: f32, _y: f32)

Source

fn on_touch_up(&mut self, _id: i64, _x: f32, _y: f32)

See Self::on_touch_down. x, y are the last known position of the finger (wl_touch.up does not carry one).

Source

fn claims_raw_touch(&self) -> bool

Return true to receive the primary finger through the raw Self::on_touch_down / move / up stream as well, bypassing the built-in single-slot gesture machine entirely — widget presses, taps and swipes stop working on touch. For apps whose surface is one self-contained input consumer (an embedded WebView, a game canvas) that needs every finger verbatim.

Source

fn claims_raw_touch_at(&self, _x: f32, _y: f32) -> bool

Per-press variant of Self::claims_raw_touch: return true to take the finger landing at (x, y) (physical pixels of the main surface) through the raw Self::on_touch_down / move / up stream, while fingers landing elsewhere keep driving the widget gesture machine. For apps that embed a self-contained input consumer (a WebView) next to regular widgets. A claimed press clears the widget focus. Only consulted for the main surface.

Source

fn on_drop_motion(&mut self, _x: f32, _y: f32)

Pointer of a cross-application drag-and-drop entered or moved inside the main surface. (x, y) is in logical pixels.

Source

fn on_drop_leave(&mut self)

The drag left without dropping.

Source

fn on_drop_received(&mut self, _x: f32, _y: f32, _mime: &str, _text: &str)

External drop payload landed on the surface. mime is the best mime the source offered (text/uri-list, text/plain, …) and text is the decoded payload (UTF-8). (x, y) is the release position in logical pixels.

Source

fn on_pointer_button(&mut self, _x: f32, _y: f32, _pressed: bool)

Called on every left-button mouse press / release, and on every primary-finger touch down / up, before the regular gesture machine fires. pressed = true for press, false for release. (x, y) are in physical pixels (same coordinate space as Self::on_pointer_move). Default: no-op.

Lets embeddings see the raw button transitions without going through LTK’s tap/long-press/drag classification — needed for forwarding clicks AND drags to an embedded view, since the tap classifier collapses press+release into a single event.

Source

fn on_pointer_secondary(&mut self, _x: f32, _y: f32)

A secondary-button press that no widget of ltk’s own answered, in physical pixels. Default: no-op.

Source

fn on_pointer_middle(&mut self, _x: f32, _y: f32)

A middle-button press, in physical pixels. Default: no-op.

Source

fn on_mouse_button(&mut self, _event: &MouseButtonEvent)

Every press and release of the left, middle and right buttons, with the modifiers held, before ltk handles it: for an app that hands the mouse on, as a terminal does to a program that asks for it. Default: no-op.

Source

fn on_modifiers(&mut self, _ctrl: bool, _shift: bool, _alt: bool)

The modifiers held changed, for an app that needs them with the wheel or a motion, which do not carry them. Default: no-op.

Source

fn on_keyboard_leave(&mut self)

The window lost the keyboard, so no release will come for the keys held: for an app that repeats keys itself. Default: no-op.

Source

fn on_raw_key( &mut self, _keysym: Keysym, _keycode: u32, _pressed: bool, _ctrl: bool, _shift: bool, )

Called on every keyboard event (press and release) before the regular focus-aware dispatch (Self::on_key / Self::on_key_with_modifiers) runs. Default: no-op.

keycode is the raw hardware scancode straight from the compositor (wl_keyboard.key’s key argument); keysym is the xkb-translated keysym the user effectively pressed. Most consumers only care about keysym, but embeddings that forward into an inner window system (a WPE web view, an X11 emulator, …) typically need both — the inner system does its own keycode → keysym translation for layout-aware shortcuts.

This bypass exists for those embeddings: it fires in addition to the normal callbacks; consume or ignore at will.

XF86_Back is not reported here: it is a navigation request, not input for the embedded system, and goes through Self::on_back.

Source

fn on_raw_key_event(&mut self, key: &RawKey)

Self::on_raw_key with the Alt state and the typed text too; the runtime calls this one, which forwards to on_raw_key unless overridden. Override it instead when Alt or dead keys matter, as in a terminal.

Source

fn claims_keyboard(&self) -> bool

true while every key belongs to Self::on_raw_key, as in a terminal: ltk then neither dispatches keys to its widgets, so Enter and Space do not press the focused or hovered one, nor repeats them. XF86_Back still goes through Self::on_back.

Source

fn on_resize(&mut self, _width: u32, _height: u32)

Called when the surface is (re-)configured with a new size.

width and height are in physical pixels (the Wayland surface size multiplied by the current buffer scale). They therefore match the coordinate space used for layout and the pointer/touch callbacks (on_drag_move, on_drop, widget hit-testing). Also fired when the buffer scale changes, so apps can refresh any state keyed off the old physical dimensions.

Source

fn on_scale_changed(&mut self, _scale: u32)

Called when the compositor reports a new integer buffer scale for the main surface. scale is the integer buffer scale (1 = standard DPI, 2 = HiDPI ×2); physical pixels equal logical pixels × scale. In a browser the scale is the page’s devicePixelRatio, which may be fractional; scale is then that ratio rounded, and on_resize gives the exact physical size. The default is inert so apps that only care about physical pixels can keep using on_resize.

Source

fn set_channel_sender(&mut self, _sender: ChannelSender<Self::Message>)

Called once at startup with a channel sender that can be used from any thread to deliver messages into the event loop. Sending a message immediately wakes the event loop — no polling delay.

Source

fn poll_interval(&self) -> Option<Duration>

How often the event loop should call poll_external for periodic work such as clock ticks. Return None (the default) to disable the timer — poll_external will still be called after every Wayland event.

Source

fn key_repeat_delay(&self) -> Option<Duration>

Delay before the runtime starts repeating a held-down key.

Some(d) overrides whatever the compositor advertises through wl_keyboard.repeat_info; None (the default) means “use the compositor’s setting, or fall back to 500 ms when the compositor did not provide one”.

Returning Some(Duration::ZERO) effectively disables the wait before the first repeat — useful only for diagnostic builds.

Source

fn key_repeat_interval(&self) -> Option<Duration>

Interval between successive synthetic key events while a key is held down past the initial Self::key_repeat_delay.

Some(d) overrides the compositor’s wl_keyboard.repeat_info rate; None (the default) means “use the compositor’s setting, or fall back to 33 ms (~30 Hz) when the compositor did not provide one”. Pass Some(Duration::ZERO) to disable repeats entirely (or override the per-key gate via Self::key_repeats).

Source

fn cursor_override(&self) -> Option<CursorShape>

Override the pointer cursor shape globally. When Some(shape), the runtime sends that shape to the compositor regardless of which widget the pointer is over — useful for “the app is busy” states (return crate::CursorShape::Wait) or while the app is doing a long synchronous operation. Returning None (the default) lets per-widget defaults take effect.

The runtime calls this every frame, so flipping a loading boolean in Self::update propagates to the cursor on the next iteration without any extra wiring.

Source

fn cursor_at(&self, _x: f32, _y: f32) -> Option<CursorShape>

The cursor the app wants at ( x, y ), in physical pixels of the main surface: below the window’s resize edges and Self::cursor_override, above the cursor of the widget under it. None, the default, leaves the widget’s.

Source

fn key_repeats(&self, keysym: Keysym) -> bool

Decide whether a given key participates in held-key repeat.

Default: every non-modifier key repeats except Escape, XF86_Back, Tab and ISO_Left_Tab — those drive one-shot UI semantics (dismiss / go back / focus cycle) where a held key would multiply the effect in surprising ways. Override to widen or narrow the gate (a chess clock app, for example, might want even Tab to repeat).

Source

fn is_animating(&self) -> bool

Return true while a frame-by-frame animation is running. The event loop will keep requesting redraws at ~60 fps until this returns false.

Source

fn needs_repaint(&mut self) -> bool

Return true when the main surface’s pixels changed but its widgets did not, such as an crate::widget::external::External with a new frame: the surface is drawn again from the cached view, without calling Self::view. Asked once per iteration, after the messages are processed.

Source

fn cap_software_animation(&self) -> bool

Cap Self::is_animating redraws to ~30 Hz when the software renderer is active. Default true: 60 fps software rasterization is a common battery sink on mobile (no GPU offload), and halving it is usually imperceptible for the animations software fallback can afford. The GLES backend is never capped. Return false to keep the full display rate on software.

Source

fn subsurface_motion_only(&self) -> bool

Return true when a finger-tracked swipe or an Self::is_animating slide only repositions the app’s input-transparent subsurfaces (Self::subsurfaces) while the main surface buffer stays unchanged. The runtime then skips the per-frame main re-raster that Self::on_swipe_progress and is_animating would otherwise force, keeping the animation pumped (motion events during the drag, a bare frame callback while is_animating) and letting the per-frame subsurface reconcile carry the motion. Use it for a slide-to-reveal panel over a static main surface.

Source

fn background_color(&self) -> Color

Background color for the canvas. Override to make the surface transparent or to deviate from the theme. Default: the active theme’s bg token, so the window matches the rest of the shell out of the box.

Source

fn input_region(&self) -> Option<Vec<Rect>>

Return the interactive input region as a list of rects (logical pixels). Only these areas receive pointer/touch input; the rest passes through. Return None (default) to receive input everywhere.

Source

fn overlay_opaque_region(&self, _id: OverlayId) -> Option<Vec<Rect>>

Rects (physical layout pixels) of overlay id that are fully opaque this frame, so the compositor can skip blending them and repainting what lies underneath. None (default) declares nothing opaque.

Source

fn window_config(&self) -> Option<(&str, &str)>

Return Some(( title, app_id )) to force an XDG toplevel window instead of layer-shell overlay. The compositor will display the title in the title bar and use the app_id for taskbar/icon matching. Return None (default) to use layer-shell when available. The app_id element is ignored: app_id names the window.

Deprecated: Use shell_mode instead.

Source

fn shell_mode(&self) -> ShellMode

Specify the Wayland shell mode for this application.

For regular applications, use the default Window mode. For shell components (panels, backgrounds, overlays), use Layer. For a screen locker, use SessionLock and request the unlock by returning true from requested_exit.

Source

fn secondary_view( &self, _width: u32, _height: u32, ) -> Option<Element<Self::Message>>

Content for the surfaces the runtime creates on every output beyond the first. In ShellMode::SessionLock these are lock surfaces — the compositor blanks any output without one, so each is covered. In ShellMode::Layer on Layer::Background they are background layer surfaces, so a shell’s wallpaper reaches extended outputs (typically without the grid or any chrome). They take no input. width / height are that output’s surface size in physical pixels. Default None: the surface is filled with background_color.

Source

fn requested_exit(&self) -> bool

Return true to tear the surface down and exit the event loop. For a ShellMode::SessionLock surface the runtime calls unlock first, so the compositor lifts the lock instead of leaving the outputs blanked. Polled after every batch of updates.

Source

fn window_size_hint(&self) -> Option<(u32, u32)>

Suggest an initial size for an xdg-shell window in logical pixels.

Returning Some(( w, h )) makes ltk call both xdg_toplevel.set_min_size( w, h ) and xdg_toplevel.set_max_size( w, h ) before the first commit, which most compositors honour as an exact size for the configure they send back. The window is still resizable from the application’s point of view — if the compositor sends a different configure, the runtime adopts that size on the next frame.

A hint larger than the screen is shrunk on the first configure, keeping its aspect ratio: to the compositor’s xdg_toplevel.configure_bounds when it sends them, otherwise to 90 % of the output. A phone-shaped 720x1440 hint therefore opens as 486x972 on a 1080p desktop instead of running off its bottom edge.

Only applies to ShellMode::Window surfaces and to the xdg-shell fallback path used when wlr-layer-shell is missing. Ignored for layer-shell surfaces, which use Self::layer_size instead.

Default: None (let the compositor pick).

Source

fn window_title(&self) -> String

The main window’s title, which the compositor’s title bar, task switcher and dock show; read once, when the window is made.

Only applies to ShellMode::Window surfaces and to the xdg-shell fallback of a layer surface.

Default: the app_id.

Source

fn window_resizable(&self) -> bool

Whether the user may resize an xdg-shell window.

When Self::window_size_hint is set, ltk pins min_size == max_size before the first commit so the compositor’s first configure honours the requested size, and then releases both from the first configure so the surface becomes user-resizable; a minimum left in place would make a mobile compositor shrink the window’s scale to honour it. Returning false skips that release: the pin stays in place for the lifetime of the toplevel, declaring a fixed-size window that compositors must not resize or restore a remembered geometry over.

Ignored when Self::window_size_hint returns None.

Default: true.

Source

fn start_fullscreen(&self) -> bool

Request fullscreen on the toplevel before its first commit.

When true, ltk calls xdg_toplevel.set_fullscreen( None ) so the compositor picks an output and maps the surface fullscreen from the start (no flicker through a windowed configure first). The compositor’s own decorations are suppressed for fullscreen surfaces; the built-in titlebar from Self::window_config is still painted by ltk on top of the surface unless the app opts out.

Only applies to ShellMode::Window surfaces and to the xdg-shell fallback path used when wlr-layer-shell is missing.

Default: false.

Source

fn fullscreen(&self) -> Option<bool>

Desired fullscreen state of the toplevel while the app runs.

Polled after every batch of updates: when the returned value changes, ltk calls xdg_toplevel.set_fullscreen / unset_fullscreen. While the compositor reports the window as fullscreen the built-in titlebar is not painted. None (the default) leaves the window state alone.

Only applies to ShellMode::Window surfaces.

Source

fn exclusive_zone(&self) -> i32

Specify the exclusive zone for layer-shell surfaces.

The exclusive zone reserves screen space for this surface. For example, a top panel with height 50 should return 50 to prevent other windows from overlapping it.

  • > 0: Reserve this many pixels from the anchored edge
  • 0: No exclusive zone (default for overlays)
  • -1: Don’t reserve space but request focus

Only applies to ShellMode::Layer surfaces. Ignored for windows.

Source

fn layer_anchor(&self) -> Anchor

Specify which screen edges the layer-shell surface is anchored to.

Anchoring determines the position and size of the surface:

Default: Anchor::ALL (full screen)

Only applies to ShellMode::Layer surfaces. Ignored for windows.

Source

fn layer_size(&self) -> (u32, u32)

Specify the desired size for layer-shell surfaces.

Returns (width, height) where:

  • 0 means “fill available space in that dimension”
  • > 0 means “use this exact size in pixels”

Examples:

  • Top bar: (0, 50) - full width, 50px height
  • Bottom bar: (0, 40) - full width, 40px height
  • Side panel: (300, 0) - 300px width, full height
  • Full screen: (0, 0) - fill everything (default)

Default: (0, 0) (fill all available space)

Only applies to ShellMode::Layer surfaces. Ignored for windows.

Source

fn keyboard_exclusive(&self) -> bool

Request exclusive keyboard focus for this layer-shell surface.

When true, the compositor sends keyboard events to this surface without requiring a pointer click first. Useful for greeters, lock screens, and other surfaces that must capture input immediately.

Default: false (on-demand keyboard interactivity)

Only applies to ShellMode::Layer surfaces. Ignored for windows.

Implementors§