1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// Copyright 2023 System76 <info@system76.com>
// SPDX-License-Identifier: MPL-2.0

//! Hyperlink button widget

use super::Builder;
use super::ButtonClass;
use crate::prelude::*;
use crate::widget::icon::{self, Handle};
use crate::widget::{button, row, tooltip};
use crate::Element;
use iced_core::text::LineHeight;
use iced_core::{font::Weight, widget::Id, Alignment, Length, Padding};
use std::borrow::Cow;

pub type Button<'a, Message> = Builder<'a, Message, Hyperlink>;

pub struct Hyperlink {
    trailing_icon: bool,
}

/// A hyperlink button.
pub fn link<'a, Message>(label: impl Into<Cow<'a, str>> + 'static) -> Button<'a, Message> {
    Button::new(
        label,
        Hyperlink {
            trailing_icon: false,
        },
    )
}

impl<'a, Message> Button<'a, Message> {
    pub fn new(label: impl Into<Cow<'a, str>> + 'static, link: Hyperlink) -> Self {
        Self {
            id: Id::unique(),
            label: label.into(),
            tooltip: Cow::Borrowed(""),
            on_press: None,
            width: Length::Shrink,
            height: Length::Shrink,
            padding: Padding::from(4),
            spacing: 0,
            icon_size: 16,
            line_height: 20,
            font_size: 14,
            font_weight: Weight::Normal,
            class: ButtonClass::Link,
            variant: link,
        }
    }

    pub const fn trailing_icon(mut self, set: bool) -> Self {
        self.variant.trailing_icon = set;
        self
    }
}

pub fn icon() -> Handle {
    icon::from_svg_bytes(&include_bytes!("external-link.svg")[..]).symbolic(true)
}

impl<'a, Message: Clone + 'static> From<Button<'a, Message>> for Element<'a, Message> {
    fn from(mut builder: Button<'a, Message>) -> Element<'a, Message> {
        let button: super::Button<'a, Message> = row::with_capacity(2)
            .push({
                // TODO: Avoid allocation
                crate::widget::text(builder.label.to_string())
                    .size(builder.font_size)
                    .line_height(LineHeight::Absolute(builder.line_height.into()))
                    .font(crate::font::Font {
                        weight: builder.font_weight,
                        ..crate::font::default()
                    })
            })
            .push_maybe(if builder.variant.trailing_icon {
                Some(icon().icon().size(builder.icon_size))
            } else {
                None
            })
            .padding(builder.padding)
            .width(builder.width)
            .height(builder.height)
            .spacing(builder.spacing)
            .align_y(Alignment::Center)
            .apply(button::custom)
            .padding(0)
            .id(builder.id)
            .on_press_maybe(builder.on_press.take())
            .class(builder.class);

        if builder.tooltip.is_empty() {
            button.into()
        } else {
            tooltip(
                button,
                crate::widget::text(builder.tooltip)
                    .size(builder.font_size)
                    .font(crate::font::Font {
                        weight: builder.font_weight,
                        ..crate::font::default()
                    }),
                tooltip::Position::Top,
            )
            .into()
        }
    }
}