@@ -699,3 +699,213 @@ impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayer
699699 common
700700 }
701701}
702+
703+ #[ cfg( test) ]
704+ mod test_transform_layer {
705+ use crate :: messages:: portfolio:: document:: graph_operation:: transform_utils;
706+ use crate :: test_utils:: test_prelude:: * ;
707+ // Use ModifyInputsContext to locate the transform node
708+ use crate :: messages:: portfolio:: document:: graph_operation:: utility_types:: ModifyInputsContext ;
709+ use crate :: messages:: prelude:: Message ;
710+ use glam:: DAffine2 ;
711+ use std:: collections:: VecDeque ;
712+
713+ async fn get_layer_transform ( editor : & mut EditorTestUtils , layer : LayerNodeIdentifier ) -> Option < DAffine2 > {
714+ let document = editor. active_document ( ) ;
715+ let network_interface = & document. network_interface ;
716+ let _responses: VecDeque < Message > = VecDeque :: new ( ) ;
717+ let transform_node_id = ModifyInputsContext :: locate_node_in_layer_chain ( "Transform" , layer, network_interface) ?;
718+ let document_node = network_interface. document_network ( ) . nodes . get ( & transform_node_id) ?;
719+ Some ( transform_utils:: get_current_transform ( & document_node. inputs ) )
720+ }
721+
722+ #[ tokio:: test]
723+ async fn test_grab_apply ( ) {
724+ let mut editor = EditorTestUtils :: create ( ) ;
725+ editor. new_document ( ) . await ;
726+
727+ editor. drag_tool ( ToolType :: Rectangle , 0. , 0. , 100. , 100. , ModifierKeys :: empty ( ) ) . await ;
728+
729+ let document = editor. active_document ( ) ;
730+ let layer = document. metadata ( ) . all_layers ( ) . next ( ) . unwrap ( ) ;
731+
732+ let original_transform = get_layer_transform ( & mut editor, layer) . await . unwrap ( ) ;
733+
734+ editor. handle_message ( TransformLayerMessage :: BeginGrab ) . await ;
735+
736+ let translation = DVec2 :: new ( 50.0 , 50.0 ) ;
737+ editor. move_mouse ( translation. x , translation. y , ModifierKeys :: empty ( ) , MouseKeys :: NONE ) . await ;
738+
739+ editor
740+ . handle_message ( TransformLayerMessage :: PointerMove {
741+ slow_key : Key :: Shift ,
742+ increments_key : Key :: Control ,
743+ } )
744+ . await ;
745+
746+ editor. handle_message ( TransformLayerMessage :: ApplyTransformOperation { final_transform : true } ) . await ;
747+
748+ let final_transform = get_layer_transform ( & mut editor, layer) . await . unwrap ( ) ;
749+
750+ let translation_diff = ( final_transform. translation - original_transform. translation ) . length ( ) ;
751+ assert ! ( translation_diff > 10.0 , "Transform should have changed after applying transformation. Diff: {}" , translation_diff) ;
752+ }
753+
754+ #[ tokio:: test]
755+ async fn test_grab_cancel ( ) {
756+ let mut editor = EditorTestUtils :: create ( ) ;
757+ editor. new_document ( ) . await ;
758+ editor. drag_tool ( ToolType :: Rectangle , 0. , 0. , 100. , 100. , ModifierKeys :: empty ( ) ) . await ;
759+
760+ let document = editor. active_document ( ) ;
761+ let layer = document. metadata ( ) . all_layers ( ) . next ( ) . unwrap ( ) ;
762+ let original_transform = get_layer_transform ( & mut editor, layer) . await . expect ( "Should be able to get the layer transform" ) ;
763+
764+ editor. handle_message ( TransformLayerMessage :: BeginGrab ) . await ;
765+ editor. move_mouse ( 50.0 , 50.0 , ModifierKeys :: empty ( ) , MouseKeys :: NONE ) . await ;
766+ editor
767+ . handle_message ( TransformLayerMessage :: PointerMove {
768+ slow_key : Key :: Shift ,
769+ increments_key : Key :: Control ,
770+ } )
771+ . await ;
772+
773+ let during_transform = get_layer_transform ( & mut editor, layer) . await . expect ( "Should be able to get the layer transform during operation" ) ;
774+
775+ assert ! ( original_transform != during_transform, "Transform should change during operation" ) ;
776+
777+ editor. handle_message ( TransformLayerMessage :: CancelTransformOperation ) . await ;
778+
779+ let final_transform = get_layer_transform ( & mut editor, layer) . await . expect ( "Should be able to get the final transform" ) ;
780+ let final_translation = final_transform. translation ;
781+ let original_translation = original_transform. translation ;
782+
783+ // Verify transform is either restored to original OR reset to identity
784+ assert ! (
785+ ( final_translation - original_translation) . length( ) < 5.0 || final_translation. length( ) < 0.001 ,
786+ "Transform neither restored to original nor reset to identity. Original: {:?}, Final: {:?}" ,
787+ original_translation,
788+ final_translation
789+ ) ;
790+ }
791+
792+ #[ tokio:: test]
793+ async fn test_rotate_apply ( ) {
794+ let mut editor = EditorTestUtils :: create ( ) ;
795+ editor. new_document ( ) . await ;
796+ editor. drag_tool ( ToolType :: Rectangle , 0. , 0. , 100. , 100. , ModifierKeys :: empty ( ) ) . await ;
797+
798+ let document = editor. active_document ( ) ;
799+ let layer = document. metadata ( ) . all_layers ( ) . next ( ) . unwrap ( ) ;
800+
801+ let original_transform = get_layer_transform ( & mut editor, layer) . await . unwrap ( ) ;
802+
803+ editor. handle_message ( TransformLayerMessage :: BeginRotate ) . await ;
804+
805+ editor. move_mouse ( 150.0 , 50.0 , ModifierKeys :: empty ( ) , MouseKeys :: NONE ) . await ;
806+
807+ editor
808+ . handle_message ( TransformLayerMessage :: PointerMove {
809+ slow_key : Key :: Shift ,
810+ increments_key : Key :: Control ,
811+ } )
812+ . await ;
813+
814+ editor. handle_message ( TransformLayerMessage :: ApplyTransformOperation { final_transform : true } ) . await ;
815+
816+ let final_transform = get_layer_transform ( & mut editor, layer) . await . unwrap ( ) ;
817+ println ! ( "Final transform: {:?}" , final_transform) ;
818+
819+ // Check matrix components have changed (rotation affects matrix2)
820+ let matrix_diff = ( final_transform. matrix2 . x_axis - original_transform. matrix2 . x_axis ) . length ( ) ;
821+ assert ! ( matrix_diff > 0.1 , "Rotation should have changed the transform matrix. Diff: {}" , matrix_diff) ;
822+ }
823+
824+ #[ tokio:: test]
825+ async fn test_rotate_cancel ( ) {
826+ let mut editor = EditorTestUtils :: create ( ) ;
827+ editor. new_document ( ) . await ;
828+ editor. drag_tool ( ToolType :: Rectangle , 0. , 0. , 100. , 100. , ModifierKeys :: empty ( ) ) . await ;
829+
830+ let document = editor. active_document ( ) ;
831+ let layer = document. metadata ( ) . all_layers ( ) . next ( ) . unwrap ( ) ;
832+ let original_transform = get_layer_transform ( & mut editor, layer) . await . unwrap ( ) ;
833+
834+ editor. handle_message ( TransformLayerMessage :: BeginRotate ) . await ;
835+ editor. handle_message ( TransformLayerMessage :: CancelTransformOperation ) . await ;
836+
837+ let after_cancel = get_layer_transform ( & mut editor, layer) . await . unwrap ( ) ;
838+
839+ assert ! ( !after_cancel. translation. x. is_nan( ) , "Transform is NaN after cancel" ) ;
840+ assert ! ( !after_cancel. translation. y. is_nan( ) , "Transform is NaN after cancel" ) ;
841+
842+ let translation_diff = ( after_cancel. translation - original_transform. translation ) . length ( ) ;
843+ assert ! ( translation_diff < 1.0 , "Translation component changed too much: {}" , translation_diff) ;
844+ }
845+
846+ #[ tokio:: test]
847+ async fn test_scale_apply ( ) {
848+ let mut editor = EditorTestUtils :: create ( ) ;
849+ editor. new_document ( ) . await ;
850+ editor. drag_tool ( ToolType :: Rectangle , 0. , 0. , 100. , 100. , ModifierKeys :: empty ( ) ) . await ;
851+
852+ let document = editor. active_document ( ) ;
853+ let layer = document. metadata ( ) . all_layers ( ) . next ( ) . unwrap ( ) ;
854+
855+ let original_transform = get_layer_transform ( & mut editor, layer) . await . unwrap ( ) ;
856+
857+ editor. handle_message ( TransformLayerMessage :: BeginScale ) . await ;
858+
859+ editor. move_mouse ( 150.0 , 150.0 , ModifierKeys :: empty ( ) , MouseKeys :: NONE ) . await ;
860+
861+ editor
862+ . handle_message ( TransformLayerMessage :: PointerMove {
863+ slow_key : Key :: Shift ,
864+ increments_key : Key :: Control ,
865+ } )
866+ . await ;
867+
868+ editor. handle_message ( TransformLayerMessage :: ApplyTransformOperation { final_transform : true } ) . await ;
869+
870+ let final_transform = get_layer_transform ( & mut editor, layer) . await . unwrap ( ) ;
871+
872+ // Check scaling components have changed
873+ let scale_diff_x = ( final_transform. matrix2 . x_axis . x - original_transform. matrix2 . x_axis . x ) . abs ( ) ;
874+ let scale_diff_y = ( final_transform. matrix2 . y_axis . y - original_transform. matrix2 . y_axis . y ) . abs ( ) ;
875+
876+ assert ! (
877+ scale_diff_x > 0.1 || scale_diff_y > 0.1 ,
878+ "Scaling should have changed the transform matrix. Diffs: x={}, y={}" ,
879+ scale_diff_x,
880+ scale_diff_y
881+ ) ;
882+ }
883+
884+ #[ tokio:: test]
885+ async fn test_scale_cancel ( ) {
886+ let mut editor = EditorTestUtils :: create ( ) ;
887+ editor. new_document ( ) . await ;
888+ editor. drag_tool ( ToolType :: Rectangle , 0. , 0. , 100. , 100. , ModifierKeys :: empty ( ) ) . await ;
889+
890+ let document = editor. active_document ( ) ;
891+ let layer = document. metadata ( ) . all_layers ( ) . next ( ) . unwrap ( ) ;
892+ let original_transform = get_layer_transform ( & mut editor, layer) . await . unwrap ( ) ;
893+
894+ editor. handle_message ( TransformLayerMessage :: BeginScale ) . await ;
895+
896+ // Cancel immediately without moving to ensure proper reset
897+ editor. handle_message ( TransformLayerMessage :: CancelTransformOperation ) . await ;
898+
899+ let after_cancel = get_layer_transform ( & mut editor, layer) . await . unwrap ( ) ;
900+
901+ // The scale factor is represented in the matrix2 part, so check those components
902+ assert ! (
903+ ( after_cancel. matrix2. x_axis. x - original_transform. matrix2. x_axis. x) . abs( ) < 0.1 && ( after_cancel. matrix2. y_axis. y - original_transform. matrix2. y_axis. y) . abs( ) < 0.1 ,
904+ "Matrix scale components should be restored after cancellation"
905+ ) ;
906+
907+ // Also check translation component is similar
908+ let translation_diff = ( after_cancel. translation - original_transform. translation ) . length ( ) ;
909+ assert ! ( translation_diff < 1.0 , "Translation component changed too much: {}" , translation_diff) ;
910+ }
911+ }
0 commit comments