mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-14 11:16:53 -04:00
Layers panel: test the Blend Mode dropdown's arrow keys with the Move tool active
The open dropdown takes ↑/↓ ahead of the Move tool's nudge, checked in the full app. The arrow-key step reads the option with get() and uses the response's own id. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01UHgFEHEsefoQoBParH47JA
This commit is contained in:
1 parent
4f99e42dcb
commit
38c97bdc5d
3 files changed
+59
-7
No files matched your search
@@ -0,0 +1,47 @@
|
||||
//! #1318: the Layers panel's Blend Mode dropdown takes the arrow keys once it is open, ahead of
|
||||
//! the tool shortcuts (the Move tool's arrow-key nudge).
|
||||
|
||||
use egui::accesskit::Role;
|
||||
use egui_kittest::Harness;
|
||||
use egui_kittest::kittest::{NodeT, Queryable};
|
||||
use photocraft_doc::BlendMode;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::PhotocraftApp;
|
||||
use crate::state::Tool;
|
||||
|
||||
#[test]
|
||||
fn arrow_keys_step_the_open_blend_mode_dropdown_without_nudging_the_layer() {
|
||||
let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).with_max_steps(64).build_eframe(|cc| {
|
||||
PhotocraftApp::setup_context(&cc.egui_ctx, Default::default());
|
||||
let mut app = PhotocraftApp::new(photocraft_engine::Session::new(), crate::Services::default());
|
||||
app.run("file.new", json!({"width": 64, "height": 64})).unwrap();
|
||||
app.run("layer.new.layer", json!({})).unwrap();
|
||||
app.run("edit.fill", json!({"color": "#ff0000"})).unwrap();
|
||||
app.ui.tool = Tool::Move;
|
||||
app
|
||||
});
|
||||
h.run_steps(6);
|
||||
let layer = |h: &Harness<'_, PhotocraftApp>| {
|
||||
let st = h.state().session.active().unwrap();
|
||||
let l = st.doc.layer(st.active_layer.unwrap()).unwrap();
|
||||
(l.blend, l.surface().unwrap().content_bounds())
|
||||
};
|
||||
let (blend, bounds) = layer(&h);
|
||||
assert_eq!(blend, BlendMode::Normal);
|
||||
let named = |n: &egui_kittest::Node<'_>| {
|
||||
let a = n.accesskit_node();
|
||||
a.value().as_deref() == Some("Normal") || a.label().as_deref() == Some("Normal")
|
||||
};
|
||||
let combo = h.query_all_by_role(Role::ComboBox).find(|n| named(n));
|
||||
combo.expect("the Layers panel's Blend Mode dropdown").click();
|
||||
h.run_steps(2);
|
||||
h.key_press(egui::Key::ArrowDown);
|
||||
h.run_steps(2);
|
||||
let (blend, moved) = layer(&h);
|
||||
assert_ne!(blend, BlendMode::Normal, "↓ picks the next mode");
|
||||
assert_eq!(moved, bounds, "the Move tool's nudge doesn't also take the key");
|
||||
h.key_press(egui::Key::ArrowUp);
|
||||
h.run_steps(2);
|
||||
assert_eq!(layer(&h).0, BlendMode::Normal);
|
||||
}
|
||||
@@ -1519,6 +1519,9 @@ mod move_auto_select_tests;
|
||||
#[cfg(test)]
|
||||
mod hidden_layer_tests;
|
||||
|
||||
#[cfg(test)]
|
||||
mod blend_dropdown_keys_tests;
|
||||
|
||||
#[cfg(test)]
|
||||
mod marquee_tests;
|
||||
|
||||
|
||||
@@ -681,20 +681,20 @@ pub fn dropdown_hovered<T: PartialEq + Clone>(ui: &mut Ui, id: &str, current: &m
|
||||
}
|
||||
}
|
||||
});
|
||||
let stepped = combo_box_arrow_keys(ui, response.response.id, &response.response, current, options);
|
||||
let stepped = combo_box_arrow_keys(ui, &response.response, current, options);
|
||||
(changed || stepped, hovered)
|
||||
}
|
||||
|
||||
/// Give a dropdown keyboard focus when it opens, then use the arrow keys to move through its
|
||||
/// choices. `egui::ComboBox` opens a popup but leaves focus on the canvas by default, which makes
|
||||
/// controls such as the Layers panel's Blend Mode dropdown unreachable from the keyboard.
|
||||
fn combo_box_arrow_keys<T: PartialEq + Clone>(ui: &mut Ui, combo_id: egui::Id, response: &Response, current: &mut T, options: &[(T, &str)]) -> bool {
|
||||
fn combo_box_arrow_keys<T: PartialEq + Clone>(ui: &mut Ui, response: &Response, current: &mut T, options: &[(T, &str)]) -> bool {
|
||||
if response.clicked() {
|
||||
response.request_focus();
|
||||
}
|
||||
// The popup, rather than its button, becomes the focused egui layer after it opens. While it
|
||||
// is open it owns its navigation keys, even though `response.has_focus()` is then false.
|
||||
if !egui::ComboBox::is_open(ui.ctx(), combo_id) || options.is_empty() {
|
||||
if !egui::ComboBox::is_open(ui.ctx(), response.id) || options.is_empty() {
|
||||
return false;
|
||||
}
|
||||
let step = if ui.input_mut(|i| i.consume_key(egui::Modifiers::NONE, egui::Key::ArrowDown)) {
|
||||
@@ -706,11 +706,13 @@ fn combo_box_arrow_keys<T: PartialEq + Clone>(ui: &mut Ui, combo_id: egui::Id, r
|
||||
};
|
||||
let index = options.iter().position(|(value, _)| value == current).unwrap_or(0);
|
||||
let next = if step > 0 { (index + 1).min(options.len() - 1) } else { index.saturating_sub(1) };
|
||||
if next == index {
|
||||
return false;
|
||||
match options.get(next) {
|
||||
Some((value, _)) if next != index => {
|
||||
*current = value.clone();
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
*current = options[next].0.clone();
|
||||
true
|
||||
}
|
||||
|
||||
/// The body of a right-click menu: as tall as its items up to the part of the window that can be
|
||||
|
||||
Reference in new issue
Block a user