From 41c64e291276e5d36ab3160faf0776b524afc23c Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Wed, 16 Oct 2024 17:51:39 +0800 Subject: [PATCH] chore: Update controls detail. (#352) - Better icon style in Input, Dropdown, DatePicker. --- crates/ui/src/button.rs | 1 + crates/ui/src/button_group.rs | 1 + crates/ui/src/checkbox.rs | 33 +++++++---------------------- crates/ui/src/context_menu.rs | 1 + crates/ui/src/divider.rs | 1 + crates/ui/src/dropdown.rs | 6 ++++-- crates/ui/src/event.rs | 7 ------ crates/ui/src/history.rs | 5 +++++ crates/ui/src/input/clear_button.rs | 11 +++++----- crates/ui/src/input/input.rs | 4 +++- crates/ui/src/input/otp_input.rs | 8 +++++++ crates/ui/src/label.rs | 4 ++-- crates/ui/src/lib.rs | 9 ++++++-- crates/ui/src/link.rs | 1 + crates/ui/src/list/list.rs | 3 ++- crates/ui/src/list/list_item.rs | 14 ++---------- crates/ui/src/notification.rs | 1 + crates/ui/src/popup_menu.rs | 3 --- crates/ui/src/progress.rs | 4 ++-- crates/ui/src/radio.rs | 12 +++++------ crates/ui/src/root.rs | 13 ++++++------ crates/ui/src/skeleton.rs | 6 ++---- crates/ui/src/slider.rs | 2 +- crates/ui/src/switch.rs | 7 ++---- crates/ui/src/time/date_picker.rs | 1 + 25 files changed, 73 insertions(+), 85 deletions(-) diff --git a/crates/ui/src/button.rs b/crates/ui/src/button.rs index 435ccea7..ab5e94ae 100644 --- a/crates/ui/src/button.rs +++ b/crates/ui/src/button.rs @@ -143,6 +143,7 @@ impl ButtonStyle { } } +/// A Button element. #[derive(IntoElement)] pub struct Button { pub base: Div, diff --git a/crates/ui/src/button_group.rs b/crates/ui/src/button_group.rs index 3db8a1f2..b11bb118 100644 --- a/crates/ui/src/button_group.rs +++ b/crates/ui/src/button_group.rs @@ -9,6 +9,7 @@ use crate::{ Disableable, Sizable, Size, }; +/// A ButtonGroup element, to wrap multiple buttons in a group. #[derive(IntoElement)] pub struct ButtonGroup { pub base: Div, diff --git a/crates/ui/src/checkbox.rs b/crates/ui/src/checkbox.rs index 2482279e..2f79646b 100644 --- a/crates/ui/src/checkbox.rs +++ b/crates/ui/src/checkbox.rs @@ -1,21 +1,17 @@ +use crate::{h_flex, theme::ActiveTheme, v_flex, Disableable, IconName, Selectable}; use gpui::{ div, prelude::FluentBuilder as _, relative, svg, ElementId, InteractiveElement, IntoElement, ParentElement, RenderOnce, SharedString, StatefulInteractiveElement as _, Styled as _, WindowContext, }; -use crate::{ - h_flex, - theme::{ActiveTheme, Colorize as _}, - v_flex, Disableable, IconName, Selectable, -}; - +/// A Checkbox element. #[derive(IntoElement)] pub struct Checkbox { id: ElementId, + label: Option, checked: bool, disabled: bool, - label: Option, on_click: Option>, } @@ -23,9 +19,9 @@ impl Checkbox { pub fn new(id: impl Into) -> Self { Self { id: id.into(), + label: None, checked: false, disabled: false, - label: None, on_click: None, } } @@ -65,22 +61,17 @@ impl Selectable for Checkbox { impl RenderOnce for Checkbox { fn render(self, cx: &mut WindowContext) -> impl IntoElement { - let theme = cx.theme(); - - let group_id = format!("checkbox_group_{:?}", self.id); - let (color, icon_color) = if self.disabled { ( - theme.primary.opacity(0.5), - theme.primary_foreground.opacity(0.5), + cx.theme().primary.opacity(0.5), + cx.theme().primary_foreground.opacity(0.5), ) } else { - (theme.primary, theme.primary_foreground) + (cx.theme().primary, cx.theme().primary_foreground) }; h_flex() .id(self.id) - .group(group_id.clone()) .gap_2() .items_center() .line_height(relative(1.)) @@ -93,16 +84,9 @@ impl RenderOnce for Checkbox { .size_4() .flex_shrink_0() .map(|this| match self.checked { - false => this.bg(theme.transparent), + false => this.bg(cx.theme().transparent), _ => this.bg(color), }) - .group_hover(group_id, |this| { - if self.disabled { - return this; - } - - this.border_color(theme.primary.divide(0.9)) - }) .child( svg() .absolute() @@ -140,7 +124,6 @@ impl RenderOnce for Checkbox { this.on_click(move |_, cx| { let checked = !self.checked; on_click(&checked, cx); - cx.refresh() }) }, ) diff --git a/crates/ui/src/context_menu.rs b/crates/ui/src/context_menu.rs index ea210bc6..2d968760 100644 --- a/crates/ui/src/context_menu.rs +++ b/crates/ui/src/context_menu.rs @@ -23,6 +23,7 @@ pub trait ContextMenuExt: ParentElement + Sized { impl ContextMenuExt for Stateful where E: ParentElement {} impl ContextMenuExt for Focusable where E: ParentElement {} +/// A context menu that can be shown on right-click. pub struct ContextMenu { id: ElementId, menu: Option) -> PopupMenu + 'static>>, diff --git a/crates/ui/src/divider.rs b/crates/ui/src/divider.rs index 4b03024e..9a049981 100644 --- a/crates/ui/src/divider.rs +++ b/crates/ui/src/divider.rs @@ -5,6 +5,7 @@ use gpui::{ use crate::theme::ActiveTheme; +/// A divider that can be either vertical or horizontal. #[derive(IntoElement)] pub struct Divider { base: Div, diff --git a/crates/ui/src/dropdown.rs b/crates/ui/src/dropdown.rs index 82713d20..7539b675 100644 --- a/crates/ui/src/dropdown.rs +++ b/crates/ui/src/dropdown.rs @@ -212,6 +212,7 @@ pub enum DropdownEvent { Confirm(Option<::Value>), } +/// A Dropdown element. pub struct Dropdown { id: ElementId, focus_handle: FocusHandle, @@ -659,9 +660,10 @@ where this.child( Icon::new(icon) + .xsmall() .text_color(match self.disabled { - true => cx.theme().muted_foreground, - false => cx.theme().accent_foreground, + true => cx.theme().muted_foreground.opacity(0.5), + false => cx.theme().muted_foreground, }) .when(self.disabled, |this| this.cursor_not_allowed()), ) diff --git a/crates/ui/src/event.rs b/crates/ui/src/event.rs index a54e59cc..07d124e3 100644 --- a/crates/ui/src/event.rs +++ b/crates/ui/src/event.rs @@ -19,10 +19,3 @@ pub trait InteractiveElementExt: InteractiveElement { } impl InteractiveElementExt for Focusable {} - -// impl InteractiveElementExt for Stateful -// where -// E: Element, -// Self: InteractiveElement, -// { -// } diff --git a/crates/ui/src/history.rs b/crates/ui/src/history.rs index 45179945..6a854b72 100644 --- a/crates/ui/src/history.rs +++ b/crates/ui/src/history.rs @@ -8,6 +8,11 @@ pub trait HistoryItem: Clone { fn set_version(&mut self, version: usize); } +/// The History is used to keep track of changes to a model and to allow undo and redo operations. +/// +/// This is now used in Input for undo/redo operations. You can also use this in +/// your own models to keep track of changes, for example to track the tab +/// history for prev/next features. #[derive(Debug)] pub struct History { undos: Vec, diff --git a/crates/ui/src/input/clear_button.rs b/crates/ui/src/input/clear_button.rs index ef545d08..9dca5cc8 100644 --- a/crates/ui/src/input/clear_button.rs +++ b/crates/ui/src/input/clear_button.rs @@ -1,17 +1,18 @@ -use gpui::{px, WindowContext}; +use gpui::{Styled, WindowContext}; use crate::{ button::{Button, ButtonStyled as _}, - IconName, Sizable as _, + theme::ActiveTheme as _, + Icon, IconName, Sizable as _, }; pub(crate) struct ClearButton {} impl ClearButton { - pub fn new(_: &mut WindowContext) -> Button { + pub fn new(cx: &mut WindowContext) -> Button { Button::new("clean") - .icon(IconName::CircleX) + .icon(Icon::new(IconName::CircleX).text_color(cx.theme().muted_foreground)) .ghost() - .with_size(px(14.)) + .xsmall() } } diff --git a/crates/ui/src/input/input.rs b/crates/ui/src/input/input.rs index 60e1662d..7ac7f8b4 100644 --- a/crates/ui/src/input/input.rs +++ b/crates/ui/src/input/input.rs @@ -1152,7 +1152,9 @@ impl Render for TextInput { input: cx.view().clone(), }), ) - .when(self.loading, |this| this.child(Indicator::new())) + .when(self.loading, |this| { + this.child(Indicator::new().color(cx.theme().muted_foreground)) + }) .when( self.cleanable && !self.loading && !self.text.is_empty(), |this| this.child(ClearButton::new(cx).on_click(cx.listener(Self::clean))), diff --git a/crates/ui/src/input/otp_input.rs b/crates/ui/src/input/otp_input.rs index 41b3fdf9..bb7054b0 100644 --- a/crates/ui/src/input/otp_input.rs +++ b/crates/ui/src/input/otp_input.rs @@ -13,6 +13,14 @@ pub enum InputOptEvent { Change(SharedString), } +/// A One Time Password (OTP) input element. +/// +/// This can accept a fixed length number and can be masked. +/// +/// Use case example: +/// +/// - SMS OTP +/// - Authenticator OTP pub struct OtpInput { focus_handle: FocusHandle, length: usize, diff --git a/crates/ui/src/label.rs b/crates/ui/src/label.rs index b87377ee..704700fb 100644 --- a/crates/ui/src/label.rs +++ b/crates/ui/src/label.rs @@ -5,6 +5,8 @@ use gpui::{ use crate::{h_flex, theme::ActiveTheme}; +const MASKED: &'static str = "•"; + #[derive(Default, PartialEq, Eq)] pub enum TextAlign { #[default] @@ -63,8 +65,6 @@ impl Styled for Label { } } -const MASKED: &'static str = "•"; - impl RenderOnce for Label { fn render(self, cx: &mut WindowContext) -> impl IntoElement { let text = self.label; diff --git a/crates/ui/src/lib.rs b/crates/ui/src/lib.rs index e0980161..1d00c124 100644 --- a/crates/ui/src/lib.rs +++ b/crates/ui/src/lib.rs @@ -58,7 +58,14 @@ pub use colors::*; pub use icon::*; pub use svg_img::*; +use std::ops::Deref; + +rust_i18n::i18n!("locales", fallback = "en"); + /// Initialize the UI module. +/// +/// This must be called before using any of the UI components. +/// You can initialize the UI module at your application's entry point. pub fn init(cx: &mut gpui::AppContext) { theme::init(cx); context_menu::init(cx); @@ -75,8 +82,6 @@ pub fn init(cx: &mut gpui::AppContext) { webview::init(cx); } -rust_i18n::i18n!("locales", fallback = "en"); -use std::ops::Deref; pub fn locale() -> impl Deref { rust_i18n::locale() } diff --git a/crates/ui/src/link.rs b/crates/ui/src/link.rs index 2e298a1e..793c25ef 100644 --- a/crates/ui/src/link.rs +++ b/crates/ui/src/link.rs @@ -5,6 +5,7 @@ use gpui::{ use crate::theme::ActiveTheme as _; +/// A Link element like a `` tag in HTML. #[derive(IntoElement)] pub struct Link { base: Stateful
, diff --git a/crates/ui/src/list/list.rs b/crates/ui/src/list/list.rs index 6470a2b9..22be1971 100644 --- a/crates/ui/src/list/list.rs +++ b/crates/ui/src/list/list.rs @@ -1,6 +1,7 @@ use std::time::Duration; use std::{cell::Cell, rc::Rc}; +use crate::Icon; use crate::{ input::{InputEvent, TextInput}, scroll::{Scrollbar, ScrollbarState}, @@ -102,7 +103,7 @@ where let query_input = cx.new_view(|cx| { TextInput::new(cx) .appearance(false) - .prefix(|_| IconName::Search) + .prefix(|cx| Icon::new(IconName::Search).text_color(cx.theme().muted_foreground)) .placeholder("Search...") .cleanable() }); diff --git a/crates/ui/src/list/list_item.rs b/crates/ui/src/list/list_item.rs index daa3cb26..443907d9 100644 --- a/crates/ui/src/list/list_item.rs +++ b/crates/ui/src/list/list_item.rs @@ -1,12 +1,11 @@ +use crate::{h_flex, theme::ActiveTheme, Disableable, Icon, IconName, Selectable, Sizable as _}; use gpui::{ div, prelude::FluentBuilder as _, AnyElement, ClickEvent, Div, ElementId, InteractiveElement, - IntoElement, MouseButton, MouseMoveEvent, ParentElement, RenderOnce, SharedString, Stateful, + IntoElement, MouseButton, MouseMoveEvent, ParentElement, RenderOnce, Stateful, StatefulInteractiveElement as _, Styled, WindowContext, }; use smallvec::SmallVec; -use crate::{h_flex, theme::ActiveTheme, Disableable, Icon, IconName, Selectable, Sizable as _}; - #[derive(IntoElement)] pub struct ListItem { id: ElementId, @@ -15,7 +14,6 @@ pub struct ListItem { selected: bool, confirmed: bool, check_icon: Option, - group_id: Option, on_click: Option>, on_mouse_enter: Option>, suffix: Option AnyElement + 'static>>, @@ -35,17 +33,10 @@ impl ListItem { on_mouse_enter: None, check_icon: None, suffix: None, - group_id: None, children: SmallVec::new(), } } - /// Set group_id - pub fn group(mut self, group_id: impl Into) -> Self { - self.group_id = Some(group_id.into()); - self - } - /// Set to show check icon, default is None. pub fn check_icon(mut self, icon: IconName) -> Self { self.check_icon = Some(Icon::new(icon)); @@ -128,7 +119,6 @@ impl RenderOnce for ListItem { let is_active = self.selected || self.confirmed; self.base - .when_some(self.group_id, |this, group_id| this.group(group_id)) .text_color(cx.theme().foreground) .relative() .items_center() diff --git a/crates/ui/src/notification.rs b/crates/ui/src/notification.rs index 6b6ce43a..e501f4ec 100644 --- a/crates/ui/src/notification.rs +++ b/crates/ui/src/notification.rs @@ -40,6 +40,7 @@ impl From<(TypeId, ElementId)> for NotificationId { } } +/// A notification element. pub struct Notification { /// The id is used make the notification unique. /// Then you push a notification with the same id, the previous notification will be replaced. diff --git a/crates/ui/src/popup_menu.rs b/crates/ui/src/popup_menu.rs index 75ae08ed..8dd1eea8 100644 --- a/crates/ui/src/popup_menu.rs +++ b/crates/ui/src/popup_menu.rs @@ -522,10 +522,7 @@ impl Render for PopupMenu { !(*ix == items_count - 1 && item.is_separator()) }) .map(|(ix, item)| { - let group_id = format!("item:{}", ix); - let this = ListItem::new(("menu-item", ix)) - .group(group_id.clone()) .relative() .text_sm() .py_0() diff --git a/crates/ui/src/progress.rs b/crates/ui/src/progress.rs index 160e571b..c3eeba99 100644 --- a/crates/ui/src/progress.rs +++ b/crates/ui/src/progress.rs @@ -1,10 +1,10 @@ +use crate::theme::ActiveTheme; use gpui::{ div, prelude::FluentBuilder, px, relative, IntoElement, ParentElement, RenderOnce, Styled, WindowContext, }; -use crate::theme::ActiveTheme; - +/// A Progress bar element. #[derive(IntoElement)] pub struct Progress { value: f32, diff --git a/crates/ui/src/radio.rs b/crates/ui/src/radio.rs index e1692d20..e10157ed 100644 --- a/crates/ui/src/radio.rs +++ b/crates/ui/src/radio.rs @@ -1,11 +1,12 @@ +use crate::{h_flex, theme::ActiveTheme, IconName}; use gpui::{ - div, prelude::FluentBuilder, relative, svg, CursorStyle, ElementId, InteractiveElement, - IntoElement, ParentElement, RenderOnce, SharedString, StatefulInteractiveElement, Styled, - WindowContext, + div, prelude::FluentBuilder, relative, svg, ElementId, InteractiveElement, IntoElement, + ParentElement, RenderOnce, SharedString, StatefulInteractiveElement, Styled, WindowContext, }; -use crate::{h_flex, theme::ActiveTheme, IconName}; - +/// A Radio element. +/// +/// This is not included the Radio group implementation, you can manage the group by yourself. #[derive(IntoElement)] pub struct Radio { id: ElementId, @@ -58,7 +59,6 @@ impl RenderOnce for Radio { h_flex() .id(self.id) .gap_x_2() - .cursor(CursorStyle::PointingHand) .text_color(cx.theme().foreground) .items_center() .line_height(relative(1.)) diff --git a/crates/ui/src/root.rs b/crates/ui/src/root.rs index ded38b75..05fa2722 100644 --- a/crates/ui/src/root.rs +++ b/crates/ui/src/root.rs @@ -1,3 +1,9 @@ +use crate::{ + drawer::Drawer, + modal::Modal, + notification::{Notification, NotificationList}, + theme::ActiveTheme, +}; use gpui::{ div, AnyView, FocusHandle, InteractiveElement, IntoElement, ParentElement as _, Render, Styled, View, ViewContext, VisualContext as _, WindowContext, @@ -7,13 +13,6 @@ use std::{ rc::Rc, }; -use crate::{ - drawer::Drawer, - modal::Modal, - notification::{Notification, NotificationList}, - theme::ActiveTheme, -}; - /// Extension trait for [`WindowContext`] and [`ViewContext`] to add drawer functionality. pub trait ContextModal: Sized { /// Opens a Drawer. diff --git a/crates/ui/src/skeleton.rs b/crates/ui/src/skeleton.rs index 517ea5e7..266db5a3 100644 --- a/crates/ui/src/skeleton.rs +++ b/crates/ui/src/skeleton.rs @@ -1,11 +1,9 @@ -use std::time::Duration; - +use crate::theme::ActiveTheme; use gpui::{ bounce, div, ease_in_out, Animation, AnimationExt, Div, IntoElement, ParentElement as _, RenderOnce, Styled, }; - -use crate::theme::ActiveTheme; +use std::time::Duration; #[derive(IntoElement)] pub struct Skeleton { diff --git a/crates/ui/src/slider.rs b/crates/ui/src/slider.rs index ce6dbb5d..f095793a 100644 --- a/crates/ui/src/slider.rs +++ b/crates/ui/src/slider.rs @@ -13,7 +13,7 @@ pub enum SliderEvent { Change(f32), } -/// A slider component. +/// A Slider element. pub struct Slider { axis: Axis, min: f32, diff --git a/crates/ui/src/switch.rs b/crates/ui/src/switch.rs index c610745c..f8a1d7c1 100644 --- a/crates/ui/src/switch.rs +++ b/crates/ui/src/switch.rs @@ -1,13 +1,10 @@ -use std::{cell::RefCell, rc::Rc, time::Duration}; - use crate::{h_flex, theme::ActiveTheme, Disableable, Sizable, Size}; use gpui::{ div, prelude::FluentBuilder as _, px, Animation, AnimationExt as _, AnyElement, Element, ElementId, GlobalElementId, InteractiveElement, IntoElement, LayoutId, ParentElement as _, SharedString, Styled as _, WindowContext, }; - -type OnClick = Rc; +use std::{cell::RefCell, rc::Rc, time::Duration}; pub enum LabelSide { Left, @@ -26,7 +23,7 @@ pub struct Switch { disabled: bool, label: Option, label_side: LabelSide, - on_click: Option, + on_click: Option>, size: Size, } diff --git a/crates/ui/src/time/date_picker.rs b/crates/ui/src/time/date_picker.rs index f7d74a66..dacb0cb9 100644 --- a/crates/ui/src/time/date_picker.rs +++ b/crates/ui/src/time/date_picker.rs @@ -238,6 +238,7 @@ impl Render for DatePicker { .when(!show_clean, |this| { this.child( Icon::new(IconName::Calendar) + .xsmall() .text_color(cx.theme().muted_foreground), ) }),