From 9f02bd2a722f6d2b8328aa694b247a8fdcc12a9f Mon Sep 17 00:00:00 2001 From: Kristian Myram Date: Wed, 14 Oct 2026 00:59:11 +0200 Subject: [PATCH] #1368: dragging an effect lights up the layer it will land on Co-Authored-By: Claude Sonnet 5.5 --- crates/ui-egui/src/panels/timeline.rs | 12 +++++- .../ui-egui/tests/ui_effect_drop_highlight.rs | 40 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 crates/ui-egui/tests/ui_effect_drop_highlight.rs diff --git a/crates/ui-egui/src/panels/timeline.rs b/crates/ui-egui/src/panels/timeline.rs index 163e6507..c63df878 100644 --- a/crates/ui-egui/src/panels/timeline.rs +++ b/crates/ui-egui/src/panels/timeline.rs @@ -3330,8 +3330,18 @@ pub fn show(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) { { let released = ctx.input(|i| i.pointer.any_released()); if let crate::panels::DragPayload::Effect(e) = payload.as_ref() { - p.rect_stroke(rows_rect, 0.0, Stroke::new(2.0, t.accent), StrokeKind::Inside); let target = hit_rows.iter().find(|(r, _)| ptr.y >= r.min.y && ptr.y < r.max.y).map(|(_, row)| row.layer); + // The layer the effect would land on (its row and its open properties) is the one + // that lights up, not the whole timeline. + if let Some(l) = target + && let Some(span) = hit_rows.iter().filter(|(_, row)| row.layer == l).map(|(r, _)| *r).reduce(|a, b| a.union(b)) + { + let lit = span.intersect(rows_rect); + let rp = p.with_clip_rect(rows_rect); + rp.rect_filled(lit, 0.0, t.accent.gamma_multiply(0.18)); + rp.rect_stroke(lit, 0.0, Stroke::new(2.0, t.accent), StrokeKind::Inside); + app.auto.add("timeline.dropTarget", lit, "Drop target layer"); + } if released && let Some(l) = target { actions.push(("effect.apply".into(), json!({"effect": e, "layers": [l.0]}))); } diff --git a/crates/ui-egui/tests/ui_effect_drop_highlight.rs b/crates/ui-egui/tests/ui_effect_drop_highlight.rs new file mode 100644 index 00000000..6c0ede47 --- /dev/null +++ b/crates/ui-egui/tests/ui_effect_drop_highlight.rs @@ -0,0 +1,40 @@ +//! Dragging an effect over the Timeline lights up the layer it would land on, not the whole panel. +use effectcraft_engine::Session; +use effectcraft_ui_egui::{EffectcraftApp, panels::DragPayload}; +use egui::{Event, Modifiers, PointerButton, Pos2, Rect, pos2, vec2}; +use egui_kittest::Harness; +use serde_json::json; + +fn rect(h: &Harness<'_, EffectcraftApp>, id: &str) -> Rect { + let e = h.state().auto.find(id).unwrap_or_else(|| panic!("missing {id}")); + Rect::from_min_size(pos2(e.rect[0], e.rect[1]), vec2(e.rect[2], e.rect[3])) +} + +fn drag_to(h: &mut Harness<'_, EffectcraftApp>, p: Pos2) { + h.input_mut().events.push(Event::PointerMoved(p)); + h.input_mut().events.push(Event::PointerButton { pos: p, button: PointerButton::Primary, pressed: true, modifiers: Modifiers::NONE }); + egui::DragAndDrop::set_payload(&h.ctx, DragPayload::Effect("CC Bend It".into())); + h.run_steps(2); +} + +#[test] +fn effect_drag_highlights_the_layer_under_the_pointer() { + let mut s = Session::default(); + s.execute("comp.new", json!({"name": "Main", "width": 640, "height": 360, "duration": 4})).unwrap(); + let a = s.execute("layer.newNull", json!({})).unwrap()["layer"].as_u64().unwrap(); + let b = s.execute("layer.newNull", json!({})).unwrap()["layer"].as_u64().unwrap(); + let mut h = Harness::builder().with_size(vec2(1600.0, 1000.0)).build_eframe(|_| EffectcraftApp::new(s)); + h.run_steps(3); + assert!(h.state().auto.find("timeline.dropTarget").is_none(), "nothing is lit without a drag"); + let (row_a, row_b) = (rect(&h, &format!("timeline.layer.{a}.row")), rect(&h, &format!("timeline.layer.{b}.row"))); + let panel = rect(&h, "timeline.layerMarquee"); + + drag_to(&mut h, row_b.center()); + let lit = rect(&h, "timeline.dropTarget"); + assert!(lit.contains(row_b.center()) && !lit.contains(row_a.center()), "lit {lit:?} for row {row_b:?}"); + assert!(lit.height() < panel.height(), "only the layer is lit, not the whole timeline"); + + drag_to(&mut h, row_a.center()); + let lit = rect(&h, "timeline.dropTarget"); + assert!(lit.contains(row_a.center()) && !lit.contains(row_b.center()), "the highlight follows the pointer: {lit:?}"); +}