commit 61bc8b5b1befa2829c799ba76b81c96e2a1d9eaa
parent 718d88f95583b5a5198722af164c467dec6c3e94
Author: triesap <tyson@radroots.org>
Date: Tue, 21 Apr 2026 03:52:41 +0000
ui: harden shared focus and control semantics
- add a labeled shared icon-button spec for icon-only desktop actions
- restore visible focus borders for shared text inputs across shipped forms
- collapse shared checkbox rows to one tab stop and localize the account more action
- extend source-guard coverage for the new settings account control label
Diffstat:
6 files changed, 57 insertions(+), 29 deletions(-)
diff --git a/crates/launchers/desktop/src/source_guards.rs b/crates/launchers/desktop/src/source_guards.rs
@@ -500,6 +500,7 @@ const REQUIRED_WINDOW_COPY_KEYS: &[&str] = &[
"AppTextKey::SettingsAccountActivationInactive",
"AppTextKey::SettingsAccountAddAction",
"AppTextKey::SettingsAccountLogOutAction",
+ "AppTextKey::SettingsAccountMoreActions",
"AppTextKey::SettingsAccountOpenWorkspaceAction",
"AppTextKey::SettingsNavFarm",
"AppTextKey::SettingsFarmPanelBody",
diff --git a/crates/launchers/desktop/src/window.rs b/crates/launchers/desktop/src/window.rs
@@ -26,8 +26,7 @@ use radroots_app_models::{
ProductsListRow, ProductsSort, RecoveryKind, RecoveryState, ReminderDeadlineProjection,
ReminderDeliveryState, ReminderId, ReminderLogEntryProjection, ReminderLogProjection,
ReminderSurface, ReminderUrgency, RepeatDemandEligibility, RepeatDemandHandoffProjection,
- ShellSection,
- TodayAgendaProjection, TodaySetupTaskKind,
+ ShellSection, TodayAgendaProjection, TodaySetupTaskKind,
};
use radroots_app_remote_signer::{
RadrootsAppRemoteSignerApprovedSession, RadrootsAppRemoteSignerPendingPollOutcome,
@@ -44,7 +43,7 @@ use radroots_app_sync::{
SyncConflictResolutionStatus, SyncConflictSeverity,
};
use radroots_app_ui::{
- APP_UI_THEME, AppCheckboxFieldSpec, AppFormFieldSpec,
+ APP_UI_THEME, AppCheckboxFieldSpec, AppFormFieldSpec, AppIconButtonSpec,
AppSegmentButtonIconSpec as IconSegmentButtonSpec, LabelValueRow, app_button_card,
app_button_choice as choice_button, app_button_compact as action_button_compact,
app_button_icon as action_icon_button, app_button_list_row as list_row_button,
@@ -1293,7 +1292,10 @@ impl HomeView {
replace_existing: bool,
cx: &mut Context<Self>,
) {
- match self.runtime.repeat_personal_order(order_id, replace_existing) {
+ match self
+ .runtime
+ .repeat_personal_order(order_id, replace_existing)
+ {
Ok(true) => cx.notify(),
Ok(false) => {}
Err(runtime_error) => {
@@ -5812,8 +5814,11 @@ impl SettingsWindowView {
cx,
))
.child(action_icon_button(
- "account-more",
- IconName::ChevronDown,
+ AppIconButtonSpec::new(
+ "account-more",
+ app_shared_text(AppTextKey::SettingsAccountMoreActions),
+ IconName::ChevronDown,
+ ),
|_, _, _| {},
cx,
)),
@@ -7818,9 +7823,8 @@ fn buyer_order_detail_card(
replace_confirmation: Option<&BuyerCartReplaceConfirmationProjection>,
cx: &mut Context<HomeView>,
) -> AnyElement {
- let repeat_confirmation =
- replace_confirmation.filter(|confirmation| confirmation.incoming_farm_display_name
- == detail.farm_display_name);
+ let repeat_confirmation = replace_confirmation
+ .filter(|confirmation| confirmation.incoming_farm_display_name == detail.farm_display_name);
home_card(
app_shared_text(AppTextKey::PersonalOrdersDetailTitle),
@@ -7883,9 +7887,7 @@ fn buyer_order_detail_card(
.child(home_body_text(format!(
"{} {} {}.",
replace_confirmation.current_farm_display_name,
- app_shared_text(
- AppTextKey::PersonalDetailReplaceCartBody,
- ),
+ app_shared_text(AppTextKey::PersonalDetailReplaceCartBody,),
replace_confirmation.incoming_farm_display_name,
)))
.child(
@@ -7945,9 +7947,7 @@ fn buyer_order_detail_card(
.into_any_element()
}
-fn buyer_repeat_demand_action_label(
- repeat_demand: &RepeatDemandHandoffProjection,
-) -> SharedString {
+fn buyer_repeat_demand_action_label(repeat_demand: &RepeatDemandHandoffProjection) -> SharedString {
match repeat_demand.eligibility {
RepeatDemandEligibility::Eligible => {
app_shared_text(AppTextKey::PersonalOrdersRepeatDemandActionEligible)
@@ -7961,9 +7961,7 @@ fn buyer_repeat_demand_action_label(
}
}
-fn buyer_repeat_demand_note(
- repeat_demand: &RepeatDemandHandoffProjection,
-) -> Option<SharedString> {
+fn buyer_repeat_demand_note(repeat_demand: &RepeatDemandHandoffProjection) -> Option<SharedString> {
match repeat_demand.eligibility {
RepeatDemandEligibility::Eligible => None,
RepeatDemandEligibility::Partial if repeat_demand.unavailable_item_count == 1 => Some(
diff --git a/crates/shared/i18n/src/keys.rs b/crates/shared/i18n/src/keys.rs
@@ -318,6 +318,7 @@ define_app_text_keys! {
SettingsAccountActivationActive => "settings.account.activation.active",
SettingsAccountAddAction => "settings.account.action.add_account",
SettingsAccountLogOutAction => "settings.account.action.log_out",
+ SettingsAccountMoreActions => "settings.account.action.more_actions",
SettingsAccountOpenWorkspaceAction => "settings.account.action.open_workspace",
SettingsViewAccount => "settings.view.account",
SettingsViewSettings => "settings.view.settings",
diff --git a/crates/shared/ui/src/lib.rs b/crates/shared/ui/src/lib.rs
@@ -5,10 +5,10 @@ mod text;
mod theme;
pub use primitives::{
- AppCheckboxFieldSpec, AppFormFieldSpec, AppSegmentButtonIconSpec, LabelValueRow,
- app_button_card, app_button_choice, app_button_compact, app_button_icon, app_button_list_row,
- app_button_primary, app_button_primary_disabled, app_button_secondary, app_button_text,
- app_checkbox_field, app_cluster, app_detail_row, app_divider, app_form_field,
+ AppCheckboxFieldSpec, AppFormFieldSpec, AppIconButtonSpec, AppSegmentButtonIconSpec,
+ LabelValueRow, app_button_card, app_button_choice, app_button_compact, app_button_icon,
+ app_button_list_row, app_button_primary, app_button_primary_disabled, app_button_secondary,
+ app_button_text, app_checkbox_field, app_cluster, app_detail_row, app_divider, app_form_field,
app_form_input_text, app_form_section, app_heading_section, app_heading_view, app_input_text,
app_scroll_panel, app_segment_button_icon, app_split_shell, app_stack_h, app_stack_v,
app_status_indicator, app_surface_card, app_surface_card_section, app_surface_panel,
diff --git a/crates/shared/ui/src/primitives.rs b/crates/shared/ui/src/primitives.rs
@@ -34,6 +34,22 @@ impl AppSegmentButtonIconSpec {
}
}
+pub struct AppIconButtonSpec {
+ pub id: &'static str,
+ pub label: SharedString,
+ pub icon: IconName,
+}
+
+impl AppIconButtonSpec {
+ pub fn new(id: &'static str, label: impl Into<SharedString>, icon: IconName) -> Self {
+ Self {
+ id,
+ label: label.into(),
+ icon,
+ }
+ }
+}
+
pub struct AppCheckboxFieldSpec {
pub id: &'static str,
pub label: SharedString,
@@ -402,7 +418,7 @@ fn app_checkbox(
);
}
- button
+ button.tab_stop(false)
}
pub fn app_checkbox_field(
@@ -545,7 +561,7 @@ pub fn app_input_text(input: &Entity<InputState>, disabled: bool) -> Input {
Input::new(input)
.with_size(Size::Medium)
.disabled(disabled)
- .focus_bordered(false)
+ .focus_bordered(true)
.bg(rgb(background))
.text_color(rgb(foreground))
.border_color(rgb(tokens.border))
@@ -647,18 +663,18 @@ fn app_button_label(
}
pub fn app_button_icon(
- id: impl Into<ElementId>,
- icon: IconName,
+ spec: AppIconButtonSpec,
on_click: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
cx: &App,
) -> impl IntoElement {
let sizing = APP_UI_THEME.components.app_button.sizing;
let colors = app_button_colors(AppButtonVariant::Secondary);
- app_button_base(id, AppButtonVariant::Secondary, on_click, cx)
+ app_button_base(spec.id, AppButtonVariant::Secondary, on_click, cx)
.with_size(Size::Size(px(sizing.square_width_px)))
+ .tooltip(spec.label)
.icon(
- Icon::new(icon)
+ Icon::new(spec.icon)
.with_size(Size::Size(px(sizing.icon_size_px)))
.text_color(rgb(colors.foreground)),
)
@@ -877,7 +893,9 @@ fn app_button_disabled_colors(variant: AppButtonVariant) -> crate::AppButtonColo
mod tests {
use gpui_component::IconName;
- use super::{AppCheckboxFieldSpec, AppFormFieldSpec, AppSegmentButtonIconSpec};
+ use super::{
+ AppCheckboxFieldSpec, AppFormFieldSpec, AppIconButtonSpec, AppSegmentButtonIconSpec,
+ };
#[test]
fn icon_segment_spec_preserves_id_and_label() {
@@ -909,4 +927,13 @@ mod tests {
Some("Saved locally")
);
}
+
+ #[test]
+ fn icon_button_spec_preserves_id_label_and_icon() {
+ let spec = AppIconButtonSpec::new("more", "More actions", IconName::ChevronDown);
+
+ assert_eq!(spec.id, "more");
+ assert_eq!(spec.label.as_ref(), "More actions");
+ assert!(matches!(spec.icon, IconName::ChevronDown));
+ }
}
diff --git a/i18n/locales/en/messages.json b/i18n/locales/en/messages.json
@@ -297,6 +297,7 @@
"settings.account.activation.active": "Activated",
"settings.account.action.add_account": "Add Account...",
"settings.account.action.log_out": "Log Out",
+ "settings.account.action.more_actions": "More Actions",
"settings.account.action.open_workspace": "Open Workspace...",
"settings.view.account": "account",
"settings.view.settings": "settings",