Fix hiding and collapsing layers (#1481)
* Hide and collapse layers * Reorder imports * Fix Ctrl+H shortcut advertized action and hotkey tooltip; improve graph top right of options bar --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
6d9dd5fc27
commit
5ee79031ab
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::document_metadata::DocumentMetadata;
|
use crate::document_metadata::{is_artboard, DocumentMetadata, LayerNodeIdentifier};
|
||||||
use crate::intersection::Quad;
|
use crate::intersection::Quad;
|
||||||
use crate::layers::folder_layer::FolderLayer;
|
use crate::layers::folder_layer::FolderLayer;
|
||||||
use crate::layers::layer_info::{Layer, LayerData, LayerDataType, LayerDataTypeDiscriminant};
|
use crate::layers::layer_info::{Layer, LayerData, LayerDataType, LayerDataTypeDiscriminant};
|
||||||
|
|
@ -6,10 +6,13 @@ use crate::layers::layer_layer::{CachedOutputData, LayerLayer};
|
||||||
use crate::layers::shape_layer::ShapeLayer;
|
use crate::layers::shape_layer::ShapeLayer;
|
||||||
use crate::layers::style::RenderData;
|
use crate::layers::style::RenderData;
|
||||||
use crate::{DocumentError, DocumentResponse, Operation};
|
use crate::{DocumentError, DocumentResponse, Operation};
|
||||||
|
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeNetwork, NodeOutput};
|
||||||
|
use graphene_core::renderer::ClickTarget;
|
||||||
|
use graphene_core::transform::Footprint;
|
||||||
|
use graphene_core::{concrete, generic, NodeIdentifier};
|
||||||
|
use graphene_std::wasm_application_io::WasmEditorApi;
|
||||||
|
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use graphene_core::transform::Footprint;
|
|
||||||
use graphene_std::wasm_application_io::WasmEditorApi;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
use std::collections::hash_map::DefaultHasher;
|
use std::collections::hash_map::DefaultHasher;
|
||||||
|
|
@ -17,10 +20,6 @@ use std::collections::HashMap;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::vec;
|
use std::vec;
|
||||||
|
|
||||||
use graph_craft::document::{DocumentNode, NodeOutput};
|
|
||||||
use graph_craft::document::{DocumentNodeImplementation, NodeId};
|
|
||||||
use graphene_core::{concrete, generic, NodeIdentifier};
|
|
||||||
|
|
||||||
/// A number that identifies a layer.
|
/// A number that identifies a layer.
|
||||||
/// This does not technically need to be unique globally, only within a folder.
|
/// This does not technically need to be unique globally, only within a folder.
|
||||||
pub type LayerId = u64;
|
pub type LayerId = u64;
|
||||||
|
|
@ -34,7 +33,9 @@ pub struct Document {
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub state_identifier: DefaultHasher,
|
pub state_identifier: DefaultHasher,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub document_network: graph_craft::document::NodeNetwork,
|
pub document_network: NodeNetwork,
|
||||||
|
#[serde(default)]
|
||||||
|
pub collapsed_folders: Vec<LayerNodeIdentifier>,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub metadata: DocumentMetadata,
|
pub metadata: DocumentMetadata,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
|
@ -53,7 +54,7 @@ impl Default for Document {
|
||||||
root: Layer::new(LayerDataType::Folder(FolderLayer::default()), DAffine2::IDENTITY.to_cols_array()),
|
root: Layer::new(LayerDataType::Folder(FolderLayer::default()), DAffine2::IDENTITY.to_cols_array()),
|
||||||
state_identifier: DefaultHasher::new(),
|
state_identifier: DefaultHasher::new(),
|
||||||
document_network: {
|
document_network: {
|
||||||
use graph_craft::document::{value::TaggedValue, NodeInput, NodeNetwork};
|
use graph_craft::document::{value::TaggedValue, NodeInput};
|
||||||
let mut network = NodeNetwork::default();
|
let mut network = NodeNetwork::default();
|
||||||
let node = graph_craft::document::DocumentNode {
|
let node = graph_craft::document::DocumentNode {
|
||||||
name: "Output".into(),
|
name: "Output".into(),
|
||||||
|
|
@ -106,12 +107,61 @@ impl Default for Document {
|
||||||
network
|
network
|
||||||
},
|
},
|
||||||
metadata: Default::default(),
|
metadata: Default::default(),
|
||||||
|
collapsed_folders: Vec::new(),
|
||||||
commit_hash: String::new(),
|
commit_hash: String::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Document {
|
impl Document {
|
||||||
|
pub fn layer_visible(&self, layer: LayerNodeIdentifier) -> bool {
|
||||||
|
!layer.ancestors(&self.metadata).any(|layer| self.document_network.disabled.contains(&layer.to_node()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn selected_visible_layers(&self) -> impl Iterator<Item = LayerNodeIdentifier> + '_ {
|
||||||
|
self.metadata.selected_layers().filter(|&layer| self.layer_visible(layer))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_network_structure(&mut self) {
|
||||||
|
self.metadata.load_structure(&self.document_network);
|
||||||
|
self.collapsed_folders.retain(|&layer| self.metadata.layer_exists(layer));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Runs an intersection test with all layers and a viewport space quad
|
||||||
|
pub fn intersect_quad<'a>(&'a self, viewport_quad: graphene_core::renderer::Quad, network: &'a NodeNetwork) -> impl Iterator<Item = LayerNodeIdentifier> + 'a {
|
||||||
|
let document_quad = self.metadata.document_to_viewport.inverse() * viewport_quad;
|
||||||
|
self.metadata
|
||||||
|
.root()
|
||||||
|
.decendants(&self.metadata)
|
||||||
|
.filter(|&layer| self.layer_visible(layer))
|
||||||
|
.filter(|&layer| !is_artboard(layer, network))
|
||||||
|
.filter_map(|layer| self.metadata.click_target(layer).map(|targets| (layer, targets)))
|
||||||
|
.filter(move |(layer, target)| target.iter().any(move |target| target.intersect_rectangle(document_quad, self.metadata.transform_to_document(*layer))))
|
||||||
|
.map(|(layer, _)| layer)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find all of the layers that were clicked on from a viewport space location
|
||||||
|
pub fn click_xray(&self, viewport_location: DVec2) -> impl Iterator<Item = LayerNodeIdentifier> + '_ {
|
||||||
|
let point = self.metadata.document_to_viewport.inverse().transform_point2(viewport_location);
|
||||||
|
self.metadata
|
||||||
|
.root()
|
||||||
|
.decendants(&self.metadata)
|
||||||
|
.filter(|&layer| self.layer_visible(layer))
|
||||||
|
.filter_map(|layer| self.metadata.click_target(layer).map(|targets| (layer, targets)))
|
||||||
|
.filter(move |(layer, target)| target.iter().any(|target: &ClickTarget| target.intersect_point(point, self.metadata.transform_to_document(*layer))))
|
||||||
|
.map(|(layer, _)| layer)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find the layer that has been clicked on from a viewport space location
|
||||||
|
pub fn click(&self, viewport_location: DVec2, network: &NodeNetwork) -> Option<LayerNodeIdentifier> {
|
||||||
|
self.click_xray(viewport_location).find(|&layer| !is_artboard(layer, network))
|
||||||
|
}
|
||||||
|
pub fn selected_visible_layers_bounding_box_viewport(&self) -> Option<[DVec2; 2]> {
|
||||||
|
self.selected_visible_layers()
|
||||||
|
.filter_map(|layer| self.metadata.bounding_box_viewport(layer))
|
||||||
|
.reduce(graphene_core::renderer::Quad::combine_bounds)
|
||||||
|
}
|
||||||
|
|
||||||
/// Wrapper around render, that returns the whole document as a Response.
|
/// Wrapper around render, that returns the whole document as a Response.
|
||||||
pub fn render_root(&mut self, render_data: &RenderData) -> String {
|
pub fn render_root(&mut self, render_data: &RenderData) -> String {
|
||||||
// Render and append to the defs section
|
// Render and append to the defs section
|
||||||
|
|
|
||||||
|
|
@ -55,10 +55,6 @@ impl DocumentMetadata {
|
||||||
self.selected_layers().any(|selected| selected == layer)
|
self.selected_layers().any(|selected| selected == layer)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn selected_visible_layers(&self) -> impl Iterator<Item = LayerNodeIdentifier> + '_ {
|
|
||||||
self.selected_layers()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn selected_nodes(&self) -> core::slice::Iter<'_, NodeId> {
|
pub fn selected_nodes(&self) -> core::slice::Iter<'_, NodeId> {
|
||||||
self.selected_nodes.iter()
|
self.selected_nodes.iter()
|
||||||
}
|
}
|
||||||
|
|
@ -71,6 +67,14 @@ impl DocumentMetadata {
|
||||||
!self.selected_nodes.is_empty()
|
!self.selected_nodes.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn layer_exists(&self, layer: LayerNodeIdentifier) -> bool {
|
||||||
|
self.structure.contains_key(&layer)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn click_target(&self, layer: LayerNodeIdentifier) -> Option<&Vec<ClickTarget>> {
|
||||||
|
self.click_targets.get(&layer)
|
||||||
|
}
|
||||||
|
|
||||||
/// Access the [`NodeRelations`] of a layer.
|
/// Access the [`NodeRelations`] of a layer.
|
||||||
fn get_relations(&self, node_identifier: LayerNodeIdentifier) -> Option<&NodeRelations> {
|
fn get_relations(&self, node_identifier: LayerNodeIdentifier) -> Option<&NodeRelations> {
|
||||||
self.structure.get(&node_identifier)
|
self.structure.get(&node_identifier)
|
||||||
|
|
@ -97,13 +101,13 @@ impl DocumentMetadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ancestor that is shared by all layers and that is deepest (more nested). Default may be the root.
|
/// Ancestor that is shared by all layers and that is deepest (more nested). Default may be the root.
|
||||||
pub fn deepest_common_ancestor(&self, layers: impl Iterator<Item = LayerNodeIdentifier>) -> Option<LayerNodeIdentifier> {
|
pub fn deepest_common_ancestor(&self, layers: impl Iterator<Item = LayerNodeIdentifier>, include_self: bool) -> Option<LayerNodeIdentifier> {
|
||||||
layers
|
layers
|
||||||
.map(|layer| {
|
.map(|layer| {
|
||||||
let mut layer_path = layer.ancestors(self).collect::<Vec<_>>();
|
let mut layer_path = layer.ancestors(self).collect::<Vec<_>>();
|
||||||
layer_path.reverse();
|
layer_path.reverse();
|
||||||
|
|
||||||
if !self.folders.contains(&layer) {
|
if include_self || !self.folders.contains(&layer) {
|
||||||
layer_path.pop();
|
layer_path.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -200,6 +204,11 @@ impl DocumentMetadata {
|
||||||
current = sibling_below(graph, current_node);
|
current = sibling_below(graph, current_node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.selected_nodes.retain(|node| graph.nodes.contains_key(node));
|
||||||
|
self.upstream_transforms.retain(|node, _| graph.nodes.contains_key(node));
|
||||||
|
self.transforms.retain(|layer, _| self.structure.contains_key(layer));
|
||||||
|
self.click_targets.retain(|layer, _| self.structure.contains_key(layer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -235,11 +244,11 @@ impl DocumentMetadata {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_artboard(layer: LayerNodeIdentifier, network: &NodeNetwork) -> bool {
|
pub fn is_artboard(layer: LayerNodeIdentifier, network: &NodeNetwork) -> bool {
|
||||||
network.primary_flow_from_node(Some(layer.to_node())).any(|(node, _)| node.name == "Artboard")
|
network.primary_flow_from_node(Some(layer.to_node())).any(|(node, _)| node.name == "Artboard")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_folder(layer: LayerNodeIdentifier, network: &NodeNetwork) -> bool {
|
pub fn is_folder(layer: LayerNodeIdentifier, network: &NodeNetwork) -> bool {
|
||||||
network.nodes.get(&layer.to_node()).and_then(|node| node.inputs.first()).is_some_and(|input| input.as_node().is_none())
|
network.nodes.get(&layer.to_node()).and_then(|node| node.inputs.first()).is_some_and(|input| input.as_node().is_none())
|
||||||
|| network
|
|| network
|
||||||
.primary_flow_from_node(Some(layer.to_node()))
|
.primary_flow_from_node(Some(layer.to_node()))
|
||||||
|
|
@ -254,32 +263,6 @@ impl DocumentMetadata {
|
||||||
self.click_targets = new_click_targets;
|
self.click_targets = new_click_targets;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs an intersection test with all layers and a viewport space quad
|
|
||||||
pub fn intersect_quad<'a>(&'a self, viewport_quad: Quad, network: &'a NodeNetwork) -> impl Iterator<Item = LayerNodeIdentifier> + 'a {
|
|
||||||
let document_quad = self.document_to_viewport.inverse() * viewport_quad;
|
|
||||||
self.root()
|
|
||||||
.decendants(self)
|
|
||||||
.filter(|&layer| !is_artboard(layer, network))
|
|
||||||
.filter_map(|layer| self.click_targets.get(&layer).map(|targets| (layer, targets)))
|
|
||||||
.filter(move |(layer, target)| target.iter().any(move |target| target.intersect_rectangle(document_quad, self.transform_to_document(*layer))))
|
|
||||||
.map(|(layer, _)| layer)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find all of the layers that were clicked on from a viewport space location
|
|
||||||
pub fn click_xray(&self, viewport_location: DVec2) -> impl Iterator<Item = LayerNodeIdentifier> + '_ {
|
|
||||||
let point = self.document_to_viewport.inverse().transform_point2(viewport_location);
|
|
||||||
self.root()
|
|
||||||
.decendants(self)
|
|
||||||
.filter_map(|layer| self.click_targets.get(&layer).map(|targets| (layer, targets)))
|
|
||||||
.filter(move |(layer, target)| target.iter().any(|target: &ClickTarget| target.intersect_point(point, self.transform_to_document(*layer))))
|
|
||||||
.map(|(layer, _)| layer)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find the layer that has been clicked on from a viewport space location
|
|
||||||
pub fn click(&self, viewport_location: DVec2, network: &NodeNetwork) -> Option<LayerNodeIdentifier> {
|
|
||||||
self.click_xray(viewport_location).find(|&layer| !is_artboard(layer, network))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the bounding box of the click target of the specified layer in the specified transform space
|
/// Get the bounding box of the click target of the specified layer in the specified transform space
|
||||||
pub fn bounding_box_with_transform(&self, layer: LayerNodeIdentifier, transform: DAffine2) -> Option<[DVec2; 2]> {
|
pub fn bounding_box_with_transform(&self, layer: LayerNodeIdentifier, transform: DAffine2) -> Option<[DVec2; 2]> {
|
||||||
self.click_targets
|
self.click_targets
|
||||||
|
|
@ -316,10 +299,6 @@ impl DocumentMetadata {
|
||||||
self.bounding_box_with_transform(layer, self.transform_to_viewport(layer))
|
self.bounding_box_with_transform(layer, self.transform_to_viewport(layer))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn selected_visible_layers_bounding_box_viewport(&self) -> Option<[DVec2; 2]> {
|
|
||||||
self.selected_layers().filter_map(|layer| self.bounding_box_viewport(layer)).reduce(Quad::combine_bounds)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calculates the document bounds used for scrolling and centring (the layer bounds or the artboard (if applicable))
|
/// Calculates the document bounds used for scrolling and centring (the layer bounds or the artboard (if applicable))
|
||||||
pub fn document_bounds(&self) -> Option<[DVec2; 2]> {
|
pub fn document_bounds(&self) -> Option<[DVec2; 2]> {
|
||||||
self.all_layers().filter_map(|layer| self.bounding_box_viewport(layer)).reduce(Quad::combine_bounds)
|
self.all_layers().filter_map(|layer| self.bounding_box_viewport(layer)).reduce(Quad::combine_bounds)
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ pub fn default_mapping() -> Mapping {
|
||||||
entry!(KeyDown(KeyX); modifiers=[Accel], action_dispatch=NodeGraphMessage::Cut),
|
entry!(KeyDown(KeyX); modifiers=[Accel], action_dispatch=NodeGraphMessage::Cut),
|
||||||
entry!(KeyDown(KeyC); modifiers=[Accel], action_dispatch=NodeGraphMessage::Copy),
|
entry!(KeyDown(KeyC); modifiers=[Accel], action_dispatch=NodeGraphMessage::Copy),
|
||||||
entry!(KeyDown(KeyD); modifiers=[Accel], action_dispatch=NodeGraphMessage::DuplicateSelectedNodes),
|
entry!(KeyDown(KeyD); modifiers=[Accel], action_dispatch=NodeGraphMessage::DuplicateSelectedNodes),
|
||||||
entry!(KeyDown(KeyH); modifiers=[Accel], action_dispatch=NodeGraphMessage::ToggleHidden),
|
entry!(KeyDown(KeyH); modifiers=[Accel], action_dispatch=NodeGraphMessage::ToggleSelectedHidden),
|
||||||
//
|
//
|
||||||
// TransformLayerMessage
|
// TransformLayerMessage
|
||||||
entry!(KeyDown(Enter); action_dispatch=TransformLayerMessage::ApplyTransformOperation),
|
entry!(KeyDown(Enter); action_dispatch=TransformLayerMessage::ApplyTransformOperation),
|
||||||
|
|
|
||||||
|
|
@ -202,6 +202,7 @@ pub enum Key {
|
||||||
|
|
||||||
// Other keys that aren't part of the W3C spec
|
// Other keys that aren't part of the W3C spec
|
||||||
Command,
|
Command,
|
||||||
|
/// "Ctrl" on Windows/Linux, "Cmd" on Mac
|
||||||
Accel,
|
Accel,
|
||||||
Lmb,
|
Lmb,
|
||||||
Rmb,
|
Rmb,
|
||||||
|
|
|
||||||
|
|
@ -185,10 +185,7 @@ pub enum DocumentMessage {
|
||||||
},
|
},
|
||||||
StartTransaction,
|
StartTransaction,
|
||||||
ToggleLayerExpansion {
|
ToggleLayerExpansion {
|
||||||
layer_path: Vec<LayerId>,
|
layer: NodeId,
|
||||||
},
|
|
||||||
ToggleLayerVisibility {
|
|
||||||
layer_path: Vec<LayerId>,
|
|
||||||
},
|
},
|
||||||
Undo,
|
Undo,
|
||||||
UndoFinished,
|
UndoFinished,
|
||||||
|
|
|
||||||
|
|
@ -162,7 +162,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
|
||||||
self.navigation_handler.process_message(
|
self.navigation_handler.process_message(
|
||||||
message,
|
message,
|
||||||
responses,
|
responses,
|
||||||
(&self.document_legacy, document_bounds, ipp, self.metadata().selected_visible_layers_bounding_box_viewport()),
|
(&self.document_legacy, document_bounds, ipp, self.document_legacy.selected_visible_layers_bounding_box_viewport()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
#[remain::unsorted]
|
#[remain::unsorted]
|
||||||
|
|
@ -223,7 +223,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
|
||||||
AlignAxis::X => DVec2::X,
|
AlignAxis::X => DVec2::X,
|
||||||
AlignAxis::Y => DVec2::Y,
|
AlignAxis::Y => DVec2::Y,
|
||||||
};
|
};
|
||||||
let Some(combined_box) = self.metadata().selected_visible_layers_bounding_box_viewport() else {
|
let Some(combined_box) = self.document_legacy.selected_visible_layers_bounding_box_viewport() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -276,13 +276,13 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
|
||||||
CreateEmptyFolder { parent } => {
|
CreateEmptyFolder { parent } => {
|
||||||
let id = generate_uuid();
|
let id = generate_uuid();
|
||||||
|
|
||||||
responses.add(DocumentMessage::DeselectAllLayers);
|
|
||||||
responses.add(GraphOperationMessage::NewCustomLayer {
|
responses.add(GraphOperationMessage::NewCustomLayer {
|
||||||
id,
|
id,
|
||||||
nodes: HashMap::new(),
|
nodes: HashMap::new(),
|
||||||
parent,
|
parent,
|
||||||
insert_index: -1,
|
insert_index: -1,
|
||||||
});
|
});
|
||||||
|
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![id] });
|
||||||
}
|
}
|
||||||
DebugPrintDocument => {
|
DebugPrintDocument => {
|
||||||
info!("{:#?}\n{:#?}", self.document_legacy, self.layer_metadata);
|
info!("{:#?}\n{:#?}", self.document_legacy, self.layer_metadata);
|
||||||
|
|
@ -356,7 +356,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
|
||||||
// Calculate the bounding box of the region to be exported
|
// Calculate the bounding box of the region to be exported
|
||||||
let bounds = match bounds {
|
let bounds = match bounds {
|
||||||
ExportBounds::AllArtwork => self.all_layer_bounds(&render_data),
|
ExportBounds::AllArtwork => self.all_layer_bounds(&render_data),
|
||||||
ExportBounds::Selection => self.metadata().selected_visible_layers_bounding_box_viewport(),
|
ExportBounds::Selection => self.document_legacy.selected_visible_layers_bounding_box_viewport(),
|
||||||
ExportBounds::Artboard(id) => self.metadata().bounding_box_document(id),
|
ExportBounds::Artboard(id) => self.metadata().bounding_box_document(id),
|
||||||
}
|
}
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
@ -387,7 +387,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
|
||||||
FlipAxis::X => DVec2::new(-1., 1.),
|
FlipAxis::X => DVec2::new(-1., 1.),
|
||||||
FlipAxis::Y => DVec2::new(1., -1.),
|
FlipAxis::Y => DVec2::new(1., -1.),
|
||||||
};
|
};
|
||||||
if let Some([min, max]) = self.metadata().selected_visible_layers_bounding_box_viewport() {
|
if let Some([min, max]) = self.document_legacy.selected_visible_layers_bounding_box_viewport() {
|
||||||
let center = (max + min) / 2.;
|
let center = (max + min) / 2.;
|
||||||
let bbox_trans = DAffine2::from_translation(-center);
|
let bbox_trans = DAffine2::from_translation(-center);
|
||||||
for layer in self.metadata().selected_layers() {
|
for layer in self.metadata().selected_layers() {
|
||||||
|
|
@ -428,7 +428,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
|
||||||
}
|
}
|
||||||
GroupSelectedLayers => {
|
GroupSelectedLayers => {
|
||||||
// TODO: Add code that changes the insert index of the new folder based on the selected layer
|
// TODO: Add code that changes the insert index of the new folder based on the selected layer
|
||||||
let parent = self.metadata().deepest_common_ancestor(self.metadata().selected_layers()).unwrap_or(LayerNodeIdentifier::ROOT);
|
let parent = self.metadata().deepest_common_ancestor(self.metadata().selected_layers(), true).unwrap_or(LayerNodeIdentifier::ROOT);
|
||||||
|
|
||||||
let folder_id = generate_uuid();
|
let folder_id = generate_uuid();
|
||||||
|
|
||||||
|
|
@ -816,17 +816,14 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
|
||||||
responses.add_front(DocumentMessage::DirtyRenderDocument);
|
responses.add_front(DocumentMessage::DirtyRenderDocument);
|
||||||
}
|
}
|
||||||
StartTransaction => self.backup(responses),
|
StartTransaction => self.backup(responses),
|
||||||
ToggleLayerExpansion { layer_path } => {
|
ToggleLayerExpansion { layer } => {
|
||||||
self.layer_metadata_mut(&layer_path).expanded ^= true;
|
let layer = LayerNodeIdentifier::new(layer, self.network());
|
||||||
responses.add(DocumentStructureChanged);
|
if self.document_legacy.collapsed_folders.contains(&layer) {
|
||||||
responses.add(LayerChanged { affected_layer_path: layer_path })
|
self.document_legacy.collapsed_folders.retain(|&collapsed_layer| collapsed_layer != layer);
|
||||||
}
|
} else {
|
||||||
ToggleLayerVisibility { layer_path } => {
|
self.document_legacy.collapsed_folders.push(layer);
|
||||||
if let Ok(layer) = self.document_legacy.layer(&layer_path) {
|
|
||||||
let visible = layer.visible;
|
|
||||||
responses.add(DocumentOperation::SetLayerVisibility { path: layer_path, visible: !visible });
|
|
||||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
|
||||||
}
|
}
|
||||||
|
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||||
}
|
}
|
||||||
Undo => {
|
Undo => {
|
||||||
self.undo_in_progress = true;
|
self.undo_in_progress = true;
|
||||||
|
|
@ -1074,13 +1071,6 @@ impl DocumentMessageHandler {
|
||||||
self.layer_metadata.get(path).map(|layer| layer.selected).unwrap_or(false)
|
self.layer_metadata.get(path).map(|layer| layer.selected).unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn selected_visible_layers(&self) -> impl Iterator<Item = &[LayerId]> {
|
|
||||||
self.selected_layers().filter(|path| match self.document_legacy.layer(path) {
|
|
||||||
Ok(layer) => layer.visible,
|
|
||||||
Err(_) => false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn visible_layers(&self) -> impl Iterator<Item = &[LayerId]> {
|
pub fn visible_layers(&self) -> impl Iterator<Item = &[LayerId]> {
|
||||||
self.all_layers().filter(|path| match self.document_legacy.layer(path) {
|
self.all_layers().filter(|path| match self.document_legacy.layer(path) {
|
||||||
Ok(layer) => layer.visible,
|
Ok(layer) => layer.visible,
|
||||||
|
|
@ -1100,7 +1090,7 @@ impl DocumentMessageHandler {
|
||||||
for layer_node in folder.children(self.metadata()) {
|
for layer_node in folder.children(self.metadata()) {
|
||||||
data.push(layer_node.to_node());
|
data.push(layer_node.to_node());
|
||||||
space += 1;
|
space += 1;
|
||||||
if layer_node.has_children(self.metadata()) {
|
if layer_node.has_children(self.metadata()) && !self.document_legacy.collapsed_folders.contains(&layer_node) {
|
||||||
path.push(layer_node.to_node());
|
path.push(layer_node.to_node());
|
||||||
|
|
||||||
// TODO: Skip if folder is not expanded.
|
// TODO: Skip if folder is not expanded.
|
||||||
|
|
@ -1414,7 +1404,7 @@ impl DocumentMessageHandler {
|
||||||
|
|
||||||
pub fn new_layer_parent(&self) -> LayerNodeIdentifier {
|
pub fn new_layer_parent(&self) -> LayerNodeIdentifier {
|
||||||
self.metadata()
|
self.metadata()
|
||||||
.deepest_common_ancestor(self.metadata().selected_layers())
|
.deepest_common_ancestor(self.metadata().selected_layers(), false)
|
||||||
.unwrap_or_else(|| self.metadata().active_artboard())
|
.unwrap_or_else(|| self.metadata().active_artboard())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -625,7 +625,7 @@ impl MessageHandler<GraphOperationMessage, (&mut Document, &mut NodeGraphMessage
|
||||||
if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.network.original_outputs()[0].node_id, 0, 0) {
|
if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.network.original_outputs()[0].node_id, 0, 0) {
|
||||||
modify_inputs.insert_artboard(artboard, layer);
|
modify_inputs.insert_artboard(artboard, layer);
|
||||||
}
|
}
|
||||||
document.metadata.load_structure(&document.document_network);
|
document.load_network_structure();
|
||||||
}
|
}
|
||||||
GraphOperationMessage::NewBitmapLayer {
|
GraphOperationMessage::NewBitmapLayer {
|
||||||
id,
|
id,
|
||||||
|
|
@ -678,14 +678,14 @@ impl MessageHandler<GraphOperationMessage, (&mut Document, &mut NodeGraphMessage
|
||||||
modify_inputs.responses.add(NodeGraphMessage::SendGraph { should_rerender: true });
|
modify_inputs.responses.add(NodeGraphMessage::SendGraph { should_rerender: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
document.metadata.load_structure(&document.document_network);
|
document.load_network_structure();
|
||||||
}
|
}
|
||||||
GraphOperationMessage::NewVectorLayer { id, subpaths, parent, insert_index } => {
|
GraphOperationMessage::NewVectorLayer { id, subpaths, parent, insert_index } => {
|
||||||
let mut modify_inputs = ModifyInputsContext::new(document, node_graph, responses);
|
let mut modify_inputs = ModifyInputsContext::new(document, node_graph, responses);
|
||||||
if let Some(layer) = modify_inputs.create_layer_with_insert_index(id, insert_index, parent) {
|
if let Some(layer) = modify_inputs.create_layer_with_insert_index(id, insert_index, parent) {
|
||||||
modify_inputs.insert_vector_data(subpaths, layer);
|
modify_inputs.insert_vector_data(subpaths, layer);
|
||||||
}
|
}
|
||||||
document.metadata.load_structure(&document.document_network);
|
document.load_network_structure();
|
||||||
}
|
}
|
||||||
GraphOperationMessage::NewTextLayer {
|
GraphOperationMessage::NewTextLayer {
|
||||||
id,
|
id,
|
||||||
|
|
@ -699,7 +699,7 @@ impl MessageHandler<GraphOperationMessage, (&mut Document, &mut NodeGraphMessage
|
||||||
if let Some(layer) = modify_inputs.create_layer_with_insert_index(id, insert_index, parent) {
|
if let Some(layer) = modify_inputs.create_layer_with_insert_index(id, insert_index, parent) {
|
||||||
modify_inputs.insert_text(text, font, size, layer);
|
modify_inputs.insert_text(text, font, size, layer);
|
||||||
}
|
}
|
||||||
document.metadata.load_structure(&document.document_network);
|
document.load_network_structure();
|
||||||
}
|
}
|
||||||
GraphOperationMessage::ResizeArtboard { id, location, dimensions } => {
|
GraphOperationMessage::ResizeArtboard { id, location, dimensions } => {
|
||||||
if let Some(mut modify_inputs) = ModifyInputsContext::new_layer(&[id], document, node_graph, responses) {
|
if let Some(mut modify_inputs) = ModifyInputsContext::new_layer(&[id], document, node_graph, responses) {
|
||||||
|
|
@ -716,7 +716,7 @@ impl MessageHandler<GraphOperationMessage, (&mut Document, &mut NodeGraphMessage
|
||||||
for id in artboard_nodes {
|
for id in artboard_nodes {
|
||||||
modify_inputs.delete_layer(id);
|
modify_inputs.delete_layer(id);
|
||||||
}
|
}
|
||||||
document.metadata.load_structure(&document.document_network);
|
document.load_network_structure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,10 @@ pub enum NodeGraphMessage {
|
||||||
ShiftNode {
|
ShiftNode {
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
},
|
},
|
||||||
ToggleHidden,
|
ToggleSelectedHidden,
|
||||||
|
ToggleHidden {
|
||||||
|
node_id: NodeId,
|
||||||
|
},
|
||||||
SetHidden {
|
SetHidden {
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
hidden: bool,
|
hidden: bool,
|
||||||
|
|
|
||||||
|
|
@ -193,11 +193,17 @@ impl NodeGraphMessageHandler {
|
||||||
if let Some(network) = document.document_network.nested_network(&self.network) {
|
if let Some(network) = document.document_network.nested_network(&self.network) {
|
||||||
let mut widgets = Vec::new();
|
let mut widgets = Vec::new();
|
||||||
|
|
||||||
|
// TODO: Replace this with an add node button
|
||||||
|
let add_nodes_label = TextLabel::new("Right Click Graph to Add Nodes").italic(true).widget_holder();
|
||||||
|
widgets.push(add_nodes_label);
|
||||||
|
|
||||||
// Don't allow disabling input or output nodes
|
// Don't allow disabling input or output nodes
|
||||||
let mut selected_nodes = document.metadata.selected_nodes().filter(|&&id| !network.inputs.contains(&id) && !network.original_outputs_contain(id));
|
let mut selected_nodes = document.metadata.selected_nodes().filter(|&&id| !network.inputs.contains(&id) && !network.original_outputs_contain(id));
|
||||||
|
|
||||||
// If there is at least one other selected node then show the hide or show button
|
// If there is at least one other selected node then show the hide or show button
|
||||||
if selected_nodes.next().is_some() {
|
if selected_nodes.next().is_some() {
|
||||||
|
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||||
|
|
||||||
// Check if any of the selected nodes are disabled
|
// Check if any of the selected nodes are disabled
|
||||||
let is_hidden = document.metadata.selected_nodes().any(|id| network.disabled.contains(id));
|
let is_hidden = document.metadata.selected_nodes().any(|id| network.disabled.contains(id));
|
||||||
|
|
||||||
|
|
@ -205,10 +211,12 @@ impl NodeGraphMessageHandler {
|
||||||
let multiple_nodes = selected_nodes.next().is_some();
|
let multiple_nodes = selected_nodes.next().is_some();
|
||||||
|
|
||||||
// Generate the enable or disable button accordingly
|
// Generate the enable or disable button accordingly
|
||||||
let hide_button = TextButton::new(if is_hidden { "Show" } else { "Hide" })
|
let (hide_show_label, hide_show_icon) = if is_hidden { ("Make Visible", "EyeHidden") } else { ("Make Hidden", "EyeVisible") };
|
||||||
.tooltip(if is_hidden { "Show node" } else { "Hide node" }.to_string() + if multiple_nodes { "s" } else { "" })
|
let hide_button = TextButton::new(hide_show_label)
|
||||||
.tooltip_shortcut(action_keys!(NodeGraphMessageDiscriminant::ToggleHidden))
|
.icon(Some(hide_show_icon.to_string()))
|
||||||
.on_update(move |_| NodeGraphMessage::ToggleHidden.into())
|
.tooltip(if is_hidden { "Show selected nodes/layers" } else { "Hide selected nodes/layers" }.to_string() + if multiple_nodes { "s" } else { "" })
|
||||||
|
.tooltip_shortcut(action_keys!(NodeGraphMessageDiscriminant::ToggleSelectedHidden))
|
||||||
|
.on_update(move |_| NodeGraphMessage::ToggleSelectedHidden.into())
|
||||||
.widget_holder();
|
.widget_holder();
|
||||||
widgets.push(hide_button);
|
widgets.push(hide_button);
|
||||||
}
|
}
|
||||||
|
|
@ -216,13 +224,16 @@ impl NodeGraphMessageHandler {
|
||||||
// If only one node is selected then show the preview or stop previewing button
|
// If only one node is selected then show the preview or stop previewing button
|
||||||
let mut selected_nodes = document.metadata.selected_nodes();
|
let mut selected_nodes = document.metadata.selected_nodes();
|
||||||
if let (Some(&node_id), None) = (selected_nodes.next(), selected_nodes.next()) {
|
if let (Some(&node_id), None) = (selected_nodes.next(), selected_nodes.next()) {
|
||||||
|
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||||
|
|
||||||
// Is this node the current output
|
// Is this node the current output
|
||||||
let is_output = network.outputs_contain(node_id);
|
let is_output = network.outputs_contain(node_id);
|
||||||
|
|
||||||
// Don't show stop previewing button on the original output node
|
// Don't show stop previewing button on the original output node
|
||||||
if !(is_output && network.previous_outputs_contain(node_id).unwrap_or(true)) {
|
if !(is_output && network.previous_outputs_contain(node_id).unwrap_or(true)) {
|
||||||
let output_button = TextButton::new(if is_output { "End Preview" } else { "Preview" })
|
let output_button = TextButton::new(if is_output { "End Preview" } else { "Preview" })
|
||||||
.tooltip(if is_output { "Restore preview to Output node" } else { "Preview node" }.to_string() + " (Shortcut: Alt-click node)")
|
.icon(Some("Rescale".to_string()))
|
||||||
|
.tooltip(if is_output { "Restore preview to the graph output" } else { "Preview selected node/layer" }.to_string() + " (Shortcut: Alt-click node/layer)")
|
||||||
.on_update(move |_| NodeGraphMessage::TogglePreview { node_id }.into())
|
.on_update(move |_| NodeGraphMessage::TogglePreview { node_id }.into())
|
||||||
.widget_holder();
|
.widget_holder();
|
||||||
widgets.push(output_button);
|
widgets.push(output_button);
|
||||||
|
|
@ -458,7 +469,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
||||||
on: BroadcastEvent::SelectionChanged,
|
on: BroadcastEvent::SelectionChanged,
|
||||||
send: Box::new(NodeGraphMessage::SelectedNodesUpdated.into()),
|
send: Box::new(NodeGraphMessage::SelectedNodesUpdated.into()),
|
||||||
});
|
});
|
||||||
document.metadata.load_structure(&document.document_network);
|
document.load_network_structure();
|
||||||
responses.add(DocumentMessage::DocumentStructureChanged);
|
responses.add(DocumentMessage::DocumentStructureChanged);
|
||||||
}
|
}
|
||||||
NodeGraphMessage::SelectedNodesUpdated => {
|
NodeGraphMessage::SelectedNodesUpdated => {
|
||||||
|
|
@ -803,7 +814,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
||||||
let structure_changed = node_input.as_node().is_some() || input.as_node().is_some();
|
let structure_changed = node_input.as_node().is_some() || input.as_node().is_some();
|
||||||
*node_input = input;
|
*node_input = input;
|
||||||
if structure_changed {
|
if structure_changed {
|
||||||
document.metadata.load_structure(&document.document_network);
|
document.load_network_structure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -882,7 +893,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
||||||
}
|
}
|
||||||
responses.add(NodeGraphMessage::SendGraph { should_rerender: false });
|
responses.add(NodeGraphMessage::SendGraph { should_rerender: false });
|
||||||
}
|
}
|
||||||
NodeGraphMessage::ToggleHidden => {
|
NodeGraphMessage::ToggleSelectedHidden => {
|
||||||
if let Some(network) = document.document_network.nested_network(&self.network) {
|
if let Some(network) = document.document_network.nested_network(&self.network) {
|
||||||
responses.add(DocumentMessage::StartTransaction);
|
responses.add(DocumentMessage::StartTransaction);
|
||||||
|
|
||||||
|
|
@ -892,6 +903,12 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
NodeGraphMessage::ToggleHidden { node_id } => {
|
||||||
|
if let Some(network) = document.document_network.nested_network(&self.network) {
|
||||||
|
let new_hidden = !network.disabled.contains(&node_id);
|
||||||
|
responses.add(NodeGraphMessage::SetHidden { node_id, hidden: new_hidden });
|
||||||
|
}
|
||||||
|
}
|
||||||
NodeGraphMessage::SetHidden { node_id, hidden } => {
|
NodeGraphMessage::SetHidden { node_id, hidden } => {
|
||||||
if let Some(network) = document.document_network.nested_network_mut(&self.network) {
|
if let Some(network) = document.document_network.nested_network_mut(&self.network) {
|
||||||
if !hidden {
|
if !hidden {
|
||||||
|
|
@ -956,7 +973,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
||||||
impl NodeGraphMessageHandler {
|
impl NodeGraphMessageHandler {
|
||||||
pub fn actions_with_node_graph_open(&self, graph_open: bool) -> ActionList {
|
pub fn actions_with_node_graph_open(&self, graph_open: bool) -> ActionList {
|
||||||
if self.has_selection && graph_open {
|
if self.has_selection && graph_open {
|
||||||
actions!(NodeGraphMessageDiscriminant; DeleteSelectedNodes, Cut, Copy, DuplicateSelectedNodes, ToggleHidden)
|
actions!(NodeGraphMessageDiscriminant; DeleteSelectedNodes, Cut, Copy, DuplicateSelectedNodes, ToggleSelectedHidden)
|
||||||
} else {
|
} else {
|
||||||
actions!(NodeGraphMessageDiscriminant;)
|
actions!(NodeGraphMessageDiscriminant;)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ use crate::messages::prelude::*;
|
||||||
use crate::messages::tool::utility_types::{HintData, HintGroup};
|
use crate::messages::tool::utility_types::{HintData, HintGroup};
|
||||||
use crate::node_graph_executor::NodeGraphExecutor;
|
use crate::node_graph_executor::NodeGraphExecutor;
|
||||||
|
|
||||||
use document_legacy::document_metadata::LayerNodeIdentifier;
|
|
||||||
use document_legacy::layers::style::RenderData;
|
use document_legacy::layers::style::RenderData;
|
||||||
use graph_craft::document::NodeId;
|
use graph_craft::document::NodeId;
|
||||||
use graphene_core::text::Font;
|
use graphene_core::text::Font;
|
||||||
|
|
@ -416,7 +415,7 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
|
||||||
PortfolioMessage::PasteSerializedData { data } => {
|
PortfolioMessage::PasteSerializedData { data } => {
|
||||||
if let Some(document) = self.active_document() {
|
if let Some(document) = self.active_document() {
|
||||||
if let Ok(data) = serde_json::from_str::<Vec<CopyBufferEntry>>(&data) {
|
if let Ok(data) = serde_json::from_str::<Vec<CopyBufferEntry>>(&data) {
|
||||||
let parent = document.metadata().deepest_common_ancestor(document.metadata().selected_layers()).unwrap_or(LayerNodeIdentifier::ROOT);
|
let parent = document.new_layer_parent();
|
||||||
|
|
||||||
responses.add(DocumentMessage::DeselectAllLayers);
|
responses.add(DocumentMessage::DeselectAllLayers);
|
||||||
responses.add(DocumentMessage::StartTransaction);
|
responses.add(DocumentMessage::StartTransaction);
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ impl PathOutline {
|
||||||
/// Performs an intersect test and generates a hovered overlay if necessary
|
/// Performs an intersect test and generates a hovered overlay if necessary
|
||||||
pub fn intersect_test_hovered(&mut self, input: &InputPreprocessorMessageHandler, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
|
pub fn intersect_test_hovered(&mut self, input: &InputPreprocessorMessageHandler, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
|
||||||
// Get the layer the user is hovering over
|
// Get the layer the user is hovering over
|
||||||
let intersection = document.metadata().click(input.mouse.position, &document.document_legacy.document_network);
|
let intersection = document.document_legacy.click(input.mouse.position, &document.document_legacy.document_network);
|
||||||
|
|
||||||
let Some(hovered_layer) = intersection else {
|
let Some(hovered_layer) = intersection else {
|
||||||
self.clear_hovered(responses);
|
self.clear_hovered(responses);
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ impl Pivot {
|
||||||
|
|
||||||
/// Recomputes the pivot position and transform.
|
/// Recomputes the pivot position and transform.
|
||||||
fn recalculate_pivot(&mut self, document: &DocumentMessageHandler) {
|
fn recalculate_pivot(&mut self, document: &DocumentMessageHandler) {
|
||||||
let mut layers = document.metadata().selected_visible_layers();
|
let mut layers = document.document_legacy.selected_visible_layers();
|
||||||
let Some(first) = layers.next() else {
|
let Some(first) = layers.next() else {
|
||||||
// If no layers are selected then we revert things back to default
|
// If no layers are selected then we revert things back to default
|
||||||
self.normalized_pivot = DVec2::splat(0.5);
|
self.normalized_pivot = DVec2::splat(0.5);
|
||||||
|
|
@ -73,7 +73,7 @@ impl Pivot {
|
||||||
} else {
|
} else {
|
||||||
// If more than one layer is selected we use the AABB with the mean of the pivots
|
// If more than one layer is selected we use the AABB with the mean of the pivots
|
||||||
let xy_summation = document
|
let xy_summation = document
|
||||||
.metadata()
|
.document_legacy
|
||||||
.selected_visible_layers()
|
.selected_visible_layers()
|
||||||
.filter_map(|layer| graph_modification_utils::get_viewport_pivot(layer, &document.document_legacy))
|
.filter_map(|layer| graph_modification_utils::get_viewport_pivot(layer, &document.document_legacy))
|
||||||
.reduce(|a, b| a + b)
|
.reduce(|a, b| a + b)
|
||||||
|
|
@ -81,7 +81,7 @@ impl Pivot {
|
||||||
|
|
||||||
let pivot = xy_summation / selected_layers_count as f64;
|
let pivot = xy_summation / selected_layers_count as f64;
|
||||||
self.pivot = Some(pivot);
|
self.pivot = Some(pivot);
|
||||||
let [min, max] = document.metadata().selected_visible_layers_bounding_box_viewport().unwrap_or([DVec2::ZERO, DVec2::ONE]);
|
let [min, max] = document.document_legacy.selected_visible_layers_bounding_box_viewport().unwrap_or([DVec2::ZERO, DVec2::ONE]);
|
||||||
self.normalized_pivot = (pivot - min) / (max - min);
|
self.normalized_pivot = (pivot - min) / (max - min);
|
||||||
|
|
||||||
self.transform_from_normalized = DAffine2::from_translation(min) * DAffine2::from_scale(max - min);
|
self.transform_from_normalized = DAffine2::from_translation(min) * DAffine2::from_scale(max - min);
|
||||||
|
|
@ -157,7 +157,7 @@ impl Pivot {
|
||||||
|
|
||||||
/// Sets the viewport position of the pivot for all selected layers.
|
/// Sets the viewport position of the pivot for all selected layers.
|
||||||
pub fn set_viewport_position(&self, position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
|
pub fn set_viewport_position(&self, position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
|
||||||
for layer in document.metadata().selected_visible_layers() {
|
for layer in document.document_legacy.selected_visible_layers() {
|
||||||
let transform = Self::get_layer_pivot_transform(layer, document);
|
let transform = Self::get_layer_pivot_transform(layer, document);
|
||||||
let pivot = transform.inverse().transform_point2(position);
|
let pivot = transform.inverse().transform_point2(position);
|
||||||
// Only update the pivot when computed position is finite. Infinite can happen when scale is 0.
|
// Only update the pivot when computed position is finite. Infinite can happen when scale is 0.
|
||||||
|
|
|
||||||
|
|
@ -150,11 +150,7 @@ impl ArtboardToolData {
|
||||||
fn select_artboard(&mut self, document: &DocumentMessageHandler, render_data: &RenderData, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) -> bool {
|
fn select_artboard(&mut self, document: &DocumentMessageHandler, render_data: &RenderData, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) -> bool {
|
||||||
responses.add(DocumentMessage::StartTransaction);
|
responses.add(DocumentMessage::StartTransaction);
|
||||||
|
|
||||||
let mut intersections = document
|
let mut intersections = document.document_legacy.click_xray(input.mouse.position).filter(|&layer| is_artboard(layer, &document.document_legacy));
|
||||||
.document_legacy
|
|
||||||
.metadata
|
|
||||||
.click_xray(input.mouse.position)
|
|
||||||
.filter(|&layer| is_artboard(layer, &document.document_legacy));
|
|
||||||
|
|
||||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||||
if let Some(intersection) = intersections.next() {
|
if let Some(intersection) = intersections.next() {
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ impl Fsm for FillToolFsmState {
|
||||||
let ToolMessage::Fill(event) = event else {
|
let ToolMessage::Fill(event) = event else {
|
||||||
return self;
|
return self;
|
||||||
};
|
};
|
||||||
let Some(layer_identifier) = document.metadata().click(input.mouse.position, &document.document_legacy.document_network) else {
|
let Some(layer_identifier) = document.document_legacy.click(input.mouse.position, &document.document_legacy.document_network) else {
|
||||||
return self;
|
return self;
|
||||||
};
|
};
|
||||||
let layer = layer_identifier.to_path();
|
let layer = layer_identifier.to_path();
|
||||||
|
|
|
||||||
|
|
@ -391,7 +391,7 @@ impl Fsm for GradientToolFsmState {
|
||||||
SelectedGradient::update(&mut tool_data.selected_gradient, document, responses);
|
SelectedGradient::update(&mut tool_data.selected_gradient, document, responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
for layer in document.metadata().selected_visible_layers() {
|
for layer in document.document_legacy.selected_visible_layers() {
|
||||||
if let Some(gradient) = get_gradient(layer, &document.document_legacy) {
|
if let Some(gradient) = get_gradient(layer, &document.document_legacy) {
|
||||||
let dragging = tool_data
|
let dragging = tool_data
|
||||||
.selected_gradient
|
.selected_gradient
|
||||||
|
|
@ -526,7 +526,7 @@ impl Fsm for GradientToolFsmState {
|
||||||
document.backup_nonmut(responses);
|
document.backup_nonmut(responses);
|
||||||
GradientToolFsmState::Drawing
|
GradientToolFsmState::Drawing
|
||||||
} else {
|
} else {
|
||||||
let selected_layer = document.metadata().click(input.mouse.position, &document.document_legacy.document_network);
|
let selected_layer = document.document_legacy.click(input.mouse.position, &document.document_legacy.document_network);
|
||||||
|
|
||||||
// Apply the gradient to the selected layer
|
// Apply the gradient to the selected layer
|
||||||
if let Some(layer) = selected_layer {
|
if let Some(layer) = selected_layer {
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ impl Fsm for ImaginateToolFsmState {
|
||||||
match (self, event) {
|
match (self, event) {
|
||||||
(_, ImaginateToolMessage::DocumentIsDirty | ImaginateToolMessage::SelectionChanged) => {
|
(_, ImaginateToolMessage::DocumentIsDirty | ImaginateToolMessage::SelectionChanged) => {
|
||||||
tool_data.path_outlines.clear_selected(responses);
|
tool_data.path_outlines.clear_selected(responses);
|
||||||
//tool_data.path_outlines.update_selected(document.selected_visible_layers(), document, responses, render_data);
|
//tool_data.path_outlines.update_selected(document.document_legacy.selected_visible_layers(), document, responses, render_data);
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -246,7 +246,7 @@ impl PathToolData {
|
||||||
PathToolFsmState::Dragging
|
PathToolFsmState::Dragging
|
||||||
}
|
}
|
||||||
// We didn't find a point nearby, so consider selecting the nearest shape instead
|
// We didn't find a point nearby, so consider selecting the nearest shape instead
|
||||||
else if let Some(layer) = document.metadata().click(input.mouse.position, &document.document_legacy.document_network) {
|
else if let Some(layer) = document.document_legacy.click(input.mouse.position, &document.document_legacy.document_network) {
|
||||||
if shift {
|
if shift {
|
||||||
responses.add(NodeGraphMessage::SelectedNodesAdd { nodes: vec![layer.to_node()] });
|
responses.add(NodeGraphMessage::SelectedNodesAdd { nodes: vec![layer.to_node()] });
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -393,10 +393,10 @@ impl Fsm for SelectToolFsmState {
|
||||||
tool_data.selected_layers_changed = selected_layers_count != tool_data.selected_layers_count;
|
tool_data.selected_layers_changed = selected_layers_count != tool_data.selected_layers_count;
|
||||||
tool_data.selected_layers_count = selected_layers_count;
|
tool_data.selected_layers_count = selected_layers_count;
|
||||||
|
|
||||||
tool_data.path_outlines.update_selected(document.metadata().selected_layers(), document, responses);
|
tool_data.path_outlines.update_selected(document.document_legacy.selected_visible_layers(), document, responses);
|
||||||
tool_data.path_outlines.intersect_test_hovered(input, document, responses);
|
tool_data.path_outlines.intersect_test_hovered(input, document, responses);
|
||||||
|
|
||||||
match (document.metadata().selected_visible_layers_bounding_box_viewport(), tool_data.bounding_box_overlays.take()) {
|
match (document.document_legacy.selected_visible_layers_bounding_box_viewport(), tool_data.bounding_box_overlays.take()) {
|
||||||
(None, Some(bounding_box_overlays)) => bounding_box_overlays.delete(responses),
|
(None, Some(bounding_box_overlays)) => bounding_box_overlays.delete(responses),
|
||||||
(Some(bounds), paths) => {
|
(Some(bounds), paths) => {
|
||||||
let mut bounding_box_overlays = paths.unwrap_or_else(|| BoundingBoxOverlays::new(responses));
|
let mut bounding_box_overlays = paths.unwrap_or_else(|| BoundingBoxOverlays::new(responses));
|
||||||
|
|
@ -417,7 +417,7 @@ impl Fsm for SelectToolFsmState {
|
||||||
}
|
}
|
||||||
(_, SelectToolMessage::EditLayer) => {
|
(_, SelectToolMessage::EditLayer) => {
|
||||||
// Edit the clicked layer
|
// Edit the clicked layer
|
||||||
if let Some(intersect) = document.metadata().click(input.mouse.position, &document.document_legacy.document_network) {
|
if let Some(intersect) = document.document_legacy.click(input.mouse.position, &document.document_legacy.document_network) {
|
||||||
match tool_data.nested_selection_behavior {
|
match tool_data.nested_selection_behavior {
|
||||||
NestedSelectionBehavior::Shallowest => edit_layer_shallowest_manipulation(document, intersect, responses),
|
NestedSelectionBehavior::Shallowest => edit_layer_shallowest_manipulation(document, intersect, responses),
|
||||||
NestedSelectionBehavior::Deepest => edit_layer_deepest_manipulation(intersect, &document.document_legacy, responses),
|
NestedSelectionBehavior::Deepest => edit_layer_deepest_manipulation(intersect, &document.document_legacy, responses),
|
||||||
|
|
@ -450,8 +450,8 @@ impl Fsm for SelectToolFsmState {
|
||||||
.map(|bounding_box| bounding_box.check_rotate(input.mouse.position))
|
.map(|bounding_box| bounding_box.check_rotate(input.mouse.position))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let mut selected: Vec<_> = document.metadata().selected_visible_layers().collect();
|
let mut selected: Vec<_> = document.document_legacy.selected_visible_layers().collect();
|
||||||
let intersection = document.metadata().click(input.mouse.position, &document.document_legacy.document_network);
|
let intersection = document.document_legacy.click(input.mouse.position, &document.document_legacy.document_network);
|
||||||
|
|
||||||
// If the user is dragging the bounding box bounds, go into ResizingBounds mode.
|
// If the user is dragging the bounding box bounds, go into ResizingBounds mode.
|
||||||
// If the user is dragging the rotate trigger, go into RotatingBounds mode.
|
// If the user is dragging the rotate trigger, go into RotatingBounds mode.
|
||||||
|
|
@ -706,7 +706,7 @@ impl Fsm for SelectToolFsmState {
|
||||||
// Deselect layer if not snap dragging
|
// Deselect layer if not snap dragging
|
||||||
if !tool_data.has_dragged && input.keyboard.key(remove_from_selection) && tool_data.layer_selected_on_start.is_none() {
|
if !tool_data.has_dragged && input.keyboard.key(remove_from_selection) && tool_data.layer_selected_on_start.is_none() {
|
||||||
let quad = tool_data.selection_quad();
|
let quad = tool_data.selection_quad();
|
||||||
let intersection = document.metadata().intersect_quad(quad, &document.document_legacy.document_network);
|
let intersection = document.document_legacy.intersect_quad(quad, &document.document_legacy.document_network);
|
||||||
|
|
||||||
if let Some(path) = intersection.last() {
|
if let Some(path) = intersection.last() {
|
||||||
let replacement_selected_layers: Vec<_> = document.metadata().selected_layers().filter(|&layer| !path.starts_with(layer, document.metadata())).collect();
|
let replacement_selected_layers: Vec<_> = document.metadata().selected_layers().filter(|&layer| !path.starts_with(layer, document.metadata())).collect();
|
||||||
|
|
@ -777,7 +777,7 @@ impl Fsm for SelectToolFsmState {
|
||||||
(SelectToolFsmState::DrawingBox, SelectToolMessage::DragStop { .. } | SelectToolMessage::Enter) => {
|
(SelectToolFsmState::DrawingBox, SelectToolMessage::DragStop { .. } | SelectToolMessage::Enter) => {
|
||||||
let quad = tool_data.selection_quad();
|
let quad = tool_data.selection_quad();
|
||||||
// For shallow select we don't update dragging layers until inside drag_start_shallowest_manipulation()
|
// For shallow select we don't update dragging layers until inside drag_start_shallowest_manipulation()
|
||||||
tool_data.layers_dragging = document.metadata().intersect_quad(quad, &document.document_legacy.document_network).collect();
|
tool_data.layers_dragging = document.document_legacy.intersect_quad(quad, &document.document_legacy.document_network).collect();
|
||||||
responses.add_front(NodeGraphMessage::SelectedNodesSet {
|
responses.add_front(NodeGraphMessage::SelectedNodesSet {
|
||||||
nodes: tool_data.layers_dragging.iter().map(|layer| layer.to_node()).collect(),
|
nodes: tool_data.layers_dragging.iter().map(|layer| layer.to_node()).collect(),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -274,7 +274,11 @@ impl TextToolData {
|
||||||
|
|
||||||
fn interact(&mut self, state: TextToolFsmState, mouse: DVec2, document: &DocumentMessageHandler, render_data: &RenderData, responses: &mut VecDeque<Message>) -> TextToolFsmState {
|
fn interact(&mut self, state: TextToolFsmState, mouse: DVec2, document: &DocumentMessageHandler, render_data: &RenderData, responses: &mut VecDeque<Message>) -> TextToolFsmState {
|
||||||
// Check if the user has selected an existing text layer
|
// Check if the user has selected an existing text layer
|
||||||
if let Some(clicked_text_layer_path) = document.metadata().click(mouse, document.network()).filter(|&layer| is_text_layer(layer, &document.document_legacy)) {
|
if let Some(clicked_text_layer_path) = document
|
||||||
|
.document_legacy
|
||||||
|
.click(mouse, document.network())
|
||||||
|
.filter(|&layer| is_text_layer(layer, &document.document_legacy))
|
||||||
|
{
|
||||||
self.start_editing_layer(clicked_text_layer_path, state, document, render_data, responses);
|
self.start_editing_layer(clicked_text_layer_path, state, document, render_data, responses);
|
||||||
|
|
||||||
TextToolFsmState::Editing
|
TextToolFsmState::Editing
|
||||||
|
|
|
||||||
|
|
@ -541,14 +541,14 @@ impl NodeGraphExecutor {
|
||||||
}
|
}
|
||||||
.to_string(),
|
.to_string(),
|
||||||
tooltip: format!("Layer id: {node_id}"),
|
tooltip: format!("Layer id: {node_id}"),
|
||||||
visible: true,
|
visible: !document.document_network.disabled.contains(&layer.to_node()),
|
||||||
layer_type: if document.metadata.is_folder(layer) {
|
layer_type: if document.metadata.is_folder(layer) {
|
||||||
LayerDataTypeDiscriminant::Folder
|
LayerDataTypeDiscriminant::Folder
|
||||||
} else {
|
} else {
|
||||||
LayerDataTypeDiscriminant::Layer
|
LayerDataTypeDiscriminant::Layer
|
||||||
},
|
},
|
||||||
layer_metadata: LayerMetadata {
|
layer_metadata: LayerMetadata {
|
||||||
expanded: layer.has_children(&document.metadata),
|
expanded: layer.has_children(&document.metadata) && !document.collapsed_folders.contains(&layer),
|
||||||
selected: document.metadata.selected_layers_contains(layer),
|
selected: document.metadata.selected_layers_contains(layer),
|
||||||
},
|
},
|
||||||
path: vec![node_id],
|
path: vec![node_id],
|
||||||
|
|
|
||||||
|
|
@ -700,14 +700,14 @@ impl JsEditorHandle {
|
||||||
/// Toggle visibility of a layer from the layer list
|
/// Toggle visibility of a layer from the layer list
|
||||||
#[wasm_bindgen(js_name = toggleLayerVisibility)]
|
#[wasm_bindgen(js_name = toggleLayerVisibility)]
|
||||||
pub fn toggle_layer_visibility(&self, layer_path: Vec<LayerId>) {
|
pub fn toggle_layer_visibility(&self, layer_path: Vec<LayerId>) {
|
||||||
let message = DocumentMessage::ToggleLayerVisibility { layer_path };
|
let message = NodeGraphMessage::ToggleHidden { node_id: *layer_path.last().unwrap() };
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Toggle expansions state of a layer from the layer list
|
/// Toggle expansions state of a layer from the layer list
|
||||||
#[wasm_bindgen(js_name = toggleLayerExpansion)]
|
#[wasm_bindgen(js_name = toggleLayerExpansion)]
|
||||||
pub fn toggle_layer_expansion(&self, layer_path: Vec<LayerId>) {
|
pub fn toggle_layer_expansion(&self, layer_path: Vec<LayerId>) {
|
||||||
let message = DocumentMessage::ToggleLayerExpansion { layer_path };
|
let message = DocumentMessage::ToggleLayerExpansion { layer: *layer_path.last().unwrap() };
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -809,9 +809,14 @@ impl NodeNetwork {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.disabled.contains(&id) {
|
if node.implementation != DocumentNodeImplementation::Unresolved("graphene_core::ops::IdNode".into()) && self.disabled.contains(&id) {
|
||||||
node.implementation = DocumentNodeImplementation::Unresolved("graphene_core::ops::IdNode".into());
|
node.implementation = DocumentNodeImplementation::Unresolved("graphene_core::ops::IdNode".into());
|
||||||
node.inputs.drain(1..);
|
if node.name == "Layer" {
|
||||||
|
// Connect layer node to the graphic group below
|
||||||
|
node.inputs.drain(..7);
|
||||||
|
} else {
|
||||||
|
node.inputs.drain(1..);
|
||||||
|
}
|
||||||
self.nodes.insert(id, node);
|
self.nodes.insert(id, node);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue