cosmic/widget/button/
icon.rs

1// Copyright 2023 System76 <info@system76.com>
2// SPDX-License-Identifier: MPL-2.0
3
4use super::{Builder, ButtonClass};
5use crate::Element;
6use crate::widget::{icon::Handle, tooltip};
7use apply::Apply;
8use iced_core::{Alignment, Length, Padding, font::Weight, text::LineHeight, widget::Id};
9use std::borrow::Cow;
10
11pub type Button<'a, Message> = Builder<'a, Message, Icon>;
12
13/// The icon variant of a button.
14pub struct Icon {
15    handle: Handle,
16    vertical: bool,
17    selected: bool,
18}
19
20/// A button constructed from an icon handle, using icon button styling.
21pub fn icon<'a, Message>(handle: impl Into<Handle>) -> Button<'a, Message> {
22    Button::new(Icon {
23        handle: handle.into(),
24        vertical: false,
25        selected: false,
26    })
27}
28
29impl<Message> Button<'_, Message> {
30    pub fn new(icon: Icon) -> Self {
31        let guard = crate::theme::THEME.lock().unwrap();
32        let theme = guard.cosmic();
33        let padding = theme.space_xxs();
34
35        Self {
36            id: Id::unique(),
37            label: Cow::Borrowed(""),
38            #[cfg(feature = "a11y")]
39            name: Cow::Borrowed(""),
40            #[cfg(feature = "a11y")]
41            description: Cow::Borrowed(""),
42            tooltip: Cow::Borrowed(""),
43            on_press: None,
44            width: Length::Shrink,
45            height: Length::Shrink,
46            padding: Padding::from(padding),
47            spacing: theme.space_xxxs(),
48            icon_size: if icon.handle.symbolic { 16 } else { 24 },
49            line_height: 20,
50            font_size: 14,
51            font_weight: Weight::Normal,
52            class: ButtonClass::Icon,
53            variant: icon,
54        }
55    }
56
57    /// Applies the **Extra Small** button size preset.
58    pub fn extra_small(mut self) -> Self {
59        let guard = crate::theme::THEME.lock().unwrap();
60        let theme = guard.cosmic();
61
62        self.font_size = 14;
63        self.font_weight = Weight::Normal;
64        self.icon_size = 16;
65        self.line_height = 20;
66        self.padding = Padding::from(theme.space_xxs());
67        self.spacing = theme.space_xxxs();
68
69        self
70    }
71
72    /// Applies the **Medium** button size preset.
73    pub fn medium(mut self) -> Self {
74        let guard = crate::theme::THEME.lock().unwrap();
75        let theme = guard.cosmic();
76
77        self.font_size = 24;
78        self.font_weight = Weight::Normal;
79        self.icon_size = 32;
80        self.line_height = 32;
81        self.padding = Padding::from(theme.space_xs());
82        self.spacing = theme.space_xxs();
83
84        self
85    }
86
87    /// Applies the **Large** button size preset.
88    pub fn large(mut self) -> Self {
89        let guard = crate::theme::THEME.lock().unwrap();
90        let theme = guard.cosmic();
91
92        self.font_size = 28;
93        self.font_weight = Weight::Normal;
94        self.icon_size = 40;
95        self.line_height = 36;
96        self.padding = Padding::from(theme.space_xs());
97        self.spacing = theme.space_xxs();
98
99        self
100    }
101
102    /// Applies the **Extra Large** button size preset.
103    pub fn extra_large(mut self) -> Self {
104        let guard = crate::theme::THEME.lock().unwrap();
105        let theme = guard.cosmic();
106        let padding = theme.space_xs();
107
108        self.font_size = 32;
109        self.font_weight = Weight::Light;
110        self.icon_size = 56;
111        self.line_height = 44;
112        self.padding = Padding::from(padding);
113        self.spacing = theme.space_xxs();
114
115        self
116    }
117
118    #[inline]
119    pub fn selected(mut self, selected: bool) -> Self {
120        self.variant.selected = selected;
121        self
122    }
123
124    #[inline]
125    pub fn vertical(mut self, vertical: bool) -> Self {
126        self.variant.vertical = vertical;
127        self.class = ButtonClass::IconVertical;
128        self
129    }
130}
131
132impl<'a, Message: Clone + 'static> From<Button<'a, Message>> for Element<'a, Message> {
133    fn from(builder: Button<'a, Message>) -> Element<'a, Message> {
134        let mut content = Vec::with_capacity(2);
135
136        content.push(
137            crate::widget::icon(builder.variant.handle.clone())
138                .size(builder.icon_size)
139                .into(),
140        );
141
142        if !builder.label.is_empty() {
143            content.push(
144                crate::widget::text(builder.label)
145                    .size(builder.font_size)
146                    .line_height(LineHeight::Absolute(builder.line_height.into()))
147                    .font(crate::font::Font {
148                        weight: builder.font_weight,
149                        ..crate::font::default()
150                    })
151                    .into(),
152            );
153        }
154
155        let mut button = if builder.variant.vertical {
156            crate::widget::column::with_children(content)
157                .padding(builder.padding)
158                .spacing(builder.spacing)
159                .align_x(Alignment::Center)
160                .apply(super::custom)
161        } else {
162            crate::widget::row::with_children(content)
163                .padding(builder.padding)
164                .width(builder.width)
165                .height(builder.height)
166                .spacing(builder.spacing)
167                .align_y(Alignment::Center)
168                .apply(super::custom)
169        };
170
171        #[cfg(feature = "a11y")]
172        {
173            button = button.name(builder.name).description(builder.description);
174        }
175
176        let button = button
177            .padding(0)
178            .id(builder.id)
179            .on_press_maybe(builder.on_press)
180            .selected(builder.variant.selected)
181            .class(builder.class);
182
183        if builder.tooltip.is_empty() {
184            button.into()
185        } else {
186            tooltip(
187                button,
188                crate::widget::text(builder.tooltip)
189                    .size(builder.font_size)
190                    .font(crate::font::Font {
191                        weight: builder.font_weight,
192                        ..crate::font::default()
193                    }),
194                tooltip::Position::Top,
195            )
196            .into()
197        }
198    }
199}