Merge pull request #2250 from slager2/fix/2172-animation-browse-presets

Fix Animation > Browse Presets command
This commit is contained in:
BFlat
2026-10-30 04:08:59 +07:00
committed by GitHub
3 changed files with 80 additions and 3 deletions
+9 -1
View File
@@ -488,7 +488,15 @@ pub fn frontend(app: &mut EffectcraftApp, ctx: &egui::Context, id: &str, p: Valu
app.ui.home_templates = true;
Value::Null
}
"effect.manage" | "anim.browsePresets" => {
"effect.manage" => {
app.show_panel(PanelKind::EffectsPresets);
Value::Null
}
"anim.browsePresets" => {
app.ui.effects_search.clear();
app.ui.effects_view.presets = true;
app.ui.effects_open.insert("presets".into());
ctx.data_mut(|data| data.insert_temp(egui::Id::unique(crate::panels::effects_presets::REVEAL_PRESETS_ID), true));
app.show_panel(PanelKind::EffectsPresets);
Value::Null
}
+30 -2
View File
@@ -17,6 +17,7 @@ use crate::{EffectcraftApp, widgets};
const ROW_H: f32 = 21.0;
const RECENT_MAX: usize = 8;
pub(crate) const REVEAL_PRESETS_ID: &str = "fx-reveal-presets";
/// A user animation preset file.
#[derive(Clone, Debug, PartialEq)]
@@ -157,6 +158,24 @@ fn folder_row(
open
}
/// Number of visible rows before Animation Presets in the current panel state.
fn rows_before_presets(favorites: usize, recent: usize, searching: bool, favorites_open: bool, recent_open: bool) -> usize {
let mut rows = 0usize;
if favorites > 0 {
rows = rows.saturating_add(1);
if searching || favorites_open {
rows = rows.saturating_add(favorites);
}
}
if !searching && recent > 0 {
rows = rows.saturating_add(1);
if recent_open {
rows = rows.saturating_add(recent);
}
}
rows
}
/// An effect row (fx icon, highlighted name, favourite star, 32 bpc badge).
#[allow(clippy::too_many_arguments)]
fn effect_row(
@@ -364,7 +383,7 @@ pub fn show(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) {
let view = app.ui.effects_view.clone();
let list = Rect::from_min_max(pos2(rect.min.x, sr.max.y + 6.0), rect.max);
let lp = p.with_clip_rect(list);
let scroll = widgets::PanelScroll::begin(ui, egui::Id::unique("fx-scroll"), list);
let mut scroll = widgets::PanelScroll::begin(ui, egui::Id::unique("fx-scroll"), list);
let query = q.trim().to_lowercase();
let searching = !query.is_empty();
let mut y = list.min.y - scroll.offset;
@@ -378,6 +397,16 @@ pub fn show(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) {
// Favorites and Recently Used.
let favs: Vec<_> = app.session.prefs.effects_favorites.iter().filter_map(|id| lookup(id)).filter(|e| keep(e)).collect();
let recent: Vec<_> = app.ui.effects_recent.iter().filter_map(|id| lookup(id)).filter(|e| keep(e)).collect();
let reveal_presets = ctx.data_mut(|data| data.remove_temp::<bool>(egui::Id::unique(REVEAL_PRESETS_ID))).unwrap_or(false);
if reveal_presets {
// These rows come before Animation Presets. Scroll past expanded lists so the section is
// visible even when Favorites or Recently Used is long.
let preceding_rows =
rows_before_presets(favs.len(), recent.len(), searching, app.ui.effects_open.contains("favorites"), app.ui.effects_open.contains("recent"));
scroll.offset = ROW_H * preceding_rows as f32;
y = list.min.y - scroll.offset;
}
if !favs.is_empty() && folder_row(app, ui, &lp, list, &mut y, "favorites", "Favorites", 0.0, searching, Some(Icon::Star)) {
let mut favs = favs;
favs.sort_by_key(|e| e.name);
@@ -385,7 +414,6 @@ pub fn show(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) {
effect_row(app, ui, &lp, list, &mut y, e, 0.0, &query, "effects.favorites", &mut apply);
}
}
let recent: Vec<_> = app.ui.effects_recent.iter().filter_map(|id| lookup(id)).filter(|e| keep(e)).collect();
if !recent.is_empty() && !searching && folder_row(app, ui, &lp, list, &mut y, "recent", "Recently Used", 0.0, false, None) {
for e in recent {
effect_row(app, ui, &lp, list, &mut y, e, 0.0, &query, "effects.recent", &mut apply);
+41
View File
@@ -65,6 +65,47 @@ fn shown(h: &Harness<'_, EffectcraftApp>, label: &str) -> bool {
h.query_by_label_contains(label).is_some()
}
#[test]
fn browse_presets_clears_search_and_scrolls_to_the_visible_presets_list() {
use effectcraft_ui_egui::dock::PanelKind;
let mut h = harness();
let ctx = h.ctx.clone();
let favorites: Vec<String> = effectcraft_engine::effects::all().iter().map(|effect| effect.id.to_string()).collect();
assert!(!favorites.is_empty(), "the test needs registered effects");
let recent: Vec<String> = favorites.iter().take(8).cloned().collect();
h.state_mut().session.prefs.effects_favorites = favorites;
h.state_mut().ui.effects_recent = recent;
h.state_mut().ui.effects_open.extend(["favorites".into(), "recent".into()]);
h.state_mut().ui.effects_search = "no matching animation preset".into();
h.state_mut().ui.effects_view.presets = true;
let managed = effectcraft_ui_egui::menus::invoke(h.state_mut(), &ctx, "effect.manage", json!({}));
assert!(managed.is_ok(), "{managed:?}");
assert_eq!(h.state().ui.focused, PanelKind::EffectsPresets);
assert_eq!(h.state().ui.effects_search, "no matching animation preset", "Manage Effects keeps its existing search behavior");
assert!(h.state().ui.effects_view.presets, "Manage Effects keeps the existing presets setting");
h.run_steps(3);
assert!(h.state().auto.find("effects.category.presets").is_none(), "the unmatched search hides Animation Presets");
h.state_mut().ui.effects_view.presets = false;
let result = effectcraft_ui_egui::menus::invoke(h.state_mut(), &ctx, "anim.browsePresets", json!({}));
assert!(result.is_ok(), "{result:?}");
assert_eq!(h.state().ui.focused, PanelKind::EffectsPresets);
assert!(h.state().ui.effects_search.is_empty());
assert!(h.state().ui.effects_view.presets);
assert!(h.state().ui.effects_open.contains("presets"));
h.run_steps(3);
assert!(
h.state().auto.find("effects.category.presets").is_some(),
"Animation Presets must be visible after Browse Presets, even below expanded Favorites and Recently Used"
);
}
/// Space (or any shortcut) while a menu is open goes to the menu, not the app: it started the
/// preview behind the open menu.
#[test]