Skip to main content

cosmic/widget/button/
link.rs

1// Copyright 2023 System76 <info@system76.com>
2// SPDX-License-Identifier: MPL-2.0
3
4//! Hyperlink button widget
5
6use super::{Builder, ButtonClass};
7use crate::Element;
8use crate::prelude::*;
9use crate::widget::icon::{self, Handle};
10use crate::widget::{button, row, tooltip};
11use iced_core::font::Weight;
12use iced_core::text::LineHeight;
13use iced_core::widget::Id;
14use iced_core::{Alignment, Length, Padding};
15use std::borrow::Cow;
16
17pub type Button<'a, Message> = Builder<'a, Message, Hyperlink>;
18
19pub struct Hyperlink {
20    trailing_icon: bool,
21}
22
23/// A hyperlink button.
24pub fn link<'a, Message>(label: impl Into<Cow<'a, str>> + 'static) -> Button<'a, Message> {
25    Button::new(
26        label,
27        Hyperlink {
28            trailing_icon: false,
29        },
30    )
31}
32
33impl<'a, Message> Button<'a, Message> {
34    pub fn new(label: impl Into<Cow<'a, str>> + 'static, link: Hyperlink) -> Self {
35        Self {
36            id: Id::unique(),
37            label: label.into(),
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(4),
47            spacing: 0,
48            icon_size: 16,
49            line_height: 20,
50            font_size: 14,
51            font_weight: Weight::Normal,
52            class: ButtonClass::Link,
53            variant: link,
54        }
55    }
56
57    pub const fn trailing_icon(mut self, set: bool) -> Self {
58        self.variant.trailing_icon = set;
59        self
60    }
61}
62
63#[inline(never)]
64pub fn icon() -> Handle {
65    icon::from_svg_bytes(&include_bytes!("external-link.svg")[..]).symbolic(true)
66}
67
68impl<'a, Message: Clone + 'static> From<Button<'a, Message>> for Element<'a, Message> {
69    fn from(mut builder: Button<'a, Message>) -> Element<'a, Message> {
70        let mut button: super::Button<'a, Message> = row::with_capacity(2)
71            .push({
72                // TODO: Avoid allocation
73                crate::widget::text(builder.label.to_string())
74                    .size(builder.font_size)
75                    .line_height(LineHeight::Absolute(builder.line_height.into()))
76                    .font(crate::font::Font {
77                        weight: builder.font_weight,
78                        ..crate::font::default()
79                    })
80            })
81            .push_maybe(if builder.variant.trailing_icon {
82                Some(icon().icon().size(builder.icon_size))
83            } else {
84                None
85            })
86            .padding(builder.padding)
87            .width(builder.width)
88            .height(builder.height)
89            .spacing(builder.spacing)
90            .align_y(Alignment::Center)
91            .apply(button::custom)
92            .padding(0)
93            .id(builder.id)
94            .on_press_maybe(builder.on_press.take())
95            .class(builder.class);
96
97        #[cfg(feature = "a11y")]
98        {
99            if !builder.label.is_empty() {
100                button = button.name(builder.label);
101            }
102
103            button = button.description(builder.description);
104        }
105
106        if builder.tooltip.is_empty() {
107            button.into()
108        } else {
109            tooltip(
110                button,
111                crate::widget::text(builder.tooltip)
112                    .size(builder.font_size)
113                    .font(crate::font::Font {
114                        weight: builder.font_weight,
115                        ..crate::font::default()
116                    }),
117                tooltip::Position::Top,
118            )
119            .into()
120        }
121    }
122}