# How the transition() Function Maps Keyboard Events to Widget State in tui-logger

> Discover how tui-logger's transition() function maps keyboard events to widget state changes, updating flags, indexes, and log levels.

- Repository: [gin66/tui-logger](https://github.com/gin66/tui-logger)
- Tags: internals
- Published: 2026-03-02

---

**The `transition()` function in `gin66/tui-logger` serves as a state dispatcher that converts `TuiWidgetEvent` keyboard inputs into direct mutations of the widget's internal model, toggling visibility flags, adjusting selection indexes, and updating log level configurations.**

The `gin66/tui-logger` crate provides a terminal user interface for filtering and viewing logs in Rust applications. At the heart of its interactive capabilities lies the `transition()` method, which bridges user input with the widget's rendering state by translating high-level keyboard events into concrete data mutations.

## Architecture of the transition() Method

The `transition()` implementation follows a two-layer architecture that separates the public API from internal state management. The public entry point resides in [`src/widget/inner.rs`](https://github.com/gin66/tui-logger/blob/main/src/widget/inner.rs) at lines 37-38, where `TuiWidgetState::transition` acquires a lock on the inner mutable state and forwards the event:

```rust
pub fn transition(&self, event: TuiWidgetEvent) {
    self.inner.lock().transition(event);
}

```

The heavy lifting occurs within `TuiWidgetInnerState::transition`, which matches against the `TuiWidgetEvent` enum defined in the same file. This method directly mutates fields such as `hide_off`, `selected`, and `config` based on the specific key pressed.

## Keyboard Event to State Mappings

The `transition()` method handles twelve distinct keyboard events, each mapped to specific state mutations that control the logger's display behavior.

### Toggle Controls (Space, Hide, Focus)

Three keys control boolean flags that determine what content appears in the widget:

- **SpaceKey**: Toggles the global *hide off* flag using XOR logic (`self.hide_off ^= true`) at lines 86-88. When enabled, the widget hides log entries filtered out by the current configuration.
- **HideKey** (`h`): Flips the `hide_target` boolean at lines 89-91, controlling whether the currently selected log target appears in the target list.
- **FocusKey** (`f`): Toggles `focus_selected` at lines 92-94, enabling a mode where only the selected line receives visual emphasis.

### Navigation Controls (Arrow Keys)

Arrow keys manipulate selection indexes and visibility levels, but only when the target list is not hidden:

- **UpKey**: Decrements `self.selected` by one if `!self.hide_target && self.selected > 0`, preventing navigation beyond the top of the list (lines 95-99).
- **DownKey**: Increments `self.selected` if within bounds (`self.selected + 1 < self.nr_items`) and targets are visible (lines 100-104).
- **LeftKey**: Decreases the *visibility* level for the selected target by consuming `opt_selected_visibility_less` and updating the `LevelConfig` via `self.config.set()` (lines 106-110).
- **RightKey**: Increases the visibility level using `opt_selected_visibility_more` with similar config updates (lines 113-119).

### Recording Level Adjustments (+ and -)

Unlike visibility levels (which affect display), recording levels control what log data is captured globally:

- **PlusKey**: Raises the minimum log level for the selected target by calling `set_level_for_target(&selected_target, selected_recording_more)` from [`src/logger/api.rs`](https://github.com/gin66/tui-logger/blob/main/src/logger/api.rs) (lines 121-126).
- **MinusKey**: Lowers the recording level using `opt_selected_recording_less` with the same API (lines 128-133).

### Paging and View Controls

Page navigation manipulates center line pointers that control which portion of the log buffer remains visible:

- **PrevPageKey**: Sets `self.opt_line_pointer_center = self.opt_line_pointer_prev_page` to jump to the previous page's center line (line 135).
- **NextPageKey**: Advances to the next stored center pointer via `self.opt_line_pointer_center = self.opt_line_pointer_next_page` (line 136).
- **EscapeKey**: Clears the explicit center pointer with `self.opt_line_pointer_center = None`, returning the view to default scrolling behavior (line 137).

## Implementation Details in src/widget/inner.rs

The core logic resides in a single `match` statement within `TuiWidgetInnerState::transition` that exhaustively handles the `TuiWidgetEvent` enum variants. Each arm performs direct field mutations on the `TuiWidgetInnerState` struct, which holds all UI-related data including selection indexes (`selected`), visibility flags (`hide_off`, `hide_target`, `focus_selected`), and the `LevelConfig` instance that persists target-specific settings.

When processing LeftKey, RightKey, PlusKey, or MinusKey, the method first validates that a target is currently selected using helper options like `opt_selected_target` before attempting configuration updates. This prevents panics when no target is active.

## Practical Usage Example

To integrate keyboard handling in your application, create a `TuiWidgetState` instance and feed it `TuiWidgetEvent` values from your event loop:

```rust
use tui_logger::widget::{TuiWidgetState, TuiWidgetEvent};
use log::LevelFilter;

// Initialize state with default display level
let widget_state = TuiWidgetState::new()
    .set_default_display_level(LevelFilter::Info);

// Simulate keyboard interactions
widget_state.transition(TuiWidgetEvent::UpKey);      // Move selection up
widget_state.transition(TuiWidgetEvent::SpaceKey);   // Toggle hide-off flag
widget_state.transition(TuiWidgetEvent::RightKey);   // Increase visibility level
widget_state.transition(TuiWidgetEvent::PlusKey);    // Raise recording level

```

The state changes immediately affect how the widget renders during the next draw call, as implemented in the various widget rendering modules that read these internal flags.

## Summary

- The `transition()` function in [`src/widget/inner.rs`](https://github.com/gin66/tui-logger/blob/main/src/widget/inner.rs) (lines 37-38) acts as the public API entry point that delegates to the inner locked state.
- **Space**, **h**, and **f** keys toggle boolean flags controlling global hiding, target hiding, and focus modes via XOR operations at lines 86-94.
- **Arrow keys** navigate the selection index (lines 95-104) and adjust target-specific visibility levels in the `LevelConfig` (lines 106-119).
- **Plus and minus keys** modify global recording levels by invoking `set_level_for_target` from [`src/logger/api.rs`](https://github.com/gin66/tui-logger/blob/main/src/logger/api.rs) (lines 121-133).
- **Page Up, Page Down, and Escape** manipulate paging pointers to control which log segment remains centered in the view (lines 135-137).

## Frequently Asked Questions

### Where is the transition() function implemented in tui-logger?

The primary implementation resides in [`src/widget/inner.rs`](https://github.com/gin66/tui-logger/blob/main/src/widget/inner.rs) within the `TuiWidgetInnerState` struct. The public `TuiWidgetState::transition` method at lines 37-38 serves as a thin wrapper that acquires a mutex lock on the inner state before calling the actual logic.

### How does the Space key specifically affect widget rendering?

When `transition()` receives `TuiWidgetEvent::SpaceKey`, it executes `self.hide_off ^= true` at lines 86-88. This XOR operation flips the `hide_off` boolean, which the rendering code checks to determine whether to suppress display of filtered-out log entries, effectively toggling their visibility without changing the underlying filter configuration.

### What is the difference between visibility levels and recording levels in tui-logger?

**Visibility levels** (controlled by Left/Right arrow keys) determine which log levels appear in the TUI display for a specific target, stored in the `LevelConfig` at lines 106-119. **Recording levels** (controlled by +/- keys) determine the minimum log level captured by the global logger for that target, persisted via `set_level_for_target` calls at lines 121-133. You can record DEBUG level logs while only displaying ERROR levels in the widget.

### How does transition() handle concurrent keyboard events?

The `TuiWidgetState::transition` method wraps the inner state in a `std::sync::Mutex` lock (line 38), ensuring that state mutations occur atomically even when called from multiple threads. This thread-safe design allows the UI event loop to safely mutate state while the rendering thread reads the same data structure.