use cgmath::Point2;
use std::{
cell::RefCell,
char,
default::default,
fmt::{Debug, Formatter, Result},
rc::Rc,
};
use stretch::{node::Node, style};
use crate::prelude::Singleton;
use crate::{
foundation::{
properties::LabelProperties, KeyCode, KeyEvent, MouseEvent, Signal, TextEditChangeEvent,
TextEditCommitEvent, TextEvent, TextEventType,
},
material::TextField,
rendering::backend::{WidgetRenderFactory, WidgetRenderHolder},
services::LayoutSystem,
};
use super::{Element, LabelElement, WidgetComponent};
#[derive(Default, Debug, Clone)]
struct TextFieldState {
index: i32, composition_start: i32, composition_length: i32, composition: String, edit: String, display: String, display_char: Option<char>,
text: String,
}
pub struct TextFieldElement {
component: Rc<RefCell<WidgetComponent>>,
pub label: LabelElement,
pub filter: Option<Box<dyn Fn(String, String, String) -> bool>>,
state: RefCell<TextFieldState>,
pub onchangeindex: Signal<i32>,
pub onchange: Signal<TextEditChangeEvent>,
pub oncommit: Signal<TextEditCommitEvent>,
pub renderer: Option<Rc<WidgetRenderHolder<Self>>>,
pub node: Node,
}
impl Debug for TextFieldElement {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
f.debug_struct("TextFieldElement").finish()
}
}
impl TextFieldElement {
pub fn new(widget: &TextField) -> Self {
let id = widget.key.id();
let node = LayoutSystem::new_node(style::Style { ..default() }, vec![]).unwrap();
let component = WidgetComponent::get(widget.key.id());
let label = LabelElement::new(&LabelProperties {
parent: Some(id),
x: 2.0,
y: 0.0,
mouse_input: false,
..Default::default()
});
let edit = String::new();
let index = edit.len() as i32;
let display_char = None;
let instance = Self {
component,
filter: None, state: RefCell::new(TextFieldState {
index,
composition_length: 0,
composition_start: 0,
composition: String::new(),
edit: edit.clone(),
display: String::new(),
display_char,
text: String::new(),
}),
label,
onchange: Signal::new(),
onchangeindex: Signal::new(),
oncommit: Signal::new(),
renderer: WidgetRenderFactory::global().get::<Self>(),
node,
};
instance.refresh(edit, false, true);
instance
}
#[inline]
fn refresh(&self, str: String, from_typing: bool, emit: bool) -> String {
let mut state = self.state.borrow_mut();
state.edit = str.clone();
match state.display_char {
Some(ch) => {
state.display = String::new();
for i in 0..str.len() {
state.display.push(ch);
}
}
None => {
let state = self.state.borrow();
let index = state.index as usize;
self.state.borrow_mut().display = format!(
"{}{}{}",
self.before(index),
state.composition,
self.after(index)
);
}
}
let state = self.state.borrow();
self.label.set_text(state.display.clone());
self.update_cur();
if emit {
self.onchange.emit(&TextEditChangeEvent {
text: state.edit.clone(),
display_text: state.display.clone(),
from_typing,
});
}
state.edit.clone()
}
pub fn index(&self) -> i32 {
self.state.borrow().index
}
pub fn text(&self) -> String {
self.state.borrow().edit.clone()
}
#[inline]
pub fn display_text(&self) -> String {
self.state.borrow().display.clone()
}
#[inline]
pub fn display_char(&self) -> Option<char> {
self.state.borrow().display_char
}
#[inline]
pub fn set_text(&self, value: String) {
self.state.borrow_mut().index = value.len() as i32;
self.refresh(value, false, true);
}
#[inline]
pub fn set_display_char(&mut self, value: Option<char>) {
let mut state = self.state.borrow_mut();
state.display_char = value;
self.refresh(state.edit.clone(), false, true);
self.update_cur();
}
pub fn move_cursor(&self, amount: i32) {
let state = self.state.borrow();
let mut index = state.index;
index += amount;
self.state.borrow_mut().index = index.clamp(0, state.edit.len() as i32);
self.update_cur();
}
#[inline]
pub fn cut(&self, start: usize, count: usize) -> String {
let text: String = self.after(start).chars().skip(count).collect();
self.refresh(format!("{}{}", self.before(start), text), true, true)
}
#[inline]
pub fn after(&self, cur: usize) -> String {
self.state.borrow().edit.chars().skip(cur).collect()
}
#[inline]
pub fn before(&self, cur: usize) -> String {
self.state.borrow().edit.chars().take(cur).collect()
}
#[inline]
pub fn after_display(&self, cur: usize) -> String {
self.state.borrow().display.chars().skip(cur).collect()
}
#[inline]
pub fn before_display(&self, cur: usize) -> String {
self.state.borrow().display.chars().take(cur).collect()
}
#[inline]
pub fn update_cur(&self) {
let index = self.state.borrow().index;
self.onchangeindex.emit(&index);
}
}
impl AsRef<RefCell<WidgetComponent>> for TextFieldElement {
fn as_ref(&self) -> &RefCell<WidgetComponent> {
self.component.as_ref()
}
}
impl Element for TextFieldElement {
fn destroy(&self) {
self.onchange.clear();
self.onchangeindex.clear();
self.oncommit.clear();
}
fn mousedown(&self, event: &mut MouseEvent) {
if self.contains(event.x as f32, event.y as f32) {
self.focus();
} else {
self.unfocus();
}
}
fn unfocus(&self) {
let mut state = self.state.borrow_mut();
state.composition = String::new();
state.composition_start = 0;
state.composition_length = 0;
}
fn textinput(&self, event: &mut TextEvent) {
let index = self.state.borrow().index;
let bef_str = self.before(index as usize);
let aft_str = self.after(index as usize);
let new_str = format!("{}{}{}", bef_str, event.text, aft_str);
match event.event_type {
TextEventType::Edit => {
let mut state = self.state.borrow_mut();
state.composition = event.text.clone();
state.composition_start = event.start;
state.composition_length = event.length;
self.refresh(state.edit.clone(), true, false);
}
TextEventType::Input => {
let mut state = self.state.borrow_mut();
state.composition = String::new();
state.composition_start = 0;
state.composition_length = 0;
if let Some(ref filter) = self.filter {
if !filter(event.text.clone(), new_str.clone(), state.edit.clone()) {
return;
}
}
let index = state.index;
self.state.borrow_mut().index = index + event.text.len() as i32;
self.refresh(new_str, true, true);
}
_ => {}
}
}
fn keydown(&self, event: &mut KeyEvent) {
let state = self.state.borrow();
match event.key {
KeyCode::Backspace => {
self.move_cursor(-1);
let index = self.state.borrow().index;
self.cut(index as usize, 1);
}
KeyCode::Delete => {
let index = self.state.borrow().index;
self.cut(index as usize, 1);
}
KeyCode::Left => self.move_cursor(-1),
KeyCode::Right => self.move_cursor(1),
KeyCode::Enter => self.oncommit.emit(&TextEditCommitEvent {
display_text: state.display.clone(),
text: state.edit.clone(),
}),
KeyCode::Escape | KeyCode::Tab | KeyCode::Unknown | KeyCode::Down | KeyCode::Up => {}
}
}
fn node(&self) -> Option<Node> {
Some(self.node)
}
fn relayout(&self, origin: Point2<f32>) {
let update_childs = match LayoutSystem::layout(self.node) {
Ok(layout) => {
let mut comp = self.as_ref().borrow_mut();
comp.x = layout.location.x + origin.x;
comp.y = layout.location.y + origin.y;
comp.w = layout.size.width;
comp.h = layout.size.height;
true
}
Err(e) => {
log::error!("{}", e);
false
}
};
if update_childs {
}
}
}