Buttons · ToggleButton disabled: shown, not pressable, no tab stop, says it is unavailable; its tooltip can give the reason
This commit is contained in:
2 files changed
+63
-16
No files matched your search
+25
-13
@@ -10,6 +10,7 @@ use super::{
|
|||||||
button::{Slot, label_size},
|
button::{Slot, label_size},
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
i18n,
|
||||||
primitives::{FocusRing, Icon, IconName, Tooltip},
|
primitives::{FocusRing, Icon, IconName, Tooltip},
|
||||||
theme::{ActiveTheme, ControlSize, Radius},
|
theme::{ActiveTheme, ControlSize, Radius},
|
||||||
};
|
};
|
||||||
@@ -93,7 +94,7 @@ fn toggle_face(
|
|||||||
item: &ToggleItem,
|
item: &ToggleItem,
|
||||||
on: bool,
|
on: bool,
|
||||||
size: ControlSize,
|
size: ControlSize,
|
||||||
press: OnPress,
|
press: Option<OnPress>,
|
||||||
cx: &App,
|
cx: &App,
|
||||||
) -> impl IntoElement + use<> {
|
) -> impl IntoElement + use<> {
|
||||||
assert!(
|
assert!(
|
||||||
@@ -125,18 +126,20 @@ fn toggle_face(
|
|||||||
.text_size(theme.text_size(text))
|
.text_size(theme.text_size(text))
|
||||||
.font_weight(FontWeight::MEDIUM)
|
.font_weight(FontWeight::MEDIUM)
|
||||||
.text_color(fg)
|
.text_color(fg)
|
||||||
.map(|face| {
|
.when(on, |face| face.bg(colors.active))
|
||||||
if on {
|
.map(|face| match press {
|
||||||
face.bg(colors.active)
|
None => face
|
||||||
} else {
|
.aria_description(i18n::text(cx, "state.unavailable", &[]))
|
||||||
face.hover(|style| style.bg(hover))
|
.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| {
|
.when_some(item.icon, |face, icon| {
|
||||||
face.child(Icon::new(icon).size(icon_size).color(fg))
|
face.child(Icon::new(icon).size(icon_size).color(fg))
|
||||||
})
|
})
|
||||||
@@ -154,6 +157,7 @@ pub struct ToggleButton {
|
|||||||
id: ElementId,
|
id: ElementId,
|
||||||
item: ToggleItem,
|
item: ToggleItem,
|
||||||
on: bool,
|
on: bool,
|
||||||
|
disabled: bool,
|
||||||
size: ControlSize,
|
size: ControlSize,
|
||||||
on_toggle: Option<OnToggle>,
|
on_toggle: Option<OnToggle>,
|
||||||
}
|
}
|
||||||
@@ -164,6 +168,7 @@ impl ToggleButton {
|
|||||||
id: id.into(),
|
id: id.into(),
|
||||||
item,
|
item,
|
||||||
on,
|
on,
|
||||||
|
disabled: false,
|
||||||
size: ControlSize::default(),
|
size: ControlSize::default(),
|
||||||
on_toggle: None,
|
on_toggle: None,
|
||||||
}
|
}
|
||||||
@@ -174,6 +179,12 @@ impl ToggleButton {
|
|||||||
self
|
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 {
|
pub fn on_toggle(mut self, handler: impl Fn(bool, &mut Window, &mut App) + 'static) -> Self {
|
||||||
self.on_toggle = Some(Rc::new(handler));
|
self.on_toggle = Some(Rc::new(handler));
|
||||||
self
|
self
|
||||||
@@ -189,6 +200,7 @@ impl RenderOnce for ToggleButton {
|
|||||||
toggle(!on, window, cx);
|
toggle(!on, window, cx);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
let press = (!self.disabled).then_some(press);
|
||||||
toggle_face(self.id, &self.item, on, self.size, press, cx)
|
toggle_face(self.id, &self.item, on, self.size, press, cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -280,7 +292,7 @@ impl RenderOnce for ToggleGroup {
|
|||||||
item,
|
item,
|
||||||
on,
|
on,
|
||||||
self.size,
|
self.size,
|
||||||
press,
|
Some(press),
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
+38
-3
@@ -1,9 +1,9 @@
|
|||||||
use gpui::{
|
use gpui::{
|
||||||
Context, FocusHandle, IntoElement, KeyBinding, KeyUpEvent, Keystroke, ParentElement, Render,
|
Context, FocusHandle, IntoElement, KeyBinding, KeyUpEvent, Keystroke, Modifiers, ParentElement,
|
||||||
Styled, TestAppContext, VisualTestContext, Window,
|
Render, Styled, TestAppContext, VisualTestContext, Window, div, point, px,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::Button;
|
use super::{Button, ToggleButton, ToggleItem};
|
||||||
use crate::{
|
use crate::{
|
||||||
primitives::{FocusNext, FocusScope},
|
primitives::{FocusNext, FocusScope},
|
||||||
theme::Theme,
|
theme::Theme,
|
||||||
@@ -63,3 +63,38 @@ fn a_loading_button_ignores_presses_and_keeps_its_tab_stop(cx: &mut TestAppConte
|
|||||||
"focus stayed on it"
|
"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<Self>) -> 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);
|
||||||
|
}
|
||||||
Reference in new issue
Block a user