Skip to main content

editor/
editor.rs

1//! Editor mínimo: text field con char insertion, backspace, enter, ctrl+L
2//! para limpiar. Valida que el bucle Elm absorbe input de teclado.
3//!
4//! Corre con: `cargo run -p llimphi-ui --example editor --release`.
5
6use llimphi_ui::llimphi_layout::taffy::{
7    prelude::{length, percent, FlexDirection, Size, Style},
8};
9use llimphi_ui::llimphi_raster::peniko::Color;
10use llimphi_ui::llimphi_text::Alignment;
11use llimphi_ui::{App, Handle, Key, KeyEvent, KeyState, NamedKey, View};
12
13#[derive(Clone)]
14enum Msg {
15    Insert(String),
16    Backspace,
17    Clear,
18}
19
20struct Editor;
21
22impl App for Editor {
23    type Model = String;
24    type Msg = Msg;
25
26    fn title() -> &'static str {
27        "llimphi · editor"
28    }
29
30    fn init(_: &Handle<Self::Msg>) -> Self::Model {
31        String::new()
32    }
33
34    fn update(model: Self::Model, msg: Self::Msg, _: &Handle<Self::Msg>) -> Self::Model {
35        match msg {
36            Msg::Insert(s) => {
37                let mut m = model;
38                m.push_str(&s);
39                m
40            }
41            Msg::Backspace => {
42                let mut m = model;
43                m.pop();
44                m
45            }
46            Msg::Clear => String::new(),
47        }
48    }
49
50    fn on_key(_: &Self::Model, e: &KeyEvent) -> Option<Self::Msg> {
51        if e.state != KeyState::Pressed {
52            return None;
53        }
54        if e.modifiers.ctrl {
55            if let Key::Character(c) = &e.key {
56                if c.eq_ignore_ascii_case("l") {
57                    return Some(Msg::Clear);
58                }
59            }
60            return None;
61        }
62        match &e.key {
63            Key::Named(NamedKey::Backspace) => Some(Msg::Backspace),
64            Key::Named(NamedKey::Enter) => Some(Msg::Insert("\n".into())),
65            Key::Named(NamedKey::Tab) => Some(Msg::Insert("    ".into())),
66            _ => e.text.clone().map(Msg::Insert),
67        }
68    }
69
70    fn view(model: &Self::Model) -> View<Self::Msg> {
71        let body_text = if model.is_empty() {
72            "tipea algo · ctrl+L limpia · enter salto · backspace borra".to_string()
73        } else {
74            // Cursor visual al final del contenido.
75            format!("{model}\u{2588}")
76        };
77        let body_color = if model.is_empty() {
78            Color::from_rgba8(110, 130, 150, 255)
79        } else {
80            Color::from_rgba8(220, 230, 240, 255)
81        };
82
83        let body = View::new(Style {
84            size: Size {
85                width: percent(1.0_f32),
86                height: percent(1.0_f32),
87            },
88            flex_grow: 1.0,
89            ..Default::default()
90        })
91        .text_aligned(body_text, 22.0, body_color, Alignment::Start);
92
93        let status = View::new(Style {
94            size: Size {
95                width: percent(1.0_f32),
96                height: length(36.0_f32),
97            },
98            ..Default::default()
99        })
100        .fill(Color::from_rgba8(30, 36, 48, 255))
101        .text(
102            format!("{} chars", model.chars().count()),
103            16.0,
104            Color::from_rgba8(160, 180, 200, 255),
105        );
106
107        View::new(Style {
108            flex_direction: FlexDirection::Column,
109            size: Size {
110                width: percent(1.0_f32),
111                height: percent(1.0_f32),
112            },
113            gap: Size {
114                width: length(0.0_f32),
115                height: length(8.0_f32),
116            },
117            padding: llimphi_ui::llimphi_layout::taffy::Rect {
118                left: length(24.0_f32),
119                right: length(24.0_f32),
120                top: length(24.0_f32),
121                bottom: length(24.0_f32),
122            },
123            ..Default::default()
124        })
125        .fill(Color::from_rgba8(20, 24, 32, 255))
126        .children(vec![body, status])
127    }
128}
129
130fn main() {
131    llimphi_ui::run::<Editor>();
132}