use crate::ui::effect::EffectUi;
use crate::{pipeline::thumbnails::ThumbnailTextures, ui::effect::widget::EffectWidget};
use egui::{Button, Color32, Ui, Vec2};
use gled_model::{
asset::scene::{Scene, effect::Effect},
collection::Collections,
patching::Patch,
};
use gled_protocol::state::SoundSnapshot;
use uuid::Uuid;
#[derive(Default, Debug)]
pub(crate) struct SceneEffectEditorState {
pub(crate) selected_effect: usize,
synced_effect: usize,
}
impl SceneEffectEditorState {
pub(crate) fn follow_engine(&mut self, engine_effect: usize) {
self.selected_effect = engine_effect;
self.synced_effect = engine_effect;
}
pub(crate) fn take_change(&mut self) -> Option<usize> {
(self.selected_effect != self.synced_effect).then(|| {
self.synced_effect = self.selected_effect;
self.selected_effect
})
}
pub(crate) fn reset(&mut self) {
self.selected_effect = 0;
self.synced_effect = 0;
}
pub(crate) fn clamp(&mut self, effect_count: usize) {
if effect_count == 0 {
self.selected_effect = 0;
} else {
self.selected_effect = self.selected_effect.min(effect_count - 1);
}
}
}
pub(crate) fn selected_effect_editor_ui(
ui: &mut Ui,
scene: &mut Scene,
owner: Uuid,
textures: &ThumbnailTextures,
state: &mut SceneEffectEditorState,
allow_animation_change: bool,
patch: &Patch,
beat_progression: f32,
collections: &mut Collections,
sound_data: &SoundSnapshot,
) -> bool {
state.clamp(scene.effects().len());
let mut changed = false;
if let Some(effect) = scene.effect(state.selected_effect) {
changed |= effect.config_ui(
ui,
textures.texture_id(&(owner, state.selected_effect)),
None,
allow_animation_change,
patch,
beat_progression,
collections,
sound_data,
);
}
ui.separator();
ui.vertical_centered_justified(|ui| {
if ui
.add(Button::new("+ Add Animation").fill(Color32::DARK_GREEN))
.clicked()
{
state.selected_effect = scene.add_effect(Effect::default());
changed = true;
}
});
ui.vertical_centered_justified(|ui| {
if ui
.add(Button::new("🗐 Duplicate Animation").fill(Color32::DARK_BLUE))
.clicked()
&& let Some(effect) = scene.effect(state.selected_effect).cloned()
{
state.selected_effect = scene.add_effect(effect);
changed = true;
}
});
ui.vertical_centered_justified(|ui| {
if ui
.add(Button::new("🗑 Remove Animation").fill(Color32::DARK_RED))
.clicked()
{
scene.remove_effect(state.selected_effect);
state.selected_effect = state.selected_effect.saturating_sub(1);
state.clamp(scene.effects().len());
changed = true;
}
});
changed
}
pub(crate) fn scene_effect_list_ui(
ui: &mut Ui,
scene: &Scene,
owner: Uuid,
textures: &ThumbnailTextures,
state: &mut SceneEffectEditorState,
show_group: bool,
patch: Option<&Patch>,
beat_progression: Option<f32>,
collections: &mut Collections,
sound_data: &SoundSnapshot,
) {
state.clamp(scene.effects().len());
ui.horizontal_wrapped(|ui| {
for (index, effect) in scene.effects().iter().enumerate() {
ui.add_sized(
Vec2::splat(100.0),
EffectWidget {
show_group,
selectable: Some((&mut state.selected_effect, index)),
effect,
thumbnail: textures.texture_id(&(owner, index)),
patch,
beat_progression,
collections,
sound_data,
},
);
}
});
}
#[cfg(test)]
mod tests {
use super::SceneEffectEditorState;
#[test]
fn an_effect_picked_here_is_sent_once() {
let mut state = SceneEffectEditorState {
selected_effect: 2,
..Default::default()
};
assert_eq!(state.take_change(), Some(2));
assert_eq!(state.take_change(), None);
}
#[test]
fn going_back_to_the_first_effect_is_not_sent() {
let mut state = SceneEffectEditorState {
selected_effect: 2,
..Default::default()
};
let _ = state.take_change();
state.reset();
assert_eq!(state.take_change(), None);
}
#[test]
fn an_effect_selected_in_the_engine_is_shown_and_not_sent_back() {
let mut state = SceneEffectEditorState::default();
state.follow_engine(3);
assert_eq!(state.selected_effect, 3);
assert_eq!(state.take_change(), None);
}
}