1use std::fmt;
5use std::rc::Rc;
6
7use crate::geometry::Size;
8use crate::menu::{Menu, MenuEntry};
9use crate::state::StateSet;
10use crate::style::{Backdrop, ButtonStyle, Icon, ImageSize, LabelStyle, StackStyle, ToggleStyle};
11
12pub trait Logic: Sized + 'static {
18 type Frame: 'static;
20 type Value: Clone + fmt::Debug + 'static;
23 type Binding: Clone + fmt::Debug + 'static;
25 type Action: Clone + fmt::Debug + 'static;
27 type Region: fmt::Debug + 'static;
29 type Error: 'static;
30
31 fn text(&self, text: &Self::Value, frame: &Self::Frame) -> Result<String, Self::Error>;
34
35 fn number(&self, number: &Self::Value, frame: &Self::Frame) -> Result<i64, Self::Error>;
38
39 fn flag(&self, flag: &Self::Value, frame: &Self::Frame) -> Result<bool, Self::Error>;
42
43 fn texts(&self, texts: &Self::Value, frame: &Self::Frame) -> Result<Vec<String>, Self::Error>;
46
47 fn read_text(
50 &self,
51 binding: &Self::Binding,
52 frame: &Self::Frame,
53 ) -> Result<String, Self::Error>;
54
55 fn read_checked(
58 &self,
59 binding: &Self::Binding,
60 frame: &Self::Frame,
61 ) -> Result<bool, Self::Error>;
62
63 fn read_number(&self, binding: &Self::Binding, frame: &Self::Frame)
66 -> Result<i64, Self::Error>;
67
68 fn write_text(
71 &self,
72 binding: &Self::Binding,
73 frame: &Self::Frame,
74 text: &str,
75 ) -> Result<(), Self::Error>;
76
77 fn write_checked(
80 &self,
81 binding: &Self::Binding,
82 frame: &Self::Frame,
83 checked: bool,
84 ) -> Result<(), Self::Error>;
85
86 fn write_number(
89 &self,
90 binding: &Self::Binding,
91 frame: &Self::Frame,
92 number: i64,
93 ) -> Result<(), Self::Error>;
94
95 fn write_texts(
98 &self,
99 binding: &Self::Binding,
100 frame: &Self::Frame,
101 texts: Vec<String>,
102 ) -> Result<(), Self::Error>;
103
104 fn run(&self, action: &Self::Action, frame: &Self::Frame) -> Result<(), Self::Error>;
107
108 fn expand(
113 &self,
114 region: &Self::Region,
115 frame: &Rc<Self::Frame>,
116 ) -> Result<Vec<Shown<Self>>, Self::Error>;
117
118 fn take_changed(&self) -> StateSet;
120
121 fn take_window_requests(&self) -> Vec<WindowRequest>;
123}
124
125#[derive(Clone, Copy, Debug, PartialEq, Eq)]
128#[repr(u32)]
129pub enum WindowAction {
130 Open,
132 Close,
134}
135
136#[derive(Clone, Copy, Debug, PartialEq, Eq)]
138pub struct WindowRequest {
139 pub action: WindowAction,
140 pub window: usize,
141}
142
143pub type Templates<L> = Rc<[Template<L>]>;
144
145pub enum Template<L: Logic> {
147 Stack {
149 style: StackStyle,
150 children: Templates<L>,
151 scrolls: bool,
152 },
153 Widget { widget: Widget<L>, deps: StateSet },
155 Region { region: L::Region, deps: StateSet },
157}
158
159pub enum Widget<L: Logic> {
160 Label {
161 text: L::Value,
162 style: LabelStyle,
163 },
164 Button {
166 title: L::Value,
167 style: ButtonStyle,
168 icon: Icon,
169 selected: Option<L::Value>,
170 action: L::Action,
171 },
172 TextField {
175 value: L::Binding,
176 placeholder: String,
177 lines: u32,
178 action: Option<L::Action>,
179 },
180 Checkbox {
181 checked: L::Binding,
182 title: Option<L::Value>,
183 style: ToggleStyle,
184 },
185 Slider {
187 value: L::Binding,
188 min: i64,
189 max: i64,
190 },
191 Picker {
193 selection: L::Binding,
194 options: L::Value,
195 },
196 NavItem {
198 title: L::Value,
199 value: L::Value,
200 selection: L::Binding,
201 icon: Icon,
202 badge: Option<L::Value>,
203 },
204 ContextMenu {
206 entries: Vec<MenuEntry<L::Action>>,
207 },
208 DropTarget {
211 dropped: L::Binding,
212 action: Option<L::Action>,
213 },
214 Image {
216 path: L::Value,
217 size: ImageSize,
218 },
219 Progress {
221 value: Option<L::Value>,
222 },
223 Spacer,
224 Divider,
225}
226
227pub struct Shown<L: Logic> {
229 pub templates: Templates<L>,
230 pub frame: Rc<L::Frame>,
231}
232
233pub struct AppTemplate<L: Logic> {
235 pub windows: Vec<WindowTemplate<L>>,
236 pub menu_bar: MenuBar<L>,
237}
238
239pub struct MenuBar<L: Logic> {
241 pub menus: Vec<Menu<L::Action>>,
242 pub frame: L::Frame,
243}
244
245pub struct WindowTemplate<L: Logic> {
247 pub title: String,
248 pub size: Size,
249 pub backdrop: Backdrop,
250 pub style: StackStyle,
251 pub children: Templates<L>,
252 pub frame: Rc<L::Frame>,
254 pub starts_open: bool,
256}
257
258impl<L: Logic> fmt::Debug for Template<L> {
259 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
260 match self {
261 Self::Stack {
262 style,
263 children,
264 scrolls,
265 } => f
266 .debug_struct("Stack")
267 .field("style", style)
268 .field("children", children)
269 .field("scrolls", scrolls)
270 .finish(),
271 Self::Widget { widget, deps } => f
272 .debug_struct("Widget")
273 .field("widget", widget)
274 .field("deps", deps)
275 .finish(),
276 Self::Region { region, deps } => f
277 .debug_struct("Region")
278 .field("region", region)
279 .field("deps", deps)
280 .finish(),
281 }
282 }
283}
284
285impl<L: Logic> fmt::Debug for Widget<L> {
286 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
287 match self {
288 Self::Label { text, style } => f
289 .debug_struct("Label")
290 .field("text", text)
291 .field("style", style)
292 .finish(),
293 Self::Button {
294 title,
295 style,
296 icon,
297 selected,
298 action,
299 } => f
300 .debug_struct("Button")
301 .field("title", title)
302 .field("style", style)
303 .field("icon", icon)
304 .field("selected", selected)
305 .field("action", action)
306 .finish(),
307 Self::TextField {
308 value,
309 placeholder,
310 lines,
311 action,
312 } => f
313 .debug_struct("TextField")
314 .field("value", value)
315 .field("placeholder", placeholder)
316 .field("lines", lines)
317 .field("action", action)
318 .finish(),
319 Self::Checkbox {
320 checked,
321 title,
322 style,
323 } => f
324 .debug_struct("Checkbox")
325 .field("checked", checked)
326 .field("title", title)
327 .field("style", style)
328 .finish(),
329 Self::Slider { value, min, max } => f
330 .debug_struct("Slider")
331 .field("value", value)
332 .field("min", min)
333 .field("max", max)
334 .finish(),
335 Self::Picker { selection, options } => f
336 .debug_struct("Picker")
337 .field("selection", selection)
338 .field("options", options)
339 .finish(),
340 Self::NavItem {
341 title,
342 value,
343 selection,
344 icon,
345 badge,
346 } => f
347 .debug_struct("NavItem")
348 .field("title", title)
349 .field("value", value)
350 .field("selection", selection)
351 .field("icon", icon)
352 .field("badge", badge)
353 .finish(),
354 Self::ContextMenu { entries } => f
355 .debug_struct("ContextMenu")
356 .field("entries", entries)
357 .finish(),
358 Self::DropTarget { dropped, action } => f
359 .debug_struct("DropTarget")
360 .field("dropped", dropped)
361 .field("action", action)
362 .finish(),
363 Self::Image { path, size } => f
364 .debug_struct("Image")
365 .field("path", path)
366 .field("size", size)
367 .finish(),
368 Self::Progress { value } => f.debug_struct("Progress").field("value", value).finish(),
369 Self::Spacer => f.write_str("Spacer"),
370 Self::Divider => f.write_str("Divider"),
371 }
372 }
373}
374
375impl<L: Logic> fmt::Debug for Shown<L> {
376 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
377 f.debug_struct("Shown")
378 .field("templates", &self.templates)
379 .finish_non_exhaustive()
380 }
381}
382
383impl<L: Logic> fmt::Debug for AppTemplate<L> {
384 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
385 f.debug_struct("AppTemplate")
386 .field("windows", &self.windows)
387 .field("menu_bar", &self.menu_bar)
388 .finish()
389 }
390}
391
392impl<L: Logic> fmt::Debug for MenuBar<L> {
393 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
394 f.debug_struct("MenuBar")
395 .field("menus", &self.menus)
396 .finish_non_exhaustive()
397 }
398}
399
400impl<L: Logic> fmt::Debug for WindowTemplate<L> {
401 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
402 f.debug_struct("WindowTemplate")
403 .field("title", &self.title)
404 .field("size", &self.size)
405 .field("backdrop", &self.backdrop)
406 .field("style", &self.style)
407 .field("children", &self.children)
408 .finish_non_exhaustive()
409 }
410}