ui: Add margin style methods to Label and LabelLike (#14032)

This PR adds margin style methods to the `Label` and `LabelLike`
components.

This allows for callers to provide a margin to these components without
needing to introduce a wrapping `div` to do so.

Release Notes:

- N/A
This commit is contained in:
Marshall Bowers 2024-07-09 17:41:54 -04:00 committed by GitHub
parent 4bb8a0845f
commit a46a562dc2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 81 additions and 50 deletions

View file

@ -2547,9 +2547,8 @@ impl CollabPanel {
.take(FACEPILE_LIMIT) .take(FACEPILE_LIMIT)
.chain(if extra_count > 0 { .chain(if extra_count > 0 {
Some( Some(
div() Label::new(format!("+{extra_count}"))
.ml_2() .ml_2()
.child(Label::new(format!("+{extra_count}")))
.into_any_element(), .into_any_element(),
) )
} else { } else {

View file

@ -1129,11 +1129,10 @@ impl CompletionsMenu {
None None
} else { } else {
Some( Some(
h_flex().ml_4().child( Label::new(text.clone())
Label::new(text.clone()) .ml_4()
.size(LabelSize::Small) .size(LabelSize::Small)
.color(Color::Muted), .color(Color::Muted),
),
) )
} }
} else { } else {
@ -1156,7 +1155,7 @@ impl CompletionsMenu {
} }
})) }))
.child(h_flex().overflow_hidden().child(completion_label)) .child(h_flex().overflow_hidden().child(completion_label))
.end_slot::<Div>(documentation_label), .end_slot::<Label>(documentation_label),
) )
}) })
.collect() .collect()

View file

@ -1458,9 +1458,9 @@ impl Render for ProjectSearchBar {
) )
.when(limit_reached, |this| { .when(limit_reached, |this| {
this.child( this.child(
div() Label::new("Search limit reached")
.child(Label::new("Search limit reached").color(Color::Warning)) .ml_2()
.ml_2(), .color(Color::Warning),
) )
}); });

View file

