Skip to main content

grafix_toolbox/gui/elements/
lineedit.rs

1use super::{util::caret as ca, *};
2
3#[derive(Default, Debug)]
4pub struct LineEdit {
5	offset: Vec2,
6	size: Vec2,
7	scale: f32,
8	caret: isize,
9	pub editing: bool,
10	pub text: CachedStr,
11}
12impl LineEdit {
13	pub fn edited(&self, r: &RenderLock) -> vec2<bool> {
14		let editing = r.focused(ref_UUID(self));
15		let r @ (_edit_started, _edited) = (!self.editing && editing, !editing && self.editing);
16		r
17	}
18	pub fn draw<'s: 'l, 'l>(&'s mut self, r: &mut RenderLock<'l>, t: &'l Theme, layout @ Surf { pos, size }: Surf, filter: Option<&'l HashSet<char>>) -> Option<&'l str> {
19		let CUR_PAD = 0.01;
20		let (id, s, font) = (ref_UUID(self), self, &t.font);
21
22		if s.text.changed() || s.size != size {
23			((s.offset, s.scale), s.size) = (u::fit_line(&s.text, font, t.font_size, size), size);
24		}
25		let Self {
26			offset, scale, ref mut caret, ref mut editing, ref mut text, ..
27		} = *s;
28
29		r.draw(Rect { pos, size, color: t.bg });
30
31		let (te @ Surf { pos, .. }, focused) = (layout.xy(offset), r.focused(id));
32		if focused {
33			let x = ca::adv(text, font, scale, (*caret, 0), CUR_PAD);
34			let Surf { pos, size } = te.x(x).size((CUR_PAD, scale));
35			r.draw(Rect { pos, size, color: t.highlight });
36		}
37
38		r.draw(Text { pos, color: t.text, scale, text, font });
39
40		let (text, edited) = (Cell::from_mut(text), !focused && *editing);
41		*editing = focused;
42		r.logic(
43			layout,
44			move |e, focused, mouse_pos| {
45				let clamp = |o| ca::set(text.bind(), (*caret + o, 0), (0, 0)).0;
46				let click = |p: Vec2| ca::at_pos(text.bind(), font, scale, 0, p.sub(pos)).0;
47				let idx = |o| ca::idx(text.bind(), (*caret, 0), (o, 0));
48				match *e {
49					OfferFocus => (),
50					MouseButton { m, .. } if m.pressed() => *caret = click(mouse_pos),
51					Keyboard { key, m } if focused && m.pressed() => match key {
52						Key::Right => *caret = u::if_ctrl(m, 10, 1).pipe(clamp),
53						Key::Left => *caret = u::if_ctrl(m, -10, -1).pipe(clamp),
54						Key::Delete if idx(0) < text.bind().len() => {
55							let i = idx(0);
56							text.mutate(|t| t.str().remove(i));
57						}
58						Key::Backspace if idx(0) > 0 => {
59							let i = idx(-1);
60							*caret = clamp(-1);
61							text.mutate(|t| t.str().remove(i));
62						}
63						Key::Return | Key::Escape => return DropFocus,
64						_ => return Pass,
65					},
66					Char { ch } if focused => {
67						let filter = filter.map(|f| f.get(&ch).is_some()).unwrap_or(true);
68						if filter {
69							let i = idx(0);
70							text.mutate(|t| t.str().insert(i, ch));
71							*caret += 1;
72						}
73					}
74					_ => return Pass,
75				}
76				Accept
77			},
78			id,
79		);
80		None.or_val(!edited, || Some(unsafe { &**text.as_ptr() }))
81	}
82}
83
84impl<'s: 'l, 'l> Lock::LineEdit<'s, 'l, '_> {
85	pub fn draw(self, g: impl Into<Surf>) -> Option<&'l str> {
86		let Self { s, r, t } = self;
87		s.draw(r, t, g.into(), None)
88	}
89}