Skip to main content

fui/controls/internal/
button_presenter.rs

1use crate::controls::ButtonColors;
2use crate::ffi::{AlignItems, FlexDirection, JustifyContent};
3use crate::node::{
4    flex_box, Border, Corners, EdgeInsets, FlexBox, PresenterHostStyle, Shadow, TextNode,
5};
6use crate::theme::Theme;
7use crate::{FontStyle, FontWeight};
8use std::rc::Rc;
9
10#[derive(Clone, Copy, Debug, Default)]
11pub struct ButtonVisualState {
12    pub hovered: bool,
13    pub pressed: bool,
14    pub focused: bool,
15    pub enabled: bool,
16}
17
18pub trait ButtonPresenter {
19    fn content_root(&self) -> FlexBox;
20    fn label_node(&self) -> TextNode;
21    fn present(
22        &self,
23        theme: &Theme,
24        state: &ButtonVisualState,
25        colors: Option<&ButtonColors>,
26    ) -> PresenterHostStyle;
27}
28
29pub trait ButtonTemplate {
30    fn create(&self) -> Rc<dyn ButtonPresenter>;
31}
32
33#[derive(Clone)]
34pub struct DefaultButtonPresenter {
35    content_root: FlexBox,
36    label_node: TextNode,
37}
38
39impl DefaultButtonPresenter {
40    pub fn new() -> Self {
41        let label_node = TextNode::new_core("");
42        let content_root = flex_box();
43        content_root
44            .flex_direction(FlexDirection::Row)
45            .align_items(AlignItems::Center)
46            .justify_content(JustifyContent::Center)
47            .child(&label_node);
48        Self {
49            content_root,
50            label_node,
51        }
52    }
53}
54
55impl ButtonPresenter for DefaultButtonPresenter {
56    fn content_root(&self) -> FlexBox {
57        self.content_root.clone()
58    }
59
60    fn label_node(&self) -> TextNode {
61        self.label_node.clone()
62    }
63
64    fn present(
65        &self,
66        theme: &Theme,
67        state: &ButtonVisualState,
68        colors: Option<&ButtonColors>,
69    ) -> PresenterHostStyle {
70        let background = if !state.enabled {
71            colors
72                .and_then(|colors| colors.background)
73                .unwrap_or(theme.colors.accent)
74        } else if state.pressed {
75            colors
76                .and_then(|colors| colors.background_pressed)
77                .or_else(|| colors.and_then(|colors| colors.background_hover))
78                .or_else(|| colors.and_then(|colors| colors.background))
79                .unwrap_or(theme.colors.accent_pressed)
80        } else if state.hovered {
81            colors
82                .and_then(|colors| colors.background_hover)
83                .or_else(|| colors.and_then(|colors| colors.background))
84                .unwrap_or(theme.colors.accent_hovered)
85        } else {
86            colors
87                .and_then(|colors| colors.background)
88                .unwrap_or(theme.colors.accent)
89        };
90        let border = colors
91            .and_then(|colors| colors.border)
92            .unwrap_or(theme.colors.border);
93        let text_color = if !state.enabled {
94            colors
95                .and_then(|colors| colors.text_muted)
96                .or_else(|| colors.and_then(|colors| colors.text_primary))
97                .unwrap_or(theme.colors.text_on_accent)
98        } else {
99            colors
100                .and_then(|colors| colors.text_primary)
101                .unwrap_or(theme.colors.text_on_accent)
102        };
103        self.content_root
104            .flex_direction(FlexDirection::Row)
105            .align_items(AlignItems::Center)
106            .justify_content(JustifyContent::Center);
107        self.label_node
108            .font_family(theme.fonts.body_family.clone())
109            .font_weight(FontWeight::Regular)
110            .font_style(FontStyle::Normal)
111            .font_size(theme.fonts.size_body)
112            .text_color(text_color);
113        PresenterHostStyle::new()
114            .flex_direction(FlexDirection::Row)
115            .justify_content(JustifyContent::Center)
116            .align_items(AlignItems::Center)
117            .corners(Corners::all(theme.spacing.sm))
118            .border(Border::solid(1.0, border))
119            .padding(EdgeInsets::new(
120                theme.spacing.md,
121                theme.spacing.sm,
122                theme.spacing.md,
123                theme.spacing.sm,
124            ))
125            .shadow(Shadow::new(0x00000000, 0.0, 0.0, 0.0, 0.0))
126            .background(background)
127    }
128}
129
130#[derive(Clone, Copy, Debug, Default)]
131pub struct DefaultButtonTemplate;
132
133impl ButtonTemplate for DefaultButtonTemplate {
134    fn create(&self) -> Rc<dyn ButtonPresenter> {
135        Rc::new(DefaultButtonPresenter::new())
136    }
137}
138
139pub const DEFAULT_BUTTON_TEMPLATE: DefaultButtonTemplate = DefaultButtonTemplate;
140
141pub fn create_default_button_presenter() -> Rc<dyn ButtonPresenter> {
142    Rc::new(DefaultButtonPresenter::new())
143}