diff --git a/src/buttons/group.rs b/src/buttons/group.rs index 384c365..2bffe6d 100644 --- a/src/buttons/group.rs +++ b/src/buttons/group.rs @@ -10,6 +10,7 @@ use super::{ button::{Slot, label_size}, }; use crate::{ + i18n, primitives::{FocusRing, Icon, IconName, Tooltip}, theme::{ActiveTheme, ControlSize, Radius}, }; @@ -93,7 +94,7 @@ fn toggle_face( item: &ToggleItem, on: bool, size: ControlSize, - press: OnPress, + press: Option, cx: &App, ) -> impl IntoElement + use<> { assert!( @@ -125,18 +126,20 @@ fn toggle_face( .text_size(theme.text_size(text)) .font_weight(FontWeight::MEDIUM) .text_color(fg) - .map(|face| { - if on { - face.bg(colors.active) - } else { - face.hover(|style| style.bg(hover)) - } + .when(on, |face| face.bg(colors.active)) + .map(|face| match press { + None => face + .aria_description(i18n::text(cx, "state.unavailable", &[])) + .opacity(0.45) + .cursor_not_allowed(), + Some(press) => face + .when(!on, |face| face.hover(|style| style.bg(hover))) + .cursor_pointer() + .tab_index(0) + .focus_ring(cx) + .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default()) + .on_click(move |_, window, cx| press(window, cx)), }) - .cursor_pointer() - .tab_index(0) - .focus_ring(cx) - .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default()) - .on_click(move |_, window, cx| press(window, cx)) .when_some(item.icon, |face, icon| { face.child(Icon::new(icon).size(icon_size).color(fg)) }) @@ -154,6 +157,7 @@ pub struct ToggleButton { id: ElementId, item: ToggleItem, on: bool, + disabled: bool, size: ControlSize, on_toggle: Option, } @@ -164,6 +168,7 @@ impl ToggleButton { id: id.into(), item, on, + disabled: false, size: ControlSize::default(), on_toggle: None, } @@ -174,6 +179,12 @@ impl ToggleButton { self } + /// Shown, not pressable; the item's tooltip can say why. + pub fn disabled(mut self, disabled: bool) -> Self { + self.disabled = disabled; + self + } + pub fn on_toggle(mut self, handler: impl Fn(bool, &mut Window, &mut App) + 'static) -> Self { self.on_toggle = Some(Rc::new(handler)); self @@ -189,6 +200,7 @@ impl RenderOnce for ToggleButton { toggle(!on, window, cx); } }); + let press = (!self.disabled).then_some(press); toggle_face(self.id, &self.item, on, self.size, press, cx) } } @@ -280,7 +292,7 @@ impl RenderOnce for ToggleGroup { item, on, self.size, - press, + Some(press), cx, ) }) diff --git a/src/buttons/tests.rs b/src/buttons/tests.rs index f27eeec..eedd059 100644 --- a/src/buttons/tests.rs +++ b/src/buttons/tests.rs @@ -1,9 +1,9 @@ use gpui::{ - Context, FocusHandle, IntoElement, KeyBinding, KeyUpEvent, Keystroke, ParentElement, Render, - Styled, TestAppContext, VisualTestContext, Window, + Context, FocusHandle, IntoElement, KeyBinding, KeyUpEvent, Keystroke, Modifiers, ParentElement, + Render, Styled, TestAppContext, VisualTestContext, Window, div, point, px, }; -use super::Button; +use super::{Button, ToggleButton, ToggleItem}; use crate::{ primitives::{FocusNext, FocusScope}, theme::Theme, @@ -63,3 +63,38 @@ fn a_loading_button_ignores_presses_and_keeps_its_tab_stop(cx: &mut TestAppConte "focus stayed on it" ); } + +/// One toggle that counts its presses. +struct Toggles { + disabled: bool, + presses: usize, +} + +impl Render for Toggles { + fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { + let view = cx.entity(); + div().size_full().child( + ToggleButton::new("bold", ToggleItem::new("bold").label("B"), false) + .disabled(self.disabled) + .on_toggle(move |_, _, cx| view.update(cx, |toggles, _| toggles.presses += 1)), + ) + } +} + +#[gpui::test] +fn a_disabled_toggle_ignores_clicks(cx: &mut TestAppContext) { + cx.update(Theme::init); + let (view, cx) = cx.add_window_view(|_, _| Toggles { + disabled: true, + presses: 0, + }); + cx.simulate_click(point(px(4.0), px(4.0)), Modifiers::none()); + assert_eq!(view.read_with(cx, |toggles, _| toggles.presses), 0); + view.update(cx, |toggles, cx| { + toggles.disabled = false; + cx.notify(); + }); + cx.run_until_parked(); + cx.simulate_click(point(px(4.0), px(4.0)), Modifiers::none()); + assert_eq!(view.read_with(cx, |toggles, _| toggles.presses), 1); +}