@ -262,9 +262,8 @@ impl TitleBar {
)) ))
.children(if extra_count > 0 { .children(if extra_count > 0 {
Some( Some(
div() Label::new(format!("+{extra_count}"))
.ml_1() .ml_1()
.child(Label::new(format!("+{extra_count}")))
.into_any_element(), .into_any_element(),
) )
} else { } else {

View file

@ -332,7 +332,7 @@ impl ButtonSize {
/// This is also used to build the prebuilt buttons. /// This is also used to build the prebuilt buttons.
#[derive(IntoElement)] #[derive(IntoElement)]
pub struct ButtonLike { pub struct ButtonLike {
pub base: Div, pub(super) base: Div,
id: ElementId, id: ElementId,
pub(super) style: ButtonStyle, pub(super) style: ButtonStyle,
pub(super) disabled: bool, pub(super) disabled: bool,

View file

@ -1,4 +1,4 @@
use gpui::WindowContext; use gpui::{StyleRefinement, WindowContext};
use crate::{prelude::*, LabelCommon, LabelLike, LabelSize, LineHeightStyle}; use crate::{prelude::*, LabelCommon, LabelLike, LabelSize, LineHeightStyle};
@ -70,6 +70,17 @@ impl Label {
} }
} }
// Style methods.
impl Label {
fn style(&mut self) -> &mut StyleRefinement {
self.base.base.style()
}
gpui::margin_style_methods!({
visibility: pub
});
}
impl LabelCommon for Label { impl LabelCommon for Label {
/// Sets the size of the label using a [`LabelSize`]. /// Sets the size of the label using a [`LabelSize`].
/// ///

View file

@ -1,4 +1,4 @@
use gpui::{relative, AnyElement, FontWeight, Styled}; use gpui::{relative, AnyElement, FontWeight, StyleRefinement, Styled};
use smallvec::SmallVec; use smallvec::SmallVec;
use crate::prelude::*; use crate::prelude::*;
@ -43,6 +43,7 @@ pub trait LabelCommon {
#[derive(IntoElement)] #[derive(IntoElement)]
pub struct LabelLike { pub struct LabelLike {
pub(super) base: Div,
size: LabelSize, size: LabelSize,
weight: FontWeight, weight: FontWeight,
line_height_style: LineHeightStyle, line_height_style: LineHeightStyle,
@ -55,6 +56,7 @@ pub struct LabelLike {
impl LabelLike { impl LabelLike {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
base: div(),
size: LabelSize::Default, size: LabelSize::Default,
weight: FontWeight::default(), weight: FontWeight::default(),
line_height_style: LineHeightStyle::default(), line_height_style: LineHeightStyle::default(),
@ -66,6 +68,17 @@ impl LabelLike {
} }
} }
// Style methods.
impl LabelLike {
fn style(&mut self) -> &mut StyleRefinement {
self.base.style()
}
gpui::margin_style_methods!({
visibility: pub
});
}
impl LabelCommon for LabelLike { impl LabelCommon for LabelLike {
fn size(mut self, size: LabelSize) -> Self { fn size(mut self, size: LabelSize) -> Self {
self.size = size; self.size = size;
@ -106,7 +119,7 @@ impl ParentElement for LabelLike {
impl RenderOnce for LabelLike { impl RenderOnce for LabelLike {
fn render(self, cx: &mut WindowContext) -> impl IntoElement { fn render(self, cx: &mut WindowContext) -> impl IntoElement {
div() self.base
.when(self.strikethrough, |this| { .when(self.strikethrough, |this| {
this.relative().child( this.relative().child(
div() div()

View file

@ -2,9 +2,9 @@ use anyhow::{Context, Result};
use fuzzy::{StringMatch, StringMatchCandidate}; use fuzzy::{StringMatch, StringMatchCandidate};
use git::repository::Branch; use git::repository::Branch;
use gpui::{ use gpui::{
actions, rems, AnyElement, AppContext, DismissEvent, Element, EventEmitter, FocusHandle, actions, rems, AnyElement, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView,
FocusableView, InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, Subscription,
Subscription, Task, View, ViewContext, VisualContext, WindowContext, Task, View, ViewContext, VisualContext, WindowContext,
}; };
use picker::{Picker, PickerDelegate}; use picker::{Picker, PickerDelegate};
use std::{ops::Not, sync::Arc}; use std::{ops::Not, sync::Arc};
@ -268,11 +268,13 @@ impl PickerDelegate for BranchListDelegate {
.start_slot(HighlightedLabel::new(shortened_branch_name, highlights)), .start_slot(HighlightedLabel::new(shortened_branch_name, highlights)),
) )
} }
fn render_header(&self, _: &mut ViewContext<Picker<Self>>) -> Option<AnyElement> { fn render_header(&self, _: &mut ViewContext<Picker<Self>>) -> Option<AnyElement> {
let label = if self.last_query.is_empty() { let label = if self.last_query.is_empty() {
h_flex() Label::new("Recent Branches")
.size(LabelSize::Small)
.ml_3() .ml_3()
.child(Label::new("Recent Branches").size(LabelSize::Small)) .into_any_element()
} else { } else {
let match_label = self.matches.is_empty().not().then(|| { let match_label = self.matches.is_empty().not().then(|| {
let suffix = if self.matches.len() == 1 { "" } else { "es" }; let suffix = if self.matches.len() == 1 { "" } else { "es" };
@ -285,43 +287,51 @@ impl PickerDelegate for BranchListDelegate {
.justify_between() .justify_between()
.child(Label::new("Branches").size(LabelSize::Small)) .child(Label::new("Branches").size(LabelSize::Small))
.children(match_label) .children(match_label)
.into_any_element()
}; };
Some(label.mt_1().into_any()) Some(v_flex().mt_1().child(label).into_any_element())
} }
fn render_footer(&self, cx: &mut ViewContext<Picker<Self>>) -> Option<AnyElement> { fn render_footer(&self, cx: &mut ViewContext<Picker<Self>>) -> Option<AnyElement> {
if self.last_query.is_empty() { if self.last_query.is_empty() {
return None; return None;
} }
Some( Some(
h_flex().mr_3().pb_2().child(h_flex().w_full()).child( h_flex()
Button::new("branch-picker-create-branch-button", "Create branch").on_click( .mr_3()
cx.listener(|_, _, cx| { .pb_2()
cx.spawn(|picker, mut cx| async move { .child(h_flex().w_full())
picker.update(&mut cx, |this, cx| { .child(
let project = this.delegate.workspace.read(cx).project().read(cx); Button::new("branch-picker-create-branch-button", "Create branch")
let current_pick = &this.delegate.last_query; .on_click(cx.listener(|_, _, cx| {
let repo = project cx.spawn(|picker, mut cx| async move {
.get_first_worktree_root_repo(cx) picker.update(&mut cx, |this, cx| {
.context("failed to get root repository for first worktree")?; let project =
let status = repo this.delegate.workspace.read(cx).project().read(cx);
.create_branch(&current_pick); let current_pick = &this.delegate.last_query;
if status.is_err() { let repo = project.get_first_worktree_root_repo(cx).context(
this.delegate.display_error_toast(format!("Failed to create branch '{current_pick}', check for conflicts or unstashed files"), cx); "failed to get root repository for first worktree",
status?; )?;
} let status = repo.create_branch(&current_pick);
let status = repo.change_branch(&current_pick); if status.is_err() {
if status.is_err() { this.delegate.display_error_toast(format!("Failed to create branch '{current_pick}', check for conflicts or unstashed files"), cx);
this.delegate.display_error_toast(format!("Failed to check branch '{current_pick}', check for conflicts or unstashed files"), cx); status?;
status?; }
} let status = repo.change_branch(&current_pick);
this.cancel(&Default::default(), cx); if status.is_err() {
Ok::<(), anyhow::Error>(()) this.delegate.display_error_toast(format!("Failed to check branch '{current_pick}', check for conflicts or unstashed files"), cx);
status?;
}
this.cancel(&Default::default(), cx);
Ok::<(), anyhow::Error>(())
}) })
})
}).detach_and_log_err(cx); .detach_and_log_err(cx);
}), }))
).style(ui::ButtonStyle::Filled)).into_any_element(), .style(ui::ButtonStyle::Filled),
)
.into_any_element(),
) )
} }
} }