Skip to main content

toddy_core/
message.rs

1//! Internal message enum and serialization helpers.
2//!
3//! [`Message`] is the iced `Message` type used by the renderer. Every
4//! widget interaction (click, input, slide, toggle, etc.) and every
5//! runtime event (keyboard, mouse, window lifecycle) maps to a variant.
6//! The renderer's `update()` method dispatches on these variants to
7//! emit outgoing events over the wire protocol.
8//!
9//! The serialization helpers convert iced types (keys, modifiers, mouse
10//! buttons, scroll deltas) into the wire-format strings expected by the
11//! host.
12
13use iced::widget::markdown;
14use iced::widget::text_editor;
15use iced::{Point, window};
16use serde_json::Value;
17
18use crate::protocol::KeyModifiers;
19
20// ---------------------------------------------------------------------------
21// Event data structs
22// ---------------------------------------------------------------------------
23
24/// Scrollable viewport state, emitted on scroll position changes.
25#[derive(Debug, Clone, Copy)]
26pub struct ScrollViewport {
27    /// Absolute scroll offset on the x axis (pixels from left).
28    pub absolute_x: f32,
29    /// Absolute scroll offset on the y axis (pixels from top).
30    pub absolute_y: f32,
31    /// Relative scroll position on the x axis (0.0 = start, 1.0 = end).
32    pub relative_x: f32,
33    /// Relative scroll position on the y axis (0.0 = top, 1.0 = bottom).
34    pub relative_y: f32,
35    /// Total content width (may exceed viewport).
36    pub content_width: f32,
37    /// Total content height (may exceed viewport).
38    pub content_height: f32,
39    /// Visible viewport width.
40    pub viewport_width: f32,
41    /// Visible viewport height.
42    pub viewport_height: f32,
43}
44
45/// All fields from an iced keyboard event, packed for Message transport.
46#[derive(Debug, Clone)]
47pub struct KeyEventData {
48    pub key: iced::keyboard::Key,
49    pub modified_key: iced::keyboard::Key,
50    pub physical_key: iced::keyboard::key::Physical,
51    pub location: iced::keyboard::Location,
52    pub modifiers: iced::keyboard::Modifiers,
53    pub text: Option<String>,
54    pub repeat: bool,
55    /// Whether iced reported this event as `Captured` (consumed by a widget).
56    pub captured: bool,
57}
58
59// ---------------------------------------------------------------------------
60// Message
61// ---------------------------------------------------------------------------
62
63#[derive(Debug, Clone)]
64pub enum Message {
65    /// A user clicked a button with the given node ID.
66    Click(String),
67    /// A text input value changed (id, new_value).
68    Input(String, String),
69    /// A text input was submitted (id, current_value).
70    Submit(String, String),
71    /// A checkbox or toggler was toggled (id, checked).
72    Toggle(String, bool),
73    /// A slider value changed (id, value).
74    Slide(String, f64),
75    /// A slider was released (id).
76    SlideRelease(String),
77    /// A pick_list/combo_box/radio selection (id, value).
78    Select(String, String),
79    /// A text editor action (id, action).
80    TextEditorAction(String, text_editor::Action),
81    /// A markdown link was clicked.
82    MarkdownUrl(markdown::Uri),
83    /// A message arrived from the stdin reader (or stdin closed).
84    Stdin(StdinEvent),
85    /// No-op: used as return value for fire-and-forget tasks (font loads, etc.)
86    NoOp,
87    /// A keyboard key was pressed (full event data).
88    KeyPressed(KeyEventData),
89    /// A keyboard key was released (full event data).
90    KeyReleased(KeyEventData),
91    /// Keyboard modifiers changed (modifiers, captured).
92    ModifiersChanged(iced::keyboard::Modifiers, bool),
93    // -- IME events --
94    /// IME session opened (captured).
95    ImeOpened(bool),
96    /// IME preedit text updated (composing text, optional cursor range, captured).
97    ImePreedit(String, Option<std::ops::Range<usize>>, bool),
98    /// IME committed final text (text, captured).
99    ImeCommit(String, bool),
100    /// IME session closed (captured).
101    ImeClosed(bool),
102    /// A window close was requested by the user (WM close button).
103    WindowCloseRequested(window::Id),
104    /// A window was actually closed by iced.
105    WindowClosed(window::Id),
106    /// A new window was opened (iced_id, toddy_id).
107    WindowOpened(window::Id, String),
108    // -- Mouse events --
109    /// Cursor moved to (x, y) in a window (position, window_id, captured).
110    CursorMoved(Point, window::Id, bool),
111    /// Cursor entered a window (window_id, captured).
112    CursorEntered(window::Id, bool),
113    /// Cursor left a window (window_id, captured).
114    CursorLeft(window::Id, bool),
115    /// Mouse button pressed (button, window_id, captured).
116    MouseButtonPressed(iced::mouse::Button, window::Id, bool),
117    /// Mouse button released (button, window_id, captured).
118    MouseButtonReleased(iced::mouse::Button, window::Id, bool),
119    /// Mouse wheel scrolled (delta, window_id, captured).
120    WheelScrolled(iced::mouse::ScrollDelta, window::Id, bool),
121    // -- Touch events --
122    /// Touch finger pressed (finger, position, window_id, captured).
123    FingerPressed(iced::touch::Finger, Point, window::Id, bool),
124    /// Touch finger moved (finger, position, window_id, captured).
125    FingerMoved(iced::touch::Finger, Point, window::Id, bool),
126    /// Touch finger lifted (finger, position, window_id, captured).
127    FingerLifted(iced::touch::Finger, Point, window::Id, bool),
128    /// Touch finger lost (finger, position, window_id, captured).
129    FingerLost(iced::touch::Finger, Point, window::Id, bool),
130    // -- Window lifecycle events --
131    /// A window event from iced (window_id, event).
132    WindowEvent(window::Id, window::Event),
133    // -- System / animation events --
134    /// Animation frame with timestamp.
135    AnimationFrame(iced::time::Instant),
136    /// System theme mode changed.
137    ThemeChanged(iced::theme::Mode),
138    /// Sensor widget resize event (id, width, height).
139    SensorResize(String, f32, f32),
140    /// Canvas interaction event (press, release, move).
141    CanvasEvent {
142        id: String,
143        kind: String,
144        x: f32,
145        y: f32,
146        extra: String,
147    },
148    /// Canvas scroll event.
149    CanvasScroll {
150        id: String,
151        cursor_x: f32,
152        cursor_y: f32,
153        delta_x: f32,
154        delta_y: f32,
155    },
156    /// PaneGrid pane was resized (grid_id, resize_event).
157    PaneResized(String, iced::widget::pane_grid::ResizeEvent),
158    /// PaneGrid pane was dragged (grid_id, drag_event).
159    PaneDragged(String, iced::widget::pane_grid::DragEvent),
160    /// PaneGrid pane was clicked (grid_id, pane).
161    PaneClicked(String, iced::widget::pane_grid::Pane),
162    /// PaneGrid focus cycle via F6 (grid_id, target_pane).
163    PaneFocusCycle(String, iced::widget::pane_grid::Pane),
164    /// Scrollable viewport changed.
165    ScrollEvent(String, ScrollViewport),
166    /// Text was pasted into a text_input (id, pasted_text).
167    Paste(String, String),
168    /// ComboBox option was hovered (combo_id, option_value).
169    OptionHovered(String, String),
170    /// MouseArea simple event (id, kind). Kind is one of: right_press,
171    /// right_release, middle_release, double_click, enter, exit.
172    MouseAreaEvent(String, String),
173    /// MouseArea cursor move event (id, x, y).
174    MouseAreaMove(String, f32, f32),
175    /// MouseArea scroll event (id, delta_x, delta_y).
176    MouseAreaScroll(String, f32, f32),
177    /// Generic widget event. Used for on_open, on_close, sort, and
178    /// other events that carry a family string and optional data.
179    Event {
180        id: String,
181        data: Value,
182        family: String,
183    },
184}
185
186/// What the stdin reader thread sends back.
187#[derive(Debug, Clone)]
188pub enum StdinEvent {
189    Message(crate::protocol::IncomingMessage),
190    Closed,
191    Warning(String),
192}
193
194// ---------------------------------------------------------------------------
195// Key serialization helpers
196// ---------------------------------------------------------------------------
197
198pub fn serialize_key(key: &iced::keyboard::Key) -> String {
199    match key {
200        iced::keyboard::Key::Named(named) => format!("{named:?}"),
201        iced::keyboard::Key::Character(c) => c.to_string(),
202        iced::keyboard::Key::Unidentified => "Unidentified".to_string(),
203    }
204}
205
206pub fn serialize_modifiers(mods: iced::keyboard::Modifiers) -> KeyModifiers {
207    KeyModifiers {
208        shift: mods.shift(),
209        ctrl: mods.control(),
210        alt: mods.alt(),
211        logo: mods.logo(),
212        command: mods.command(),
213    }
214}
215
216pub fn serialize_physical_key(physical: &iced::keyboard::key::Physical) -> String {
217    match physical {
218        iced::keyboard::key::Physical::Code(code) => format!("{code:?}"),
219        iced::keyboard::key::Physical::Unidentified(code) => {
220            format!("Unidentified({code:?})")
221        }
222    }
223}
224
225pub fn serialize_location(location: &iced::keyboard::Location) -> &'static str {
226    match location {
227        iced::keyboard::Location::Standard => "standard",
228        iced::keyboard::Location::Left => "left",
229        iced::keyboard::Location::Right => "right",
230        iced::keyboard::Location::Numpad => "numpad",
231    }
232}
233
234// ---------------------------------------------------------------------------
235// Mouse serialization helpers
236// ---------------------------------------------------------------------------
237
238pub fn serialize_mouse_button(button: &iced::mouse::Button) -> String {
239    match button {
240        iced::mouse::Button::Left => "left".to_string(),
241        iced::mouse::Button::Right => "right".to_string(),
242        iced::mouse::Button::Middle => "middle".to_string(),
243        iced::mouse::Button::Back => "back".to_string(),
244        iced::mouse::Button::Forward => "forward".to_string(),
245        iced::mouse::Button::Other(n) => format!("other_{n}"),
246    }
247}
248
249pub fn serialize_scroll_delta(delta: &iced::mouse::ScrollDelta) -> (f32, f32, &'static str) {
250    match delta {
251        iced::mouse::ScrollDelta::Lines { x, y } => (*x, *y, "lines"),
252        iced::mouse::ScrollDelta::Pixels { x, y } => (*x, *y, "pixels"),
253    }
254}