gpui_component/
link.rs

1use gpui::{
2    div, AnyElement, ClickEvent, ElementId, InteractiveElement, IntoElement, MouseButton,
3    ParentElement, RenderOnce, SharedString, StatefulInteractiveElement, StyleRefinement, Styled,
4};
5
6use crate::{ActiveTheme as _, StyledExt};
7
8/// A Link element like a `<a>` tag in HTML.
9#[derive(IntoElement)]
10pub struct Link {
11    id: ElementId,
12    style: StyleRefinement,
13    href: Option<SharedString>,
14    disabled: bool,
15    on_click: Option<Box<dyn Fn(&ClickEvent, &mut gpui::Window, &mut gpui::App) + 'static>>,
16    children: Vec<AnyElement>,
17}
18
19impl Link {
20    pub fn new(id: impl Into<ElementId>) -> Self {
21        Self {
22            id: id.into(),
23            style: StyleRefinement::default(),
24            href: None,
25            on_click: None,
26            disabled: false,
27            children: Vec::new(),
28        }
29    }
30
31    pub fn href(mut self, href: impl Into<SharedString>) -> Self {
32        self.href = Some(href.into());
33        self
34    }
35
36    pub fn on_click(
37        mut self,
38        handler: impl Fn(&ClickEvent, &mut gpui::Window, &mut gpui::App) + 'static,
39    ) -> Self {
40        self.on_click = Some(Box::new(handler));
41        self
42    }
43
44    pub fn disabled(mut self, disabled: bool) -> Self {
45        self.disabled = disabled;
46        self
47    }
48}
49
50impl Styled for Link {
51    fn style(&mut self) -> &mut gpui::StyleRefinement {
52        &mut self.style
53    }
54}
55
56impl ParentElement for Link {
57    fn extend(&mut self, elements: impl IntoIterator<Item = gpui::AnyElement>) {
58        self.children.extend(elements)
59    }
60}
61
62impl RenderOnce for Link {
63    fn render(self, _: &mut gpui::Window, cx: &mut gpui::App) -> impl IntoElement {
64        let href = self.href.clone();
65        let on_click = self.on_click;
66
67        div()
68            .id(self.id)
69            .text_color(cx.theme().link)
70            .text_decoration_1()
71            .text_decoration_color(cx.theme().link)
72            .hover(|this| {
73                this.text_color(cx.theme().link.opacity(0.8))
74                    .text_decoration_1()
75            })
76            .active(|this| {
77                this.text_color(cx.theme().link.opacity(0.6))
78                    .text_decoration_1()
79            })
80            .cursor_pointer()
81            .refine_style(&self.style)
82            .on_mouse_down(MouseButton::Left, |_, _, cx| {
83                cx.stop_propagation();
84            })
85            .on_click({
86                move |e, window, cx| {
87                    if let Some(href) = &href {
88                        cx.open_url(&href.clone());
89                    }
90                    if let Some(on_click) = &on_click {
91                        on_click(e, window, cx);
92                    }
93                }
94            })
95            .children(self.children)
96    }
97}