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.
This commit is contained in:
dotCooCoo
2026-11-14 18:23:54 -08:00
parent 0939157d12
commit cdedb71499
2 changed files with 24 additions and 10 deletions
+5 -10
View File
@@ -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
@@ -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::<Vec<_>>();
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"});