Effect Controls: drop a replaced project's lock before answering a lock request (#3193)

A ui.set {effectControlsLocked: true} that ran after the project was
replaced but before the next frame reported the old project's lock as
success; the next frame then cleared it. set_lock now scopes the lock
first.
This commit is contained in:
vangel76 committed 2026-11-07 16:24:57 +01:00
1 parent f3a9afda14
commit 7dabd6e6f1
2 files changed
+29

No files matched your search

@@ -49,6 +49,9 @@ pub(super) fn shown_layer(app: &EffectcraftApp) -> Option<Layer> {
/// does nothing; locking while already locked keeps the layer it is locked to. Returns whether
/// the panel is locked afterwards. The tab's lock icon and `ui.set {effectControlsLocked}`.
pub fn set_lock(app: &mut EffectcraftApp, on: bool) -> bool {
// A lock left from a replaced project is gone before this one is answered (a control
// request can run between the replacement and the next frame).
scope_lock(app);
if !on {
app.ui.effect_controls_lock = None;
return false;
@@ -1013,6 +1013,32 @@ fn replacing_the_project_unlocks_effect_controls() {
assert_eq!(h.state().ui.effect_controls_lock, None, "the lock belongs to the replaced project");
}
/// #3193: a lock request right after the project is replaced, before the next frame, is not
/// answered with the old project's lock: the reply matches the lock the panel keeps.
#[test]
fn a_lock_request_after_a_project_replacement_does_not_report_the_old_lock() {
use effectcraft_ui_egui::control::{self, ControlRequest, Outcome};
fn send(h: &mut Harness<'_, EffectcraftApp>) -> serde_json::Value {
let ctx = h.ctx.clone();
let (req, _rx) = ControlRequest::new("ui.set", json!({"effectControlsLocked": true}));
let now = h.state().now();
match control::handle(h.state_mut(), &ctx, &req, now) {
Outcome::Done(v) => v,
_ => panic!("ui.set: no immediate reply"),
}
}
let (app, a, _, _, _) = two_layers();
let mut h = Harness::builder().with_size(egui::vec2(1600.0, 1000.0)).build_eframe(|_| app);
h.run_steps(4);
assert_eq!(send(&mut h)["ok"], true);
assert_eq!(h.state().ui.effect_controls_lock.map(|l| l.layer), Some(a));
let project = (*h.state().session.project).clone();
h.state_mut().session.replace_project(project, None);
let reply = send(&mut h);
h.run_steps(3);
assert_eq!(reply["ok"] == true, h.state().ui.effect_controls_lock.is_some(), "{reply}");
}
/// #3193: an effect dropped on the locked panel goes to the locked layer and leaves the selection
/// alone; one applied to another layer says why the panel doesn't show it.
#[test]