diff --git a/crates/ui-egui/src/i18n/ui.rs b/crates/ui-egui/src/i18n/ui.rs index f31d0951c..3a49acad4 100644 --- a/crates/ui-egui/src/i18n/ui.rs +++ b/crates/ui-egui/src/i18n/ui.rs @@ -295,6 +295,7 @@ const JAPANESE: &[(&str, &str)] = &[ ("MIT OR Apache-2.0 • Fonts: Inter, JetBrains Mono (OFL)", "MIT OR Apache-2.0 • フォント:Inter、JetBrains Mono (OFL)"), ("Magnification ratio popup", "拡大率のポップアップ"), ("Make Comp Size", "コンポジションサイズにする"), + ("Markers", "マーカー"), ("Masks", "マスク"), ("Maximize Panel", "パネルを最大化"), ("Media Replacement", "メディアの置き換え"), @@ -742,6 +743,7 @@ const SIMPLIFIED_CHINESE: &[(&str, &str)] = &[ ("MIT OR Apache-2.0 • Fonts: Inter, JetBrains Mono (OFL)", "MIT 或 Apache-2.0 • 字体:Inter、JetBrains Mono(OFL)"), ("Magnification ratio popup", "放大倍率弹出菜单"), ("Make Comp Size", "设为合成长宽"), + ("Markers", "标记"), ("Masks", "蒙版"), ("Maximize Panel", "最大化面板"), ("Media Replacement", "媒体替换"), @@ -1277,6 +1279,7 @@ const TRADITIONAL_CHINESE: &[(&str, &str)] = &[ ("MIT OR Apache-2.0 • Fonts: Inter, JetBrains Mono (OFL)", "MIT 或 Apache-2.0 • 字型:Inter、JetBrains Mono(OFL)"), ("Magnification ratio popup", "放大倍率快顯選單"), ("Make Comp Size", "設為合成長寬"), + ("Markers", "標記"), ("Masks", "遮色片"), ("Maximize Panel", "最大化面板"), ("Media Replacement", "媒體替換"), @@ -1815,6 +1818,7 @@ const UKRAINIAN: &[(&str, &str)] = &[ ("MIT OR Apache-2.0 • Fonts: Inter, JetBrains Mono (OFL)", "MIT OR Apache-2.0 • Шрифти: Inter, JetBrains Mono (OFL)"), ("Magnification ratio popup", "Меню коефіцієнта збільшення"), ("Make Comp Size", "За розміром композиції"), + ("Markers", "Маркери"), ("Masks", "Маски"), ("Maximize Panel", "Розгорнути панель"), ("Media Replacement", "Заміна матеріалу"), @@ -2351,6 +2355,7 @@ const PORTUGUESE: &[(&str, &str)] = &[ ("MIT OR Apache-2.0 • Fonts: Inter, JetBrains Mono (OFL)", "MIT OU Apache-2.0 • Fontes: Inter, JetBrains Mono (OFL)"), ("Magnification ratio popup", "Menu de proporção de ampliação"), ("Make Comp Size", "Ajustar ao tamanho da composição"), + ("Markers", "Marcadores"), ("Masks", "Máscaras"), ("Maximize Panel", "Maximizar painel"), ("Media Replacement", "Substituição de mídia"), @@ -2795,6 +2800,7 @@ const CZECH: &[(&str, &str)] = &[ ("MIT OR Apache-2.0 • Fonts: Inter, JetBrains Mono (OFL)", "MIT OR Apache-2.0 • Písma: Inter, JetBrains Mono (OFL)"), ("Magnification ratio popup", "Nabídka míry přiblížení"), ("Make Comp Size", "Nastavit velikost kompozice"), + ("Markers", "Značky"), ("Masks", "Masky"), ("Maximize Panel", "Maximalizovat panel"), ("Media Replacement", "Nahrazení médií"), diff --git a/crates/ui-egui/src/panels/timeline.rs b/crates/ui-egui/src/panels/timeline.rs index 72f104290..8f4a6be72 100644 --- a/crates/ui-egui/src/panels/timeline.rs +++ b/crates/ui-egui/src/panels/timeline.rs @@ -3569,6 +3569,9 @@ pub(crate) fn layer_menu(ui: &mut egui::Ui, layers: &[u64], vector: bool, action item(ui, "Freeze Frame", "layer.freezeFrame", json!({"layers": layers})); item(ui, "Freeze on Last Frame", "layer.freezeOnLastFrame", json!({"layers": layers})); }); + ui.menu_button(crate::i18n::tr("Markers"), |ui| { + item(ui, "Add Marker", "layer.addMarker", json!({"layers": layers})); + }); ui.menu_button(crate::i18n::tr("Blending Mode"), |ui| { widgets::menu_scroll(ui, |ui| { for m in BlendMode::ALL { diff --git a/crates/ui-egui/tests/ui_blank_context_menus.rs b/crates/ui-egui/tests/ui_blank_context_menus.rs index fcbf3d09c..e6fc87b2c 100644 --- a/crates/ui-egui/tests/ui_blank_context_menus.rs +++ b/crates/ui-egui/tests/ui_blank_context_menus.rs @@ -226,3 +226,32 @@ fn context_menu_rows_leave_no_gap_between_them() { } snapshot(&mut h, "project-rows"); } + +/// #976: a layer's context menu has After Effects' Markers ▸ Add Marker, which marks the layer that +/// was right-clicked (not the one selected before, and not the composition) at the current time. +#[test] +fn layer_context_menu_adds_a_layer_marker() { + let mut h = harness(); + let solid = |h: &mut Harness<'_, EffectcraftApp>, name: &str| -> u64 { + h.state_mut().session.execute("layer.newSolid", json!({"name": name, "color": "#406080"})).unwrap()["layer"].as_u64().unwrap() + }; + let (below, top) = (solid(&mut h, "Below"), solid(&mut h, "Top")); + h.state_mut().session.execute("layer.select", json!({"layers": [below]})).unwrap(); + h.state_mut().session.execute("time.set", json!({"time": 1.5})).unwrap(); + h.run_steps(3); + let r = rect(&h, &format!("timeline.layer.{top}.row")); + right_click(&mut h, pos2(r.min.x + 210.0, r.center().y)); + h.get_by_label("Markers ⏵").click(); + h.run_steps(3); + snapshot(&mut h, "layer-markers"); + h.get_by_label("Add Marker").click(); + h.run_steps(3); + let now = h.state().session.time().seconds(); + let comp = h.state().session.active_comp().unwrap(); + let markers = |id: u64| -> Vec { comp.layer(effectcraft_engine::project::LayerId(id)).unwrap().markers.iter().map(|m| m.time.seconds()).collect() }; + assert_eq!(markers(top), vec![now]); + assert!((now - 1.5).abs() < 0.01, "at the current time: {now}"); + assert!(markers(below).is_empty(), "{:?}", markers(below)); + assert!(comp.markers.is_empty(), "no composition marker"); + assert_eq!(h.state().session.history.undo.last().map(|u| u.0.as_str()), Some("Add Marker")); +}