use super::*;
use util::{state::{AtomState, AtomStateMut}, caption::CaptionMut, LocalGlyphCache};
use state::TBState;
use std::sync::Arc;
use validation::*;
pub trait ITextBox<E> where E: Env {
}
impl<'w,E,Text,Scroll,Curs,CursorStickX,GlyphCache> ITextBox<E> for TextBox<'w,E,Text,Scroll,Curs,CursorStickX,GlyphCache> where
E: Env,
Text: 'w,
Scroll: 'w,
Curs: 'w,
CursorStickX: 'w,
GlyphCache: 'w,
{
}
pub trait ITextBoxMut<E>: ITextBox<E> where E: Env {
fn insert_text(&mut self, t: &str, ctx: &mut E::Context);
fn remove_selection_or_n(&mut self, n: u32, ctx: &mut E::Context);
fn remove_selection(&mut self, ctx: &mut E::Context) -> bool;
fn move_cursor_x(&mut self, o: i32, skip_unselect: bool, ctx: &mut E::Context);
fn move_cursor_y(&mut self, o: i32, skip_unselect: bool, ctx: &mut E::Context, widget_bounds: &Bounds);
fn _m(&mut self, mouse_down: Option<MouseDown<E>>, mouse_pressed: bool, mouse: Offset, b: Bounds, ctx: &mut E::Context);
fn scroll_to_cursor(&mut self, ctx: &mut E::Context, b: &Bounds);
}
impl<'w,E,Text,Scroll,Curs,CursorStickX,GlyphCache> ITextBoxMut<E> for TextBox<'w,E,Text,Scroll,Curs,CursorStickX,GlyphCache> where
E: Env,
ERenderer<E>: RenderStdWidgets<E>,
EEvent<E>: StdVarSup<E>,
E::Context: CtxStdState<E> + CtxClipboardAccess<E>,
Text: CaptionMut<E>+ValidationMut<E>+'w,
Scroll: AtomStateMut<E,(u32,u32)>,
Curs: AtomStateMut<E,Cursor>,
CursorStickX: AtomStateMut<E,Option<u32>>,
GlyphCache: AtomStateMut<E,LocalGlyphCache<E>>+Clone,
{
fn insert_text(&mut self, s: &str, ctx: &mut E::Context) {
let mut cursor = self.cursor.get(ctx);
if cursor.is_selection() {
cursor.del_selection(&mut self.text);
}
self.text.push(cursor.caret as usize,&s);
cursor.unselect_add(s.len() as u32,false);
cursor.limit(self.text.len() as u32);
self.cursor.set(cursor,ctx);
self.cursor_stick_x.set(None,ctx);
}
fn remove_selection_or_n(&mut self, n: u32, ctx: &mut E::Context) {
if self.remove_selection(ctx) {return;}
let mut cursor = self.cursor.get(ctx);
self.text.pop_left(cursor.caret as usize,n as usize);
cursor.unselect_sub(n,false);
self.cursor.set(cursor,ctx);
self.cursor_stick_x.set(None,ctx);
}
fn remove_selection(&mut self, ctx: &mut E::Context) -> bool {
let mut cursor = self.cursor.get(ctx);
if cursor.is_selection() {
cursor.del_selection(&mut self.text);
self.cursor.set(cursor,ctx);
self.cursor_stick_x.set(None,ctx);
true
}else{
false
}
}
fn move_cursor_x(&mut self, o: i32, skip_unselect: bool, ctx: &mut E::Context) {
let mut cursor = self.cursor.get(ctx);
cursor.unselect_addi(o,skip_unselect);
self.cursor.set(cursor,ctx);
self.cursor_stick_x.set(None,ctx);
}
fn move_cursor_y(&mut self, o: i32, skip_unselect: bool, ctx: &mut E::Context, b: &Bounds) {
let g = self.glyphs2(ctx);
let mut s = TBState::<E>::retrieve(&self.text,g,&self.scroll,&self.cursor,ctx,&b);
if s.glyphs.line_count() != 0 {
let (_,mut line) = s.glyphs.coord_of(s.cursor.caret).unwrap();
let (mut dx,_) = s.cursor_display_pos(s.cursor.caret).unwrap();
let mut new_stick_x = self.cursor_stick_x.get(ctx);
if new_stick_x.is_none() {
new_stick_x = Some(dx);
}
dx = dx.max(new_stick_x.unwrap());
line = (line as i32 +o).max(0).min(s.glyphs.line_count() as i32 -1) as u32;
let x = s.cursor_pos_reverse_line_centric(line,dx as i32).unwrap();
let i = s.glyphs.at_coord((x,line)).unwrap();
s.cursor.caret = i;
if !ctx.state().is_pressed(&[EEKey::<E>::CTRL]).is_some() {
s.cursor.select = s.cursor.caret;
}
self.cursor.set(s.cursor,ctx);
self.cursor_stick_x.set(new_stick_x,ctx);
}
}
fn _m(&mut self, mouse_down: Option<MouseDown<E>>, mouse_pressed: bool, mouse: Offset, b: Bounds, ctx: &mut E::Context) {
let g = self.glyphs2(ctx);
let mut s = TBState::<E>::retrieve(&self.text,g,&self.scroll,&self.cursor,ctx,&b);
let mut tpos = mouse - b.off + Offset::from(s.off);
tpos.y += s.glyphs.line_ascent() as i32;
if let Some(ee) = mouse_down {
s.cursor.select = s.cursor_pos_reverse(tpos);
s.cursor.caret = s.cursor.select;
} else if mouse_pressed {
s.cursor.caret = s.cursor_pos_reverse(tpos);
}
assert!(s.cursor.caret < s.glyphs.chars());
self.cursor.set(s.cursor,ctx);
self.cursor_stick_x.set(None,ctx);
}
fn scroll_to_cursor(&mut self, ctx: &mut E::Context, b: &Bounds) {
let g = self.glyphs2(ctx);
let s = TBState::<E>::retrieve(&self.text,g,&self.scroll,&self.cursor,ctx,&b);
if let Some(c) = s.cursor_display_pos(s.cursor.caret) {
let cb = Bounds::from_xywh(c.0 as i32, c.1 as i32 - s.glyphs.line_ascent() as i32, 2, s.glyphs.line_height());
let mut vb = Bounds{
off: s.off.into(),
size: b.size,
};
vb.shift_to_fit(&cb);
let off = (vb.off.x as u32, vb.off.y as u32);
self.scroll.set(off,ctx);
}
}
}
traitcast_for!(ITextBox<E>;ITextBoxMut<E>);
impl<'w,E,Text,Scroll,Curs,CursorStickX,GlyphCache> TextBox<'w,E,Text,Scroll,Curs,CursorStickX,GlyphCache> where
E: Env,
ERenderer<E>: RenderStdWidgets<E>,
EEvent<E>: StdVarSup<E>,
E::Context: CtxStdState<E> + CtxClipboardAccess<E>,
Text: Caption<E>+Validation<E>+'w,
Scroll: AtomState<E,(u32,u32)>,
Curs: AtomState<E,Cursor>,
CursorStickX: AtomState<E,Option<u32>>,
GlyphCache: AtomState<E,LocalGlyphCache<E>>+Clone,
{
pub(crate) fn glyphs(&self, mut l: Link<E>) -> Arc<ESGlyphs<E>> {
if let Some((v,c)) = self.glyph_cache.get(l.ctx) {
if self.text.valid(&c) {
return v;
}
}
let text = self.text.caption();
let glyphs = Arc::new(ESGlyphs::<E>::generate(text.as_ref(),(20.0,20.0),l.ctx));
let g = glyphs.refc();
l.mutate_closure(Box::new(move |mut w,ctx,_| {
let vali = w.traitcast_mut::<dyn ValidationMut<E>>().unwrap();
let key = vali.validate();
let cache = w.traitcast_mut::<dyn AtomStateMut<E,LocalGlyphCache<E>>>().unwrap();
cache.set( Some((g,key)) ,ctx);
}));
glyphs
}
}
impl<'w,E,Text,Scroll,Curs,CursorStickX,GlyphCache> TextBox<'w,E,Text,Scroll,Curs,CursorStickX,GlyphCache> where
E: Env,
ERenderer<E>: RenderStdWidgets<E>,
EEvent<E>: StdVarSup<E>,
E::Context: CtxStdState<E> + CtxClipboardAccess<E>,
Text: CaptionMut<E>+ValidationMut<E>+'w,
Scroll: AtomStateMut<E,(u32,u32)>,
Curs: AtomStateMut<E,Cursor>,
CursorStickX: AtomStateMut<E,Option<u32>>,
GlyphCache: AtomStateMut<E,LocalGlyphCache<E>>+Clone,
{
pub(crate) fn glyphs2(&mut self, ctx: &mut E::Context) -> Arc<ESGlyphs<E>> {
if let Some((v,c)) = self.glyph_cache.get(ctx) {
if self.text.valid(&c) {
return v;
}
}
let text = self.text.caption();
let glyphs = Arc::new(ESGlyphs::<E>::generate(text.as_ref(),(20.0,20.0),ctx));
let key = self.text.validate();
self.glyph_cache.set( Some((glyphs.refc(),key)),ctx);
glyphs
}
}