Repository: GraphiteEditor/Graphite (language: rust)

Issue:
Resizing an artboard by its top/left edges should keep its content stationary
Using the Artboard tool to resize an artboard from the top or left edge makes it shift its contents in canvas space, which isn't the desired behavior. It does this because the content is positioned in artboard space, so the Artboard tool needs to run special logic when resizing a top/left edge to compensate for the reverse of this, making the content translate by the inverse of the artboard edge resizing. The easy but bad way of doing this is adding a Transform node directly before the Artboard node. The better way is to loop through each layer that's a direct child of the artboard layer and apply a translation to it just as if the user had dragged the layers manually.

https://github.com/user-attachments/assets/492a6672-9ec2-4590-8b61-6a288214fe25




Discussion / hints:
i want to work on this issue 

@ayushpatil2122 go for it, and open a PR when you have something functional!

Fix patch:
diff --git a/editor/src/messages/tool/tool_messages/artboard_tool.rs b/editor/src/messages/tool/tool_messages/artboard_tool.rs
index 1d89e4f306..a6406730d1 100644
--- a/editor/src/messages/tool/tool_messages/artboard_tool.rs
+++ b/editor/src/messages/tool/tool_messages/artboard_tool.rs
@@ -1,4 +1,5 @@
 use super::tool_prelude::*;
+use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
 use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
 use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
 use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
@@ -111,6 +112,7 @@ struct ArtboardToolData {
 	drag_current: DVec2,
 	auto_panning: AutoPanning,
 	snap_candidates: Vec<SnapCandidatePoint>,
+	dragging_current_artboad_location: IVec2,
 }
 
 impl ArtboardToolData {
@@ -138,6 +140,7 @@ impl ArtboardToolData {
 	fn start_resizing(&mut self, _selected_edges: (bool, bool, bool, bool), _document: &DocumentMessageHandler, _input: &InputPreprocessorMessageHandler) {
 		if let Some(bounds) = &mut self.bounding_box_manager {
 			bounds.center_of_transformation = bounds.transform.transform_point2((bounds.bounds[0] + bounds.bounds[1]) / 2.);
+			self.dragging_current_artboad_location = bounds.bounds[0].round().as_ivec2();
 		}
 	}
 
@@ -197,18 +200,17 @@ impl ArtboardToolData {
 			dimensions: size.round().as_ivec2(),
 		});
 
-		// TODO: Resize artboard children when resizing left/top edges so that they stay in the same viewport space
-		// let old_top_left = bounds.bounds[0].round().as_ivec2();
-		// let new_top_left = position.round().as_ivec2();
-		// let top_left_delta = new_top_left - old_top_left;
-		// if top_left_delta != IVec2::ZERO {
-		// 	responses.add(GraphOperationMessage::TransformChange {
-		// 		layer: self.selected_artboard.unwrap(),
-		// 		transform: DAffine2::from_translation((-top_left_delta).into()),
-		// 		transform_in: TransformIn::Local,
-		// 		skip_rerender: false,
-		// 	});
-		// }
+		let translation = position.round().as_ivec2() - self.dragging_current_artboad_location;
+		self.dragging_current_artboad_location = position.round().as_ivec2();
+		for child in self.selected_artboard.unwrap().children(&document.metadata()) {
+			let local_translation = document.metadata().downstream_transform_to_document(child).inverse().transform_vector2(-translation.as_dvec2());
+			responses.add(GraphOperationMessage::TransformChange {
+				layer: child,
+				transform: DAffine2::from_translation(local_translation),
+				transform_in: TransformIn::Local,
+				skip_rerender: false,
+			});
+		}
 	}
 }
 


Test patch: