use minui::prelude::*;
use minui::widgets::{TextInput, TextInputState};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Focus {
Plain,
Wrapped,
}
struct State {
focus: Focus,
plain: TextInputState,
wrapped: TextInputState,
last_submit: String,
mouse_down: bool,
dragging: bool,
}
fn main() -> minui::Result<()> {
let mut plain = TextInputState::new();
plain.set_focused(true);
let mut wrapped = TextInputState::new();
wrapped.set_focused(false);
let initial = State {
focus: Focus::Plain,
plain,
wrapped,
last_submit: String::from("(press Enter to submit focused field)"),
mouse_down: false,
dragging: false,
};
let mut app = App::new(initial)?;
app.window_mut().mouse_mut().set_movement_tracking(false);
app.run(
|state, event| {
if matches!(event, Event::MouseMove { .. } | Event::Unknown) {
return true;
}
if matches!(event, Event::Character('q')) {
return false;
}
if matches!(event, Event::Tab) {
toggle_focus(state);
return true;
}
match event {
Event::MouseClick { x, y, button: _ } => {
state.mouse_down = true;
state.dragging = false;
let plain_hit = hit_test_field(&state.plain, x, y);
let wrapped_hit = hit_test_field(&state.wrapped, x, y);
if !plain_hit && !wrapped_hit {
state.mouse_down = false;
state.dragging = false;
return true;
}
if plain_hit {
set_focus(state, Focus::Plain);
state.plain.click_set_cursor(x);
return true;
}
if wrapped_hit {
set_focus(state, Focus::Wrapped);
state.wrapped.click_set_cursor(x);
return true;
}
}
Event::MouseDrag { x, y: _, button: _ } => {
if !state.mouse_down {
return true;
}
state.dragging = true;
match state.focus {
Focus::Plain => {
state.plain.drag_select_to(x);
return true;
}
Focus::Wrapped => {
state.wrapped.drag_select_to(x);
return true;
}
}
}
Event::MouseRelease { x, y: _, button: _ } => {
if state.mouse_down && state.dragging {
match state.focus {
Focus::Plain => state.plain.drag_select_to(x),
Focus::Wrapped => state.wrapped.drag_select_to(x),
}
}
state.mouse_down = false;
state.dragging = false;
return true;
}
_ => {}
}
if matches!(event, Event::Enter) {
state.last_submit = match state.focus {
Focus::Plain => format!("plain: {}", state.plain.text()),
Focus::Wrapped => format!("wrapped: {}", state.wrapped.text()),
};
return true;
}
let consumed = match state.focus {
Focus::Plain => state.plain.handle_event(event),
Focus::Wrapped => state.wrapped.handle_event(event),
};
consumed
},
|state, window| {
let (w, h) = window.get_size();
window.clear_cursor_request();
let margin: u16 = 2;
let row_gap: u16 = 2;
window.write_str(
0,
0,
"TextInput Demo — click fields, type, Enter to submit, Tab to switch, q to quit",
)?;
let field_w = w.saturating_sub(margin * 2);
let plain_y = 2;
let plain = TextInput::new()
.with_position(margin, plain_y)
.with_width(field_w)
.with_border(true)
.with_placeholder("Plain input (click to focus)…");
let wrapped_container_y = plain_y + row_gap + 2;
let container_h: u16 = 3;
let container = Container::new()
.with_position_and_size(margin, wrapped_container_y, field_w, container_h)
.with_border()
.with_border_chars(BorderChars::single_line())
.with_border_color(ColorPair::new(Color::LightCyan, Color::Transparent))
.with_title("Wrapped in Container")
.with_title_alignment(TitleAlignment::Left)
.with_padding(minui::widgets::ContainerPadding::uniform(0));
let output_y = h.saturating_sub(3);
let output_w = w.saturating_sub(margin * 2);
let output_prefix = "Last submit: ";
let output_text = format!("{}{}", output_prefix, state.last_submit);
let output_line = fit_to_cells(&output_text, output_w, TabPolicy::SingleCell, true);
plain.draw(window, &mut state.plain)?;
container.draw(window)?;
let inner_x = margin + 1;
let inner_y = wrapped_container_y + 1;
let inner_w = field_w.saturating_sub(2);
let wrapped = TextInput::new()
.with_position(inner_x, inner_y)
.with_width(inner_w)
.with_border(false)
.with_placeholder("Wrapped input (click to focus)…");
wrapped.draw(window, &mut state.wrapped)?;
window.write_str_colored(
output_y.saturating_sub(1),
margin,
"─ Output ─",
ColorPair::new(Color::Yellow, Color::Transparent),
)?;
window.write_str_colored(
output_y,
margin,
&output_line,
ColorPair::new(Color::LightGray, Color::Transparent),
)?;
window.write_str_colored(
output_y + 1,
margin,
"Tip: Tab switches focus • Enter copies focused text here",
ColorPair::new(Color::DarkGray, Color::Transparent),
)?;
window.end_frame()?;
Ok(())
},
)?;
Ok(())
}
fn toggle_focus(state: &mut State) {
let next = match state.focus {
Focus::Plain => Focus::Wrapped,
Focus::Wrapped => Focus::Plain,
};
set_focus(state, next);
}
fn set_focus(state: &mut State, focus: Focus) {
state.focus = focus;
state.plain.set_focused(focus == Focus::Plain);
state.wrapped.set_focused(focus == Focus::Wrapped);
}
fn hit_test_field(field: &TextInputState, x: u16, y: u16) -> bool {
y == field.last_y && x >= field.last_x && x < field.last_x.saturating_add(field.last_w)
}