button: Impl Selectable for the dropdown button (#913)

## Break Change
- Add `is_selected` to the `Selectable` trait; Returns true if the
element is selected.
This commit is contained in:
Floyd Wang 2025-06-02 14:25:18 +08:00 committed by GitHub
parent 901312bc5a
commit dc1d516a30
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 72 additions and 4 deletions

View file

@ -695,6 +695,7 @@ impl Render for ButtonStory {
DropdownButton::new("dropdown-button1")
.small()
.button(Button::new("btn").label("Click Me"))
.selected(selected)
.popup_menu(move |this, _, _| {
this.menu("Disabled", Box::new(Disabled))
.menu("Loading", Box::new(Loading))
@ -705,6 +706,7 @@ impl Render for ButtonStory {
.child(
DropdownButton::new("dropdown-button2")
.button(Button::new("btn").label("Click Me"))
.selected(selected)
.popup_menu(move |this, _, _| {
this.menu("Disabled", Box::new(Disabled))
.menu("Loading", Box::new(Loading))
@ -716,6 +718,19 @@ impl Render for ButtonStory {
DropdownButton::new("dropdown-button3")
.outline()
.button(Button::new("btn").label("Outline Dropdown"))
.selected(selected)
.popup_menu(move |this, _, _| {
this.menu("Disabled", Box::new(Disabled))
.menu("Loading", Box::new(Loading))
.menu("Selected", Box::new(Selected))
.menu("Compact", Box::new(Compact))
}),
)
.child(
DropdownButton::new("dropdown-button4")
.ghost()
.button(Button::new("btn").label("Ghost Dropdown"))
.selected(selected)
.popup_menu(move |this, _, _| {
this.menu("Disabled", Box::new(Disabled))
.menu("Loading", Box::new(Loading))

View file

@ -331,6 +331,10 @@ impl Selectable for Button {
self.selected = selected;
self
}
fn is_selected(&self) -> bool {
self.selected
}
}
impl Sizable for Button {

View file

@ -6,7 +6,7 @@ use gpui::{
use crate::{
h_flex,
popup_menu::{PopupMenu, PopupMenuExt},
IconName, Sizable, Size,
IconName, Selectable, Sizable, Size,
};
use super::{Button, ButtonRounded, ButtonVariant, ButtonVariants};
@ -18,6 +18,7 @@ pub struct DropdownButton {
button: Option<Button>,
popup_menu:
Option<Box<dyn Fn(PopupMenu, &mut Window, &mut Context<PopupMenu>) -> PopupMenu + 'static>>,
selected: bool,
// The button props
compact: Option<bool>,
@ -34,7 +35,7 @@ impl DropdownButton {
id: id.into(),
button: None,
popup_menu: None,
selected: false,
compact: None,
outline: None,
variant: None,
@ -70,6 +71,11 @@ impl DropdownButton {
self.outline = Some(true);
self
}
pub fn selected(mut self, selected: bool) -> Self {
self.selected = selected;
self
}
}
impl Styled for DropdownButton {
@ -92,6 +98,21 @@ impl ButtonVariants for DropdownButton {
}
}
impl Selectable for DropdownButton {
fn element_id(&self) -> &ElementId {
&self.id
}
fn selected(mut self, selected: bool) -> Self {
self.selected = selected;
self
}
fn is_selected(&self) -> bool {
self.selected
}
}
impl RenderOnce for DropdownButton {
fn render(self, _: &mut Window, _: &mut App) -> impl IntoElement {
self.base
@ -112,6 +133,7 @@ impl RenderOnce for DropdownButton {
right: true,
bottom: true,
})
.selected(self.selected)
.when_some(self.compact, |this, _| this.compact())
.when_some(self.outline, |this, _| this.outline())
.when_some(self.size, |this, size| this.with_size(size))
@ -119,7 +141,7 @@ impl RenderOnce for DropdownButton {
)
.when_some(self.popup_menu, |this, popup_menu| {
this.child(
Button::new("btn")
Button::new("popup")
.icon(IconName::ChevronDown)
.rounded(self.rounded)
.border_edges(Edges {
@ -134,6 +156,7 @@ impl RenderOnce for DropdownButton {
bottom_left: false,
bottom_right: true,
})
.selected(self.selected)
.when_some(self.compact, |this, _| this.compact())
.when_some(self.outline, |this, _| this.outline())
.when_some(self.size, |this, size| this.with_size(size))

View file

@ -79,6 +79,10 @@ impl Selectable for Checkbox {
fn selected(self, selected: bool) -> Self {
self.checked(selected)
}
fn is_selected(&self) -> bool {
self.checked
}
}
impl ParentElement for Checkbox {

View file

@ -105,6 +105,10 @@ impl Selectable for ListItem {
self.selected = selected;
self
}
fn is_selected(&self) -> bool {
self.selected
}
}
impl Styled for ListItem {

View file

@ -108,7 +108,8 @@ where
T: Selectable + IntoElement + 'static,
{
self.trigger = Some(Box::new(|is_open, _, _| {
trigger.selected(is_open).into_any_element()
let selected = trigger.is_selected();
trigger.selected(selected || is_open).into_any_element()
}));
self
}

View file

@ -32,6 +32,10 @@ impl Selectable for SidebarFooter {
fn element_id(&self) -> &gpui::ElementId {
&self.id
}
fn is_selected(&self) -> bool {
self.selected
}
}
impl Collapsible for SidebarFooter {
fn is_collapsed(&self) -> bool {

View file

@ -32,6 +32,10 @@ impl Selectable for SidebarHeader {
fn element_id(&self) -> &gpui::ElementId {
&self.id
}
fn is_selected(&self) -> bool {
self.selected
}
}
impl Collapsible for SidebarHeader {

View file

@ -289,9 +289,14 @@ impl From<Pixels> for Size {
/// A trait for defining element that can be selected.
pub trait Selectable: Sized {
/// Returns the element id of the element.
fn element_id(&self) -> &ElementId;
/// Set the selected state of the element.
fn selected(self, selected: bool) -> Self;
/// Returns true if the element is selected.
fn is_selected(&self) -> bool;
}
/// A trait for defining element that can be disabled.

View file

@ -536,6 +536,10 @@ impl Selectable for Tab {
self.selected = selected;
self
}
fn is_selected(&self) -> bool {
self.selected
}
}
impl InteractiveElement for Tab {