mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-29 12:02:04 +03:00
chore: Revert changes made to ui crate structure (#17516)
As a part of https://github.com/zed-industries/zed/pull/17488 I flattened module structure of ui crate to fix module_inception lint. However, that's actually unnecessary as we can pass that lint via a custom knob for clippy. Closes #ISSUE Release Notes: - N/A
This commit is contained in:
parent
986e0113e5
commit
832e3e7dd7
@ -576,7 +576,7 @@ new_ret_no_self = { level = "allow" }
|
||||
# We have a few `next` functions that differ in lifetimes
|
||||
# compared to Iterator::next. Yet, clippy complains about those.
|
||||
should_implement_trait = { level = "allow" }
|
||||
|
||||
module_inception = { level = "deny" }
|
||||
# Individual rules that have violations in the codebase:
|
||||
type_complexity = "allow"
|
||||
|
||||
|
1
clippy.toml
Normal file
1
clippy.toml
Normal file
@ -0,0 +1 @@
|
||||
allow-private-module-inception = true
|
@ -1,133 +1,7 @@
|
||||
mod avatar;
|
||||
mod avatar_audio_status_indicator;
|
||||
mod avatar_availability_indicator;
|
||||
|
||||
pub use avatar::*;
|
||||
pub use avatar_audio_status_indicator::*;
|
||||
pub use avatar_availability_indicator::*;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use gpui::{img, AnyElement, Hsla, ImageSource, Img, IntoElement, Styled};
|
||||
|
||||
/// The shape of an [`Avatar`].
|
||||
#[derive(Debug, Default, PartialEq, Clone)]
|
||||
pub enum AvatarShape {
|
||||
/// The avatar is shown in a circle.
|
||||
#[default]
|
||||
Circle,
|
||||
/// The avatar is shown in a rectangle with rounded corners.
|
||||
RoundedRectangle,
|
||||
}
|
||||
|
||||
/// An element that renders a user avatar with customizable appearance options.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::{Avatar, AvatarShape};
|
||||
///
|
||||
/// Avatar::new("path/to/image.png")
|
||||
/// .shape(AvatarShape::Circle)
|
||||
/// .grayscale(true)
|
||||
/// .border_color(gpui::red());
|
||||
/// ```
|
||||
#[derive(IntoElement)]
|
||||
pub struct Avatar {
|
||||
image: Img,
|
||||
size: Option<AbsoluteLength>,
|
||||
border_color: Option<Hsla>,
|
||||
indicator: Option<AnyElement>,
|
||||
}
|
||||
|
||||
impl Avatar {
|
||||
pub fn new(src: impl Into<ImageSource>) -> Self {
|
||||
Avatar {
|
||||
image: img(src),
|
||||
size: None,
|
||||
border_color: None,
|
||||
indicator: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the shape of the avatar image.
|
||||
///
|
||||
/// This method allows the shape of the avatar to be specified using an [`AvatarShape`].
|
||||
/// It modifies the corner radius of the image to match the specified shape.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::{Avatar, AvatarShape};
|
||||
///
|
||||
/// Avatar::new("path/to/image.png").shape(AvatarShape::Circle);
|
||||
/// ```
|
||||
pub fn shape(mut self, shape: AvatarShape) -> Self {
|
||||
self.image = match shape {
|
||||
AvatarShape::Circle => self.image.rounded_full(),
|
||||
AvatarShape::RoundedRectangle => self.image.rounded_md(),
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
/// Applies a grayscale filter to the avatar image.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::{Avatar, AvatarShape};
|
||||
///
|
||||
/// let avatar = Avatar::new("path/to/image.png").grayscale(true);
|
||||
/// ```
|
||||
pub fn grayscale(mut self, grayscale: bool) -> Self {
|
||||
self.image = self.image.grayscale(grayscale);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn border_color(mut self, color: impl Into<Hsla>) -> Self {
|
||||
self.border_color = Some(color.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Size overrides the avatar size. By default they are 1rem.
|
||||
pub fn size<L: Into<AbsoluteLength>>(mut self, size: impl Into<Option<L>>) -> Self {
|
||||
self.size = size.into().map(Into::into);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn indicator<E: IntoElement>(mut self, indicator: impl Into<Option<E>>) -> Self {
|
||||
self.indicator = indicator.into().map(IntoElement::into_any_element);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Avatar {
|
||||
fn render(mut self, cx: &mut WindowContext) -> impl IntoElement {
|
||||
if self.image.style().corner_radii.top_left.is_none() {
|
||||
self = self.shape(AvatarShape::Circle);
|
||||
}
|
||||
|
||||
let border_width = if self.border_color.is_some() {
|
||||
px(2.)
|
||||
} else {
|
||||
px(0.)
|
||||
};
|
||||
|
||||
let image_size = self.size.unwrap_or_else(|| rems(1.).into());
|
||||
let container_size = image_size.to_pixels(cx.rem_size()) + border_width * 2.;
|
||||
|
||||
div()
|
||||
.size(container_size)
|
||||
.map(|mut div| {
|
||||
div.style().corner_radii = self.image.style().corner_radii.clone();
|
||||
div
|
||||
})
|
||||
.when_some(self.border_color, |this, color| {
|
||||
this.border(border_width).border_color(color)
|
||||
})
|
||||
.child(
|
||||
self.image
|
||||
.size(image_size)
|
||||
.bg(cx.theme().colors().ghost_element_background),
|
||||
)
|
||||
.children(self.indicator.map(|indicator| div().child(indicator)))
|
||||
}
|
||||
}
|
||||
|
127
crates/ui/src/components/avatar/avatar.rs
Normal file
127
crates/ui/src/components/avatar/avatar.rs
Normal file
@ -0,0 +1,127 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
use gpui::{img, AnyElement, Hsla, ImageSource, Img, IntoElement, Styled};
|
||||
|
||||
/// The shape of an [`Avatar`].
|
||||
#[derive(Debug, Default, PartialEq, Clone)]
|
||||
pub enum AvatarShape {
|
||||
/// The avatar is shown in a circle.
|
||||
#[default]
|
||||
Circle,
|
||||
/// The avatar is shown in a rectangle with rounded corners.
|
||||
RoundedRectangle,
|
||||
}
|
||||
|
||||
/// An element that renders a user avatar with customizable appearance options.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::{Avatar, AvatarShape};
|
||||
///
|
||||
/// Avatar::new("path/to/image.png")
|
||||
/// .shape(AvatarShape::Circle)
|
||||
/// .grayscale(true)
|
||||
/// .border_color(gpui::red());
|
||||
/// ```
|
||||
#[derive(IntoElement)]
|
||||
pub struct Avatar {
|
||||
image: Img,
|
||||
size: Option<AbsoluteLength>,
|
||||
border_color: Option<Hsla>,
|
||||
indicator: Option<AnyElement>,
|
||||
}
|
||||
|
||||
impl Avatar {
|
||||
pub fn new(src: impl Into<ImageSource>) -> Self {
|
||||
Avatar {
|
||||
image: img(src),
|
||||
size: None,
|
||||
border_color: None,
|
||||
indicator: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the shape of the avatar image.
|
||||
///
|
||||
/// This method allows the shape of the avatar to be specified using an [`AvatarShape`].
|
||||
/// It modifies the corner radius of the image to match the specified shape.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::{Avatar, AvatarShape};
|
||||
///
|
||||
/// Avatar::new("path/to/image.png").shape(AvatarShape::Circle);
|
||||
/// ```
|
||||
pub fn shape(mut self, shape: AvatarShape) -> Self {
|
||||
self.image = match shape {
|
||||
AvatarShape::Circle => self.image.rounded_full(),
|
||||
AvatarShape::RoundedRectangle => self.image.rounded_md(),
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
/// Applies a grayscale filter to the avatar image.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::{Avatar, AvatarShape};
|
||||
///
|
||||
/// let avatar = Avatar::new("path/to/image.png").grayscale(true);
|
||||
/// ```
|
||||
pub fn grayscale(mut self, grayscale: bool) -> Self {
|
||||
self.image = self.image.grayscale(grayscale);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn border_color(mut self, color: impl Into<Hsla>) -> Self {
|
||||
self.border_color = Some(color.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Size overrides the avatar size. By default they are 1rem.
|
||||
pub fn size<L: Into<AbsoluteLength>>(mut self, size: impl Into<Option<L>>) -> Self {
|
||||
self.size = size.into().map(Into::into);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn indicator<E: IntoElement>(mut self, indicator: impl Into<Option<E>>) -> Self {
|
||||
self.indicator = indicator.into().map(IntoElement::into_any_element);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Avatar {
|
||||
fn render(mut self, cx: &mut WindowContext) -> impl IntoElement {
|
||||
if self.image.style().corner_radii.top_left.is_none() {
|
||||
self = self.shape(AvatarShape::Circle);
|
||||
}
|
||||
|
||||
let border_width = if self.border_color.is_some() {
|
||||
px(2.)
|
||||
} else {
|
||||
px(0.)
|
||||
};
|
||||
|
||||
let image_size = self.size.unwrap_or_else(|| rems(1.).into());
|
||||
let container_size = image_size.to_pixels(cx.rem_size()) + border_width * 2.;
|
||||
|
||||
div()
|
||||
.size(container_size)
|
||||
.map(|mut div| {
|
||||
div.style().corner_radii = self.image.style().corner_radii.clone();
|
||||
div
|
||||
})
|
||||
.when_some(self.border_color, |this, color| {
|
||||
this.border(border_width).border_color(color)
|
||||
})
|
||||
.child(
|
||||
self.image
|
||||
.size(image_size)
|
||||
.bg(cx.theme().colors().ghost_element_background),
|
||||
)
|
||||
.children(self.indicator.map(|indicator| div().child(indicator)))
|
||||
}
|
||||
}
|
@ -1,445 +1,10 @@
|
||||
mod button_icon;
|
||||
mod button;
|
||||
pub(self) mod button_icon;
|
||||
mod button_like;
|
||||
mod icon_button;
|
||||
mod toggle_button;
|
||||
|
||||
pub use button::*;
|
||||
pub use button_like::*;
|
||||
pub use icon_button::*;
|
||||
pub use toggle_button::*;
|
||||
|
||||
use gpui::{AnyView, DefiniteLength};
|
||||
|
||||
use crate::{prelude::*, ElevationIndex, KeyBinding, Spacing};
|
||||
use crate::{IconName, IconSize, Label, LineHeightStyle};
|
||||
|
||||
use button_icon::ButtonIcon;
|
||||
|
||||
/// An element that creates a button with a label and an optional icon.
|
||||
///
|
||||
/// Common buttons:
|
||||
/// - Label, Icon + Label: [`Button`] (this component)
|
||||
/// - Icon only: [`IconButton`]
|
||||
/// - Custom: [`ButtonLike`]
|
||||
///
|
||||
/// To create a more complex button than what the [`Button`] or [`IconButton`] components provide, use
|
||||
/// [`ButtonLike`] directly.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// **A button with a label**, is typically used in scenarios such as a form, where the button's label
|
||||
/// indicates what action will be performed when the button is clicked.
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// Button::new("button_id", "Click me!")
|
||||
/// .on_click(|event, cx| {
|
||||
/// // Handle click event
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// **A toggleable button**, is typically used in scenarios such as a toolbar,
|
||||
/// where the button's state indicates whether a feature is enabled or not, or
|
||||
/// a trigger for a popover menu, where clicking the button toggles the visibility of the menu.
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// Button::new("button_id", "Click me!")
|
||||
/// .icon(IconName::Check)
|
||||
/// .selected(true)
|
||||
/// .on_click(|event, cx| {
|
||||
/// // Handle click event
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// To change the style of the button when it is selected use the [`selected_style`][Button::selected_style] method.
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
/// use ui::TintColor;
|
||||
///
|
||||
/// Button::new("button_id", "Click me!")
|
||||
/// .selected(true)
|
||||
/// .selected_style(ButtonStyle::Tinted(TintColor::Accent))
|
||||
/// .on_click(|event, cx| {
|
||||
/// // Handle click event
|
||||
/// });
|
||||
/// ```
|
||||
/// This will create a button with a blue tinted background when selected.
|
||||
///
|
||||
/// **A full-width button**, is typically used in scenarios such as the bottom of a modal or form, where it occupies the entire width of its container.
|
||||
/// The button's content, including text and icons, is centered by default.
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// let button = Button::new("button_id", "Click me!")
|
||||
/// .full_width()
|
||||
/// .on_click(|event, cx| {
|
||||
/// // Handle click event
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
#[derive(IntoElement)]
|
||||
pub struct Button {
|
||||
base: ButtonLike,
|
||||
label: SharedString,
|
||||
label_color: Option<Color>,
|
||||
label_size: Option<LabelSize>,
|
||||
selected_label: Option<SharedString>,
|
||||
selected_label_color: Option<Color>,
|
||||
icon: Option<IconName>,
|
||||
icon_position: Option<IconPosition>,
|
||||
icon_size: Option<IconSize>,
|
||||
icon_color: Option<Color>,
|
||||
selected_icon: Option<IconName>,
|
||||
selected_icon_color: Option<Color>,
|
||||
key_binding: Option<KeyBinding>,
|
||||
alpha: Option<f32>,
|
||||
}
|
||||
|
||||
impl Button {
|
||||
/// Creates a new [`Button`] with a specified identifier and label.
|
||||
///
|
||||
/// This is the primary constructor for a [`Button`] component. It initializes
|
||||
/// the button with the provided identifier and label text, setting all other
|
||||
/// properties to their default values, which can be customized using the
|
||||
/// builder pattern methods provided by this struct.
|
||||
pub fn new(id: impl Into<ElementId>, label: impl Into<SharedString>) -> Self {
|
||||
Self {
|
||||
base: ButtonLike::new(id),
|
||||
label: label.into(),
|
||||
label_color: None,
|
||||
label_size: None,
|
||||
selected_label: None,
|
||||
selected_label_color: None,
|
||||
icon: None,
|
||||
icon_position: None,
|
||||
icon_size: None,
|
||||
icon_color: None,
|
||||
selected_icon: None,
|
||||
selected_icon_color: None,
|
||||
key_binding: None,
|
||||
alpha: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the color of the button's label.
|
||||
pub fn color(mut self, label_color: impl Into<Option<Color>>) -> Self {
|
||||
self.label_color = label_color.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Defines the size of the button's label.
|
||||
pub fn label_size(mut self, label_size: impl Into<Option<LabelSize>>) -> Self {
|
||||
self.label_size = label_size.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the label used when the button is in a selected state.
|
||||
pub fn selected_label<L: Into<SharedString>>(mut self, label: impl Into<Option<L>>) -> Self {
|
||||
self.selected_label = label.into().map(Into::into);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the label color used when the button is in a selected state.
|
||||
pub fn selected_label_color(mut self, color: impl Into<Option<Color>>) -> Self {
|
||||
self.selected_label_color = color.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Assigns an icon to the button.
|
||||
pub fn icon(mut self, icon: impl Into<Option<IconName>>) -> Self {
|
||||
self.icon = icon.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the position of the icon relative to the label.
|
||||
pub fn icon_position(mut self, icon_position: impl Into<Option<IconPosition>>) -> Self {
|
||||
self.icon_position = icon_position.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Specifies the size of the button's icon.
|
||||
pub fn icon_size(mut self, icon_size: impl Into<Option<IconSize>>) -> Self {
|
||||
self.icon_size = icon_size.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the color of the button's icon.
|
||||
pub fn icon_color(mut self, icon_color: impl Into<Option<Color>>) -> Self {
|
||||
self.icon_color = icon_color.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Chooses an icon to display when the button is in a selected state.
|
||||
pub fn selected_icon(mut self, icon: impl Into<Option<IconName>>) -> Self {
|
||||
self.selected_icon = icon.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the icon color used when the button is in a selected state.
|
||||
pub fn selected_icon_color(mut self, color: impl Into<Option<Color>>) -> Self {
|
||||
self.selected_icon_color = color.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Binds a key combination to the button for keyboard shortcuts.
|
||||
pub fn key_binding(mut self, key_binding: impl Into<Option<KeyBinding>>) -> Self {
|
||||
self.key_binding = key_binding.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the alpha property of the color of label.
|
||||
pub fn alpha(mut self, alpha: f32) -> Self {
|
||||
self.alpha = Some(alpha);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Selectable for Button {
|
||||
/// Sets the selected state of the button.
|
||||
///
|
||||
/// This method allows the selection state of the button to be specified.
|
||||
/// It modifies the button's appearance to reflect its selected state.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// Button::new("button_id", "Click me!")
|
||||
/// .selected(true)
|
||||
/// .on_click(|event, cx| {
|
||||
/// // Handle click event
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// Use [`selected_style`](Button::selected_style) to change the style of the button when it is selected.
|
||||
fn selected(mut self, selected: bool) -> Self {
|
||||
self.base = self.base.selected(selected);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl SelectableButton for Button {
|
||||
/// Sets the style for the button when selected.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
/// use ui::TintColor;
|
||||
///
|
||||
/// Button::new("button_id", "Click me!")
|
||||
/// .selected(true)
|
||||
/// .selected_style(ButtonStyle::Tinted(TintColor::Accent))
|
||||
/// .on_click(|event, cx| {
|
||||
/// // Handle click event
|
||||
/// });
|
||||
/// ```
|
||||
/// This results in a button with a blue tinted background when selected.
|
||||
fn selected_style(mut self, style: ButtonStyle) -> Self {
|
||||
self.base = self.base.selected_style(style);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Disableable for Button {
|
||||
/// Disables the button.
|
||||
///
|
||||
/// This method allows the button to be disabled. When a button is disabled,
|
||||
/// it doesn't react to user interactions and its appearance is updated to reflect this.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// Button::new("button_id", "Click me!")
|
||||
/// .disabled(true)
|
||||
/// .on_click(|event, cx| {
|
||||
/// // Handle click event
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// This results in a button that is disabled and does not respond to click events.
|
||||
fn disabled(mut self, disabled: bool) -> Self {
|
||||
self.base = self.base.disabled(disabled);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Clickable for Button {
|
||||
/// Sets the click event handler for the button.
|
||||
fn on_click(
|
||||
mut self,
|
||||
handler: impl Fn(&gpui::ClickEvent, &mut WindowContext) + 'static,
|
||||
) -> Self {
|
||||
self.base = self.base.on_click(handler);
|
||||
self
|
||||
}
|
||||
|
||||
fn cursor_style(mut self, cursor_style: gpui::CursorStyle) -> Self {
|
||||
self.base = self.base.cursor_style(cursor_style);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl FixedWidth for Button {
|
||||
/// Sets a fixed width for the button.
|
||||
///
|
||||
/// This function allows a button to have a fixed width instead of automatically growing or shrinking.
|
||||
/// Sets a fixed width for the button.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// Button::new("button_id", "Click me!")
|
||||
/// .width(px(100.).into())
|
||||
/// .on_click(|event, cx| {
|
||||
/// // Handle click event
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// This sets the button's width to be exactly 100 pixels.
|
||||
fn width(mut self, width: DefiniteLength) -> Self {
|
||||
self.base = self.base.width(width);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the button to occupy the full width of its container.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// Button::new("button_id", "Click me!")
|
||||
/// .full_width()
|
||||
/// .on_click(|event, cx| {
|
||||
/// // Handle click event
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// This stretches the button to the full width of its container.
|
||||
fn full_width(mut self) -> Self {
|
||||
self.base = self.base.full_width();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ButtonCommon for Button {
|
||||
/// Sets the button's id.
|
||||
fn id(&self) -> &ElementId {
|
||||
self.base.id()
|
||||
}
|
||||
|
||||
/// Sets the visual style of the button using a [`ButtonStyle`].
|
||||
fn style(mut self, style: ButtonStyle) -> Self {
|
||||
self.base = self.base.style(style);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the button's size using a [`ButtonSize`].
|
||||
fn size(mut self, size: ButtonSize) -> Self {
|
||||
self.base = self.base.size(size);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a tooltip for the button.
|
||||
///
|
||||
/// This method allows a tooltip to be set for the button. The tooltip is a function that
|
||||
/// takes a mutable reference to a [`WindowContext`] and returns an [`AnyView`]. The tooltip
|
||||
/// is displayed when the user hovers over the button.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
/// use ui::Tooltip;
|
||||
///
|
||||
/// Button::new("button_id", "Click me!")
|
||||
/// .tooltip(move |cx| {
|
||||
/// Tooltip::text("This is a tooltip", cx)
|
||||
/// })
|
||||
/// .on_click(|event, cx| {
|
||||
/// // Handle click event
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// This will create a button with a tooltip that displays "This is a tooltip" when hovered over.
|
||||
fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
|
||||
self.base = self.base.tooltip(tooltip);
|
||||
self
|
||||
}
|
||||
|
||||
fn layer(mut self, elevation: ElevationIndex) -> Self {
|
||||
self.base = self.base.layer(elevation);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Button {
|
||||
#[allow(refining_impl_trait)]
|
||||
fn render(self, cx: &mut WindowContext) -> ButtonLike {
|
||||
let is_disabled = self.base.disabled;
|
||||
let is_selected = self.base.selected;
|
||||
|
||||
let label = self
|
||||
.selected_label
|
||||
.filter(|_| is_selected)
|
||||
.unwrap_or(self.label);
|
||||
|
||||
let label_color = if is_disabled {
|
||||
Color::Disabled
|
||||
} else if is_selected {
|
||||
self.selected_label_color.unwrap_or(Color::Selected)
|
||||
} else {
|
||||
self.label_color.unwrap_or_default()
|
||||
};
|
||||
|
||||
self.base.child(
|
||||
h_flex()
|
||||
.gap(Spacing::Small.rems(cx))
|
||||
.when(self.icon_position == Some(IconPosition::Start), |this| {
|
||||
this.children(self.icon.map(|icon| {
|
||||
ButtonIcon::new(icon)
|
||||
.disabled(is_disabled)
|
||||
.selected(is_selected)
|
||||
.selected_icon(self.selected_icon)
|
||||
.selected_icon_color(self.selected_icon_color)
|
||||
.size(self.icon_size)
|
||||
.color(self.icon_color)
|
||||
}))
|
||||
})
|
||||
.child(
|
||||
h_flex()
|
||||
.gap(Spacing::Medium.rems(cx))
|
||||
.justify_between()
|
||||
.child(
|
||||
Label::new(label)
|
||||
.color(label_color)
|
||||
.size(self.label_size.unwrap_or_default())
|
||||
.when_some(self.alpha, |this, alpha| this.alpha(alpha))
|
||||
.line_height_style(LineHeightStyle::UiLabel),
|
||||
)
|
||||
.children(self.key_binding),
|
||||
)
|
||||
.when(self.icon_position != Some(IconPosition::Start), |this| {
|
||||
this.children(self.icon.map(|icon| {
|
||||
ButtonIcon::new(icon)
|
||||
.disabled(is_disabled)
|
||||
.selected(is_selected)
|
||||
.selected_icon(self.selected_icon)
|
||||
.selected_icon_color(self.selected_icon_color)
|
||||
.size(self.icon_size)
|
||||
.color(self.icon_color)
|
||||
}))
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
438
crates/ui/src/components/button/button.rs
Normal file
438
crates/ui/src/components/button/button.rs
Normal file
@ -0,0 +1,438 @@
|
||||
use gpui::{AnyView, DefiniteLength};
|
||||
|
||||
use crate::{prelude::*, ElevationIndex, IconPosition, KeyBinding, Spacing};
|
||||
use crate::{
|
||||
ButtonCommon, ButtonLike, ButtonSize, ButtonStyle, IconName, IconSize, Label, LineHeightStyle,
|
||||
};
|
||||
|
||||
use super::button_icon::ButtonIcon;
|
||||
|
||||
/// An element that creates a button with a label and an optional icon.
|
||||
///
|
||||
/// Common buttons:
|
||||
/// - Label, Icon + Label: [`Button`] (this component)
|
||||
/// - Icon only: [`IconButton`]
|
||||
/// - Custom: [`ButtonLike`]
|
||||
///
|
||||
/// To create a more complex button than what the [`Button`] or [`IconButton`] components provide, use
|
||||
/// [`ButtonLike`] directly.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// **A button with a label**, is typically used in scenarios such as a form, where the button's label
|
||||
/// indicates what action will be performed when the button is clicked.
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// Button::new("button_id", "Click me!")
|
||||
/// .on_click(|event, cx| {
|
||||
/// // Handle click event
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// **A toggleable button**, is typically used in scenarios such as a toolbar,
|
||||
/// where the button's state indicates whether a feature is enabled or not, or
|
||||
/// a trigger for a popover menu, where clicking the button toggles the visibility of the menu.
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// Button::new("button_id", "Click me!")
|
||||
/// .icon(IconName::Check)
|
||||
/// .selected(true)
|
||||
/// .on_click(|event, cx| {
|
||||
/// // Handle click event
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// To change the style of the button when it is selected use the [`selected_style`][Button::selected_style] method.
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
/// use ui::TintColor;
|
||||
///
|
||||
/// Button::new("button_id", "Click me!")
|
||||
/// .selected(true)
|
||||
/// .selected_style(ButtonStyle::Tinted(TintColor::Accent))
|
||||
/// .on_click(|event, cx| {
|
||||
/// // Handle click event
|
||||
/// });
|
||||
/// ```
|
||||
/// This will create a button with a blue tinted background when selected.
|
||||
///
|
||||
/// **A full-width button**, is typically used in scenarios such as the bottom of a modal or form, where it occupies the entire width of its container.
|
||||
/// The button's content, including text and icons, is centered by default.
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// let button = Button::new("button_id", "Click me!")
|
||||
/// .full_width()
|
||||
/// .on_click(|event, cx| {
|
||||
/// // Handle click event
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
#[derive(IntoElement)]
|
||||
pub struct Button {
|
||||
base: ButtonLike,
|
||||
label: SharedString,
|
||||
label_color: Option<Color>,
|
||||
label_size: Option<LabelSize>,
|
||||
selected_label: Option<SharedString>,
|
||||
selected_label_color: Option<Color>,
|
||||
icon: Option<IconName>,
|
||||
icon_position: Option<IconPosition>,
|
||||
icon_size: Option<IconSize>,
|
||||
icon_color: Option<Color>,
|
||||
selected_icon: Option<IconName>,
|
||||
selected_icon_color: Option<Color>,
|
||||
key_binding: Option<KeyBinding>,
|
||||
alpha: Option<f32>,
|
||||
}
|
||||
|
||||
impl Button {
|
||||
/// Creates a new [`Button`] with a specified identifier and label.
|
||||
///
|
||||
/// This is the primary constructor for a [`Button`] component. It initializes
|
||||
/// the button with the provided identifier and label text, setting all other
|
||||
/// properties to their default values, which can be customized using the
|
||||
/// builder pattern methods provided by this struct.
|
||||
pub fn new(id: impl Into<ElementId>, label: impl Into<SharedString>) -> Self {
|
||||
Self {
|
||||
base: ButtonLike::new(id),
|
||||
label: label.into(),
|
||||
label_color: None,
|
||||
label_size: None,
|
||||
selected_label: None,
|
||||
selected_label_color: None,
|
||||
icon: None,
|
||||
icon_position: None,
|
||||
icon_size: None,
|
||||
icon_color: None,
|
||||
selected_icon: None,
|
||||
selected_icon_color: None,
|
||||
key_binding: None,
|
||||
alpha: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the color of the button's label.
|
||||
pub fn color(mut self, label_color: impl Into<Option<Color>>) -> Self {
|
||||
self.label_color = label_color.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Defines the size of the button's label.
|
||||
pub fn label_size(mut self, label_size: impl Into<Option<LabelSize>>) -> Self {
|
||||
self.label_size = label_size.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the label used when the button is in a selected state.
|
||||
pub fn selected_label<L: Into<SharedString>>(mut self, label: impl Into<Option<L>>) -> Self {
|
||||
self.selected_label = label.into().map(Into::into);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the label color used when the button is in a selected state.
|
||||
pub fn selected_label_color(mut self, color: impl Into<Option<Color>>) -> Self {
|
||||
self.selected_label_color = color.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Assigns an icon to the button.
|
||||
pub fn icon(mut self, icon: impl Into<Option<IconName>>) -> Self {
|
||||
self.icon = icon.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the position of the icon relative to the label.
|
||||
pub fn icon_position(mut self, icon_position: impl Into<Option<IconPosition>>) -> Self {
|
||||
self.icon_position = icon_position.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Specifies the size of the button's icon.
|
||||
pub fn icon_size(mut self, icon_size: impl Into<Option<IconSize>>) -> Self {
|
||||
self.icon_size = icon_size.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the color of the button's icon.
|
||||
pub fn icon_color(mut self, icon_color: impl Into<Option<Color>>) -> Self {
|
||||
self.icon_color = icon_color.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Chooses an icon to display when the button is in a selected state.
|
||||
pub fn selected_icon(mut self, icon: impl Into<Option<IconName>>) -> Self {
|
||||
self.selected_icon = icon.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the icon color used when the button is in a selected state.
|
||||
pub fn selected_icon_color(mut self, color: impl Into<Option<Color>>) -> Self {
|
||||
self.selected_icon_color = color.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Binds a key combination to the button for keyboard shortcuts.
|
||||
pub fn key_binding(mut self, key_binding: impl Into<Option<KeyBinding>>) -> Self {
|
||||
self.key_binding = key_binding.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the alpha property of the color of label.
|
||||
pub fn alpha(mut self, alpha: f32) -> Self {
|
||||
self.alpha = Some(alpha);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Selectable for Button {
|
||||
/// Sets the selected state of the button.
|
||||
///
|
||||
/// This method allows the selection state of the button to be specified.
|
||||
/// It modifies the button's appearance to reflect its selected state.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// Button::new("button_id", "Click me!")
|
||||
/// .selected(true)
|
||||
/// .on_click(|event, cx| {
|
||||
/// // Handle click event
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// Use [`selected_style`](Button::selected_style) to change the style of the button when it is selected.
|
||||
fn selected(mut self, selected: bool) -> Self {
|
||||
self.base = self.base.selected(selected);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl SelectableButton for Button {
|
||||
/// Sets the style for the button when selected.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
/// use ui::TintColor;
|
||||
///
|
||||
/// Button::new("button_id", "Click me!")
|
||||
/// .selected(true)
|
||||
/// .selected_style(ButtonStyle::Tinted(TintColor::Accent))
|
||||
/// .on_click(|event, cx| {
|
||||
/// // Handle click event
|
||||
/// });
|
||||
/// ```
|
||||
/// This results in a button with a blue tinted background when selected.
|
||||
fn selected_style(mut self, style: ButtonStyle) -> Self {
|
||||
self.base = self.base.selected_style(style);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Disableable for Button {
|
||||
/// Disables the button.
|
||||
///
|
||||
/// This method allows the button to be disabled. When a button is disabled,
|
||||
/// it doesn't react to user interactions and its appearance is updated to reflect this.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// Button::new("button_id", "Click me!")
|
||||
/// .disabled(true)
|
||||
/// .on_click(|event, cx| {
|
||||
/// // Handle click event
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// This results in a button that is disabled and does not respond to click events.
|
||||
fn disabled(mut self, disabled: bool) -> Self {
|
||||
self.base = self.base.disabled(disabled);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Clickable for Button {
|
||||
/// Sets the click event handler for the button.
|
||||
fn on_click(
|
||||
mut self,
|
||||
handler: impl Fn(&gpui::ClickEvent, &mut WindowContext) + 'static,
|
||||
) -> Self {
|
||||
self.base = self.base.on_click(handler);
|
||||
self
|
||||
}
|
||||
|
||||
fn cursor_style(mut self, cursor_style: gpui::CursorStyle) -> Self {
|
||||
self.base = self.base.cursor_style(cursor_style);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl FixedWidth for Button {
|
||||
/// Sets a fixed width for the button.
|
||||
///
|
||||
/// This function allows a button to have a fixed width instead of automatically growing or shrinking.
|
||||
/// Sets a fixed width for the button.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// Button::new("button_id", "Click me!")
|
||||
/// .width(px(100.).into())
|
||||
/// .on_click(|event, cx| {
|
||||
/// // Handle click event
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// This sets the button's width to be exactly 100 pixels.
|
||||
fn width(mut self, width: DefiniteLength) -> Self {
|
||||
self.base = self.base.width(width);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the button to occupy the full width of its container.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// Button::new("button_id", "Click me!")
|
||||
/// .full_width()
|
||||
/// .on_click(|event, cx| {
|
||||
/// // Handle click event
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// This stretches the button to the full width of its container.
|
||||
fn full_width(mut self) -> Self {
|
||||
self.base = self.base.full_width();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ButtonCommon for Button {
|
||||
/// Sets the button's id.
|
||||
fn id(&self) -> &ElementId {
|
||||
self.base.id()
|
||||
}
|
||||
|
||||
/// Sets the visual style of the button using a [`ButtonStyle`].
|
||||
fn style(mut self, style: ButtonStyle) -> Self {
|
||||
self.base = self.base.style(style);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the button's size using a [`ButtonSize`].
|
||||
fn size(mut self, size: ButtonSize) -> Self {
|
||||
self.base = self.base.size(size);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a tooltip for the button.
|
||||
///
|
||||
/// This method allows a tooltip to be set for the button. The tooltip is a function that
|
||||
/// takes a mutable reference to a [`WindowContext`] and returns an [`AnyView`]. The tooltip
|
||||
/// is displayed when the user hovers over the button.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
/// use ui::Tooltip;
|
||||
///
|
||||
/// Button::new("button_id", "Click me!")
|
||||
/// .tooltip(move |cx| {
|
||||
/// Tooltip::text("This is a tooltip", cx)
|
||||
/// })
|
||||
/// .on_click(|event, cx| {
|
||||
/// // Handle click event
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// This will create a button with a tooltip that displays "This is a tooltip" when hovered over.
|
||||
fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
|
||||
self.base = self.base.tooltip(tooltip);
|
||||
self
|
||||
}
|
||||
|
||||
fn layer(mut self, elevation: ElevationIndex) -> Self {
|
||||
self.base = self.base.layer(elevation);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Button {
|
||||
#[allow(refining_impl_trait)]
|
||||
fn render(self, cx: &mut WindowContext) -> ButtonLike {
|
||||
let is_disabled = self.base.disabled;
|
||||
let is_selected = self.base.selected;
|
||||
|
||||
let label = self
|
||||
.selected_label
|
||||
.filter(|_| is_selected)
|
||||
.unwrap_or(self.label);
|
||||
|
||||
let label_color = if is_disabled {
|
||||
Color::Disabled
|
||||
} else if is_selected {
|
||||
self.selected_label_color.unwrap_or(Color::Selected)
|
||||
} else {
|
||||
self.label_color.unwrap_or_default()
|
||||
};
|
||||
|
||||
self.base.child(
|
||||
h_flex()
|
||||
.gap(Spacing::Small.rems(cx))
|
||||
.when(self.icon_position == Some(IconPosition::Start), |this| {
|
||||
this.children(self.icon.map(|icon| {
|
||||
ButtonIcon::new(icon)
|
||||
.disabled(is_disabled)
|
||||
.selected(is_selected)
|
||||
.selected_icon(self.selected_icon)
|
||||
.selected_icon_color(self.selected_icon_color)
|
||||
.size(self.icon_size)
|
||||
.color(self.icon_color)
|
||||
}))
|
||||
})
|
||||
.child(
|
||||
h_flex()
|
||||
.gap(Spacing::Medium.rems(cx))
|
||||
.justify_between()
|
||||
.child(
|
||||
Label::new(label)
|
||||
.color(label_color)
|
||||
.size(self.label_size.unwrap_or_default())
|
||||
.when_some(self.alpha, |this, alpha| this.alpha(alpha))
|
||||
.line_height_style(LineHeightStyle::UiLabel),
|
||||
)
|
||||
.children(self.key_binding),
|
||||
)
|
||||
.when(self.icon_position != Some(IconPosition::Start), |this| {
|
||||
this.children(self.icon.map(|icon| {
|
||||
ButtonIcon::new(icon)
|
||||
.disabled(is_disabled)
|
||||
.selected(is_selected)
|
||||
.selected_icon(self.selected_icon)
|
||||
.selected_icon_color(self.selected_icon_color)
|
||||
.size(self.icon_size)
|
||||
.color(self.icon_color)
|
||||
}))
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
@ -1,190 +1,7 @@
|
||||
mod highlighted_label;
|
||||
mod label;
|
||||
mod label_like;
|
||||
|
||||
pub use highlighted_label::*;
|
||||
pub use label::*;
|
||||
pub use label_like::*;
|
||||
|
||||
use gpui::{StyleRefinement, WindowContext};
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
/// A struct representing a label element in the UI.
|
||||
///
|
||||
/// The `Label` struct stores the label text and common properties for a label element.
|
||||
/// It provides methods for modifying these properties.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// Label::new("Hello, World!");
|
||||
/// ```
|
||||
///
|
||||
/// **A colored label**, for example labeling a dangerous action:
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// let my_label = Label::new("Delete").color(Color::Error);
|
||||
/// ```
|
||||
///
|
||||
/// **A label with a strikethrough**, for example labeling something that has been deleted:
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// let my_label = Label::new("Deleted").strikethrough(true);
|
||||
/// ```
|
||||
#[derive(IntoElement)]
|
||||
pub struct Label {
|
||||
base: LabelLike,
|
||||
label: SharedString,
|
||||
single_line: bool,
|
||||
}
|
||||
|
||||
impl Label {
|
||||
/// Creates a new [`Label`] with the given text.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// let my_label = Label::new("Hello, World!");
|
||||
/// ```
|
||||
pub fn new(label: impl Into<SharedString>) -> Self {
|
||||
Self {
|
||||
base: LabelLike::new(),
|
||||
label: label.into(),
|
||||
single_line: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Make the label display in a single line mode
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// let my_label = Label::new("Hello, World!").single_line();
|
||||
/// ```
|
||||
pub fn single_line(mut self) -> Self {
|
||||
self.single_line = true;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// Style methods.
|
||||
impl Label {
|
||||
fn style(&mut self) -> &mut StyleRefinement {
|
||||
self.base.base.style()
|
||||
}
|
||||
|
||||
gpui::margin_style_methods!({
|
||||
visibility: pub
|
||||
});
|
||||
}
|
||||
|
||||
impl LabelCommon for Label {
|
||||
/// Sets the size of the label using a [`LabelSize`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// let my_label = Label::new("Hello, World!").size(LabelSize::Small);
|
||||
/// ```
|
||||
fn size(mut self, size: LabelSize) -> Self {
|
||||
self.base = self.base.size(size);
|
||||
self
|
||||
}
|
||||
|
||||
fn weight(mut self, weight: gpui::FontWeight) -> Self {
|
||||
self.base = self.base.weight(weight);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the line height style of the label using a [`LineHeightStyle`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// let my_label = Label::new("Hello, World!").line_height_style(LineHeightStyle::UiLabel);
|
||||
/// ```
|
||||
fn line_height_style(mut self, line_height_style: LineHeightStyle) -> Self {
|
||||
self.base = self.base.line_height_style(line_height_style);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the color of the label using a [`Color`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// let my_label = Label::new("Hello, World!").color(Color::Accent);
|
||||
/// ```
|
||||
fn color(mut self, color: Color) -> Self {
|
||||
self.base = self.base.color(color);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the strikethrough property of the label.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// let my_label = Label::new("Hello, World!").strikethrough(true);
|
||||
/// ```
|
||||
fn strikethrough(mut self, strikethrough: bool) -> Self {
|
||||
self.base = self.base.strikethrough(strikethrough);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the italic property of the label.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// let my_label = Label::new("Hello, World!").italic(true);
|
||||
/// ```
|
||||
fn italic(mut self, italic: bool) -> Self {
|
||||
self.base = self.base.italic(italic);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the alpha property of the color of label.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// let my_label = Label::new("Hello, World!").alpha(0.5);
|
||||
/// ```
|
||||
fn alpha(mut self, alpha: f32) -> Self {
|
||||
self.base = self.base.alpha(alpha);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Label {
|
||||
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
|
||||
let target_label = if self.single_line {
|
||||
SharedString::from(self.label.replace('\n', ""))
|
||||
} else {
|
||||
self.label
|
||||
};
|
||||
self.base.child(target_label)
|
||||
}
|
||||
}
|
||||
|
184
crates/ui/src/components/label/label.rs
Normal file
184
crates/ui/src/components/label/label.rs
Normal file
@ -0,0 +1,184 @@
|
||||
use gpui::{StyleRefinement, WindowContext};
|
||||
|
||||
use crate::{prelude::*, LabelCommon, LabelLike, LabelSize, LineHeightStyle};
|
||||
|
||||
/// A struct representing a label element in the UI.
|
||||
///
|
||||
/// The `Label` struct stores the label text and common properties for a label element.
|
||||
/// It provides methods for modifying these properties.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// Label::new("Hello, World!");
|
||||
/// ```
|
||||
///
|
||||
/// **A colored label**, for example labeling a dangerous action:
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// let my_label = Label::new("Delete").color(Color::Error);
|
||||
/// ```
|
||||
///
|
||||
/// **A label with a strikethrough**, for example labeling something that has been deleted:
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// let my_label = Label::new("Deleted").strikethrough(true);
|
||||
/// ```
|
||||
#[derive(IntoElement)]
|
||||
pub struct Label {
|
||||
base: LabelLike,
|
||||
label: SharedString,
|
||||
single_line: bool,
|
||||
}
|
||||
|
||||
impl Label {
|
||||
/// Creates a new [`Label`] with the given text.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// let my_label = Label::new("Hello, World!");
|
||||
/// ```
|
||||
pub fn new(label: impl Into<SharedString>) -> Self {
|
||||
Self {
|
||||
base: LabelLike::new(),
|
||||
label: label.into(),
|
||||
single_line: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Make the label display in a single line mode
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// let my_label = Label::new("Hello, World!").single_line();
|
||||
/// ```
|
||||
pub fn single_line(mut self) -> Self {
|
||||
self.single_line = true;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// Style methods.
|
||||
impl Label {
|
||||
fn style(&mut self) -> &mut StyleRefinement {
|
||||
self.base.base.style()
|
||||
}
|
||||
|
||||
gpui::margin_style_methods!({
|
||||
visibility: pub
|
||||
});
|
||||
}
|
||||
|
||||
impl LabelCommon for Label {
|
||||
/// Sets the size of the label using a [`LabelSize`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// let my_label = Label::new("Hello, World!").size(LabelSize::Small);
|
||||
/// ```
|
||||
fn size(mut self, size: LabelSize) -> Self {
|
||||
self.base = self.base.size(size);
|
||||
self
|
||||
}
|
||||
|
||||
fn weight(mut self, weight: gpui::FontWeight) -> Self {
|
||||
self.base = self.base.weight(weight);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the line height style of the label using a [`LineHeightStyle`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// let my_label = Label::new("Hello, World!").line_height_style(LineHeightStyle::UiLabel);
|
||||
/// ```
|
||||
fn line_height_style(mut self, line_height_style: LineHeightStyle) -> Self {
|
||||
self.base = self.base.line_height_style(line_height_style);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the color of the label using a [`Color`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// let my_label = Label::new("Hello, World!").color(Color::Accent);
|
||||
/// ```
|
||||
fn color(mut self, color: Color) -> Self {
|
||||
self.base = self.base.color(color);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the strikethrough property of the label.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// let my_label = Label::new("Hello, World!").strikethrough(true);
|
||||
/// ```
|
||||
fn strikethrough(mut self, strikethrough: bool) -> Self {
|
||||
self.base = self.base.strikethrough(strikethrough);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the italic property of the label.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// let my_label = Label::new("Hello, World!").italic(true);
|
||||
/// ```
|
||||
fn italic(mut self, italic: bool) -> Self {
|
||||
self.base = self.base.italic(italic);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the alpha property of the color of label.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::prelude::*;
|
||||
///
|
||||
/// let my_label = Label::new("Hello, World!").alpha(0.5);
|
||||
/// ```
|
||||
fn alpha(mut self, alpha: f32) -> Self {
|
||||
self.base = self.base.alpha(alpha);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Label {
|
||||
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
|
||||
let target_label = if self.single_line {
|
||||
SharedString::from(self.label.replace('\n', ""))
|
||||
} else {
|
||||
self.label
|
||||
};
|
||||
self.base.child(target_label)
|
||||
}
|
||||
}
|
@ -1,76 +1,11 @@
|
||||
mod list;
|
||||
mod list_header;
|
||||
mod list_item;
|
||||
mod list_separator;
|
||||
mod list_sub_header;
|
||||
|
||||
pub use list::*;
|
||||
pub use list_header::*;
|
||||
pub use list_item::*;
|
||||
pub use list_separator::*;
|
||||
pub use list_sub_header::*;
|
||||
|
||||
use gpui::AnyElement;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::{prelude::*, v_flex, Label};
|
||||
|
||||
pub use ListHeader;
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct List {
|
||||
/// Message to display when the list is empty
|
||||
/// Defaults to "No items"
|
||||
empty_message: SharedString,
|
||||
header: Option<ListHeader>,
|
||||
toggle: Option<bool>,
|
||||
children: SmallVec<[AnyElement; 2]>,
|
||||
}
|
||||
|
||||
impl Default for List {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl List {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
empty_message: "No items".into(),
|
||||
header: None,
|
||||
toggle: None,
|
||||
children: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn empty_message(mut self, empty_message: impl Into<SharedString>) -> Self {
|
||||
self.empty_message = empty_message.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn header(mut self, header: impl Into<Option<ListHeader>>) -> Self {
|
||||
self.header = header.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn toggle(mut self, toggle: impl Into<Option<bool>>) -> Self {
|
||||
self.toggle = toggle.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ParentElement for List {
|
||||
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
|
||||
self.children.extend(elements)
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for List {
|
||||
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
|
||||
v_flex().w_full().py_1().children(self.header).map(|this| {
|
||||
match (self.children.is_empty(), self.toggle) {
|
||||
(false, _) => this.children(self.children),
|
||||
(true, Some(false)) => this,
|
||||
(true, _) => this.child(Label::new(self.empty_message.clone()).color(Color::Muted)),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -13,12 +13,6 @@ pub struct List {
|
||||
children: SmallVec<[AnyElement; 2]>,
|
||||
}
|
||||
|
||||
impl Default for List {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl List {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
|
Loading…
Reference in New Issue
Block a user