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

134 lines
4.2 KiB
Rust

//! 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<Uuid, (Point, Point)>, // (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<Uuid, (Point, Point)>) -> 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);
}
}
}