1use gpui::prelude::*;
4use gpui::{div, px, App, ClickEvent, ElementId, IntoElement, SharedString, Window};
5
6use super::{control_box_size, ClickHandler};
7use crate::devtools::Probed;
8use crate::theme::{theme, ColorName, Size};
9
10#[derive(IntoElement)]
13pub struct Radio {
14 id: ElementId,
15 checked: bool,
16 label: Option<SharedString>,
17 size: Size,
18 color: ColorName,
19 disabled: bool,
20 on_change: Option<ClickHandler>,
21}
22
23impl Radio {
24 pub fn new(id: impl Into<ElementId>) -> Self {
25 Radio {
26 id: id.into(),
27 checked: false,
28 label: None,
29 size: Size::Sm,
30 color: ColorName::Blue,
31 disabled: false,
32 on_change: None,
33 }
34 }
35
36 pub fn checked(mut self, checked: bool) -> Self {
37 self.checked = checked;
38 self
39 }
40
41 pub fn label(mut self, label: impl Into<SharedString>) -> Self {
42 self.label = Some(label.into());
43 self
44 }
45
46 pub fn size(mut self, size: Size) -> Self {
47 self.size = size;
48 self
49 }
50
51 pub fn color(mut self, color: ColorName) -> Self {
52 self.color = color;
53 self
54 }
55
56 pub fn disabled(mut self, disabled: bool) -> Self {
57 self.disabled = disabled;
58 self
59 }
60
61 pub fn on_change(
62 mut self,
63 handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
64 ) -> Self {
65 self.on_change = Some(Box::new(handler));
66 self
67 }
68}
69
70impl RenderOnce for Radio {
71 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
72 let t = theme(cx);
73 let outer = control_box_size(self.size);
74 let accent = t.color(self.color, t.primary_shade());
75
76 let mut ring = div()
77 .w(px(outer))
78 .h(px(outer))
79 .rounded(px(outer))
80 .flex()
81 .items_center()
82 .justify_center();
83 if self.checked {
84 ring = ring.bg(accent.hsla()).child(
85 div()
86 .w(px(outer * 0.36))
87 .h(px(outer * 0.36))
88 .rounded(px(outer))
89 .bg(t.white.hsla()),
90 );
91 } else {
92 ring = ring
93 .bg(t.surface().hsla())
94 .border_1()
95 .border_color(t.border().hsla());
96 }
97
98 let mut row = div()
99 .id(self.id)
100 .flex()
101 .items_center()
102 .gap(px(8.0))
103 .child(ring);
104 if let Some(label) = self.label {
105 row = row.child(
106 div()
107 .text_size(px(t.font_size(self.size)))
108 .text_color(t.text().hsla())
109 .child(label),
110 );
111 }
112
113 let element = if self.disabled {
114 row.opacity(0.5)
115 } else {
116 if let Some(handler) = self.on_change {
117 row = row.on_click(handler);
118 }
119 row
120 };
121
122 element
123 .probe("Radio")
124 .attr("size", self.size.label())
125 .attr_if("checked", self.checked)
126 .attr_if("disabled", self.disabled)
127 }
128}