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