From cdedb71499d30c54fcef072bf1c305d02700b0c4 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Sat, 14 Nov 2026 18:23:54 -0800 Subject: [PATCH] 3D and Materials: a light pressed in the lighting widget keeps the keyboard focus Pressing a light other than the selected one, or tabbing to it, selected it after the frame, and selecting another light clears the keyboard focus, so the arrow keys then moved nothing. The widget now selects the light first and gives it the focus after that. --- crates/ui-egui/src/dialogs/revolve_lights.rs | 15 +++++---------- crates/ui-egui/src/panels/tests_three_d.rs | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/crates/ui-egui/src/dialogs/revolve_lights.rs b/crates/ui-egui/src/dialogs/revolve_lights.rs index dc57cae943..1a4116b7ee 100644 --- a/crates/ui-egui/src/dialogs/revolve_lights.rs +++ b/crates/ui-egui/src/dialogs/revolve_lights.rs @@ -142,7 +142,6 @@ pub(super) fn show(ui: &mut egui::Ui, d: &mut Dialog) { // The selected light last, so it is drawn above the others and a press there takes it. let order = (0..lights.len()).filter(|&i| i != current).chain([current]); let mut moved = None; - let mut picked = None; let enabled = ui.is_enabled(); for index in order { let light = lights.get(index).copied().unwrap_or(first); @@ -150,13 +149,12 @@ pub(super) fn show(ui: &mut egui::Ui, d: &mut Dialog) { let id = Id::unique(("three-d-lighting-widget", index)); let response = ui.interact(Rect::from_center_size(at, Vec2::splat((SUN + 4.0) * 2.0)), id, Sense::click_and_drag()); response.widget_info(|| egui::WidgetInfo::labeled(egui::Role::Slider, enabled, light.name(index))); - if response.drag_started() || response.clicked() { - // A light the pointer took takes the keyboard focus too, so the arrow keys move it next. + // A light the pointer took, or that the keyboard focus reached, becomes the selected light and + // keeps the focus, so the arrow keys move it next. Selecting another light stops the editor + // that had the focus, which clears the focus, so the light asks for it after that. + if response.drag_started() || response.clicked() || response.gained_focus() { + select(ui, d, index); response.request_focus(); - picked = Some(index); - } - if response.gained_focus() { - picked = Some(index); } let origin = ui.input(|i| i.pointer.press_origin()); if response.drag_started() @@ -213,9 +211,6 @@ pub(super) fn show(ui: &mut egui::Ui, d: &mut Dialog) { } response.on_hover_text(tl!("Drag a light to move it. The arrow keys move the focused light; Shift steps 15°.")); } - if let Some(index) = picked { - select(ui, d, index); - } // Only a setting that moved is written: a press that leaves a light where it is, or a key at a // limit, changes nothing, and a key leaves the other setting as each selected effect has it. if let Some((index, (rotation, height))) = moved diff --git a/crates/ui-egui/src/panels/tests_three_d.rs b/crates/ui-egui/src/panels/tests_three_d.rs index 3004195d36..fd3a30c7bf 100644 --- a/crates/ui-egui/src/panels/tests_three_d.rs +++ b/crates/ui-egui/src/panels/tests_three_d.rs @@ -965,6 +965,25 @@ fn the_arrow_keys_move_the_focused_light() { assert_eq!(undo_len(&app), history + 2); } +/// A press on another light selects it and gives it the keyboard focus, so the next arrow key +/// moves that light and leaves the one selected before where it is. +#[test] +fn the_arrow_keys_move_the_light_pressed_last() { + use crate::panels::tests_appearance::{click, frame_events}; + let light = + |rotation: f64| json!({"color": "#ffffff", "intensity": 80.0, "rotation": rotation, "height": 30.0, "softness": 0.0, "behind": false}); + let (mut app, ctx) = lighting_app(json!({"lights": [light(0.0), light(180.0)]})); + let (_, first) = light_node(&mut app, &ctx, 1); + click(&ctx, &mut app, first, show); + let (_, second) = light_node(&mut app, &ctx, 2); + click(&ctx, &mut app, second, show); + let left = egui::Event::Key { key: egui::Key::ArrowLeft, physical_key: None, pressed: true, repeat: false, modifiers: egui::Modifiers::NONE }; + frame_events(&ctx, &mut app, vec![left], show); + frame_events(&ctx, &mut app, vec![], show); + let rotations = effect(&app).1["lights"].as_array().unwrap().iter().map(|l| l["rotation"].clone()).collect::>(); + assert_eq!(rotations, [json!(0.0), json!(-179.0)]); +} + #[test] fn changed_settings_lists_only_new_values() { let before = json!({"depth": 50.0, "cap": true, "bevel": "none"});