chore: Update controls detail. (#352)

- Better icon style in Input, Dropdown, DatePicker.
This commit is contained in:
Jason Lee 2024-10-16 17:51:39 +08:00 committed by GitHub
parent 000c0bec57
commit 41c64e2912
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 73 additions and 85 deletions

View file

@ -143,6 +143,7 @@ impl ButtonStyle {
}
}
/// A Button element.
#[derive(IntoElement)]
pub struct Button {
pub base: Div,

View file

@ -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,

View file

@ -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<SharedString>,
checked: bool,
disabled: bool,
label: Option<SharedString>,
on_click: Option<Box<dyn Fn(&bool, &mut WindowContext) + 'static>>,
}
@ -23,9 +19,9 @@ impl Checkbox {
pub fn new(id: impl Into<ElementId>) -> 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()
})
},
)

View file

@ -23,6 +23,7 @@ pub trait ContextMenuExt: ParentElement + Sized {
impl<E> ContextMenuExt for Stateful<E> where E: ParentElement {}
impl<E> ContextMenuExt for Focusable<E> where E: ParentElement {}
/// A context menu that can be shown on right-click.
pub struct ContextMenu {
id: ElementId,
menu: Option<Box<dyn Fn(PopupMenu, &mut ViewContext<PopupMenu>) -> PopupMenu + 'static>>,

View file

@ -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,

View file

@ -212,6 +212,7 @@ pub enum DropdownEvent<D: DropdownDelegate + 'static> {
Confirm(Option<<D::Item as DropdownItem>::Value>),
}
/// A Dropdown element.
pub struct Dropdown<D: DropdownDelegate + 'static> {
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()),
)

View file

@ -19,10 +19,3 @@ pub trait InteractiveElementExt: InteractiveElement {
}
impl<E: InteractiveElement> InteractiveElementExt for Focusable<E> {}
// impl<E> InteractiveElementExt for Stateful<E>
// where
// E: Element,
// Self: InteractiveElement,
// {
// }

View file

@ -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<I: HistoryItem> {
undos: Vec<I>,

View file

@ -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()
}
}

View file

@ -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))),

View file

@ -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,

View file

@ -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;

View file

@ -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<Target = str> {
rust_i18n::locale()
}

View file

@ -5,6 +5,7 @@ use gpui::{
use crate::theme::ActiveTheme as _;
/// A Link element like a `<a>` tag in HTML.
#[derive(IntoElement)]
pub struct Link {
base: Stateful<Div>,

View file

@ -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()
});

View file

@ -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<Icon>,
group_id: Option<SharedString>,
on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
on_mouse_enter: Option<Box<dyn Fn(&MouseMoveEvent, &mut WindowContext) + 'static>>,
suffix: Option<Box<dyn Fn(&mut WindowContext) -> 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<SharedString>) -> 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()

View file

@ -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.

View file

@ -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()

View file

@ -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,

View file

@ -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.))

View file

@ -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.

View file

@ -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 {

View file

@ -13,7 +13,7 @@ pub enum SliderEvent {
Change(f32),
}
/// A slider component.
/// A Slider element.
pub struct Slider {
axis: Axis,
min: f32,

View file

@ -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<dyn Fn(&bool, &mut WindowContext)>;
use std::{cell::RefCell, rc::Rc, time::Duration};
pub enum LabelSide {
Left,
@ -26,7 +23,7 @@ pub struct Switch {
disabled: bool,
label: Option<SharedString>,
label_side: LabelSide,
on_click: Option<OnClick>,
on_click: Option<Rc<dyn Fn(&bool, &mut WindowContext)>>,
size: Size,
}

View file

@ -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),
)
}),