Skip to content

Transfer pasted layers to the viewport center when outside the current view area #2306

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 25 commits into from
May 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
bf85018
Transfer pasted layers to viewport center when not in viewport
mTvare6 Feb 18, 2025
68ad92f
Move a layer as a whole and use change
mTvare6 Feb 18, 2025
bf09d6b
Make a bbox and move content according to that
mTvare6 Feb 18, 2025
6b6b4dc
Merge branch 'master' into pasting-offset
mTvare6 Feb 20, 2025
134e191
partial fix
mTvare6 Mar 4, 2025
69b42cc
Only move if none within viewport
mTvare6 Mar 4, 2025
e724050
Fix import error
mTvare6 Mar 4, 2025
b178ed4
Fix artboard
mTvare6 Mar 4, 2025
7cd5abc
tmp, skip ones being added to ab
mTvare6 Mar 5, 2025
0d38951
Merge branch 'master' into pasting-offset
Keavon Mar 19, 2025
b207008
Merge branch 'master' into pasting-offset
mTvare6 Apr 19, 2025
c68a1c1
Merge branch 'master' into pasting-offset
mTvare6 Apr 20, 2025
c0c41c4
Merge branch 'master' into pasting-offset
mTvare6 Apr 20, 2025
3b6c589
Merge branch 'master' into pasting-offset
mTvare6 Apr 23, 2025
42f396c
Merge branch 'master' into pasting-offset
mTvare6 Apr 30, 2025
dfd449b
Merge branch 'master' into pasting-offset
Keavon Apr 30, 2025
c4fe3ae
Merge branch 'master' into pasting-offset
Keavon Apr 30, 2025
2ad6d6d
Merge branch 'master' into pasting-offset
mTvare6 May 1, 2025
09f1ca7
Merge branch 'master' into pasting-offset
mTvare6 May 11, 2025
f0fcd1b
fix stuff
mTvare6 May 19, 2025
99df280
Merge branch 'master' into pasting-offset
mTvare6 May 19, 2025
58effb8
fix formatting
mTvare6 May 19, 2025
9d0b287
Merge branch 'master' into pasting-offset
Keavon May 19, 2025
7912673
Code review
Keavon May 20, 2025
a124ede
Merge branch 'master' into pasting-offset
Keavon May 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions editor/src/messages/portfolio/portfolio_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ pub enum PortfolioMessage {
PasteSerializedData {
data: String,
},
CenterPastedLayers {
layers: Vec<LayerNodeIdentifier>,
},
PasteImage {
name: Option<String>,
image: Image<Color>,
Expand Down
115 changes: 114 additions & 1 deletion editor/src/messages/portfolio/portfolio_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::messages::dialog::simple_dialogs;
use crate::messages::frontend::utility_types::FrontendDocumentDetails;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::DocumentMessageData;
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type;
use crate::messages::portfolio::document::utility_types::clipboards::{Clipboard, CopyBufferEntry, INTERNAL_CLIPBOARD_COUNT};
use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes;
Expand All @@ -18,9 +19,10 @@ use crate::messages::prelude::*;
use crate::messages::tool::utility_types::{HintData, HintGroup, ToolType};
use crate::node_graph_executor::{ExportConfig, NodeGraphExecutor};
use bezier_rs::Subpath;
use glam::IVec2;
use glam::{DAffine2, DVec2, IVec2};
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{DocumentNodeImplementation, NodeId, NodeInput};
use graphene_core::renderer::Quad;
use graphene_core::text::{Font, TypesettingConfig};
use graphene_std::vector::style::{Fill, FillType, Gradient};
use graphene_std::vector::{VectorData, VectorDataTable};
Expand Down Expand Up @@ -1003,6 +1005,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
if let Some(document) = self.active_document() {
if let Ok(data) = serde_json::from_str::<Vec<CopyBufferEntry>>(&data) {
let parent = document.new_layer_parent(false);
let mut layers = Vec::new();

let mut added_nodes = false;

Expand All @@ -1012,16 +1015,126 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
responses.add(DocumentMessage::AddTransaction);
added_nodes = true;
}

document.load_layer_resources(responses);
let new_ids: HashMap<_, _> = entry.nodes.iter().map(|(id, _)| (*id, NodeId::new())).collect();
let layer = LayerNodeIdentifier::new_unchecked(new_ids[&NodeId(0)]);
responses.add(NodeGraphMessage::AddNodes { nodes: entry.nodes, new_ids });
responses.add(NodeGraphMessage::MoveLayerToStack { layer, parent, insert_index: 0 });
layers.push(layer);
}

responses.add(NodeGraphMessage::RunDocumentGraph);
responses.add(Message::StartBuffer);
responses.add(PortfolioMessage::CenterPastedLayers { layers });
}
}
}
PortfolioMessage::CenterPastedLayers { layers } => {
if let Some(document) = self.active_document_mut() {
let viewport_bounds_quad_pixels = Quad::from_box([DVec2::ZERO, ipp.viewport_bounds.size()]);
let viewport_center_pixels = viewport_bounds_quad_pixels.center(); // In viewport pixel coordinates

let doc_to_viewport_transform = document.metadata().document_to_viewport;
let viewport_to_doc_transform = doc_to_viewport_transform.inverse();

let viewport_quad_doc_space = viewport_to_doc_transform * viewport_bounds_quad_pixels;

let mut top_level_items_to_center: Vec<LayerNodeIdentifier> = Vec::new();
let mut artboards_in_selection: Vec<LayerNodeIdentifier> = Vec::new();

for &layer_id in &layers {
if document.network_interface.is_artboard(&layer_id.to_node(), &document.node_graph_handler.network) {
artboards_in_selection.push(layer_id);
}
}

for &layer_id in &layers {
let is_child_of_selected_artboard = artboards_in_selection.iter().any(|&artboard_id| {
if layer_id == artboard_id {
return false;
}
layer_id.ancestors(document.metadata()).any(|ancestor| ancestor == artboard_id)
});

if !is_child_of_selected_artboard {
top_level_items_to_center.push(layer_id);
}
}

if top_level_items_to_center.is_empty() {
return;
}

let mut combined_min_doc = DVec2::MAX;
let mut combined_max_doc = DVec2::MIN;
let mut has_any_bounds = false;

for &item_id in &top_level_items_to_center {
if let Some(bounds_doc) = document.metadata().bounding_box_document(item_id) {
combined_min_doc = combined_min_doc.min(bounds_doc[0]);
combined_max_doc = combined_max_doc.max(bounds_doc[1]);
has_any_bounds = true;
}
}

if !has_any_bounds {
return;
}

let combined_bounds_doc_quad = Quad::from_box([combined_min_doc, combined_max_doc]);

if combined_bounds_doc_quad.intersects(viewport_quad_doc_space) {
return;
}

let combined_center_doc = combined_bounds_doc_quad.center();
let combined_center_viewport_pixels = doc_to_viewport_transform.transform_point2(combined_center_doc);
let translation_viewport_pixels_rounded = (viewport_center_pixels - combined_center_viewport_pixels).round();

let final_translation_offset_doc = viewport_to_doc_transform.transform_vector2(translation_viewport_pixels_rounded);

if final_translation_offset_doc.abs_diff_eq(glam::DVec2::ZERO, 1e-9) {
return;
}

responses.add(DocumentMessage::AddTransaction);

for &item_id in &top_level_items_to_center {
if document.network_interface.is_artboard(&item_id.to_node(), &document.node_graph_handler.network) {
if let Some(bounds_doc) = document.metadata().bounding_box_document(item_id) {
let current_artboard_origin_doc = bounds_doc[0];
let dimensions_doc = bounds_doc[1] - bounds_doc[0];
let new_artboard_origin_doc = current_artboard_origin_doc + final_translation_offset_doc;

responses.add(GraphOperationMessage::ResizeArtboard {
layer: item_id,
location: new_artboard_origin_doc.round().as_ivec2(),
dimensions: dimensions_doc.round().as_ivec2(),
});
}
} else {
let current_abs_doc_transform = document.metadata().transform_to_document(item_id);

let new_abs_doc_transform = DAffine2 {
matrix2: current_abs_doc_transform.matrix2,
translation: current_abs_doc_transform.translation + final_translation_offset_doc,
};

let transform = doc_to_viewport_transform * new_abs_doc_transform;

responses.add(GraphOperationMessage::TransformSet {
layer: item_id,
transform,
transform_in: TransformIn::Viewport,
skip_rerender: false,
});
}
}

responses.add(NodeGraphMessage::RunDocumentGraph);
}
}
PortfolioMessage::PasteImage {
name,
image,
Expand Down
Loading