Skip to content

Commit

Permalink
Add drag-and-drop and copy-paste file importing/opening throughout th…
Browse files Browse the repository at this point in the history
…e UI (GraphiteEditor#2012)

* Add file importing by dragging and dropping throughout the UI

* Disable comment-profiling-changes.yaml

* Fix CI
  • Loading branch information
Keavon authored Sep 28, 2024
1 parent 20470b5 commit 904cf09
Show file tree
Hide file tree
Showing 35 changed files with 573 additions and 254 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/comment-profiling-changes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ env:

jobs:
profile:
# TODO(TrueDoctor): Fix and reenable this action
if: false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
Expand Down
4 changes: 4 additions & 0 deletions editor/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ pub const SLOWING_DIVISOR: f64 = 10.;
pub const NUDGE_AMOUNT: f64 = 1.;
pub const BIG_NUDGE_AMOUNT: f64 = 10.;

// Tools
pub const DEFAULT_STROKE_WIDTH: f64 = 2.;

// Select tool
pub const SELECTION_TOLERANCE: f64 = 5.;
pub const SELECTION_DRAG_ANGLE: f64 = 90.;
Expand All @@ -65,6 +68,7 @@ pub const LINE_ROTATE_SNAP_ANGLE: f64 = 15.;

// Brush tool
pub const BRUSH_SIZE_CHANGE_KEYBOARD: f64 = 5.;
pub const DEFAULT_BRUSH_SIZE: f64 = 20.;

// Scrollbars
pub const SCROLLBAR_SPACING: f64 = 0.1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ impl MessageHandler<NewDocumentDialogMessage, ()> for NewDocumentDialogMessageHa

responses.add(NodeGraphMessage::RunDocumentGraph);
responses.add(NodeGraphMessage::UpdateNewNodeGraph);

// TODO: Figure out how to get StartBuffer to work here so we can delete this and use `DocumentMessage::ZoomCanvasToFitAll` instead
responses.add(Message::StartBuffer);
responses.add(FrontendMessage::TriggerDelayedZoomCanvasToFitAll);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,22 @@ impl DialogLayoutHolder for CloseDocumentDialog {

impl LayoutHolder for CloseDocumentDialog {
fn layout(&self) -> Layout {
let max_length = 60;
let max_one_line_length = 40;

let mut name = self.document_name.clone();

name.truncate(max_length);
let ellipsis = if self.document_name.len() > max_length { "…" } else { "" };

let break_lines = if self.document_name.len() > max_one_line_length { '\n' } else { ' ' };

Layout::WidgetLayout(WidgetLayout::new(vec![
LayoutGroup::Row {
widgets: vec![TextLabel::new("Save document before closing it?").bold(true).widget_holder()],
},
LayoutGroup::Row {
widgets: vec![TextLabel::new(format!("\"{}\" has unsaved changes", self.document_name)).multiline(true).widget_holder()],
widgets: vec![TextLabel::new(format!("\"{name}{ellipsis}\"{break_lines}has unsaved changes")).multiline(true).widget_holder()],
},
]))
}
Expand Down
14 changes: 7 additions & 7 deletions editor/src/messages/portfolio/document/document_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,6 @@ pub enum DocumentMessage {
imaginate_node: Vec<NodeId>,
then_generate: bool,
},
ImportSvg {
id: NodeId,
svg: String,
transform: DAffine2,
parent: LayerNodeIdentifier,
insert_index: usize,
},
MoveSelectedLayersTo {
parent: LayerNodeIdentifier,
insert_index: usize,
Expand All @@ -98,12 +91,16 @@ pub enum DocumentMessage {
resize_opposite_corner: Key,
},
PasteImage {
name: Option<String>,
image: Image<Color>,
mouse: Option<(f64, f64)>,
parent_and_insert_index: Option<(LayerNodeIdentifier, usize)>,
},
PasteSvg {
name: Option<String>,
svg: String,
mouse: Option<(f64, f64)>,
parent_and_insert_index: Option<(LayerNodeIdentifier, usize)>,
},
Redo,
RenameDocument {
Expand Down Expand Up @@ -176,6 +173,9 @@ pub enum DocumentMessage {
PTZUpdate,
SelectionStepBack,
SelectionStepForward,
WrapContentInArtboard {
place_artboard_at_origin: bool,
},
ZoomCanvasTo100Percent,
ZoomCanvasTo200Percent,
ZoomCanvasToFitAll,
Expand Down
131 changes: 101 additions & 30 deletions editor/src/messages/portfolio/document/document_message_handler.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use super::node_graph::document_node_definitions;
use super::node_graph::utility_types::Transform;
use super::overlays::utility_types::Pivot;
use super::utility_types::clipboards::Clipboard;
use super::utility_types::error::EditorError;
use super::utility_types::misc::{SnappingOptions, SnappingState, GET_SNAP_BOX_FUNCTIONS, GET_SNAP_GEOMETRY_FUNCTIONS};
use super::utility_types::network_interface::{NodeNetworkInterface, TransactionStatus};
use super::utility_types::network_interface::{self, NodeNetworkInterface, TransactionStatus};
use super::utility_types::nodes::{CollapsedLayers, SelectedNodes};
use crate::application::{generate_uuid, GRAPHITE_GIT_COMMIT_HASH};
use crate::consts::{ASYMPTOTIC_EFFECT, COLOR_OVERLAY_GRAY, DEFAULT_DOCUMENT_NAME, FILE_SAVE_SUFFIX, SCALE_EFFECT, SCROLLBAR_SPACING, VIEWPORT_ROTATE_SNAP_INTERVAL};
Expand All @@ -18,7 +19,7 @@ use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate,
use crate::messages::portfolio::document::utility_types::nodes::RawBuffer;
use crate::messages::portfolio::utility_types::PersistentData;
use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::graph_modification_utils::{get_blend_mode, get_opacity};
use crate::messages::tool::common_functionality::graph_modification_utils::{self, get_blend_mode, get_opacity};
use crate::messages::tool::tool_messages::select_tool::SelectToolPointerKeys;
use crate::messages::tool::tool_messages::tool_prelude::Key;
use crate::messages::tool::utility_types::ToolType;
Expand All @@ -29,7 +30,7 @@ use graph_craft::document::{NodeId, NodeNetwork, OldNodeNetwork};
use graphene_core::raster::{BlendMode, ImageFrame};
use graphene_core::vector::style::ViewMode;

use glam::{DAffine2, DVec2};
use glam::{DAffine2, DVec2, IVec2};

pub struct DocumentMessageData<'a> {
pub document_id: DocumentId,
Expand Down Expand Up @@ -380,7 +381,11 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
let Some(bounds) = self.metadata().bounding_box_document(layer) else { continue };

let name = self.network_interface.frontend_display_name(&layer.to_node(), &[]);
let transform = self.metadata().document_to_viewport * DAffine2::from_translation(bounds[0].min(bounds[1]) - DVec2::Y * 4.);

let (_, angle, translation) = self.metadata().document_to_viewport.to_scale_angle_translation();
let translation = translation + bounds[0].min(bounds[1]) - DVec2::Y * 4.;
let transform = DAffine2::from_angle_translation(angle, translation);

overlay_context.text_with_transform(&name, COLOR_OVERLAY_GRAY, None, transform, Pivot::BottomLeft);
}
}
Expand Down Expand Up @@ -498,7 +503,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
let insert_index = DocumentMessageHandler::get_calculated_insert_index(self.metadata(), self.network_interface.selected_nodes(&[]).unwrap(), parent);

let node_id = NodeId(generate_uuid());
let new_group_node = super::node_graph::document_node_definitions::resolve_document_node_type("Merge")
let new_group_node = document_node_definitions::resolve_document_node_type("Merge")
.expect("Failed to create merge node")
.default_node_template();
responses.add(NodeGraphMessage::InsertNode {
Expand Down Expand Up @@ -546,23 +551,6 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
responses.add(DocumentMessage::ImaginateGenerate { imaginate_node });
}
}
DocumentMessage::ImportSvg {
id,
svg,
transform,
parent,
insert_index,
} => {
responses.add(DocumentMessage::StartTransaction);
responses.add(GraphOperationMessage::NewSvg {
id,
svg,
transform,
parent,
insert_index,
});
responses.add(DocumentMessage::EndTransaction);
}
DocumentMessage::MoveSelectedLayersTo { parent, insert_index } => {
if !self.selection_network_path.is_empty() {
log::error!("Moving selected layers is only supported for the Document Network");
Expand Down Expand Up @@ -608,7 +596,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag

let layers_to_move = self.network_interface.shallowest_unique_layers_sorted(&self.selection_network_path);
// Offset the index for layers to move that are below another layer to move. For example when moving 1 and 2 between 3 and 4, 2 should be inserted at the same index as 1 since 1 is moved first.
let layers_to_move_with_insert_offset: Vec<(LayerNodeIdentifier, usize)> = layers_to_move
let layers_to_move_with_insert_offset = layers_to_move
.iter()
.map(|layer| {
if layer.parent(self.metadata()) != Some(parent) {
Expand Down Expand Up @@ -727,7 +715,12 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
}
}
}
DocumentMessage::PasteImage { image, mouse } => {
DocumentMessage::PasteImage {
name,
image,
mouse,
parent_and_insert_index,
} => {
// All the image's pixels have been converted to 0..=1, linear, and premultiplied by `Color::from_rgba8_srgb`

let image_size = DVec2::new(image.width as f64, image.height as f64);
Expand All @@ -744,12 +737,27 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag

let transform = center_in_viewport_layerspace * fit_image_size;

let layer_node_id = NodeId(generate_uuid());
let layer_id = LayerNodeIdentifier::new_unchecked(layer_node_id);

responses.add(DocumentMessage::AddTransaction);

let image_frame = ImageFrame { image, ..Default::default() };
let layer = graph_modification_utils::new_image_layer(image_frame, layer_node_id, self.new_layer_parent(true), responses);

use crate::messages::tool::common_functionality::graph_modification_utils;
let layer = graph_modification_utils::new_image_layer(image_frame, NodeId(generate_uuid()), self.new_layer_parent(true), responses);
if let Some(name) = name {
responses.add(NodeGraphMessage::SetDisplayName {
node_id: layer.to_node(),
alias: name,
});
}
if let Some((parent, insert_index)) = parent_and_insert_index {
responses.add(NodeGraphMessage::MoveLayerToStack {
layer: layer_id,
parent,
insert_index,
});
}

// `layer` cannot be `ROOT_PARENT` since it is the newly created layer
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![layer.to_node()] });
Expand All @@ -764,12 +772,37 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
// Force chosen tool to be Select Tool after importing image.
responses.add(ToolMessage::ActivateTool { tool_type: ToolType::Select });
}
DocumentMessage::PasteSvg { svg, mouse } => {
use crate::messages::tool::common_functionality::graph_modification_utils;
let viewport_location = mouse.map_or(ipp.viewport_bounds.center() + ipp.viewport_bounds.top_left, |pos| pos.into());
DocumentMessage::PasteSvg {
name,
svg,
mouse,
parent_and_insert_index,
} => {
let document_to_viewport = self.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.center(), &self.document_ptz);
let viewport_location = mouse.map_or(ipp.viewport_bounds.center() + ipp.viewport_bounds.top_left, |pos| pos.into());
let center_in_viewport = DAffine2::from_translation(document_to_viewport.inverse().transform_point2(viewport_location - ipp.viewport_bounds.top_left));
let layer = graph_modification_utils::new_svg_layer(svg, center_in_viewport, NodeId(generate_uuid()), self.new_layer_parent(true), responses);

let layer_node_id = NodeId(generate_uuid());
let layer_id = LayerNodeIdentifier::new_unchecked(layer_node_id);

responses.add(DocumentMessage::AddTransaction);

let layer = graph_modification_utils::new_svg_layer(svg, center_in_viewport, layer_node_id, self.new_layer_parent(true), responses);

if let Some(name) = name {
responses.add(NodeGraphMessage::SetDisplayName {
node_id: layer.to_node(),
alias: name,
});
}
if let Some((parent, insert_index)) = parent_and_insert_index {
responses.add(NodeGraphMessage::MoveLayerToStack {
layer: layer_id,
parent,
insert_index,
});
}

responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![layer.to_node()] });
responses.add(ToolMessage::ActivateTool { tool_type: ToolType::Select });
}
Expand Down Expand Up @@ -1181,6 +1214,44 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
self.network_interface.selection_step_forward(&self.selection_network_path);
responses.add(BroadcastEvent::SelectionChanged);
}
DocumentMessage::WrapContentInArtboard { place_artboard_at_origin } => {
// Get bounding box of all layers
let bounds = self.network_interface.document_bounds_document_space(false);
let Some(bounds) = bounds else { return };
let bounds_rounded_dimensions = (bounds[1] - bounds[0]).round();

// Create an artboard and set its dimensions to the bounding box size and location
let node_id = NodeId(generate_uuid());
let node_layer_id = LayerNodeIdentifier::new_unchecked(node_id);
let new_artboard_node = document_node_definitions::resolve_document_node_type("Artboard")
.expect("Failed to create artboard node")
.default_node_template();
responses.add(NodeGraphMessage::InsertNode {
node_id,
node_template: new_artboard_node,
});
responses.add(NodeGraphMessage::ShiftNodePosition { node_id, x: 15, y: -3 });
responses.add(GraphOperationMessage::ResizeArtboard {
layer: LayerNodeIdentifier::new_unchecked(node_id),
location: if place_artboard_at_origin { IVec2::ZERO } else { bounds[0].round().as_ivec2() },
dimensions: bounds_rounded_dimensions.as_ivec2(),
});

// Connect the current output data to the artboard's input data, and the artboard's output to the document output
responses.add(NodeGraphMessage::InsertNodeBetween {
node_id,
input_connector: network_interface::InputConnector::Export(0),
insert_node_input_index: 1,
});

// Shift the content by half its width and height so it gets centered in the artboard
responses.add(GraphOperationMessage::TransformChange {
layer: node_layer_id,
transform: DAffine2::from_translation(bounds_rounded_dimensions / 2.),
transform_in: TransformIn::Local,
skip_rerender: true,
});
}
DocumentMessage::ZoomCanvasTo100Percent => {
responses.add_front(NavigationMessage::CanvasZoomSet { zoom_factor: 1. });
}
Expand Down
Loading

0 comments on commit 904cf09

Please sign in to comment.