Lightningbeam/lightningbeam-ui/lightningbeam-core/src/actions/add_layer.rs

193 lines
6.1 KiB
Rust

//! Add layer action
//!
//! Handles adding a new layer to the document.
use crate::action::Action;
use crate::document::Document;
use crate::layer::{AnyLayer, VectorLayer};
use uuid::Uuid;
/// Action that adds a new layer to the document
pub struct AddLayerAction {
/// The layer to add
layer: AnyLayer,
/// If Some, add to this VectorClip's layers instead of root
target_clip_id: Option<Uuid>,
/// If Some, add as a child of this GroupLayer instead of root
target_group_id: Option<Uuid>,
/// ID of the created layer (set after execution)
created_layer_id: Option<Uuid>,
}
impl AddLayerAction {
/// Create a new add layer action with a vector layer
///
/// # Arguments
///
/// * `name` - The name for the new layer
pub fn new_vector(name: impl Into<String>) -> Self {
let layer = VectorLayer::new(name);
Self {
layer: AnyLayer::Vector(layer),
target_clip_id: None,
target_group_id: None,
created_layer_id: None,
}
}
/// Create a new add layer action with any layer type
///
/// # Arguments
///
/// * `layer` - The layer to add
pub fn new(layer: AnyLayer) -> Self {
Self {
layer,
target_clip_id: None,
target_group_id: None,
created_layer_id: None,
}
}
/// Set the target clip for this action (add layer inside a movie clip)
pub fn with_target_clip(mut self, clip_id: Option<Uuid>) -> Self {
self.target_clip_id = clip_id;
self
}
/// Set the target group for this action (add layer inside a group layer)
pub fn with_target_group(mut self, group_id: Uuid) -> Self {
self.target_group_id = Some(group_id);
self
}
/// Get the ID of the created layer (after execution)
pub fn created_layer_id(&self) -> Option<Uuid> {
self.created_layer_id
}
}
impl Action for AddLayerAction {
fn execute(&mut self, document: &mut Document) -> Result<(), String> {
let layer_id = if let Some(group_id) = self.target_group_id {
// Add layer inside a group layer
let id = self.layer.id();
if let Some(AnyLayer::Group(g)) = document.root.children.iter_mut()
.find(|l| l.id() == group_id)
{
g.add_child(self.layer.clone());
} else {
return Err(format!("Target group {} not found", group_id));
}
id
} else if let Some(clip_id) = self.target_clip_id {
// Add layer inside a vector clip (movie clip)
let clip = document.vector_clips.get_mut(&clip_id)
.ok_or_else(|| format!("Target clip {} not found", clip_id))?;
let id = self.layer.id();
clip.layers.add_root(self.layer.clone());
// Register in layer_to_clip_map for O(1) lookup
document.layer_to_clip_map.insert(id, clip_id);
id
} else {
// Add layer to the document's root
document.root_mut().add_child(self.layer.clone())
};
// Store the ID for rollback
self.created_layer_id = Some(layer_id);
Ok(())
}
fn rollback(&mut self, document: &mut Document) -> Result<(), String> {
// Remove the created layer if it exists
if let Some(layer_id) = self.created_layer_id {
if let Some(group_id) = self.target_group_id {
// Remove from group layer
if let Some(AnyLayer::Group(g)) = document.root.children.iter_mut()
.find(|l| l.id() == group_id)
{
g.children.retain(|l| l.id() != layer_id);
}
} else if let Some(clip_id) = self.target_clip_id {
// Remove from vector clip
if let Some(clip) = document.vector_clips.get_mut(&clip_id) {
clip.layers.roots.retain(|node| node.data.id() != layer_id);
}
document.layer_to_clip_map.remove(&layer_id);
} else {
document.root_mut().remove_child(&layer_id);
}
// Clear the stored ID
self.created_layer_id = None;
}
Ok(())
}
fn description(&self) -> String {
match &self.layer {
AnyLayer::Vector(_) => "Add vector layer",
AnyLayer::Audio(_) => "Add audio layer",
AnyLayer::Video(_) => "Add video layer",
AnyLayer::Effect(_) => "Add effect layer",
AnyLayer::Group(_) => "Add group layer",
AnyLayer::Raster(_) => "Add raster layer",
}
.to_string()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add_vector_layer() {
let mut document = Document::new("Test");
assert_eq!(document.root.children.len(), 0);
// Create and execute action
let mut action = AddLayerAction::new_vector("New Layer");
action.execute(&mut document).unwrap();
// Verify layer was added
assert_eq!(document.root.children.len(), 1);
let layer = &document.root.children[0];
assert_eq!(layer.layer().name, "New Layer");
assert!(matches!(layer, AnyLayer::Vector(_)));
// Rollback
action.rollback(&mut document).unwrap();
// Verify layer was removed
assert_eq!(document.root.children.len(), 0);
}
#[test]
fn test_add_layer_description() {
let action = AddLayerAction::new_vector("Test");
assert_eq!(action.description(), "Add vector layer");
}
#[test]
fn test_add_multiple_layers() {
let mut document = Document::new("Test");
let mut action1 = AddLayerAction::new_vector("Layer 1");
let mut action2 = AddLayerAction::new_vector("Layer 2");
action1.execute(&mut document).unwrap();
action2.execute(&mut document).unwrap();
assert_eq!(document.root.children.len(), 2);
assert_eq!(document.root.children[0].layer().name, "Layer 1");
assert_eq!(document.root.children[1].layer().name, "Layer 2");
}
}