use dioxus::prelude::*;
use freya_elements::elements as dioxus_elements;
use freya_elements::events::keyboard::Key;
use freya_elements::events::{KeyboardData, MouseEvent};
use freya_hooks::use_platform;
use freya_hooks::{
use_applied_theme, use_editable, use_focus, EditableConfig, EditableEvent, EditableMode,
FontTheme, InputTheme, InputThemeWith, TextEditor,
};
use winit::window::CursorIcon;
#[derive(Default)]
pub enum InputMode {
#[default]
Shown,
Hidden(char),
}
impl InputMode {
pub fn new_password() -> Self {
Self::Hidden('*')
}
}
#[derive(Debug, Default, PartialEq, Clone, Copy)]
pub enum InputStatus {
#[default]
Idle,
Hovering,
}
#[derive(Props)]
pub struct InputProps<'a> {
#[props(optional)]
pub theme: Option<InputThemeWith>,
pub value: String,
pub onchange: EventHandler<'a, String>,
#[props(default = InputMode::Shown, into)]
pub mode: InputMode,
}
#[allow(non_snake_case)]
pub fn Input<'a>(cx: Scope<'a, InputProps<'a>>) -> Element {
let platform = use_platform(cx);
let status = use_ref(cx, InputStatus::default);
let editable = use_editable(
cx,
|| EditableConfig::new(cx.props.value.to_string()),
EditableMode::MultipleLinesSingleEditor,
);
let theme = use_applied_theme!(cx, &cx.props.theme, input);
let focus_manager = use_focus(cx);
if &cx.props.value != editable.editor().current().rope() {
editable.editor().with_mut(|editor| {
editor.set(&cx.props.value);
});
}
let text = match cx.props.mode {
InputMode::Hidden(ch) => ch.to_string().repeat(cx.props.value.len()),
InputMode::Shown => cx.props.value.clone(),
};
use_on_destroy(cx, {
to_owned![status, platform];
move || {
if *status.read() == InputStatus::Hovering {
platform.set_cursor(CursorIcon::default());
}
}
});
let onkeydown = {
to_owned![editable, focus_manager];
move |e: Event<KeyboardData>| {
if focus_manager.is_focused() && e.data.key != Key::Enter {
editable.process_event(&EditableEvent::KeyDown(e.data));
cx.props
.onchange
.call(editable.editor().current().to_string());
}
}
};
let onmousedown = {
to_owned![editable];
move |e: MouseEvent| {
editable.process_event(&EditableEvent::MouseDown(e.data, 0));
focus_manager.focus();
}
};
let onmouseover = {
to_owned![editable];
move |e: MouseEvent| {
editable.process_event(&EditableEvent::MouseOver(e.data, 0));
}
};
let onmouseenter = {
to_owned![platform];
move |_| {
platform.set_cursor(CursorIcon::Text);
*status.write_silent() = InputStatus::Hovering;
}
};
let onmouseleave = move |_| {
platform.set_cursor(CursorIcon::default());
*status.write_silent() = InputStatus::default();
};
let onglobalclick = {
to_owned![editable];
move |_| match *status.read() {
InputStatus::Idle if focus_manager.is_focused() => {
focus_manager.unfocus();
}
InputStatus::Hovering => {
editable.process_event(&EditableEvent::Click);
}
_ => {}
}
};
let cursor_attr = editable.cursor_attr(cx);
let highlights_attr = editable.highlights_attr(cx, 0);
let (background, cursor_char) = if focus_manager.is_focused() {
(
theme.hover_background,
editable.editor().cursor_pos().to_string(),
)
} else {
(theme.background, "none".to_string())
};
let InputTheme {
border_fill,
width,
margin,
font_theme: FontTheme { color },
..
} = theme;
render!(
rect {
width: "{width}",
direction: "vertical",
color: "{color}",
background: "{background}",
border: "1 solid {border_fill}",
shadow: "0 3 15 0 rgb(0, 0, 0, 0.3)",
corner_radius: "10",
margin: "{margin}",
cursor_reference: cursor_attr,
main_align: "center",
paragraph {
margin: "8 12",
onkeydown: onkeydown,
onglobalclick: onglobalclick,
onmouseenter: onmouseenter,
onmouseleave: onmouseleave,
onmousedown: onmousedown,
onmouseover: onmouseover,
width: "100%",
cursor_id: "0",
cursor_index: "{cursor_char}",
cursor_mode: "editable",
cursor_color: "{color}",
max_lines: "1",
highlights: highlights_attr,
text {
"{text}"
}
}
}
)
}