onboarding: Wire up tab index (#35659)
Closes #ISSUE Allows tabbing through everything in all three pages. Until #35075 is merged it is not possible to actually "click" tab focused buttons with the keyboard. Additionally adds an action `onboarding::Finish` and displays the keybind. The action corresponds to both the "Skip all" and "Start Building" buttons, with the keybind displayed similar to how it is for the page nav buttons Release Notes: - N/A *or* Added/Fixed/Improved ... --------- Co-authored-by: MrSubidubi <finn@zed.dev>
This commit is contained in:
parent
0b5592d788
commit
6b77654f66
11 changed files with 505 additions and 280 deletions
|
@ -412,6 +412,7 @@ where
|
|||
size: ToggleButtonGroupSize,
|
||||
button_width: Rems,
|
||||
selected_index: usize,
|
||||
tab_index: Option<isize>,
|
||||
}
|
||||
|
||||
impl<T: ButtonBuilder, const COLS: usize> ToggleButtonGroup<T, COLS> {
|
||||
|
@ -423,6 +424,7 @@ impl<T: ButtonBuilder, const COLS: usize> ToggleButtonGroup<T, COLS> {
|
|||
size: ToggleButtonGroupSize::Default,
|
||||
button_width: rems_from_px(100.),
|
||||
selected_index: 0,
|
||||
tab_index: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -436,6 +438,7 @@ impl<T: ButtonBuilder, const COLS: usize> ToggleButtonGroup<T, COLS, 2> {
|
|||
size: ToggleButtonGroupSize::Default,
|
||||
button_width: rems_from_px(100.),
|
||||
selected_index: 0,
|
||||
tab_index: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -460,6 +463,15 @@ impl<T: ButtonBuilder, const COLS: usize, const ROWS: usize> ToggleButtonGroup<T
|
|||
self.selected_index = index;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the tab index for the toggle button group.
|
||||
/// The tab index is set to the initial value provided, then the
|
||||
/// value is incremented by the number of buttons in the group.
|
||||
pub fn tab_index(mut self, tab_index: &mut isize) -> Self {
|
||||
self.tab_index = Some(*tab_index);
|
||||
*tab_index += (COLS * ROWS) as isize;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ButtonBuilder, const COLS: usize, const ROWS: usize> RenderOnce
|
||||
|
@ -479,6 +491,9 @@ 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))
|
||||
.when_some(self.tab_index, |this, tab_index| {
|
||||
this.tab_index(tab_index + entry_index as isize)
|
||||
})
|
||||
.when(entry_index == self.selected_index || selected, |this| {
|
||||
this.toggle_state(true)
|
||||
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
|
||||
|
|
|
@ -19,6 +19,7 @@ pub struct NumericStepper {
|
|||
/// Whether to reserve space for the reset button.
|
||||
reserve_space_for_reset: bool,
|
||||
on_reset: Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
|
||||
tab_index: Option<isize>,
|
||||
}
|
||||
|
||||
impl NumericStepper {
|
||||
|
@ -36,6 +37,7 @@ impl NumericStepper {
|
|||
on_increment: Box::new(on_increment),
|
||||
reserve_space_for_reset: false,
|
||||
on_reset: None,
|
||||
tab_index: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,6 +58,11 @@ impl NumericStepper {
|
|||
self.on_reset = Some(Box::new(on_reset));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn tab_index(mut self, tab_index: isize) -> Self {
|
||||
self.tab_index = Some(tab_index);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for NumericStepper {
|
||||
|
@ -64,6 +71,7 @@ impl RenderOnce for NumericStepper {
|
|||
let icon_size = IconSize::Small;
|
||||
|
||||
let is_outlined = matches!(self.style, NumericStepperStyle::Outlined);
|
||||
let mut tab_index = self.tab_index;
|
||||
|
||||
h_flex()
|
||||
.id(self.id)
|
||||
|
@ -74,6 +82,10 @@ impl RenderOnce for NumericStepper {
|
|||
IconButton::new("reset", IconName::RotateCcw)
|
||||
.shape(shape)
|
||||
.icon_size(icon_size)
|
||||
.when_some(tab_index.as_mut(), |this, tab_index| {
|
||||
*tab_index += 1;
|
||||
this.tab_index(*tab_index - 1)
|
||||
})
|
||||
.on_click(on_reset),
|
||||
)
|
||||
} else if self.reserve_space_for_reset {
|
||||
|
@ -113,6 +125,12 @@ impl RenderOnce for NumericStepper {
|
|||
.border_r_1()
|
||||
.border_color(cx.theme().colors().border_variant)
|
||||
.child(Icon::new(IconName::Dash).size(IconSize::Small))
|
||||
.when_some(tab_index.as_mut(), |this, tab_index| {
|
||||
*tab_index += 1;
|
||||
this.tab_index(*tab_index - 1).focus(|style| {
|
||||
style.bg(cx.theme().colors().element_hover)
|
||||
})
|
||||
})
|
||||
.on_click(self.on_decrement),
|
||||
)
|
||||
} else {
|
||||
|
@ -120,6 +138,10 @@ impl RenderOnce for NumericStepper {
|
|||
IconButton::new("decrement", IconName::Dash)
|
||||
.shape(shape)
|
||||
.icon_size(icon_size)
|
||||
.when_some(tab_index.as_mut(), |this, tab_index| {
|
||||
*tab_index += 1;
|
||||
this.tab_index(*tab_index - 1)
|
||||
})
|
||||
.on_click(self.on_decrement),
|
||||
)
|
||||
}
|
||||
|
@ -137,6 +159,12 @@ impl RenderOnce for NumericStepper {
|
|||
.border_l_1()
|
||||
.border_color(cx.theme().colors().border_variant)
|
||||
.child(Icon::new(IconName::Plus).size(IconSize::Small))
|
||||
.when_some(tab_index.as_mut(), |this, tab_index| {
|
||||
*tab_index += 1;
|
||||
this.tab_index(*tab_index - 1).focus(|style| {
|
||||
style.bg(cx.theme().colors().element_hover)
|
||||
})
|
||||
})
|
||||
.on_click(self.on_increment),
|
||||
)
|
||||
} else {
|
||||
|
@ -144,6 +172,10 @@ impl RenderOnce for NumericStepper {
|
|||
IconButton::new("increment", IconName::Dash)
|
||||
.shape(shape)
|
||||
.icon_size(icon_size)
|
||||
.when_some(tab_index.as_mut(), |this, tab_index| {
|
||||
*tab_index += 1;
|
||||
this.tab_index(*tab_index - 1)
|
||||
})
|
||||
.on_click(self.on_increment),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -424,6 +424,7 @@ pub struct Switch {
|
|||
label: Option<SharedString>,
|
||||
key_binding: Option<KeyBinding>,
|
||||
color: SwitchColor,
|
||||
tab_index: Option<isize>,
|
||||
}
|
||||
|
||||
impl Switch {
|
||||
|
@ -437,6 +438,7 @@ impl Switch {
|
|||
label: None,
|
||||
key_binding: None,
|
||||
color: SwitchColor::default(),
|
||||
tab_index: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -472,6 +474,11 @@ impl Switch {
|
|||
self.key_binding = key_binding.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn tab_index(mut self, tab_index: impl Into<isize>) -> Self {
|
||||
self.tab_index = Some(tab_index.into());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Switch {
|
||||
|
@ -501,6 +508,20 @@ impl RenderOnce for Switch {
|
|||
.w(DynamicSpacing::Base32.rems(cx))
|
||||
.h(DynamicSpacing::Base20.rems(cx))
|
||||
.group(group_id.clone())
|
||||
.border_1()
|
||||
.p(px(1.0))
|
||||
.border_color(cx.theme().colors().border_transparent)
|
||||
.rounded_full()
|
||||
.id((self.id.clone(), "switch"))
|
||||
.when_some(
|
||||
self.tab_index.filter(|_| !self.disabled),
|
||||
|this, tab_index| {
|
||||
this.tab_index(tab_index).focus(|mut style| {
|
||||
style.border_color = Some(cx.theme().colors().border_focused);
|
||||
style
|
||||
})
|
||||
},
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.when(is_on, |on| on.justify_end())
|
||||
|
@ -572,6 +593,7 @@ pub struct SwitchField {
|
|||
disabled: bool,
|
||||
color: SwitchColor,
|
||||
tooltip: Option<Rc<dyn Fn(&mut Window, &mut App) -> AnyView>>,
|
||||
tab_index: Option<isize>,
|
||||
}
|
||||
|
||||
impl SwitchField {
|
||||
|
@ -591,6 +613,7 @@ impl SwitchField {
|
|||
disabled: false,
|
||||
color: SwitchColor::Accent,
|
||||
tooltip: None,
|
||||
tab_index: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -615,14 +638,33 @@ impl SwitchField {
|
|||
self.tooltip = Some(Rc::new(tooltip));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn tab_index(mut self, tab_index: isize) -> Self {
|
||||
self.tab_index = Some(tab_index);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for SwitchField {
|
||||
fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
|
||||
let tooltip = self.tooltip;
|
||||
let tooltip = self.tooltip.map(|tooltip_fn| {
|
||||
h_flex()
|
||||
.gap_0p5()
|
||||
.child(Label::new(self.label.clone()))
|
||||
.child(
|
||||
IconButton::new("tooltip_button", IconName::Info)
|
||||
.icon_size(IconSize::XSmall)
|
||||
.icon_color(Color::Muted)
|
||||
.shape(crate::IconButtonShape::Square)
|
||||
.tooltip({
|
||||
let tooltip = tooltip_fn.clone();
|
||||
move |window, cx| tooltip(window, cx)
|
||||
}),
|
||||
)
|
||||
});
|
||||
|
||||
h_flex()
|
||||
.id(SharedString::from(format!("{}-container", self.id)))
|
||||
.id((self.id.clone(), "container"))
|
||||
.when(!self.disabled, |this| {
|
||||
this.hover(|this| this.cursor_pointer())
|
||||
})
|
||||
|
@ -630,25 +672,11 @@ impl RenderOnce for SwitchField {
|
|||
.gap_4()
|
||||
.justify_between()
|
||||
.flex_wrap()
|
||||
.child(match (&self.description, &tooltip) {
|
||||
.child(match (&self.description, tooltip) {
|
||||
(Some(description), Some(tooltip)) => v_flex()
|
||||
.gap_0p5()
|
||||
.max_w_5_6()
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_0p5()
|
||||
.child(Label::new(self.label.clone()))
|
||||
.child(
|
||||
IconButton::new("tooltip_button", IconName::Info)
|
||||
.icon_size(IconSize::XSmall)
|
||||
.icon_color(Color::Muted)
|
||||
.shape(crate::IconButtonShape::Square)
|
||||
.tooltip({
|
||||
let tooltip = tooltip.clone();
|
||||
move |window, cx| tooltip(window, cx)
|
||||
}),
|
||||
),
|
||||
)
|
||||
.child(tooltip)
|
||||
.child(Label::new(description.clone()).color(Color::Muted))
|
||||
.into_any_element(),
|
||||
(Some(description), None) => v_flex()
|
||||
|
@ -657,35 +685,23 @@ impl RenderOnce for SwitchField {
|
|||
.child(Label::new(self.label.clone()))
|
||||
.child(Label::new(description.clone()).color(Color::Muted))
|
||||
.into_any_element(),
|
||||
(None, Some(tooltip)) => h_flex()
|
||||
.gap_0p5()
|
||||
.child(Label::new(self.label.clone()))
|
||||
.child(
|
||||
IconButton::new("tooltip_button", IconName::Info)
|
||||
.icon_size(IconSize::XSmall)
|
||||
.icon_color(Color::Muted)
|
||||
.shape(crate::IconButtonShape::Square)
|
||||
.tooltip({
|
||||
let tooltip = tooltip.clone();
|
||||
move |window, cx| tooltip(window, cx)
|
||||
}),
|
||||
)
|
||||
.into_any_element(),
|
||||
(None, Some(tooltip)) => tooltip.into_any_element(),
|
||||
(None, None) => Label::new(self.label.clone()).into_any_element(),
|
||||
})
|
||||
.child(
|
||||
Switch::new(
|
||||
SharedString::from(format!("{}-switch", self.id)),
|
||||
self.toggle_state,
|
||||
)
|
||||
.color(self.color)
|
||||
.disabled(self.disabled)
|
||||
.on_click({
|
||||
let on_click = self.on_click.clone();
|
||||
move |state, window, cx| {
|
||||
(on_click)(state, window, cx);
|
||||
}
|
||||
}),
|
||||
Switch::new((self.id.clone(), "switch"), self.toggle_state)
|
||||
.color(self.color)
|
||||
.disabled(self.disabled)
|
||||
.when_some(
|
||||
self.tab_index.filter(|_| !self.disabled),
|
||||
|this, tab_index| this.tab_index(tab_index),
|
||||
)
|
||||
.on_click({
|
||||
let on_click = self.on_click.clone();
|
||||
move |state, window, cx| {
|
||||
(on_click)(state, window, cx);
|
||||
}
|
||||
}),
|
||||
)
|
||||
.when(!self.disabled, |this| {
|
||||
this.on_click({
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue