From 58dbf88af7b9d9c2db2eaef203a0a24ca3e22fc7 Mon Sep 17 00:00:00 2001 From: Floyd Wang Date: Fri, 12 Jul 2024 16:37:31 +0800 Subject: [PATCH] Add Radio (#24) --- crates/story/src/checkbox_story.rs | 31 +++++++++- crates/ui/src/lib.rs | 1 + crates/ui/src/radio.rs | 98 ++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 crates/ui/src/radio.rs diff --git a/crates/story/src/checkbox_story.rs b/crates/story/src/checkbox_story.rs index 9ed98052..cd688a94 100644 --- a/crates/story/src/checkbox_story.rs +++ b/crates/story/src/checkbox_story.rs @@ -3,12 +3,14 @@ use gpui::{ WindowContext, }; -use ui::{checkbox::Checkbox, h_flex, v_flex, Disableable as _, Selection}; +use ui::{checkbox::Checkbox, h_flex, radio::Radio, v_flex, Disableable as _, Selection}; pub struct CheckboxStory { check1: Selection, check2: Selection, check3: Selection, + select1: bool, + select2: bool, } impl CheckboxStory { @@ -17,6 +19,8 @@ impl CheckboxStory { check1: Selection::Unselected, check2: Selection::Indeterminate, check3: Selection::Selected, + select1: false, + select2: true, } } @@ -90,5 +94,30 @@ impl Render for CheckboxStory { ), ), ) + .child( + h_flex() + .gap_4() + .child( + Radio::new("radio1") + .selected(self.select1) + .on_click(cx.listener(|this, v, _cx| { + this.select1 = *v; + })), + ) + .child( + Radio::new("radio2") + .label("Radio") + .selected(self.select2) + .on_click(cx.listener(|this, v, _cx| { + this.select2 = *v; + })), + ) + .child( + Radio::new("radio3") + .label("Disabled Radio") + .selected(true) + .disabled(true), + ), + ) } } diff --git a/crates/ui/src/lib.rs b/crates/ui/src/lib.rs index 56bff5d6..e8a4aaec 100644 --- a/crates/ui/src/lib.rs +++ b/crates/ui/src/lib.rs @@ -22,6 +22,7 @@ pub mod popover; pub mod popup_menu; pub mod prelude; pub mod progress; +pub mod radio; pub mod resizable; pub mod switch; pub mod tab; diff --git a/crates/ui/src/radio.rs b/crates/ui/src/radio.rs new file mode 100644 index 00000000..e780c832 --- /dev/null +++ b/crates/ui/src/radio.rs @@ -0,0 +1,98 @@ +use gpui::{ + div, prelude::FluentBuilder, svg, CursorStyle, ElementId, InteractiveElement, IntoElement, + ParentElement, RenderOnce, SharedString, StatefulInteractiveElement, Styled, WindowContext, +}; + +use crate::{ + h_flex, + theme::{ActiveTheme, Colorize}, + IconName, +}; + +#[derive(IntoElement)] +pub struct Radio { + id: ElementId, + label: Option, + selected: bool, + disabled: bool, + on_click: Option>, +} + +impl Radio { + pub fn new(id: impl Into) -> Self { + Self { + id: id.into(), + label: None, + selected: false, + disabled: false, + on_click: None, + } + } + + pub fn label(mut self, label: impl Into) -> Self { + self.label = Some(label.into()); + self + } + + pub fn selected(mut self, selected: bool) -> Self { + self.selected = selected; + self + } + + pub fn disabled(mut self, disabled: bool) -> Self { + self.disabled = disabled; + self + } + + pub fn on_click(mut self, handler: impl Fn(&bool, &mut WindowContext) + 'static) -> Self { + self.on_click = Some(Box::new(handler)); + self + } +} + +impl RenderOnce for Radio { + fn render(self, cx: &mut WindowContext) -> impl IntoElement { + let color = if self.disabled { + cx.theme().primary.opacity(0.5) + } else { + cx.theme().primary + }; + + h_flex() + .id(self.id) + .gap_x_2() + .cursor(CursorStyle::PointingHand) + .text_color(color) + .child( + div() + .relative() + .w_3p5() + .h_3p5() + .rounded_full() + .border_1() + .border_color(color) + .mt_neg_0p5() + .child( + svg() + .absolute() + .top_px() + .left_px() + .size_2p5() + .text_color(color) + .map(|this| match self.selected { + true => this.path(IconName::Check.path()), + false => this, + }), + ), + ) + .when_some(self.label, |this, label| this.child(label)) + .when_some( + self.on_click.filter(|_| !self.disabled), + |this, on_click| { + this.on_click(move |_event, cx| { + (on_click)(&!self.selected, cx); + }) + }, + ) + } +}