Skip to content

Commit 19c1504

Browse files
authored
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 #34804, secondarily on #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
1 parent 5940ed9 commit 19c1504

File tree

6 files changed

+53
-6
lines changed

6 files changed

+53
-6
lines changed

crates/onboarding/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ path = "src/onboarding.rs"
1515
default = []
1616

1717
[dependencies]
18-
anyhow.workspace = true
1918
ai_onboarding.workspace = true
19+
anyhow.workspace = true
2020
client.workspace = true
2121
command_palette_hooks.workspace = true
2222
component.workspace = true
23-
documented.workspace = true
2423
db.workspace = true
24+
documented.workspace = true
2525
editor.workspace = true
2626
feature_flags.workspace = true
2727
fs.workspace = true

crates/onboarding/src/welcome.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use gpui::{
22
Action, App, Context, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement,
33
NoAction, ParentElement, Render, Styled, Window, actions,
44
};
5+
use menu::{SelectNext, SelectPrevious};
56
use ui::{ButtonLike, Divider, DividerColor, KeyBinding, Vector, VectorName, prelude::*};
67
use workspace::{
78
NewFile, Open, WorkspaceId,
@@ -124,6 +125,7 @@ impl SectionEntry {
124125
cx: &App,
125126
) -> impl IntoElement {
126127
ButtonLike::new(("onboarding-button-id", button_index))
128+
.tab_index(button_index as isize)
127129
.full_width()
128130
.size(ButtonSize::Medium)
129131
.child(
@@ -153,10 +155,23 @@ pub struct WelcomePage {
153155
focus_handle: FocusHandle,
154156
}
155157

158+
impl WelcomePage {
159+
fn select_next(&mut self, _: &SelectNext, window: &mut Window, cx: &mut Context<Self>) {
160+
window.focus_next();
161+
cx.notify();
162+
}
163+
164+
fn select_previous(&mut self, _: &SelectPrevious, window: &mut Window, cx: &mut Context<Self>) {
165+
window.focus_prev();
166+
cx.notify();
167+
}
168+
}
169+
156170
impl Render for WelcomePage {
157171
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
158-
let (first_section, second_entries) = CONTENT;
172+
let (first_section, second_section) = CONTENT;
159173
let first_section_entries = first_section.entries.len();
174+
let last_index = first_section_entries + second_section.entries.len();
160175

161176
h_flex()
162177
.size_full()
@@ -165,6 +180,8 @@ impl Render for WelcomePage {
165180
.bg(cx.theme().colors().editor_background)
166181
.key_context("Welcome")
167182
.track_focus(&self.focus_handle(cx))
183+
.on_action(cx.listener(Self::select_previous))
184+
.on_action(cx.listener(Self::select_next))
168185
.child(
169186
h_flex()
170187
.px_12()
@@ -202,7 +219,7 @@ impl Render for WelcomePage {
202219
window,
203220
cx,
204221
))
205-
.child(second_entries.render(
222+
.child(second_section.render(
206223
first_section_entries,
207224
&self.focus_handle,
208225
window,
@@ -220,6 +237,7 @@ impl Render for WelcomePage {
220237
.border_dashed()
221238
.child(
222239
Button::new("welcome-exit", "Return to Setup")
240+
.tab_index(last_index as isize)
223241
.full_width()
224242
.label_size(LabelSize::XSmall)
225243
.on_click(|_, window, cx| {

crates/ui/src/components/button/button.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,11 @@ impl ButtonCommon for Button {
393393
self
394394
}
395395

396+
fn tab_index(mut self, tab_index: impl Into<isize>) -> Self {
397+
self.base = self.base.tab_index(tab_index);
398+
self
399+
}
400+
396401
fn layer(mut self, elevation: ElevationIndex) -> Self {
397402
self.base = self.base.layer(elevation);
398403
self

crates/ui/src/components/button/button_like.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use documented::Documented;
22
use gpui::{
33
AnyElement, AnyView, ClickEvent, CursorStyle, DefiniteLength, Hsla, MouseButton,
4-
MouseDownEvent, MouseUpEvent, Rems, relative, transparent_black,
4+
MouseDownEvent, MouseUpEvent, Rems, StyleRefinement, relative, transparent_black,
55
};
66
use smallvec::SmallVec;
77

@@ -37,6 +37,8 @@ pub trait ButtonCommon: Clickable + Disableable {
3737
/// exceptions might a scroll bar, or a slider.
3838
fn tooltip(self, tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self;
3939

40+
fn tab_index(self, tab_index: impl Into<isize>) -> Self;
41+
4042
fn layer(self, elevation: ElevationIndex) -> Self;
4143
}
4244

@@ -393,6 +395,7 @@ pub struct ButtonLike {
393395
pub(super) width: Option<DefiniteLength>,
394396
pub(super) height: Option<DefiniteLength>,
395397
pub(super) layer: Option<ElevationIndex>,
398+
tab_index: Option<isize>,
396399
size: ButtonSize,
397400
rounding: Option<ButtonLikeRounding>,
398401
tooltip: Option<Box<dyn Fn(&mut Window, &mut App) -> AnyView>>,
@@ -421,6 +424,7 @@ impl ButtonLike {
421424
on_click: None,
422425
on_right_click: None,
423426
layer: None,
427+
tab_index: None,
424428
}
425429
}
426430

@@ -525,6 +529,11 @@ impl ButtonCommon for ButtonLike {
525529
self
526530
}
527531

532+
fn tab_index(mut self, tab_index: impl Into<isize>) -> Self {
533+
self.tab_index = Some(tab_index.into());
534+
self
535+
}
536+
528537
fn layer(mut self, elevation: ElevationIndex) -> Self {
529538
self.layer = Some(elevation);
530539
self
@@ -554,6 +563,7 @@ impl RenderOnce for ButtonLike {
554563
self.base
555564
.h_flex()
556565
.id(self.id.clone())
566+
.when_some(self.tab_index, |this, tab_index| this.tab_index(tab_index))
557567
.font_ui(cx)
558568
.group("")
559569
.flex_none()
@@ -591,8 +601,12 @@ impl RenderOnce for ButtonLike {
591601
}
592602
})
593603
.when(!self.disabled, |this| {
604+
let hovered_style = style.hovered(self.layer, cx);
605+
let focus_color =
606+
|refinement: StyleRefinement| refinement.bg(hovered_style.background);
594607
this.cursor(self.cursor_style)
595-
.hover(|hover| hover.bg(style.hovered(self.layer, cx).background))
608+
.hover(focus_color)
609+
.focus(focus_color)
596610
.active(|active| active.bg(style.active(cx).background))
597611
})
598612
.when_some(

crates/ui/src/components/button/icon_button.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,11 @@ impl ButtonCommon for IconButton {
164164
self
165165
}
166166

167+
fn tab_index(mut self, tab_index: impl Into<isize>) -> Self {
168+
self.base = self.base.tab_index(tab_index);
169+
self
170+
}
171+
167172
fn layer(mut self, elevation: ElevationIndex) -> Self {
168173
self.base = self.base.layer(elevation);
169174
self

crates/ui/src/components/button/toggle_button.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,11 @@ impl ButtonCommon for ToggleButton {
121121
self
122122
}
123123

124+
fn tab_index(mut self, tab_index: impl Into<isize>) -> Self {
125+
self.base = self.base.tab_index(tab_index);
126+
self
127+
}
128+
124129
fn layer(mut self, elevation: ElevationIndex) -> Self {
125130
self.base = self.base.layer(elevation);
126131
self

0 commit comments

Comments
 (0)