use super::Editor;
use kas::prelude::*;
use std::fmt::{Debug, Display};
use std::marker::PhantomData;
use std::str::FromStr;
pub trait EditGuard: Sized {
type Data;
fn configure(&mut self, edit: &mut Editor, cx: &mut ConfigCx) {
let _ = (edit, cx);
}
fn update(&mut self, edit: &mut Editor, cx: &mut ConfigCx, data: &Self::Data) {
let _ = (edit, cx, data);
}
fn activate(&mut self, edit: &mut Editor, cx: &mut EventCx, data: &Self::Data) -> IsUsed {
if edit.is_editable() {
self.focus_lost(edit, cx, data);
Used
} else {
Unused
}
}
fn focus_gained(&mut self, edit: &mut Editor, cx: &mut EventCx, data: &Self::Data) {
let _ = (edit, cx, data);
}
fn focus_lost(&mut self, edit: &mut Editor, cx: &mut EventCx, data: &Self::Data) {
self.update(edit, cx, data);
}
fn edit(&mut self, edit: &mut Editor, cx: &mut EventCx, data: &Self::Data) {
let _ = (edit, cx, data);
}
}
#[autoimpl(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct DefaultGuard<A>(PhantomData<A>);
impl<A: 'static> EditGuard for DefaultGuard<A> {
type Data = A;
}
#[impl_self]
mod StringGuard {
#[autoimpl(Debug ignore self.value_fn, self.on_afl)]
pub struct StringGuard<A> {
value_fn: Box<dyn Fn(&A) -> String>,
on_afl: Option<Box<dyn Fn(&mut EventCx, &A, &str)>>,
edited: bool,
}
impl Self {
pub fn new(value_fn: impl Fn(&A) -> String + 'static) -> Self {
StringGuard {
value_fn: Box::new(value_fn),
on_afl: None,
edited: false,
}
}
pub fn with(mut self, f: impl Fn(&mut EventCx, &A, &str) + 'static) -> Self {
debug_assert!(self.on_afl.is_none());
self.on_afl = Some(Box::new(f));
self
}
pub fn with_msg<M: Debug + 'static>(self, f: impl Fn(&str) -> M + 'static) -> Self {
self.with(move |cx, _, value| cx.push(f(value)))
}
}
impl EditGuard for Self {
type Data = A;
fn update(&mut self, edit: &mut Editor, cx: &mut ConfigCx, data: &A) {
let string = (self.value_fn)(data);
edit.set_string(cx, string);
}
fn focus_lost(&mut self, edit: &mut Editor, cx: &mut EventCx, data: &A) {
if self.edited {
self.edited = false;
if let Some(ref on_afl) = self.on_afl {
on_afl(cx, data, edit.as_str());
}
} else {
self.update(edit, cx, data);
}
}
fn edit(&mut self, _: &mut Editor, _: &mut EventCx, _: &Self::Data) {
self.edited = true;
}
}
}
#[impl_self]
mod ParseGuard {
#[autoimpl(Debug ignore self.value_fn, self.on_afl)]
pub struct ParseGuard<A, T: Debug + Display + FromStr> {
parsed: Option<T>,
value_fn: Box<dyn Fn(&A) -> T>,
on_afl: Box<dyn Fn(&mut EventCx, T)>,
}
impl Self {
pub fn new<M: Debug + 'static>(
value_fn: impl Fn(&A) -> T + 'static,
on_afl: impl Fn(T) -> M + 'static,
) -> Self {
ParseGuard {
parsed: None,
value_fn: Box::new(value_fn),
on_afl: Box::new(move |cx, value| cx.push(on_afl(value))),
}
}
}
impl EditGuard for Self {
type Data = A;
fn update(&mut self, edit: &mut Editor, cx: &mut ConfigCx, data: &A) {
let value = (self.value_fn)(data);
edit.set_string(cx, format!("{value}"));
self.parsed = None;
}
fn focus_lost(&mut self, edit: &mut Editor, cx: &mut EventCx, data: &A) {
if let Some(value) = self.parsed.take() {
(self.on_afl)(cx, value);
} else {
self.update(edit, cx, data);
}
}
fn edit(&mut self, edit: &mut Editor, cx: &mut EventCx, _: &A) {
self.parsed = edit.as_str().parse().ok();
if self.parsed.is_none() {
edit.set_error(cx, Some("parse failure".into()));
}
}
}
}
#[impl_self]
mod InstantParseGuard {
#[autoimpl(Debug ignore self.value_fn, self.on_afl)]
pub struct InstantParseGuard<A, T: Debug + Display + FromStr> {
value_fn: Box<dyn Fn(&A) -> T>,
on_afl: Box<dyn Fn(&mut EventCx, T)>,
}
impl Self {
pub fn new<M: Debug + 'static>(
value_fn: impl Fn(&A) -> T + 'static,
on_afl: impl Fn(T) -> M + 'static,
) -> Self {
InstantParseGuard {
value_fn: Box::new(value_fn),
on_afl: Box::new(move |cx, value| cx.push(on_afl(value))),
}
}
}
impl EditGuard for Self {
type Data = A;
fn update(&mut self, edit: &mut Editor, cx: &mut ConfigCx, data: &A) {
let value = (self.value_fn)(data);
edit.set_string(cx, format!("{value}"));
}
fn focus_lost(&mut self, edit: &mut Editor, cx: &mut EventCx, data: &A) {
self.update(edit, cx, data);
}
fn edit(&mut self, edit: &mut Editor, cx: &mut EventCx, _: &A) {
let result = edit.as_str().parse();
if result.is_err() {
edit.set_error(cx, Some("parse failure".into()));
}
if let Ok(value) = result {
(self.on_afl)(cx, value);
}
}
}
}