teksilo_widgets/stepper/
nav.rs1use std::cell::RefCell;
9use std::rc::Rc;
10
11use teksilo_core::signal::{Prop, Signal};
12use teksilo_core::widget::EventContext;
13use teksilo_core::widget_id::WidgetId;
14
15use super::controller::StepperController;
16use super::step::{StepStatus, StepValidator};
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum FinishOutcome {
26 Finished,
29 Rejected,
33}
34
35pub trait IntoFinishOutcome {
46 fn into_finish_outcome(self) -> FinishOutcome;
47}
48
49impl IntoFinishOutcome for () {
50 fn into_finish_outcome(self) -> FinishOutcome {
51 FinishOutcome::Finished
52 }
53}
54
55impl IntoFinishOutcome for bool {
56 fn into_finish_outcome(self) -> FinishOutcome {
57 if self {
58 FinishOutcome::Finished
59 } else {
60 FinishOutcome::Rejected
61 }
62 }
63}
64
65impl IntoFinishOutcome for FinishOutcome {
66 fn into_finish_outcome(self) -> FinishOutcome {
67 self
68 }
69}
70
71impl<T, E> IntoFinishOutcome for Result<T, E> {
72 fn into_finish_outcome(self) -> FinishOutcome {
73 match self {
74 Ok(_) => FinishOutcome::Finished,
75 Err(_) => FinishOutcome::Rejected,
76 }
77 }
78}
79
80pub(crate) type FinishAction = Rc<dyn Fn(&mut EventContext, &StepperController) -> FinishOutcome>;
81
82pub(crate) struct StepNav {
88 controller: StepperController,
89 validators: Vec<Option<StepValidator>>,
90 completion: Vec<Option<Prop<bool>>>,
91 finish_action: Option<FinishAction>,
92 next_focus: RefCell<Option<WidgetId>>,
95 finish_focus: RefCell<Option<WidgetId>>,
96}
97
98impl StepNav {
99 pub(crate) fn new(
100 controller: StepperController,
101 validators: Vec<Option<StepValidator>>,
102 completion: Vec<Option<Prop<bool>>>,
103 finish_action: Option<FinishAction>,
104 ) -> Self {
105 Self {
106 controller,
107 validators,
108 completion,
109 finish_action,
110 next_focus: RefCell::new(None),
111 finish_focus: RefCell::new(None),
112 }
113 }
114
115 pub(crate) fn controller(&self) -> &StepperController {
116 &self.controller
117 }
118
119 pub(crate) fn set_focus_targets(&self, next: WidgetId, finish: WidgetId) {
120 *self.next_focus.borrow_mut() = Some(next);
121 *self.finish_focus.borrow_mut() = Some(finish);
122 }
123
124 pub(crate) fn gate_open(&self, idx: usize) -> bool {
127 self.completion
128 .get(idx)
129 .and_then(|c| c.as_ref())
130 .map(|p| p.get())
131 .unwrap_or(true)
132 }
133
134 pub(crate) fn completion_signals(&self) -> Vec<Signal<bool>> {
137 self.completion
138 .iter()
139 .map(|c| {
140 c.as_ref()
141 .map(|p| p.as_signal())
142 .unwrap_or_else(|| Signal::new(true))
143 })
144 .collect()
145 }
146
147 fn validates(&self, idx: usize) -> bool {
150 match self.validators.get(idx) {
151 Some(Some(v)) => v(),
152 _ => true,
153 }
154 }
155
156 pub(crate) fn focus_primary(&self, ctx: &mut EventContext) {
160 let target = if self.controller.has_next() {
161 *self.next_focus.borrow()
162 } else {
163 *self.finish_focus.borrow()
164 };
165 if let Some(t) = target {
166 ctx.request_focus(t);
167 }
168 }
169
170 pub(crate) fn advance(&self, ctx: &mut EventContext) -> bool {
172 let i = self.controller.current();
173 if !self.gate_open(i) {
174 return false;
175 }
176 if !self.validates(i) {
177 self.controller.set_status(i, StepStatus::Error);
178 return false;
179 }
180 self.controller.set_status(i, StepStatus::Complete);
184 self.controller.next();
185 self.focus_primary(ctx);
186 true
187 }
188
189 pub(crate) fn finish(&self, ctx: &mut EventContext) -> FinishOutcome {
193 let i = self.controller.current();
194 if !self.gate_open(i) {
195 return FinishOutcome::Rejected;
196 }
197 if !self.validates(i) {
198 self.controller.set_status(i, StepStatus::Error);
199 return FinishOutcome::Rejected;
200 }
201 let outcome = match &self.finish_action {
202 Some(action) => action(ctx, &self.controller),
203 None => FinishOutcome::Finished,
204 };
205 match outcome {
206 FinishOutcome::Finished => self.controller.set_status(i, StepStatus::Complete),
207 FinishOutcome::Rejected => self.controller.set_status(i, StepStatus::Error),
208 }
209 outcome
210 }
211
212 pub(crate) fn activate_primary(&self, ctx: &mut EventContext) -> bool {
215 if self.controller.has_next() {
216 self.advance(ctx)
217 } else {
218 self.finish(ctx) == FinishOutcome::Finished
219 }
220 }
221}
222
223impl std::fmt::Debug for StepNav {
224 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
225 f.debug_struct("StepNav")
226 .field("steps", &self.validators.len())
227 .finish()
228 }
229}