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, Clone, PartialEq)]
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, Clone, PartialEq)]
pub struct InputProps {
pub theme: Option<InputThemeWith>,
pub value: String,
pub onchange: EventHandler<String>,
#[props(default = InputMode::Shown, into)]
pub mode: InputMode,
}
#[allow(non_snake_case)]
pub fn Input(
InputProps {
theme,
value,
onchange,
mode,
}: InputProps,
) -> Element {
let platform = use_platform();
let status = use_signal(InputStatus::default);
let mut editable = use_editable(
|| EditableConfig::new(value.to_string()),
EditableMode::MultipleLinesSingleEditor,
);
let theme = use_applied_theme!(&theme, input);
let focus = use_focus();
if &value != editable.editor().read().rope() {
editable.editor_mut().write().set(&value);
}
let text = match mode {
InputMode::Hidden(ch) => ch.to_string().repeat(value.len()),
InputMode::Shown => value.clone(),
};
use_drop({
to_owned![status, platform];
move || {
if *status.peek() == InputStatus::Hovering {
platform.set_cursor(CursorIcon::default());
}
}
});
let onkeydown = {
to_owned![editable, focus];
move |e: Event<KeyboardData>| {
if focus.is_focused() && e.data.key != Key::Enter {
editable.process_event(&EditableEvent::KeyDown(e.data));
onchange.call(editable.editor().peek().to_string());
}
}
};
let onmousedown = {
to_owned![editable, focus];
move |e: MouseEvent| {
editable.process_event(&EditableEvent::MouseDown(e.data, 0));
focus.focus();
}
};
let onmouseover = {
to_owned![editable];
move |e: MouseEvent| {
editable.process_event(&EditableEvent::MouseOver(e.data, 0));
}
};
let onmouseenter = {
to_owned![platform, status];
move |_| {
platform.set_cursor(CursorIcon::Text);
*status.write() = InputStatus::Hovering;
}
};
let onmouseleave = {
to_owned![platform, status];
move |_| {
platform.set_cursor(CursorIcon::default());
*status.write() = InputStatus::default();
}
};
let onglobalclick = {
to_owned![editable, focus];
move |_| match *status.read() {
InputStatus::Idle if focus.is_focused() => {
focus.unfocus();
}
InputStatus::Hovering => {
editable.process_event(&EditableEvent::Click);
}
_ => {}
}
};
let focus_id = focus.attribute();
let cursor_reference = editable.cursor_attr();
let highlights = editable.highlights_attr(0);
let (background, cursor_char) = if focus.is_focused() {
(
theme.hover_background,
editable.editor().read().cursor_pos().to_string(),
)
} else {
(theme.background, "none".to_string())
};
let InputTheme {
border_fill,
width,
margin,
font_theme: FontTheme { color },
..
} = theme;
rsx!(
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,
focus_id,
role: "textInput",
main_align: "center",
paragraph {
margin: "8 12",
onkeydown,
onglobalclick,
onmouseenter,
onmouseleave,
onmousedown,
onmouseover,
width: "100%",
cursor_id: "0",
cursor_index: "{cursor_char}",
cursor_mode: "editable",
cursor_color: "{color}",
max_lines: "1",
highlights,
text {
"{text}"
}
}
}
)
}