fui/controls/internal/
button_presenter.rs1use 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}