ui: Make toggle button group responsive (#36100)

This PR improves the toggle button group to be more responsive across
different layouts. This is accomplished by ensuring each button takes up
the same amount of space in the parent containers layout.

Ideally, this should be done with grids instead of a flexbox container,
as this would be much better suited for this purpose. Yet, since we lack
support for this, we go with this route for now.

| Before | After |
| --- | --- |
| <img width="1608" height="1094" alt="Bildschirmfoto 2025-08-13 um 11
24 26"
src="https://github.com/user-attachments/assets/2a4b5a59-6483-4f79-8fcb-e26e22071795"
/> | <img width="1608" height="1094" alt="Bildschirmfoto 2025-08-13 um
11 29 36"
src="https://github.com/user-attachments/assets/e6402729-6a8f-4a44-b79e-a569406edfff"
/> |


Release Notes:

- N/A
This commit is contained in:
Finn Evers 2025-08-13 14:02:20 +02:00 committed by GitHub
parent 6307105976
commit 7f1a5c6ad7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 50 additions and 36 deletions

View file

@ -3011,7 +3011,7 @@ impl EditorElement {
.icon_color(Color::Custom(cx.theme().colors().editor_line_number))
.selected_icon_color(Color::Custom(cx.theme().colors().editor_foreground))
.icon_size(IconSize::Custom(rems(editor_font_size / window.rem_size())))
.width(width.into())
.width(width)
.on_click(move |_, window, cx| {
editor.update(cx, |editor, cx| {
editor.expand_excerpt(excerpt_id, direction, window, cx);
@ -3627,7 +3627,7 @@ impl EditorElement {
ButtonLike::new("toggle-buffer-fold")
.style(ui::ButtonStyle::Transparent)
.height(px(28.).into())
.width(px(28.).into())
.width(px(28.))
.children(toggle_chevron_icon)
.tooltip({
let focus_handle = focus_handle.clone();

View file

@ -58,7 +58,7 @@ fn render_theme_section(tab_index: &mut isize, cx: &mut App) -> impl IntoElement
.tab_index(tab_index)
.selected_index(theme_mode as usize)
.style(ui::ToggleButtonGroupStyle::Outlined)
.button_width(rems_from_px(64.)),
.width(rems_from_px(3. * 64.)),
),
)
.child(
@ -305,8 +305,8 @@ fn render_base_keymap_section(tab_index: &mut isize, cx: &mut App) -> impl IntoE
.when_some(base_keymap, |this, base_keymap| {
this.selected_index(base_keymap)
})
.full_width()
.tab_index(tab_index)
.button_width(rems_from_px(216.))
.size(ui::ToggleButtonGroupSize::Medium)
.style(ui::ToggleButtonGroupStyle::Outlined),
);

View file

@ -706,7 +706,7 @@ fn render_popular_settings_section(
})
.tab_index(tab_index)
.style(ToggleButtonGroupStyle::Outlined)
.button_width(ui::rems_from_px(64.)),
.width(ui::rems_from_px(3. * 64.)),
),
)
}

View file

@ -295,7 +295,7 @@ impl NotebookEditor {
_cx: &mut Context<Self>,
) -> IconButton {
let id: ElementId = ElementId::Name(id.into());
IconButton::new(id, icon).width(px(CONTROL_SIZE).into())
IconButton::new(id, icon).width(px(CONTROL_SIZE))
}
fn render_notebook_controls(

View file

@ -324,7 +324,7 @@ impl FixedWidth for Button {
/// ```
///
/// This sets the button's width to be exactly 100 pixels.
fn width(mut self, width: DefiniteLength) -> Self {
fn width(mut self, width: impl Into<DefiniteLength>) -> Self {
self.base = self.base.width(width);
self
}

View file

@ -499,8 +499,8 @@ impl Clickable for ButtonLike {
}
impl FixedWidth for ButtonLike {
fn width(mut self, width: DefiniteLength) -> Self {
self.width = Some(width);
fn width(mut self, width: impl Into<DefiniteLength>) -> Self {
self.width = Some(width.into());
self
}

View file

@ -133,7 +133,7 @@ impl Clickable for IconButton {
}
impl FixedWidth for IconButton {
fn width(mut self, width: DefiniteLength) -> Self {
fn width(mut self, width: impl Into<DefiniteLength>) -> Self {
self.base = self.base.width(width);
self
}
@ -194,7 +194,7 @@ impl RenderOnce for IconButton {
.map(|this| match self.shape {
IconButtonShape::Square => {
let size = self.icon_size.square(window, cx);
this.width(size.into()).height(size.into())
this.width(size).height(size.into())
}
IconButtonShape::Wide => this,
})

View file

@ -1,6 +1,6 @@
use std::rc::Rc;
use gpui::{AnyView, ClickEvent};
use gpui::{AnyView, ClickEvent, relative};
use crate::{ButtonLike, ButtonLikeRounding, ElevationIndex, TintColor, Tooltip, prelude::*};
@ -73,8 +73,8 @@ impl SelectableButton for ToggleButton {
}
impl FixedWidth for ToggleButton {
fn width(mut self, width: DefiniteLength) -> Self {
self.base.width = Some(width);
fn width(mut self, width: impl Into<DefiniteLength>) -> Self {
self.base.width = Some(width.into());
self
}
@ -429,7 +429,7 @@ where
rows: [[T; COLS]; ROWS],
style: ToggleButtonGroupStyle,
size: ToggleButtonGroupSize,
button_width: Rems,
group_width: Option<DefiniteLength>,
selected_index: usize,
tab_index: Option<isize>,
}
@ -441,7 +441,7 @@ impl<T: ButtonBuilder, const COLS: usize> ToggleButtonGroup<T, COLS> {
rows: [buttons],
style: ToggleButtonGroupStyle::Transparent,
size: ToggleButtonGroupSize::Default,
button_width: rems_from_px(100.),
group_width: None,
selected_index: 0,
tab_index: None,
}
@ -455,7 +455,7 @@ impl<T: ButtonBuilder, const COLS: usize> ToggleButtonGroup<T, COLS, 2> {
rows: [first_row, second_row],
style: ToggleButtonGroupStyle::Transparent,
size: ToggleButtonGroupSize::Default,
button_width: rems_from_px(100.),
group_width: None,
selected_index: 0,
tab_index: None,
}
@ -473,11 +473,6 @@ impl<T: ButtonBuilder, const COLS: usize, const ROWS: usize> ToggleButtonGroup<T
self
}
pub fn button_width(mut self, button_width: Rems) -> Self {
self.button_width = button_width;
self
}
pub fn selected_index(mut self, index: usize) -> Self {
self.selected_index = index;
self
@ -491,6 +486,24 @@ impl<T: ButtonBuilder, const COLS: usize, const ROWS: usize> ToggleButtonGroup<T
*tab_index += (COLS * ROWS) as isize;
self
}
const fn button_width() -> DefiniteLength {
relative(1. / COLS as f32)
}
}
impl<T: ButtonBuilder, const COLS: usize, const ROWS: usize> FixedWidth
for ToggleButtonGroup<T, COLS, ROWS>
{
fn width(mut self, width: impl Into<DefiniteLength>) -> Self {
self.group_width = Some(width.into());
self
}
fn full_width(mut self) -> Self {
self.group_width = Some(relative(1.));
self
}
}
impl<T: ButtonBuilder, const COLS: usize, const ROWS: usize> RenderOnce
@ -511,6 +524,7 @@ impl<T: ButtonBuilder, const COLS: usize, const ROWS: usize> RenderOnce
let entry_index = row_index * COLS + col_index;
ButtonLike::new((self.group_name, entry_index))
.full_width()
.rounding(None)
.when_some(self.tab_index, |this, tab_index| {
this.tab_index(tab_index + entry_index as isize)
@ -527,7 +541,7 @@ impl<T: ButtonBuilder, const COLS: usize, const ROWS: usize> RenderOnce
})
.child(
h_flex()
.min_w(self.button_width)
.w_full()
.gap_1p5()
.px_3()
.py_1()
@ -561,6 +575,13 @@ impl<T: ButtonBuilder, const COLS: usize, const ROWS: usize> RenderOnce
let is_transparent = self.style == ToggleButtonGroupStyle::Transparent;
v_flex()
.map(|this| {
if let Some(width) = self.group_width {
this.w(width)
} else {
this.w_full()
}
})
.rounded_md()
.overflow_hidden()
.map(|this| {
@ -583,6 +604,8 @@ impl<T: ButtonBuilder, const COLS: usize, const ROWS: usize> RenderOnce
.when(is_outlined_or_filled && !last_item, |this| {
this.border_r_1().border_color(border_color)
})
.w(Self::button_width())
.overflow_hidden()
.child(item)
}))
}))
@ -630,7 +653,6 @@ impl<T: ButtonBuilder, const COLS: usize, const ROWS: usize> Component
],
)
.selected_index(1)
.button_width(rems_from_px(100.))
.into_any_element(),
),
single_example(
@ -656,7 +678,6 @@ impl<T: ButtonBuilder, const COLS: usize, const ROWS: usize> Component
],
)
.selected_index(1)
.button_width(rems_from_px(100.))
.into_any_element(),
),
single_example(
@ -675,7 +696,6 @@ impl<T: ButtonBuilder, const COLS: usize, const ROWS: usize> Component
],
)
.selected_index(3)
.button_width(rems_from_px(100.))
.into_any_element(),
),
single_example(
@ -718,7 +738,6 @@ impl<T: ButtonBuilder, const COLS: usize, const ROWS: usize> Component
],
)
.selected_index(3)
.button_width(rems_from_px(100.))
.into_any_element(),
),
],
@ -763,7 +782,6 @@ impl<T: ButtonBuilder, const COLS: usize, const ROWS: usize> Component
],
)
.selected_index(1)
.button_width(rems_from_px(100.))
.style(ToggleButtonGroupStyle::Outlined)
.into_any_element(),
),
@ -783,7 +801,6 @@ impl<T: ButtonBuilder, const COLS: usize, const ROWS: usize> Component
],
)
.selected_index(3)
.button_width(rems_from_px(100.))
.style(ToggleButtonGroupStyle::Outlined)
.into_any_element(),
),
@ -827,7 +844,6 @@ impl<T: ButtonBuilder, const COLS: usize, const ROWS: usize> Component
],
)
.selected_index(3)
.button_width(rems_from_px(100.))
.style(ToggleButtonGroupStyle::Outlined)
.into_any_element(),
),
@ -873,7 +889,6 @@ impl<T: ButtonBuilder, const COLS: usize, const ROWS: usize> Component
],
)
.selected_index(1)
.button_width(rems_from_px(100.))
.style(ToggleButtonGroupStyle::Filled)
.into_any_element(),
),
@ -893,7 +908,7 @@ impl<T: ButtonBuilder, const COLS: usize, const ROWS: usize> Component
],
)
.selected_index(3)
.button_width(rems_from_px(100.))
.width(rems_from_px(100.))
.style(ToggleButtonGroupStyle::Filled)
.into_any_element(),
),
@ -937,7 +952,7 @@ impl<T: ButtonBuilder, const COLS: usize, const ROWS: usize> Component
],
)
.selected_index(3)
.button_width(rems_from_px(100.))
.width(rems_from_px(100.))
.style(ToggleButtonGroupStyle::Filled)
.into_any_element(),
),
@ -957,7 +972,6 @@ impl<T: ButtonBuilder, const COLS: usize, const ROWS: usize> Component
],
)
.selected_index(1)
.button_width(rems_from_px(100.))
.into_any_element(),
)])
.into_any_element(),

View file

@ -3,7 +3,7 @@ use gpui::DefiniteLength;
/// A trait for elements that can have a fixed with. Enables the use of the `width` and `full_width` methods.
pub trait FixedWidth {
/// Sets the width of the element.
fn width(self, width: DefiniteLength) -> Self;
fn width(self, width: impl Into<DefiniteLength>) -> Self;
/// Sets the element's width to the full width of its container.
fn full_width(self) -> Self;

View file

@ -216,7 +216,7 @@ impl QuickActionBar {
.size(IconSize::XSmall)
.color(Color::Muted),
)
.width(rems(1.).into())
.width(rems(1.))
.disabled(menu_state.popover_disabled),
Tooltip::text("REPL Menu"),
);