@@ -2785,6 +2785,7 @@ mod document_message_handler_tests {
27852785 assert ! ( !selected_nodes. selected_layers_contains( layers[ 1 ] , document. metadata( ) ) ) ;
27862786 assert ! ( selected_nodes. selected_layers_contains( layers[ 2 ] , document. metadata( ) ) ) ;
27872787 }
2788+
27882789 #[ tokio:: test]
27892790 async fn test_layer_rearrangement ( ) {
27902791 let mut editor = EditorTestUtils :: create ( ) ;
@@ -2831,6 +2832,7 @@ mod document_message_handler_tests {
28312832 let new_index_middle = get_layer_index ( & mut editor, layer_middle) . await . unwrap ( ) ;
28322833 assert ! ( new_index_middle < initial_index_middle, "Middle layer should have moved up" ) ;
28332834 }
2835+
28342836 #[ tokio:: test]
28352837 async fn test_move_folder_into_itself_doesnt_crash ( ) {
28362838 let mut editor = EditorTestUtils :: create ( ) ;
@@ -2859,4 +2861,116 @@ mod document_message_handler_tests {
28592861 editor. handle_message ( DocumentMessage :: CreateEmptyFolder ) . await ;
28602862 assert ! ( true , "Application didn't crash after folder move operation" ) ;
28612863 }
2864+ #[ tokio:: test]
2865+ async fn test_moving_folder_with_children ( ) {
2866+ let mut editor = EditorTestUtils :: create ( ) ;
2867+ editor. new_document ( ) . await ;
2868+
2869+ // Creating two folders at root level
2870+ editor. handle_message ( DocumentMessage :: CreateEmptyFolder ) . await ;
2871+ editor. handle_message ( DocumentMessage :: CreateEmptyFolder ) . await ;
2872+
2873+ let folder1 = editor. active_document ( ) . metadata ( ) . all_layers ( ) . next ( ) . unwrap ( ) ;
2874+ let folder2 = editor. active_document ( ) . metadata ( ) . all_layers ( ) . nth ( 1 ) . unwrap ( ) ;
2875+
2876+ editor. drag_tool ( ToolType :: Rectangle , 0. , 0. , 100. , 100. , ModifierKeys :: empty ( ) ) . await ;
2877+ let rect_layer = editor. active_document ( ) . metadata ( ) . all_layers ( ) . next ( ) . unwrap ( ) ;
2878+
2879+ // First move rectangle into folder1
2880+ editor. handle_message ( NodeGraphMessage :: SelectedNodesSet { nodes : vec ! [ rect_layer. to_node( ) ] } ) . await ;
2881+ editor. handle_message ( DocumentMessage :: MoveSelectedLayersTo { parent : folder1, insert_index : 0 } ) . await ;
2882+
2883+ // Verifying rectagle is now in folder1
2884+ let rect_parent = rect_layer. parent ( editor. active_document ( ) . metadata ( ) ) . unwrap ( ) ;
2885+ assert_eq ! ( rect_parent, folder1, "Rectangle should be inside folder1" ) ;
2886+
2887+ // Moving folder1 into folder2
2888+ editor. handle_message ( NodeGraphMessage :: SelectedNodesSet { nodes : vec ! [ folder1. to_node( ) ] } ) . await ;
2889+ editor. handle_message ( DocumentMessage :: MoveSelectedLayersTo { parent : folder2, insert_index : 0 } ) . await ;
2890+
2891+ // Verifing hirarchy: folder2 > folder1 > rectangle
2892+ let document = editor. active_document ( ) ;
2893+ let folder1_parent = folder1. parent ( document. metadata ( ) ) . unwrap ( ) ;
2894+ assert_eq ! ( folder1_parent, folder2, "Folder1 should be inside folder2" ) ;
2895+
2896+ // Verifing rectangle moved with its parent
2897+ let rect_parent = rect_layer. parent ( document. metadata ( ) ) . unwrap ( ) ;
2898+ assert_eq ! ( rect_parent, folder1, "Rectangle should still be inside folder1" ) ;
2899+
2900+ let rect_grandparent = rect_parent. parent ( document. metadata ( ) ) . unwrap ( ) ;
2901+ assert_eq ! ( rect_grandparent, folder2, "Rectangle's grandparent should be folder2" ) ;
2902+ }
2903+
2904+ #[ tokio:: test]
2905+ async fn test_moving_layers_retains_transforms ( ) {
2906+ let mut editor = EditorTestUtils :: create ( ) ;
2907+ editor. new_document ( ) . await ;
2908+
2909+ editor. handle_message ( DocumentMessage :: CreateEmptyFolder ) . await ;
2910+ editor. handle_message ( DocumentMessage :: CreateEmptyFolder ) . await ;
2911+
2912+ let folder2 = editor. active_document ( ) . metadata ( ) . all_layers ( ) . next ( ) . unwrap ( ) ;
2913+ let folder1 = editor. active_document ( ) . metadata ( ) . all_layers ( ) . nth ( 1 ) . unwrap ( ) ;
2914+
2915+ // Applying transform to folder1 (translation)
2916+ editor. handle_message ( NodeGraphMessage :: SelectedNodesSet { nodes : vec ! [ folder1. to_node( ) ] } ) . await ;
2917+ editor. handle_message ( TransformLayerMessage :: BeginGrab ) . await ;
2918+ editor. move_mouse ( 100.0 , 50.0 , ModifierKeys :: empty ( ) , MouseKeys :: NONE ) . await ;
2919+ editor
2920+ . handle_message ( TransformLayerMessage :: PointerMove {
2921+ slow_key : Key :: Shift ,
2922+ increments_key : Key :: Control ,
2923+ } )
2924+ . await ;
2925+ editor. handle_message ( TransformLayerMessage :: ApplyTransformOperation { final_transform : true } ) . await ;
2926+
2927+ // Applying different transform to folder2 (translation)
2928+ editor. handle_message ( NodeGraphMessage :: SelectedNodesSet { nodes : vec ! [ folder2. to_node( ) ] } ) . await ;
2929+ editor. handle_message ( TransformLayerMessage :: BeginGrab ) . await ;
2930+ editor. move_mouse ( 200.0 , 100.0 , ModifierKeys :: empty ( ) , MouseKeys :: NONE ) . await ;
2931+ editor
2932+ . handle_message ( TransformLayerMessage :: PointerMove {
2933+ slow_key : Key :: Shift ,
2934+ increments_key : Key :: Control ,
2935+ } )
2936+ . await ;
2937+ editor. handle_message ( TransformLayerMessage :: ApplyTransformOperation { final_transform : true } ) . await ;
2938+
2939+ // Creating rectangle in folder1
2940+ editor. handle_message ( NodeGraphMessage :: SelectedNodesSet { nodes : vec ! [ folder1. to_node( ) ] } ) . await ;
2941+ editor. drag_tool ( ToolType :: Rectangle , 0. , 0. , 100. , 100. , ModifierKeys :: empty ( ) ) . await ;
2942+ let rect_layer = editor. active_document ( ) . metadata ( ) . all_layers ( ) . next ( ) . unwrap ( ) ;
2943+
2944+ // Moving the rectangle to folder1 to ensure it's inside
2945+ editor. handle_message ( NodeGraphMessage :: SelectedNodesSet { nodes : vec ! [ rect_layer. to_node( ) ] } ) . await ;
2946+ editor. handle_message ( DocumentMessage :: MoveSelectedLayersTo { parent : folder1, insert_index : 0 } ) . await ;
2947+
2948+ editor. handle_message ( TransformLayerMessage :: BeginGrab ) . await ;
2949+ editor. move_mouse ( 50.0 , 25.0 , ModifierKeys :: empty ( ) , MouseKeys :: NONE ) . await ;
2950+ editor
2951+ . handle_message ( TransformLayerMessage :: PointerMove {
2952+ slow_key : Key :: Shift ,
2953+ increments_key : Key :: Control ,
2954+ } )
2955+ . await ;
2956+ editor. handle_message ( TransformLayerMessage :: ApplyTransformOperation { final_transform : true } ) . await ;
2957+
2958+ // Rectangle's viewport position before moving
2959+ let document = editor. active_document ( ) ;
2960+ let rect_bbox_before = document. metadata ( ) . bounding_box_viewport ( rect_layer) . unwrap ( ) ;
2961+
2962+ // Moving rectangle from folder1 --> folder2
2963+ editor. handle_message ( DocumentMessage :: MoveSelectedLayersTo { parent : folder2, insert_index : 0 } ) . await ;
2964+
2965+ // Rectangle's viewport position after moving
2966+ let document = editor. active_document ( ) ;
2967+ let rect_bbox_after = document. metadata ( ) . bounding_box_viewport ( rect_layer) . unwrap ( ) ;
2968+
2969+ // Verifing the rectangle maintains approximately the same position in viewport space
2970+ let before_center = ( rect_bbox_before[ 0 ] + rect_bbox_before[ 1 ] ) / 2.0 ;
2971+ let after_center = ( rect_bbox_after[ 0 ] + rect_bbox_after[ 1 ] ) / 2.0 ;
2972+ let distance = before_center. distance ( after_center) ;
2973+
2974+ assert ! ( distance < 1.0 , "Rectangle should maintain its viewport position after moving between transformed groups" ) ;
2975+ }
28622976}
0 commit comments