//! Move shape instances action //! //! Handles moving one or more shape instances to new positions. use crate::action::Action; use crate::document::Document; use crate::layer::AnyLayer; use std::collections::HashMap; use uuid::Uuid; use vello::kurbo::Point; /// Action that moves shape instances to new positions pub struct MoveShapeInstancesAction { /// Layer ID containing the shape instances layer_id: Uuid, /// Map of object IDs to their old and new positions shape_instance_positions: HashMap, // (old_pos, new_pos) } impl MoveShapeInstancesAction { /// Create a new move shape instances action /// /// # Arguments /// /// * `layer_id` - The layer containing the shape instances /// * `shape_instance_positions` - Map of object IDs to (old_position, new_position) pub fn new(layer_id: Uuid, shape_instance_positions: HashMap) -> Self { Self { layer_id, shape_instance_positions, } } } impl Action for MoveShapeInstancesAction { fn execute(&mut self, document: &mut Document) -> Result<(), String> { let layer = match document.get_layer_mut(&self.layer_id) { Some(l) => l, None => return Ok(()), }; if let AnyLayer::Vector(vector_layer) = layer { for (shape_instance_id, (_old, new)) in &self.shape_instance_positions { vector_layer.modify_object_internal(shape_instance_id, |obj| { obj.transform.x = new.x; obj.transform.y = new.y; }); } } Ok(()) } fn rollback(&mut self, document: &mut Document) -> Result<(), String> { let layer = match document.get_layer_mut(&self.layer_id) { Some(l) => l, None => return Ok(()), }; if let AnyLayer::Vector(vector_layer) = layer { for (shape_instance_id, (old, _new)) in &self.shape_instance_positions { vector_layer.modify_object_internal(shape_instance_id, |obj| { obj.transform.x = old.x; obj.transform.y = old.y; }); } } Ok(()) } fn description(&self) -> String { let count = self.shape_instance_positions.len(); if count == 1 { "Move shape instance".to_string() } else { format!("Move {} shape instances", count) } } } #[cfg(test)] mod tests { use super::*; use crate::layer::VectorLayer; use crate::object::ShapeInstance; use crate::shape::Shape; use vello::kurbo::{Circle, Shape as KurboShape}; #[test] fn test_move_shape_instances_action() { // Create a document with a test object let mut document = Document::new("Test"); let circle = Circle::new((100.0, 100.0), 50.0); let path = circle.to_path(0.1); let shape = Shape::new(path); let object = ShapeInstance::new(shape.id).with_position(50.0, 50.0); let mut vector_layer = VectorLayer::new("Layer 1"); vector_layer.add_shape(shape); let shape_instance_id = vector_layer.add_object(object); let layer_id = document.root.add_child(AnyLayer::Vector(vector_layer)); // Create move action let mut positions = HashMap::new(); positions.insert( shape_instance_id, (Point::new(50.0, 50.0), Point::new(150.0, 200.0)) ); let mut action = MoveShapeInstancesAction::new(layer_id, positions); // Execute action.execute(&mut document).unwrap(); // Verify position changed if let Some(AnyLayer::Vector(layer)) = document.get_layer(&layer_id) { let obj = layer.get_object(&shape_instance_id).unwrap(); assert_eq!(obj.transform.x, 150.0); assert_eq!(obj.transform.y, 200.0); } // Rollback action.rollback(&mut document).unwrap(); // Verify position restored if let Some(AnyLayer::Vector(layer)) = document.get_layer(&layer_id) { let obj = layer.get_object(&shape_instance_id).unwrap(); assert_eq!(obj.transform.x, 50.0); assert_eq!(obj.transform.y, 50.0); } } }