ui: Wire up tab indices within buttons (#35368)
This change adds the current tab index functionality to buttons and implements a proof of concept for the new welcome page. Primarily blocked on https://github.com/zed-industries/zed/pull/34804, secondarily on https://github.com/zed-industries/zed/pull/35075 so we can ensure navigation always works as intended. Another thing to consider here is whether we want to assign the tab order more implicitly / "automatically" based on the current layout ordering. This would generally enable us to add a default order to focusable elements if we want this. See [the specification](https://html.spec.whatwg.org/multipage/interaction.html#flattened-tabindex-ordered-focus-navigation-scope) on some more context on how the web usually handles this for focusable elements. Release Notes: - N/A
This commit is contained in:
parent
5940ed979f
commit
19c1504c8f
6 changed files with 53 additions and 6 deletions
|
@ -15,13 +15,13 @@ path = "src/onboarding.rs"
|
|||
default = []
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
ai_onboarding.workspace = true
|
||||
anyhow.workspace = true
|
||||
client.workspace = true
|
||||
command_palette_hooks.workspace = true
|
||||
component.workspace = true
|
||||
documented.workspace = true
|
||||
db.workspace = true
|
||||
documented.workspace = true
|
||||
editor.workspace = true
|
||||
feature_flags.workspace = true
|
||||
fs.workspace = true
|
||||
|
|
|
@ -2,6 +2,7 @@ use gpui::{
|
|||
Action, App, Context, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement,
|
||||
NoAction, ParentElement, Render, Styled, Window, actions,
|
||||
};
|
||||
use menu::{SelectNext, SelectPrevious};
|
||||
use ui::{ButtonLike, Divider, DividerColor, KeyBinding, Vector, VectorName, prelude::*};
|
||||
use workspace::{
|
||||
NewFile, Open, WorkspaceId,
|
||||
|
@ -124,6 +125,7 @@ impl SectionEntry {
|
|||
cx: &App,
|
||||
) -> impl IntoElement {
|
||||
ButtonLike::new(("onboarding-button-id", button_index))
|
||||
.tab_index(button_index as isize)
|
||||
.full_width()
|
||||
.size(ButtonSize::Medium)
|
||||
.child(
|
||||
|
@ -153,10 +155,23 @@ pub struct WelcomePage {
|
|||
focus_handle: FocusHandle,
|
||||
}
|
||||
|
||||
impl WelcomePage {
|
||||
fn select_next(&mut self, _: &SelectNext, window: &mut Window, cx: &mut Context<Self>) {
|
||||
window.focus_next();
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn select_previous(&mut self, _: &SelectPrevious, window: &mut Window, cx: &mut Context<Self>) {
|
||||
window.focus_prev();
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for WelcomePage {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let (first_section, second_entries) = CONTENT;
|
||||
let (first_section, second_section) = CONTENT;
|
||||
let first_section_entries = first_section.entries.len();
|
||||
let last_index = first_section_entries + second_section.entries.len();
|
||||
|
||||
h_flex()
|
||||
.size_full()
|
||||
|
@ -165,6 +180,8 @@ impl Render for WelcomePage {
|
|||
.bg(cx.theme().colors().editor_background)
|
||||
.key_context("Welcome")
|
||||
.track_focus(&self.focus_handle(cx))
|
||||
.on_action(cx.listener(Self::select_previous))
|
||||
.on_action(cx.listener(Self::select_next))
|
||||
.child(
|
||||
h_flex()
|
||||
.px_12()
|
||||
|
@ -202,7 +219,7 @@ impl Render for WelcomePage {
|
|||
window,
|
||||
cx,
|
||||
))
|
||||
.child(second_entries.render(
|
||||
.child(second_section.render(
|
||||
first_section_entries,
|
||||
&self.focus_handle,
|
||||
window,
|
||||
|
@ -220,6 +237,7 @@ impl Render for WelcomePage {
|
|||
.border_dashed()
|
||||
.child(
|
||||
Button::new("welcome-exit", "Return to Setup")
|
||||
.tab_index(last_index as isize)
|
||||
.full_width()
|
||||
.label_size(LabelSize::XSmall)
|
||||
.on_click(|_, window, cx| {
|
||||
|
|
|
@ -393,6 +393,11 @@ impl ButtonCommon for Button {
|
|||
self
|
||||
}
|
||||
|
||||
fn tab_index(mut self, tab_index: impl Into<isize>) -> Self {
|
||||
self.base = self.base.tab_index(tab_index);
|
||||
self
|
||||
}
|
||||
|
||||
fn layer(mut self, elevation: ElevationIndex) -> Self {
|
||||
self.base = self.base.layer(elevation);
|
||||
self
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use documented::Documented;
|
||||
use gpui::{
|
||||
AnyElement, AnyView, ClickEvent, CursorStyle, DefiniteLength, Hsla, MouseButton,
|
||||
MouseDownEvent, MouseUpEvent, Rems, relative, transparent_black,
|
||||
MouseDownEvent, MouseUpEvent, Rems, StyleRefinement, relative, transparent_black,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
|
@ -37,6 +37,8 @@ pub trait ButtonCommon: Clickable + Disableable {
|
|||
/// exceptions might a scroll bar, or a slider.
|
||||
fn tooltip(self, tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self;
|
||||
|
||||
fn tab_index(self, tab_index: impl Into<isize>) -> Self;
|
||||
|
||||
fn layer(self, elevation: ElevationIndex) -> Self;
|
||||
}
|
||||
|
||||
|
@ -393,6 +395,7 @@ pub struct ButtonLike {
|
|||
pub(super) width: Option<DefiniteLength>,
|
||||
pub(super) height: Option<DefiniteLength>,
|
||||
pub(super) layer: Option<ElevationIndex>,
|
||||
tab_index: Option<isize>,
|
||||
size: ButtonSize,
|
||||
rounding: Option<ButtonLikeRounding>,
|
||||
tooltip: Option<Box<dyn Fn(&mut Window, &mut App) -> AnyView>>,
|
||||
|
@ -421,6 +424,7 @@ impl ButtonLike {
|
|||
on_click: None,
|
||||
on_right_click: None,
|
||||
layer: None,
|
||||
tab_index: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -525,6 +529,11 @@ impl ButtonCommon for ButtonLike {
|
|||
self
|
||||
}
|
||||
|
||||
fn tab_index(mut self, tab_index: impl Into<isize>) -> Self {
|
||||
self.tab_index = Some(tab_index.into());
|
||||
self
|
||||
}
|
||||
|
||||
fn layer(mut self, elevation: ElevationIndex) -> Self {
|
||||
self.layer = Some(elevation);
|
||||
self
|
||||
|
@ -554,6 +563,7 @@ impl RenderOnce for ButtonLike {
|
|||
self.base
|
||||
.h_flex()
|
||||
.id(self.id.clone())
|
||||
.when_some(self.tab_index, |this, tab_index| this.tab_index(tab_index))
|
||||
.font_ui(cx)
|
||||
.group("")
|
||||
.flex_none()
|
||||
|
@ -591,8 +601,12 @@ impl RenderOnce for ButtonLike {
|
|||
}
|
||||
})
|
||||
.when(!self.disabled, |this| {
|
||||
let hovered_style = style.hovered(self.layer, cx);
|
||||
let focus_color =
|
||||
|refinement: StyleRefinement| refinement.bg(hovered_style.background);
|
||||
this.cursor(self.cursor_style)
|
||||
.hover(|hover| hover.bg(style.hovered(self.layer, cx).background))
|
||||
.hover(focus_color)
|
||||
.focus(focus_color)
|
||||
.active(|active| active.bg(style.active(cx).background))
|
||||
})
|
||||
.when_some(
|
||||
|
|
|
@ -164,6 +164,11 @@ impl ButtonCommon for IconButton {
|
|||
self
|
||||
}
|
||||
|
||||
fn tab_index(mut self, tab_index: impl Into<isize>) -> Self {
|
||||
self.base = self.base.tab_index(tab_index);
|
||||
self
|
||||
}
|
||||
|
||||
fn layer(mut self, elevation: ElevationIndex) -> Self {
|
||||
self.base = self.base.layer(elevation);
|
||||
self
|
||||
|
|
|
@ -121,6 +121,11 @@ impl ButtonCommon for ToggleButton {
|
|||
self
|
||||
}
|
||||
|
||||
fn tab_index(mut self, tab_index: impl Into<isize>) -> Self {
|
||||
self.base = self.base.tab_index(tab_index);
|
||||
self
|
||||
}
|
||||
|
||||
fn layer(mut self, elevation: ElevationIndex) -> Self {
|
||||
self.base = self.base.layer(elevation);
|
||||
self
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue