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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
use freya_core::prelude::*;
use freya_router::prelude::{
NavigationTarget,
RouterContext,
};
use crate::{
define_theme,
get_theme,
tooltip::{
Tooltip,
TooltipContainer,
},
};
define_theme! {
%[component]
pub Link {
%[fields]
color: Color,
}
}
/// Tooltip configuration for the [`Link`] component.
#[derive(Clone, PartialEq)]
pub enum LinkTooltip {
/// No tooltip at all.
None,
/// Default tooltip.
///
/// - For a route, this is the same as [`None`](LinkTooltip::None).
/// - For a URL, this is the value of that URL.
Default,
/// Custom tooltip to always show.
Custom(String),
}
/// An wrapper component that navigates to an internal route or external URL on press.
///
/// Pass a route variant to push it through the nearest router when pressed. Pass
/// an absolute URL to open it in the system browser.
///
/// See the [router example](https://github.com/marc2332/freya/blob/main/examples/feature_router.rs).
#[derive(PartialEq)]
pub struct Link {
/// Theme override.
pub(crate) theme: Option<LinkThemePartial>,
/// The route or external URL string to navigate to.
to: NavigationTarget,
/// Inner children for the Link.
children: Vec<Element>,
/// A text hint to show when hovering over the link.
tooltip: LinkTooltip,
/// Key for the component.
key: DiffKey,
}
impl ChildrenExt for Link {
fn children_mut(&mut self) -> &mut Vec<Element> {
&mut self.children
}
}
impl KeyExt for Link {
fn key_mut(&mut self) -> &mut DiffKey {
&mut self.key
}
}
impl Link {
/// Creates a link to an internal route or external URL.
pub fn new(to: impl Into<NavigationTarget>) -> Self {
Self {
to: to.into(),
children: Vec::new(),
tooltip: LinkTooltip::Default,
theme: None,
key: DiffKey::None,
}
}
/// Sets the tooltip shown while hovering over this link.
pub fn tooltip(mut self, tooltip: impl Into<LinkTooltip>) -> Self {
self.tooltip = tooltip.into();
self
}
}
impl Component for Link {
fn render(&self) -> impl IntoElement {
let theme = get_theme!(&self.theme, LinkThemePreference, "link");
let mut is_hovering = use_state(|| false);
let url = if let NavigationTarget::External(ref url) = self.to {
Some(url.clone())
} else {
None
};
let on_pointer_enter = move |_| {
is_hovering.set(true);
};
let on_pointer_leave = move |_| {
is_hovering.set(false);
};
let on_press = {
let to = self.to.clone();
let url = url.clone();
move |_| {
// Open the url if there is any
// otherwise change the freya router route
if let Some(url) = &url {
Platform::get().open_url(url.clone());
} else {
let _ = RouterContext::get().push(to.clone());
}
}
};
let color = if *is_hovering.read() {
Some(theme.color)
} else {
None
};
let tooltip_text = match &self.tooltip {
LinkTooltip::Default => url,
LinkTooltip::None => None,
LinkTooltip::Custom(str) => Some(str.clone()),
};
let link = rect()
.cursor(CursorIcon::Pointer)
.on_press(on_press)
.on_pointer_enter(on_pointer_enter)
.on_pointer_leave(on_pointer_leave)
.map(color, |rect, color| rect.color(color))
.children(self.children.clone());
if let Some(tooltip_text) = tooltip_text {
TooltipContainer::new(Tooltip::new_text(tooltip_text))
.child(link)
.into_element()
} else {
link.into()
}
}
fn render_key(&self) -> DiffKey {
self.key.clone().or(self.default_key())
}
}