@@ -699,3 +699,213 @@ impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayer
699
699
common
700
700
}
701
701
}
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