embedded_ui/kit/focus_input.rs
1// use core::ops::Range;
2
3// use embedded_text::TextBox;
4
5// use crate::{
6// el::ElId,
7// event::{Capture, CommonEvent, Event, Propagate},
8// layout::Layout,
9// render::Renderer,
10// size::{Length, Size},
11// state::{State, StateTag},
12// style::component_style,
13// ui::UiCtx,
14// value::Value,
15// widget::Widget,
16// };
17
18// #[derive(Clone, Copy)]
19// struct FocusInputState {
20// is_active: bool,
21// is_pressed: bool,
22// }
23
24// impl Default for FocusInputState {
25// fn default() -> Self {
26// Self { is_active: false, is_pressed: false }
27// }
28// }
29
30// // const ALPHABET: &[char] = &[
31
32// // ];
33
34// #[derive(Clone, Copy)]
35// pub enum FocusInputStatus {
36// Normal,
37// Focused,
38// Pressed,
39// Active,
40// }
41
42// component_style! {
43// pub FocusInputStyle: FocusInputStyler(FocusInputStatus) {
44// background: background,
45// border: border,
46// }
47// }
48
49// pub struct FocusInput<'a, Message, R, S>
50// where
51// R: Renderer,
52// S: FocusInputStyler<R::Color>,
53// {
54// id: ElId,
55// size: Size<Length>,
56// // length: usize,
57// value: Value<String>,
58// position: usize,
59// on_change: Option<Box<dyn Fn(&str) -> Message + 'a>>,
60// class: S::Class<'a>,
61// }
62
63// impl<'a, Message, R, S> FocusInput<'a, Message, R, S>
64// where
65// R: Renderer,
66// S: FocusInputStyler<R::Color>,
67// {
68// pub fn new(value: Value<String>) -> Self {
69// Self {
70// id: ElId::unique(),
71// size: Size::fill(),
72// // length: 128,
73// value,
74// position: 0,
75// on_change: None,
76// class: S::default(),
77// }
78// }
79
80// // pub fn length(mut self, length: usize) -> Self {
81// // self.length = length;
82// // self
83// // }
84
85// pub fn width(mut self, width: impl Into<Length>) -> Self {
86// self.size.width = width.into();
87// self
88// }
89
90// pub fn height(mut self, height: impl Into<Length>) -> Self {
91// self.size.height = height.into();
92// self
93// }
94
95// // Helpers //
96// fn status<E: Event>(&self, ctx: &UiCtx<Message>, state: &FocusInputState) -> FocusInputStatus {
97// match (UiCtx::is_focused::<R, E, S>(&ctx, self), state) {
98// (_, FocusInputState { is_active: true, .. }) => FocusInputStatus::Active,
99// (_, FocusInputState { is_pressed: true, .. }) => FocusInputStatus::Pressed,
100// (true, FocusInputState { is_active: false, is_pressed: false }) => {
101// FocusInputStatus::Focused
102// },
103// (false, FocusInputState { is_active: false, is_pressed: false }) => {
104// FocusInputStatus::Normal
105// },
106// }
107// }
108// }
109
110// impl<'a, Message, R, E, S> Widget<Message, R, E, S> for FocusInput<'a, Message, R, S>
111// where
112// R: Renderer,
113// E: Event,
114// S: FocusInputStyler<R::Color>,
115// {
116// fn id(&self) -> Option<ElId> {
117// Some(self.id)
118// }
119
120// fn tree_ids(&self) -> Vec<ElId> {
121// vec![self.id]
122// }
123
124// fn size(&self) -> Size<Length> {
125// self.size
126// }
127
128// fn state_tag(&self) -> crate::state::StateTag {
129// StateTag::of::<FocusInputState>()
130// }
131
132// fn state(&self) -> crate::state::State {
133// State::new(FocusInputState::default())
134// }
135
136// fn state_children(&self) -> Vec<crate::state::StateNode> {
137// vec![]
138// }
139
140// fn on_event(
141// &mut self,
142// ctx: &mut UiCtx<Message>,
143// event: E,
144// state: &mut crate::state::StateNode,
145// ) -> crate::event::EventResponse<E> {
146// let focused = ctx.is_focused::<R, E, S>(self);
147// let current_state = *state.get::<FocusInputState>();
148
149// if current_state.is_active {
150// if let Some(offset) = event.as_input_letter_scroll() {
151// if self.position >= self.value.get().len() {
152// // self.value.get_mut().
153// }
154
155// let prev_char = self.value.get().chars().nth(self.position).unwrap_or(' ');
156
157// const CHAR_ASCII_RANGE: Range<u8> = 32..127;
158// const ALPHABET_SIZE: i32 =
159// CHAR_ASCII_RANGE.end as i32 - CHAR_ASCII_RANGE.start as i32;
160
161// let new_char = ((prev_char as i32 + offset % ALPHABET_SIZE + ALPHABET_SIZE)
162// % ALPHABET_SIZE) as u8 as char;
163
164// self.value.get_mut()[self.position] = new_char;
165
166// if prev_char != new_char {
167// if let Some(on_change) = self.on_change.as_ref() {
168// ctx.publish((on_change)(&String::from_iter(self.value.get().iter())))
169// }
170// }
171
172// return Capture::Captured.into();
173// }
174// }
175
176// if let Some(common) = event.as_common() {
177// match common {
178// CommonEvent::FocusMove(_) if focused => {
179// return Propagate::BubbleUp(self.id, event).into()
180// },
181// CommonEvent::FocusClickDown if focused => {
182// state.get_mut::<FocusInputState>().is_pressed = true;
183// return Capture::Captured.into();
184// },
185// CommonEvent::FocusClickUp if focused => {
186// state.get_mut::<FocusInputState>().is_pressed = false;
187
188// if current_state.is_pressed {
189// state.get_mut::<FocusInputState>().is_active =
190// !state.get::<FocusInputState>().is_active;
191
192// return Capture::Captured.into();
193// }
194// },
195// CommonEvent::FocusClickDown
196// | CommonEvent::FocusClickUp
197// | CommonEvent::FocusMove(_) => {
198// // Should we reset state on any event? Or only on common
199// state.reset::<FocusInputState>();
200// },
201// }
202// }
203
204// Propagate::Ignored.into()
205// }
206
207// fn layout(
208// &self,
209// ctx: &mut UiCtx<Message>,
210// state: &mut crate::state::StateNode,
211// styler: &S,
212// limits: &crate::layout::Limits,
213// ) -> crate::layout::LayoutNode {
214// Layout::sized(limits, self.size, |limits| {
215// limits.resolve_size(self.size.width, self.size.height, Size::zero())
216// })
217// }
218
219// fn draw(
220// &self,
221// ctx: &mut UiCtx<Message>,
222// state: &mut crate::state::StateNode,
223// renderer: &mut R,
224// styler: &S,
225// layout: crate::layout::Layout,
226// ) {
227// let state = state.get::<FocusInputState>();
228// let style = styler.style(&self.class, self.status::<E>(ctx, state));
229
230// let bounds = layout.bounds();
231
232// renderer.block(&crate::block::Block {
233// border: style.border,
234// rect: bounds.into(),
235// background: style.background,
236// });
237
238// renderer.text(TextBox::new(sel, bounds, character_style))
239// }
240// }