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    /// Create a new Link element.
21    pub fn new(id: impl Into<ElementId>) -> Self {
22        Self {
23            id: id.into(),
24            style: StyleRefinement::default(),
25            href: None,
26            on_click: None,
27            disabled: false,
28            children: Vec::new(),
29        }
30    }
31
32    /// Set the href of the link.
33    pub fn href(mut self, href: impl Into<SharedString>) -> Self {
34        self.href = Some(href.into());
35        self
36    }
37
38    /// Set the click handler of the link.
39    ///
40    /// If this set, the handler will be called when the link is clicked.
41    /// Otherwise, the link will only open the href if set.
42    pub fn on_click(
43        mut self,
44        handler: impl Fn(&ClickEvent, &mut gpui::Window, &mut gpui::App) + 'static,
45    ) -> Self {
46        self.on_click = Some(Box::new(handler));
47        self
48    }
49
50    /// Set the disabled state, default false.
51    pub fn disabled(mut self, disabled: bool) -> Self {
52        self.disabled = disabled;
53        self
54    }
55}
56
57impl Styled for Link {
58    fn style(&mut self) -> &mut gpui::StyleRefinement {
59        &mut self.style
60    }
61}
62
63impl ParentElement for Link {
64    fn extend(&mut self, elements: impl IntoIterator<Item = gpui::AnyElement>) {
65        self.children.extend(elements)
66    }
67}
68
69impl RenderOnce for Link {
70    fn render(self, _: &mut gpui::Window, cx: &mut gpui::App) -> impl IntoElement {
71        let href = self.href.clone();
72        let on_click = self.on_click;
73
74        div()
75            .id(self.id)
76            .text_color(cx.theme().link)
77            .text_decoration_1()
78            .text_decoration_color(cx.theme().link)
79            .hover(|this| {
80                this.text_color(cx.theme().link.opacity(0.8))
81                    .text_decoration_1()
82            })
83            .active(|this| {
84                this.text_color(cx.theme().link.opacity(0.6))
85                    .text_decoration_1()
86            })
87            .cursor_pointer()
88            .refine_style(&self.style)
89            .on_mouse_down(MouseButton::Left, |_, _, cx| {
90                cx.stop_propagation();
91            })
92            .on_click({
93                move |e, window, cx| {
94                    if let Some(href) = &href {
95                        cx.open_url(&href.clone());
96                    }
97                    if let Some(on_click) = &on_click {
98                        on_click(e, window, cx);
99                    }
100                }
101            })
102            .children(self.children)
103    }
104}