mod layout;
mod node;
mod reconcile;
use std::sync::Arc;
pub(crate) use layout::measure_input;
pub use node::InputNode;
pub(crate) use reconcile::reconcile_input;
use crate::callback::{Callback, KeyHandler};
use crate::core::element::{Element, ElementKind};
use crate::core::event::MouseEvent;
use crate::style::{
BorderStyle, CaretShape, Color, LayoutConstraints, Length, Padding, Style, StyleSlot,
};
use crate::text::edit::TextEditEvent;
use crate::text::input::TextInput;
#[derive(Clone)]
pub struct Input {
pub(crate) value: Arc<str>,
pub(crate) cursor: usize,
pub(crate) anchor: Option<usize>,
pub(crate) placeholder: Option<Arc<str>>,
pub(crate) prefix: Option<Arc<str>>,
pub(crate) suffix: Option<Arc<str>>,
pub(crate) truncate_head: bool,
pub(crate) style: Style,
pub(crate) hover_style: StyleSlot,
pub(crate) focus_style: StyleSlot,
pub(crate) focus_content_style: Style,
pub(crate) hover_border_style: Option<BorderStyle>,
pub(crate) placeholder_style: Style,
pub(crate) focus_placeholder_style: Style,
pub(crate) prefix_style: Style,
pub(crate) focus_prefix_style: Style,
pub(crate) suffix_style: Style,
pub(crate) focus_suffix_style: Style,
pub(crate) caret_shape: CaretShape,
pub(crate) caret_color: Option<Color>,
pub(crate) selection_style: StyleSlot,
pub(crate) border: bool,
pub(crate) border_style: BorderStyle,
pub(crate) padding: Padding,
pub(crate) mask: Option<char>,
pub(crate) disabled: bool,
pub(crate) disabled_style: Style,
pub(crate) read_only: bool,
pub(crate) error: Option<Arc<str>>,
pub(crate) error_style: Style,
pub(crate) reserve_error_row: bool,
pub(crate) width: Length,
pub(crate) height: Length,
pub(crate) on_change: Option<Callback<InputEvent>>,
pub(crate) on_edit: Option<Callback<TextEditEvent>>,
pub(crate) on_click: Option<Callback<MouseEvent>>,
pub(crate) on_key: Option<KeyHandler>,
pub(crate) key_interceptor: Option<KeyHandler>,
pub(crate) focusable: bool,
pub(crate) tab_order: bool,
}
impl Input {
pub fn new(value: impl Into<Arc<str>>) -> Self {
let value = value.into();
let cursor = value.len();
Self {
value,
cursor,
anchor: None,
placeholder: None,
prefix: None,
suffix: None,
truncate_head: false,
style: Style::default(),
hover_style: StyleSlot::Inherit,
focus_style: StyleSlot::Inherit,
focus_content_style: Style::default(),
hover_border_style: None,
placeholder_style: Style::default(),
focus_placeholder_style: Style::default(),
prefix_style: Style::default(),
focus_prefix_style: Style::default(),
suffix_style: Style::default(),
focus_suffix_style: Style::default(),
caret_shape: CaretShape::default(),
caret_color: None,
selection_style: StyleSlot::Inherit,
border: true,
border_style: BorderStyle::Plain,
padding: Padding {
left: 1,
right: 1,
top: 0,
bottom: 0,
},
mask: None,
disabled: false,
disabled_style: Style::default(),
read_only: false,
error: None,
error_style: Style::default(),
reserve_error_row: false,
width: Length::Flex(1),
height: Length::Auto,
on_change: None,
on_edit: None,
on_click: None,
on_key: None,
key_interceptor: None,
focusable: true,
tab_order: true,
}
}
pub fn bound(state: &TextInput) -> Self {
Self::new("").bind(state)
}
pub fn cursor(mut self, cursor: usize) -> Self {
self.cursor = cursor;
self
}
pub fn anchor(mut self, anchor: Option<usize>) -> Self {
self.anchor = anchor;
self
}
pub fn bind(mut self, state: &TextInput) -> Self {
self.value = state.text().into();
self.cursor = state.cursor();
self.anchor = state.anchor();
self
}
pub fn placeholder(mut self, placeholder: impl Into<Arc<str>>) -> Self {
self.placeholder = Some(placeholder.into());
self
}
pub fn prefix(mut self, prefix: impl Into<Arc<str>>) -> Self {
self.prefix = Some(prefix.into());
self
}
pub fn suffix(mut self, suffix: impl Into<Arc<str>>) -> Self {
self.suffix = Some(suffix.into());
self
}
pub fn truncate_head(mut self, truncate_head: bool) -> Self {
self.truncate_head = truncate_head;
self
}
pub fn style(mut self, style: Style) -> Self {
self.style = style;
self
}
pub fn hover_style(mut self, style: Style) -> Self {
self.hover_style = StyleSlot::Replace(style);
self
}
pub fn extend_hover_style(mut self, style: Style) -> Self {
self.hover_style = StyleSlot::Extend(style);
self
}
pub fn inherit_hover_style(mut self) -> Self {
self.hover_style = StyleSlot::Inherit;
self
}
pub fn hover_style_slot(mut self, slot: StyleSlot) -> Self {
self.hover_style = slot;
self
}
pub fn focus_style(mut self, style: Style) -> Self {
self.focus_style = StyleSlot::Replace(style);
self
}
pub fn extend_focus_style(mut self, style: Style) -> Self {
self.focus_style = StyleSlot::Extend(style);
self
}
pub fn inherit_focus_style(mut self) -> Self {
self.focus_style = StyleSlot::Inherit;
self
}
pub fn focus_style_slot(mut self, slot: StyleSlot) -> Self {
self.focus_style = slot;
self
}
pub fn focus_content_style(mut self, style: Style) -> Self {
self.focus_content_style = style;
self
}
pub fn hover_border_style(mut self, border_style: BorderStyle) -> Self {
self.hover_border_style = Some(border_style);
self
}
pub fn placeholder_style(mut self, style: Style) -> Self {
self.placeholder_style = style;
self
}
pub fn focus_placeholder_style(mut self, style: Style) -> Self {
self.focus_placeholder_style = style;
self
}
pub fn prefix_style(mut self, style: Style) -> Self {
self.prefix_style = style;
self
}
pub fn focus_prefix_style(mut self, style: Style) -> Self {
self.focus_prefix_style = style;
self
}
pub fn suffix_style(mut self, style: Style) -> Self {
self.suffix_style = style;
self
}
pub fn focus_suffix_style(mut self, style: Style) -> Self {
self.focus_suffix_style = style;
self
}
pub fn caret_shape(mut self, shape: CaretShape) -> Self {
self.caret_shape = shape;
self
}
pub fn caret_color(mut self, color: Color) -> Self {
self.caret_color = Some(color);
self
}
pub fn selection_style(mut self, style: Style) -> Self {
self.selection_style = StyleSlot::Replace(style);
self
}
pub fn extend_selection_style(mut self, style: Style) -> Self {
self.selection_style = StyleSlot::Extend(style);
self
}
pub fn inherit_selection_style(mut self) -> Self {
self.selection_style = StyleSlot::Inherit;
self
}
pub fn selection_style_slot(mut self, slot: StyleSlot) -> Self {
self.selection_style = slot;
self
}
pub fn border(mut self, border: bool) -> Self {
self.border = border;
self
}
pub fn border_style(mut self, border_style: BorderStyle) -> Self {
self.border_style = border_style;
self
}
pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
self.padding = padding.into();
self
}
pub fn mask(mut self, mask: Option<char>) -> Self {
self.mask = mask;
self
}
pub fn width(mut self, width: Length) -> Self {
self.width = width;
self
}
pub fn height(mut self, height: Length) -> Self {
self.height = height;
self
}
pub fn on_change(mut self, cb: Callback<InputEvent>) -> Self {
self.on_change = Some(cb);
self
}
pub fn on_edit(mut self, cb: Callback<TextEditEvent>) -> Self {
self.on_edit = Some(cb);
self
}
pub fn on_click(mut self, cb: Callback<MouseEvent>) -> Self {
self.on_click = Some(cb);
self
}
pub fn on_key(mut self, handler: KeyHandler) -> Self {
self.on_key = Some(handler);
self
}
pub fn key_interceptor(mut self, handler: KeyHandler) -> Self {
self.key_interceptor = Some(handler);
self
}
pub fn disabled(mut self, disabled: bool) -> Self {
self.disabled = disabled;
self
}
pub fn disabled_style(mut self, style: Style) -> Self {
self.disabled_style = style;
self
}
pub fn read_only(mut self, read_only: bool) -> Self {
self.read_only = read_only;
self
}
pub fn error<S>(mut self, message: Option<S>) -> Self
where
S: Into<Arc<str>>,
{
self.error = message.map(Into::into);
self
}
pub fn error_style(mut self, style: Style) -> Self {
self.error_style = style;
self
}
pub fn reserve_error_row(mut self, reserve_error_row: bool) -> Self {
self.reserve_error_row = reserve_error_row;
self
}
pub fn focusable(mut self, focusable: bool) -> Self {
self.focusable = focusable;
self
}
pub fn tab_order(mut self, tab_order: bool) -> Self {
self.tab_order = tab_order;
self
}
}
impl From<Input> for Element {
fn from(value: Input) -> Self {
let mut layout = LayoutConstraints::default();
if value.focusable {
let (min_w, min_h) = measure_input(&value);
layout.focus_min_w = min_w;
layout.min_h = Length::Px(min_h);
} else {
let (_, min_h) = measure_input(&value);
layout.min_h = Length::Px(min_h);
}
Element::new(ElementKind::Input(Box::new(value))).with_layout(layout)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct InputEvent {
pub value: Arc<str>,
pub cursor: usize,
pub anchor: Option<usize>,
}
impl InputEvent {
pub fn apply_to(&self, state: &mut TextInput) {
state.core.text = self.value.to_string();
state.core.cursor = crate::utils::text::clamp_cursor(&state.core.text, self.cursor);
state.core.anchor = self
.anchor
.map(|anchor| crate::utils::text::clamp_cursor(&state.core.text, anchor));
}
}
impl crate::layout::hash::LayoutHash for Input {
fn layout_hash(
&self,
hasher: &mut impl std::hash::Hasher,
_recurse: &dyn Fn(&Element) -> Option<u64>,
) -> Option<()> {
use std::hash::Hash;
self.width.hash(hasher);
self.height.hash(hasher);
self.border.hash(hasher);
self.padding.hash(hasher);
self.focusable.hash(hasher);
self.value.hash(hasher);
self.placeholder.hash(hasher);
self.prefix.hash(hasher);
self.suffix.hash(hasher);
self.error.hash(hasher);
self.reserve_error_row.hash(hasher);
Some(())
}
}