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