//! Demo de `text_area_view`: el input de texto **multilínea** con paridad real //! (mismo motor que el single-line), en vivo. //! //! Corre con: `cargo run -p llimphi-widget-text-input --example area_demo --release`. //! //! Qué mirar / probar (esto es lo que el viejo text-area de `String` plano NO //! podía — su caret era un bloque fijo al final del texto): //! - **Click en cualquier lado** coloca el caret en el renglón + columna exactos //! (glifos reales, no ancho fijo, no "el final"). //! - **↑/↓** navegan entre renglones (preservan la columna deseada); ←/→, //! Inicio/Fin, salto de palabra (Ctrl+←/→). //! - **Selección**: arrastre a través de líneas, doble-click = palabra, //! triple = renglón, Shift+flechas. //! - **Copiar/cortar/pegar** (Ctrl+C/X/V), **deshacer/rehacer** (Ctrl+Z/Y), //! **acentos/IME** (´ + a → á). //! - **Soft-wrap**: la línea larga se **envuelve** sola al ancho de la caja //! (redimensiona la ventana y se re-acomoda) — el click sigue cayendo en la //! columna correcta del renglón envuelto. //! - **Scroll vertical**: hay más líneas que renglones visibles; muévete con ↑/↓ //! más allá del borde y el bloque **scrollea** para mantener el caret a la vista. //! - **Enter** inserta salto de línea (no "submit"). use llimphi_ui::llimphi_layout::taffy::{ prelude::{length, percent, FlexDirection, Size, Style}, AlignItems, Rect, }; use llimphi_ui::llimphi_raster::peniko::Color; use llimphi_ui::llimphi_text::Alignment; use llimphi_ui::{App, Handle, KeyEvent, View}; use llimphi_widget_text_input::{ text_area_view, MemClipboard, TextAreaEvent, TextInputPalette, TextInputState, }; #[derive(Clone, Debug)] enum Msg { Area(TextAreaEvent), Tecla(KeyEvent), } struct Model { texto: TextInputState, focado: bool, clip: MemClipboard, } struct Demo; impl App for Demo { type Model = Model; type Msg = Msg; fn title() -> &'static str { "llimphi · text-area (input multilínea, paridad real)" } fn initial_size() -> (u32, u32) { (720, 460) } fn init(_: &Handle) -> Model { let mut texto = TextInputState::multiline(); texto.set_text( "Este es un text-area REAL sobre el mismo motor EditorState que el input de una línea, así que hereda selección, undo, copiar/pegar e IME — y además esta primera línea es deliberadamente larga para que veas el soft-wrap: se parte sola al ancho de la caja.\n\nClickeá en cualquier palabra: el caret cae donde haces click (renglón y columna real), no al final.\n\nArrastrá para seleccionar a través de líneas.\nDoble-click = palabra, triple = renglón.\nCtrl+C / Ctrl+X / Ctrl+V, Ctrl+Z para deshacer.\n↑/↓ navegan; hay más líneas que las visibles,\nasí que al bajar el bloque scrollea solo.\n\nEnter agrega líneas. Prueba redimensionar la\nventana: el wrap se re-acomoda al ancho nuevo.", ); Model { texto, focado: true, clip: MemClipboard::new(), } } fn on_key(model: &Model, event: &KeyEvent) -> Option { model.focado.then(|| Msg::Tecla(event.clone())) } fn ime_allowed() -> bool { true } fn update(model: Model, msg: Msg, _handle: &Handle) -> Model { let mut m = model; let mut clip = std::mem::take(&mut m.clip); match msg { Msg::Area(ev) => { if matches!(ev, TextAreaEvent::Press(..) | TextAreaEvent::Context { .. }) { m.focado = true; } m.texto.handle_area(ev, &mut clip); } Msg::Tecla(e) => { m.texto.handle_area(TextAreaEvent::Key(e), &mut clip); } } m.clip = clip; m } fn view(model: &Model) -> View { let pal = TextInputPalette::default(); let area = text_area_view( &model.texto, "Escribe algo multilínea…", model.focado, 7, &pal, Msg::Area, ); let hint = View::new(Style { size: Size { width: percent(1.0_f32), height: length(22.0_f32), }, ..Default::default() }) .text_aligned( "click = caret 2D · arrastre/doble-click = selección · ↑↓ renglones · Ctrl+C/V · Enter = salto".to_string(), 11.5, Color::from_rgba8(150, 160, 180, 255), Alignment::Start, ); View::new(Style { flex_direction: FlexDirection::Column, size: Size { width: percent(1.0_f32), height: percent(1.0_f32), }, gap: Size { width: length(0.0_f32), height: length(12.0_f32), }, padding: Rect { left: length(28.0_f32), right: length(28.0_f32), top: length(24.0_f32), bottom: length(24.0_f32), }, align_items: Some(AlignItems::STRETCH), ..Default::default() }) .fill(Color::from_rgba8(16, 19, 26, 255)) .children(vec![hint, area]) } } fn main() { llimphi_ui::run::(); }