1use std::hash::Hash;
8use std::path::PathBuf;
9use std::rc::Rc;
10
11use crabshell_reactive::{Memo, Signal};
12
13use crate::cx::Cx;
14use crate::input::{Handler, Handlers};
15use crate::keyboard::KeyEvent;
16use crate::keyed::{self, KeyedMount};
17use crate::prop::{IntoProp, Prop};
18use crate::screencopy::ScreencopyProps;
19use crate::scroll::ScrollProps;
20use crate::style::Style;
21use crate::text_input::InputProps;
22
23pub trait Widget: 'static {
43 fn into_element(self: Box<Self>) -> Element;
48}
49
50pub struct Element {
53 pub(crate) name: &'static str,
54 pub(crate) style: Option<Prop<Style>>,
55 pub(crate) kind: ElementKind,
56 pub(crate) children: Vec<ElementChild>,
57}
58
59pub(crate) enum ElementKind {
60 Flex(FlexProps),
61 Stack,
62 Spacer,
63 Text(TextProps),
64 Icon(IconProps),
65 Image(ImageProps),
66 Capture(ScreencopyProps),
67 Button(Box<ButtonProps>),
69 Scroll(ScrollProps),
70 Input(Box<InputProps>),
71}
72
73pub(crate) enum Child {
76 One(Box<dyn Widget>),
77 Keyed(KeyedMount),
78}
79
80pub(crate) enum ElementChild {
82 One(Box<Element>),
83 Keyed(KeyedMount),
84}
85
86fn describe(children: Vec<Child>) -> Vec<ElementChild> {
89 children
90 .into_iter()
91 .map(|child| match child {
92 Child::One(widget) => ElementChild::One(Box::new(widget.into_element())),
93 Child::Keyed(mount) => ElementChild::Keyed(mount),
94 })
95 .collect()
96}
97
98pub(crate) struct FlexProps {
99 pub(crate) column: bool,
100 pub(crate) grow: bool,
103 pub(crate) gap: Option<Prop<u32>>,
104 pub(crate) padding_vertical: Option<Prop<u32>>,
105 pub(crate) padding_horizontal: Option<Prop<u32>>,
106 pub(crate) align: Option<Prop<Align>>,
107 pub(crate) justify: Option<Prop<Justify>>,
108}
109
110pub(crate) struct TextProps {
111 pub(crate) text: Prop<String>,
112 pub(crate) ellipsis: Option<Prop<bool>>,
113 pub(crate) max_lines: Option<Prop<u32>>,
114}
115
116pub(crate) struct IconProps {
117 pub(crate) source: Prop<IconSource>,
118 pub(crate) size: Option<Prop<u32>>,
119}
120
121pub(crate) struct ImageProps {
122 pub(crate) source: Prop<PathBuf>,
123 pub(crate) width: Option<Prop<u32>>,
124 pub(crate) height: Option<Prop<u32>>,
125 pub(crate) fit: Option<Prop<Fit>>,
126}
127
128#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
131pub enum Fit {
132 #[default]
134 Fill,
135 Contain,
137 Cover,
140}
141
142pub(crate) struct ButtonProps {
143 pub(crate) hover_style: Option<Prop<Style>>,
144 pub(crate) pressed_style: Option<Prop<Style>>,
145 pub(crate) focus_style: Option<Prop<Style>>,
146 pub(crate) hovered: Option<Signal<bool>>,
147 pub(crate) handlers: Handlers,
148}
149
150#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
153pub enum Align {
154 Start,
156 Center,
158 End,
160 #[default]
162 Stretch,
163}
164
165#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
168pub enum Justify {
169 #[default]
171 Start,
172 Center,
174 End,
176 SpaceBetween,
178 SpaceAround,
180 SpaceEvenly,
182}
183
184#[derive(Clone, Debug, PartialEq)]
186pub enum IconSource {
187 Name(String),
190 File(PathBuf),
192 Pixels(IconPixels),
194}
195
196#[derive(Clone, Debug, PartialEq)]
199pub struct IconPixels {
200 pub width: u32,
202 pub height: u32,
204 pub data: Rc<[u8]>,
206}
207
208macro_rules! container_methods {
210 () => {
211 pub fn child(mut self, child: impl Widget) -> Self {
213 self.children.push(Child::One(Box::new(child)));
214 self
215 }
216
217 pub fn children_keyed<T, K, W>(
228 mut self,
229 items: impl FnMut() -> Vec<T> + 'static,
230 key: impl FnMut(&T) -> K + 'static,
231 view: impl FnMut(&Cx, Memo<T>) -> W + 'static,
232 ) -> Self
233 where
234 T: Clone + PartialEq + 'static,
235 K: Eq + Hash + 'static,
236 W: Widget,
237 {
238 self
239 .children
240 .push(Child::Keyed(keyed::mount(items, key, view)));
241 self
242 }
243 };
244}
245
246macro_rules! style_method {
248 () => {
249 pub fn style(mut self, style: impl IntoProp<Style>) -> Self {
251 self.style = Some(style.into_prop());
252 self
253 }
254 };
255}
256
257macro_rules! flex_widget {
259 ($name:ident, $column:literal, $doc:literal) => {
260 #[doc = $doc]
261 pub struct $name {
265 flex: FlexProps,
266 style: Option<Prop<Style>>,
267 children: Vec<Child>,
268 }
269
270 impl $name {
271 pub fn new() -> Self {
273 $name {
274 flex: FlexProps {
275 column: $column,
276 grow: false,
277 gap: None,
278 padding_vertical: None,
279 padding_horizontal: None,
280 align: None,
281 justify: None,
282 },
283 style: None,
284 children: Vec::new(),
285 }
286 }
287
288 pub fn gap(mut self, gap: impl IntoProp<u32>) -> Self {
290 self.flex.gap = Some(gap.into_prop());
291 self
292 }
293
294 pub fn padding(
298 mut self,
299 vertical: impl IntoProp<u32>,
300 horizontal: impl IntoProp<u32>,
301 ) -> Self {
302 self.flex.padding_vertical = Some(vertical.into_prop());
303 self.flex.padding_horizontal = Some(horizontal.into_prop());
304 self
305 }
306
307 pub fn align(mut self, align: impl IntoProp<Align>) -> Self {
309 self.flex.align = Some(align.into_prop());
310 self
311 }
312
313 pub fn justify(mut self, justify: impl IntoProp<Justify>) -> Self {
315 self.flex.justify = Some(justify.into_prop());
316 self
317 }
318
319 container_methods!();
320 style_method!();
321 }
322
323 impl Default for $name {
324 fn default() -> Self {
325 Self::new()
326 }
327 }
328
329 impl Widget for $name {
330 fn into_element(self: Box<Self>) -> Element {
331 Element {
332 name: stringify!($name),
333 style: self.style,
334 kind: ElementKind::Flex(self.flex),
335 children: describe(self.children),
336 }
337 }
338 }
339 };
340}
341
342flex_widget!(
343 Row,
344 false,
345 "Lays its children out left to right, like a CSS flexbox row."
346);
347flex_widget!(
348 Column,
349 true,
350 "Lays its children out top to bottom, like a CSS flexbox column."
351);
352
353pub struct Stack {
356 style: Option<Prop<Style>>,
357 children: Vec<Child>,
358}
359
360impl Stack {
361 pub fn new() -> Stack {
363 Stack {
364 style: None,
365 children: Vec::new(),
366 }
367 }
368
369 container_methods!();
370 style_method!();
371}
372
373impl Default for Stack {
374 fn default() -> Self {
375 Self::new()
376 }
377}
378
379impl Widget for Stack {
380 fn into_element(self: Box<Self>) -> Element {
381 Element {
382 name: "Stack",
383 style: self.style,
384 kind: ElementKind::Stack,
385 children: describe(self.children),
386 }
387 }
388}
389
390pub struct Spacer;
393
394impl Spacer {
395 pub fn new() -> Spacer {
397 Spacer
398 }
399}
400
401impl Default for Spacer {
402 fn default() -> Self {
403 Self::new()
404 }
405}
406
407impl Widget for Spacer {
408 fn into_element(self: Box<Self>) -> Element {
409 Element {
410 name: "Spacer",
411 style: None,
412 kind: ElementKind::Spacer,
413 children: Vec::new(),
414 }
415 }
416}
417
418pub struct Text {
424 text: TextProps,
425 style: Option<Prop<Style>>,
426}
427
428impl Text {
429 pub fn new(text: impl IntoProp<String>) -> Text {
431 Text {
432 text: TextProps {
433 text: text.into_prop(),
434 ellipsis: None,
435 max_lines: None,
436 },
437 style: None,
438 }
439 }
440
441 pub fn ellipsis(mut self, ellipsis: impl IntoProp<bool>) -> Text {
444 self.text.ellipsis = Some(ellipsis.into_prop());
445 self
446 }
447
448 pub fn max_lines(mut self, max_lines: impl IntoProp<u32>) -> Text {
452 self.text.max_lines = Some(max_lines.into_prop());
453 self
454 }
455
456 style_method!();
457}
458
459impl Widget for Text {
460 fn into_element(self: Box<Self>) -> Element {
461 Element {
462 name: "Text",
463 style: self.style,
464 kind: ElementKind::Text(self.text),
465 children: Vec::new(),
466 }
467 }
468}
469
470pub struct Icon {
472 icon: IconProps,
473 style: Option<Prop<Style>>,
474}
475
476impl Icon {
477 pub const DEFAULT_SIZE: u32 = 16;
479
480 pub fn new(source: impl IntoProp<IconSource>) -> Icon {
483 Icon {
484 icon: IconProps {
485 source: source.into_prop(),
486 size: None,
487 },
488 style: None,
489 }
490 }
491
492 pub fn size(mut self, size: impl IntoProp<u32>) -> Icon {
494 self.icon.size = Some(size.into_prop());
495 self
496 }
497
498 style_method!();
499}
500
501pub struct Image {
504 image: ImageProps,
505 style: Option<Prop<Style>>,
506}
507
508impl Image {
509 pub const DEFAULT_SIZE: u32 = 64;
511
512 pub fn new(source: impl IntoProp<PathBuf>) -> Image {
515 Image {
516 image: ImageProps {
517 source: source.into_prop(),
518 width: None,
519 height: None,
520 fit: None,
521 },
522 style: None,
523 }
524 }
525
526 pub fn width(mut self, width: impl IntoProp<u32>) -> Image {
528 self.image.width = Some(width.into_prop());
529 self
530 }
531
532 pub fn height(mut self, height: impl IntoProp<u32>) -> Image {
534 self.image.height = Some(height.into_prop());
535 self
536 }
537
538 pub fn fit(mut self, fit: impl IntoProp<Fit>) -> Image {
540 self.image.fit = Some(fit.into_prop());
541 self
542 }
543
544 style_method!();
545}
546
547impl Widget for Image {
548 fn into_element(self: Box<Self>) -> Element {
549 Element {
550 name: "Image",
551 style: self.style,
552 kind: ElementKind::Image(self.image),
553 children: Vec::new(),
554 }
555 }
556}
557
558impl Widget for Icon {
559 fn into_element(self: Box<Self>) -> Element {
560 Element {
561 name: "Icon",
562 style: self.style,
563 kind: ElementKind::Icon(self.icon),
564 children: Vec::new(),
565 }
566 }
567}
568
569pub struct Button {
576 button: ButtonProps,
577 style: Option<Prop<Style>>,
578 child: Box<dyn Widget>,
579}
580
581impl Button {
582 pub fn new(child: impl Widget) -> Button {
584 Button {
585 child: Box::new(child),
586 button: ButtonProps {
587 hover_style: None,
588 pressed_style: None,
589 focus_style: None,
590 hovered: None,
591 handlers: Handlers::default(),
592 },
593 style: None,
594 }
595 }
596
597 pub fn hover_style(mut self, style: impl IntoProp<Style>) -> Button {
600 self.button.hover_style = Some(style.into_prop());
601 self
602 }
603
604 pub fn hovered(mut self, hovered: Signal<bool>) -> Button {
611 self.button.hovered = Some(hovered);
612 self
613 }
614
615 pub fn pressed_style(mut self, style: impl IntoProp<Style>) -> Button {
618 self.button.pressed_style = Some(style.into_prop());
619 self
620 }
621
622 pub fn focus_style(mut self, style: impl IntoProp<Style>) -> Button {
625 self.button.focus_style = Some(style.into_prop());
626 self
627 }
628
629 pub fn on_key(mut self, handler: impl Fn(&Cx, &KeyEvent) -> bool + 'static) -> Button {
634 self.button.handlers.key = Some(Rc::new(Handler::new(handler)));
635 self
636 }
637
638 pub fn on_press(mut self, handler: impl Fn(&Cx) + 'static) -> Button {
642 self.button.handlers.press = Some(Rc::new(Handler::new(handler)));
643 self
644 }
645
646 pub fn on_click(mut self, handler: impl Fn(&Cx) + 'static) -> Button {
649 self.button.handlers.click = Some(Rc::new(Handler::new(handler)));
650 self
651 }
652
653 pub fn on_right_click(mut self, handler: impl Fn(&Cx) + 'static) -> Button {
656 self.button.handlers.right_click = Some(Rc::new(Handler::new(handler)));
657 self
658 }
659
660 pub fn on_middle_click(mut self, handler: impl Fn(&Cx) + 'static) -> Button {
663 self.button.handlers.middle_click = Some(Rc::new(Handler::new(handler)));
664 self
665 }
666
667 pub fn on_scroll(mut self, handler: impl Fn(&Cx, i32) + 'static) -> Button {
672 self.button.handlers.scroll = Some(Rc::new(Handler::new(handler)));
673 self
674 }
675
676 style_method!();
677}
678
679impl Widget for Button {
680 fn into_element(self: Box<Self>) -> Element {
681 Element {
682 name: "Button",
683 style: self.style,
684 kind: ElementKind::Button(Box::new(self.button)),
685 children: vec![ElementChild::One(Box::new(self.child.into_element()))],
686 }
687 }
688}