Remove most of document-legacy (#1519)

* Remove boolean ops and unused doc-legacy Operations

* Remove Shape legacy layers

* Remove legacy layer Properties panel code

* Remove additional unused doc-legacy Operations

* Removed unused rendering-related legacy-layer code

* Upgrade dep so CI builds

* Remove various additional unused functions and messages

* Remove the LayerData trait

* Remove RenderData struct and usages

* Banish the Operations system

* Further removals
This commit is contained in:
Keavon Chambers
2023-12-19 04:36:19 -08:00
parent c42d030f18
commit 9a7d7de8fa
64 changed files with 330 additions and 5868 deletions

View File

@@ -35,7 +35,7 @@ const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::RenderDocument)),
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::NodeGraph(NodeGraphMessageDiscriminant::SendGraph))),
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::PropertiesPanel(
PropertiesPanelMessageDiscriminant::ResendActiveProperties,
PropertiesPanelMessageDiscriminant::Refresh,
))),
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::FolderChanged)),
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::DocumentStructureChanged)),
@@ -262,8 +262,8 @@ mod test {
use crate::messages::tool::tool_messages::tool_prelude::ToolType;
use crate::test_utils::EditorTestUtils;
use document_legacy::document::LayerId;
use document_legacy::document_metadata::LayerNodeIdentifier;
use document_legacy::LayerId;
use graphene_core::raster::color::Color;
fn init_logger() {
@@ -475,10 +475,6 @@ mod test {
)
};
editor.handle_message(DocumentMessage::SetSelectedLayers {
replacement_selected_layers: sorted_layers[..2].to_vec(),
});
editor.handle_message(DocumentMessage::SelectedLayersRaise);
let (all, non_selected, selected) = verify_order(editor.dispatcher.message_handlers.portfolio_message_handler.active_document_mut().unwrap());
assert_eq!(all, non_selected.into_iter().chain(selected).collect::<Vec<_>>());

View File

@@ -1,5 +1,5 @@
use document_legacy::document::LayerId;
use document_legacy::document_metadata::LayerNodeIdentifier;
use document_legacy::LayerId;
use serde::{Deserialize, Serialize};
#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize, specta::Type)]

View File

@@ -1,8 +1,6 @@
use crate::messages::portfolio::utility_types::KeyboardPlatformLayout;
use crate::messages::prelude::*;
pub use document_legacy::DocumentResponse;
use bitflags::bitflags;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Formatter};

View File

@@ -2,7 +2,7 @@ use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::prelude::*;
use document_legacy::LayerId;
use document_legacy::document::LayerId;
use graphene_core::raster::color::Color;
use graphene_core::text::Font;

View File

@@ -1,8 +1,8 @@
use crate::messages::input_mapper::utility_types::misc::ActionKeys;
use crate::messages::layout::utility_types::widget_prelude::*;
use document_legacy::document::LayerId;
use document_legacy::layers::layer_info::LayerDataTypeDiscriminant;
use document_legacy::LayerId;
use graphene_core::raster::curve::Curve;
use graphite_proc_macros::WidgetBuilder;

View File

@@ -4,13 +4,12 @@ use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate,
use crate::messages::prelude::*;
use document_legacy::document::Document as DocumentLegacy;
use document_legacy::document::LayerId;
use document_legacy::document_metadata::LayerNodeIdentifier;
use document_legacy::layers::style::ViewMode;
use document_legacy::LayerId;
use document_legacy::Operation as DocumentOperation;
use graph_craft::document::NodeId;
use graphene_core::raster::BlendMode;
use graphene_core::raster::Image;
use graphene_core::vector::style::ViewMode;
use graphene_core::Color;
use serde::{Deserialize, Serialize};
@@ -20,8 +19,6 @@ use serde::{Deserialize, Serialize};
pub enum DocumentMessage {
// Sub-messages
#[remain::unsorted]
DispatchOperation(Box<DocumentOperation>),
#[remain::unsorted]
#[child]
Navigation(NavigationMessage),
#[remain::unsorted]
@@ -52,9 +49,6 @@ pub enum DocumentMessage {
},
ClearLayerTree,
CommitTransaction,
CopyToClipboardLayerImageOutput {
layer_path: Vec<LayerId>,
},
CreateEmptyFolder {
parent: LayerNodeIdentifier,
},
@@ -64,14 +58,9 @@ pub enum DocumentMessage {
},
DeleteSelectedLayers,
DeselectAllLayers,
DirtyRenderDocument,
DirtyRenderDocumentInOutlineView,
DocumentHistoryBackward,
DocumentHistoryForward,
DocumentStructureChanged,
DownloadLayerImageOutput {
layer_path: Vec<LayerId>,
},
DuplicateSelectedLayers,
FlipSelectedLayers {
flip_axis: FlipAxis,
@@ -79,7 +68,6 @@ pub enum DocumentMessage {
FolderChanged {
affected_folder_path: Vec<LayerId>,
},
FrameClear,
GroupSelectedLayers,
ImaginateClear {
layer_path: Vec<LayerId>,
@@ -116,14 +104,9 @@ pub enum DocumentMessage {
RenameDocument {
new_name: String,
},
RenameLayer {
layer_path: Vec<LayerId>,
new_name: String,
},
RenderDocument,
RenderRulers,
RenderScrollbars,
RollbackTransaction,
SaveDocument,
SelectAllLayers,
SelectedLayersLower,
@@ -141,16 +124,6 @@ pub enum DocumentMessage {
SetBlendModeForSelectedLayers {
blend_mode: BlendMode,
},
SetImageBlobUrl {
layer_path: Vec<LayerId>,
blob_url: String,
resolution: (f64, f64),
document_id: u64,
},
SetLayerExpansion {
layer_path: Vec<LayerId>,
set_expanded: bool,
},
SetOpacityForSelectedLayers {
opacity: f64,
},
@@ -160,9 +133,6 @@ pub enum DocumentMessage {
SetRangeSelectionLayer {
new_layer: Option<LayerNodeIdentifier>,
},
SetSelectedLayers {
replacement_selected_layers: Vec<Vec<LayerId>>,
},
SetSnapping {
snapping_enabled: Option<bool>,
bounding_box_snapping: Option<bool>,
@@ -181,23 +151,7 @@ pub enum DocumentMessage {
UpdateDocumentTransform {
transform: glam::DAffine2,
},
UpdateLayerMetadata {
layer_path: Vec<LayerId>,
layer_metadata: LayerMetadata,
},
ZoomCanvasTo100Percent,
ZoomCanvasTo200Percent,
ZoomCanvasToFitAll,
}
impl From<DocumentOperation> for DocumentMessage {
fn from(operation: DocumentOperation) -> DocumentMessage {
DocumentMessage::DispatchOperation(Box::new(operation))
}
}
impl From<DocumentOperation> for Message {
fn from(operation: DocumentOperation) -> Message {
DocumentMessage::DispatchOperation(Box::new(operation)).into()
}
}

View File

@@ -17,14 +17,15 @@ use crate::messages::tool::utility_types::ToolType;
use crate::node_graph_executor::NodeGraphExecutor;
use document_legacy::document::Document as DocumentLegacy;
use document_legacy::document::LayerId;
use document_legacy::document_metadata::LayerNodeIdentifier;
use document_legacy::layers::layer_info::{LayerDataTypeDiscriminant, LegacyLayerType};
use document_legacy::layers::style::{RenderData, ViewMode};
use document_legacy::{DocumentError, DocumentResponse, LayerId, Operation as DocumentOperation};
use document_legacy::layers::layer_info::LayerDataTypeDiscriminant;
use document_legacy::DocumentError;
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{NodeInput, NodeNetwork};
use graphene_core::raster::BlendMode;
use graphene_core::raster::ImageFrame;
use graphene_core::vector::style::ViewMode;
use glam::{DAffine2, DVec2};
use serde::{Deserialize, Serialize};
@@ -121,46 +122,10 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
} = document_inputs;
use DocumentMessage::*;
let render_data = RenderData::new(&persistent_data.font_cache, self.view_mode, Some(ipp.document_bounds()));
#[remain::sorted]
match message {
// Sub-messages
#[remain::unsorted]
DispatchOperation(op) => {
match self.document_legacy.handle_operation(*op) {
Ok(Some(document_responses)) => {
for response in document_responses {
match &response {
DocumentResponse::FolderChanged { path } => responses.add(FolderChanged { affected_folder_path: path.clone() }),
DocumentResponse::DeletedLayer { path } => {
self.layer_metadata.remove(path);
}
DocumentResponse::LayerChanged { path } => responses.add(LayerChanged { affected_layer_path: path.clone() }),
DocumentResponse::CreatedLayer { .. } => {
unimplemented!("We should no longer be creating layers in the document and should instead be using the node graph.")
}
DocumentResponse::DocumentChanged => responses.add(RenderDocument),
DocumentResponse::DeletedSelectedManipulatorPoints => {
// Clear Properties panel after deleting all points by updating backend widget state.
responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout::new(vec![])),
layout_target: LayoutTarget::PropertiesOptions,
});
responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout::new(vec![])),
layout_target: LayoutTarget::PropertiesSections,
});
}
};
responses.add(BroadcastEvent::DocumentIsDirty);
}
}
Err(e) => error!("DocumentError: {e:?}"),
Ok(_) => (),
}
}
#[remain::unsorted]
Navigation(message) => {
let document_bounds = self.metadata().document_bounds_viewport_space();
self.navigation_handler.process_message(
@@ -212,7 +177,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
}
AddSelectedLayers { additional_layers } => {
for layer_path in &additional_layers {
responses.extend(self.select_layer(layer_path, &render_data));
responses.extend(self.select_layer(layer_path));
}
// TODO: Correctly update layer panel in clear_selection instead of here
@@ -269,15 +234,6 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
});
}
CommitTransaction => (),
CopyToClipboardLayerImageOutput { layer_path } => {
let layer = self.document_legacy.layer(&layer_path).ok();
let blob_url = layer.and_then(|layer| layer.as_layer().ok()).and_then(|layer_layer| layer_layer.as_blob_url()).cloned();
if let Some(blob_url) = blob_url {
responses.add(FrontendMessage::TriggerCopyToClipboardBlobUrl { blob_url });
}
}
CreateEmptyFolder { parent } => {
let id = generate_uuid();
@@ -295,7 +251,6 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
DeleteLayer { layer_path } => {
responses.add(GraphOperationMessage::DeleteLayer { id: layer_path[0] });
responses.add_front(BroadcastEvent::ToolAbort);
responses.add(PropertiesPanelMessage::CheckSelectedWasDeleted { path: layer_path });
}
DeleteSelectedLayers => {
self.backup(responses);
@@ -313,40 +268,19 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![] });
self.layer_range_selection_reference = None;
}
DirtyRenderDocument => {
// Mark all non-overlay caches as dirty
DocumentLegacy::mark_children_as_dirty(&mut self.document_legacy.root);
responses.add(DocumentMessage::RenderDocument);
}
DirtyRenderDocumentInOutlineView => {
if self.view_mode == ViewMode::Outline {
responses.add_front(DocumentMessage::DirtyRenderDocument);
}
}
DocumentHistoryBackward => self.undo(responses),
DocumentHistoryForward => self.redo(responses),
DocumentStructureChanged => {
let data_buffer: RawBuffer = self.serialize_root().as_slice().into();
responses.add(FrontendMessage::UpdateDocumentLayerTreeStructure { data_buffer })
}
DownloadLayerImageOutput { layer_path } => {
let layer = self.document_legacy.layer(&layer_path).ok();
let layer_name = layer.map(|layer| layer.name.clone().unwrap_or_else(|| "Untitled Layer".to_string()));
let blob_url = layer.and_then(|layer| layer.as_layer().ok()).and_then(|layer_layer| layer_layer.as_blob_url()).cloned();
if let (Some(layer_name), Some(blob_url)) = (layer_name, blob_url) {
responses.add(FrontendMessage::TriggerDownloadBlobUrl { layer_name, blob_url });
}
}
DuplicateSelectedLayers => {
self.backup(responses);
responses.add_front(SetSelectedLayers { replacement_selected_layers: vec![] });
self.layer_range_selection_reference = None;
for path in self.selected_layers_sorted() {
responses.add(DocumentOperation::DuplicateLayer { path: path.to_vec() });
}
// TODO: Reimplement selected layer duplication
// self.backup(responses);
// self.layer_range_selection_reference = None;
// for path in self.selected_layers_sorted() {
// responses.add(DocumentOperation::DuplicateLayer { path: path.to_vec() });
// }
}
FlipSelectedLayers { flip_axis } => {
self.backup(responses);
@@ -372,27 +306,6 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
let affected_layer_path = affected_folder_path;
responses.extend([LayerChanged { affected_layer_path }.into(), DocumentStructureChanged.into()]);
}
FrameClear => {
let mut selected_frame_layers = self.selected_layers_with_type(LayerDataTypeDiscriminant::Layer);
// Get what is hopefully the only selected Layer layer
let layer_path = selected_frame_layers.next();
// Abort if we didn't have any Layer layer, or if there are additional ones also selected
if layer_path.is_none() || selected_frame_layers.next().is_some() {
return;
}
let layer_path = layer_path.unwrap();
let layer = self.document_legacy.layer(layer_path).expect("Clearing Layer image for invalid layer");
let previous_blob_url = match &layer.data {
LegacyLayerType::Layer(layer) => layer.as_blob_url(),
x => panic!("Cannot find blob url for layer type {}", LayerDataTypeDiscriminant::from(x)),
};
if let Some(url) = previous_blob_url {
responses.add(FrontendMessage::TriggerRevokeBlobUrl { url: url.clone() });
}
responses.add(DocumentOperation::ClearBlobURL { path: layer_path.into() });
}
GroupSelectedLayers => {
// 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(), true).unwrap_or(LayerNodeIdentifier::ROOT);
@@ -442,10 +355,9 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
}
InputFrameRasterizeRegionBelowLayer { layer_path } => responses.add(PortfolioMessage::SubmitGraphRender { document_id, layer_path }),
LayerChanged { affected_layer_path } => {
if let Ok(layer_entry) = self.layer_panel_entry(affected_layer_path.clone(), &render_data) {
if let Ok(layer_entry) = self.layer_panel_entry(affected_layer_path.clone()) {
responses.add(FrontendMessage::UpdateDocumentLayerDetails { data: layer_entry });
}
responses.add(PropertiesPanelMessage::CheckSelectedWasUpdated { path: affected_layer_path });
self.update_layers_panel_options_bar_widgets(responses);
}
MoveSelectedLayersTo { parent, insert_index } => {
@@ -567,11 +479,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
responses.add(NodeGraphMessage::UpdateNewNodeGraph);
}
RenameLayer { layer_path, new_name } => responses.add(DocumentOperation::RenameLayer { layer_path, new_name }),
RenderDocument => {
// responses.add(FrontendMessage::UpdateDocumentArtwork {
// svg: self.document_legacy.render_root(&render_data),
// });
responses.add(OverlaysMessage::Draw);
}
RenderRulers => {
@@ -610,10 +518,6 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
multiplier: scrollbar_multiplier.into(),
});
}
RollbackTransaction => {
self.rollback(responses);
responses.extend([RenderDocument.into(), DocumentStructureChanged.into()]);
}
SaveDocument => {
self.set_save_state(true);
responses.add(PortfolioMessage::AutoSaveActiveDocument);
@@ -699,40 +603,6 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
responses.add(GraphOperationMessage::BlendModeSet { layer: layer.to_path(), blend_mode });
}
}
SetImageBlobUrl {
layer_path,
blob_url,
resolution,
document_id,
} => {
let Ok(layer) = self.document_legacy.layer(&layer_path) else {
warn!("Setting blob URL for invalid layer");
return;
};
// Revoke the old blob URL
match &layer.data {
LegacyLayerType::Layer(layer) => {
if let Some(url) = layer.as_blob_url() {
responses.add(FrontendMessage::TriggerRevokeBlobUrl { url: url.clone() });
}
}
other => {
warn!("Setting blob URL for invalid layer type, which must be a `Layer` layer type. Found: `{other:?}`");
return;
}
}
responses.add(PortfolioMessage::DocumentPassMessage {
document_id,
message: DocumentOperation::SetLayerBlobUrl { layer_path, blob_url, resolution }.into(),
});
}
SetLayerExpansion { layer_path, set_expanded } => {
self.layer_metadata_mut(&layer_path).expanded = set_expanded;
responses.add(DocumentStructureChanged);
responses.add(LayerChanged { affected_layer_path: layer_path })
}
SetOpacityForSelectedLayers { opacity } => {
self.backup(responses);
let opacity = opacity.clamp(0., 1.) as f32;
@@ -749,16 +619,6 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
SetRangeSelectionLayer { new_layer } => {
self.layer_range_selection_reference = new_layer;
}
SetSelectedLayers { replacement_selected_layers } => {
let selected = self.layer_metadata.iter_mut().filter(|(_, layer_metadata)| layer_metadata.selected);
selected.for_each(|(path, layer_metadata)| {
layer_metadata.selected = false;
responses.add(LayerChanged { affected_layer_path: path.clone() })
});
let additional_layers = replacement_selected_layers;
responses.add_front(AddSelectedLayers { additional_layers });
}
SetSnapping {
snapping_enabled,
bounding_box_snapping,
@@ -829,9 +689,6 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
responses.add(DocumentMessage::RenderScrollbars);
responses.add(NodeGraphMessage::RunDocumentGraph);
}
UpdateLayerMetadata { layer_path, layer_metadata } => {
self.layer_metadata.insert(layer_path, layer_metadata);
}
ZoomCanvasTo100Percent => {
responses.add_front(NavigationMessage::SetCanvasZoom { zoom_factor: 1. });
}
@@ -948,17 +805,15 @@ impl DocumentMessageHandler {
&& self.name.starts_with(DEFAULT_DOCUMENT_NAME)
}
fn select_layer(&mut self, path: &[LayerId], render_data: &RenderData) -> Option<Message> {
fn select_layer(&mut self, path: &[LayerId]) -> Option<Message> {
println!("Select_layer fail: {:?}", self.all_layers_sorted());
if let Some(layer) = self.layer_metadata.get_mut(path) {
let render_data = RenderData::new(render_data.font_cache, self.view_mode, None);
layer.selected = true;
let data = self.layer_panel_entry(path.to_vec(), &render_data).ok()?;
let data = self.layer_panel_entry(path.to_vec()).ok()?;
(!path.is_empty()).then(|| FrontendMessage::UpdateDocumentLayerDetails { data }.into())
} else {
warn!("Tried to select non existing layer {path:?}");
warn!("Tried to select non-existing layer {path:?}");
None
}
}
@@ -998,11 +853,11 @@ impl DocumentMessageHandler {
})
}
/// Returns the bounding boxes for all visible layers, optionally excluding any paths.
pub fn bounding_boxes<'a>(&'a self, ignore_document: Option<&'a Vec<Vec<LayerId>>>, _ignore_artboard: Option<LayerId>, render_data: &'a RenderData) -> impl Iterator<Item = [DVec2; 2]> + 'a {
self.visible_layers()
.filter(move |path| ignore_document.map_or(true, |ignore_document| !ignore_document.iter().any(|ig| ig.as_slice() == *path)))
.filter_map(|path| self.document_legacy.viewport_bounding_box(path, render_data).ok()?)
/// Returns the bounding boxes for all visible layers.
pub fn bounding_boxes<'a>(&'a self) -> impl Iterator<Item = [DVec2; 2]> + 'a {
// TODO: Remove this function entirely?
// self.visible_layers().filter_map(|path| self.document_legacy.viewport_bounding_box(path, font_cache).ok()?)
std::iter::empty()
}
fn serialize_structure(&self, folder: LayerNodeIdentifier, structure: &mut Vec<u64>, data: &mut Vec<LayerId>, path: &mut Vec<LayerId>) {
@@ -1159,7 +1014,6 @@ impl DocumentMessageHandler {
let old_root = self.metadata().document_to_viewport;
let document = std::mem::replace(&mut self.document_legacy, document);
self.document_legacy.metadata.document_to_viewport = old_root;
self.document_legacy.root.cache_dirty = true;
let layer_metadata = std::mem::replace(&mut self.layer_metadata, layer_metadata);
@@ -1260,31 +1114,21 @@ impl DocumentMessageHandler {
}
// TODO: This should probably take a slice not a vec, also why does this even exist when `layer_panel_entry_from_path` also exists?
pub fn layer_panel_entry(&mut self, path: Vec<LayerId>, render_data: &RenderData) -> Result<LayerPanelEntry, EditorError> {
pub fn layer_panel_entry(&mut self, path: Vec<LayerId>) -> Result<LayerPanelEntry, EditorError> {
let data: LayerMetadata = *self
.layer_metadata
.get_mut(&path)
.ok_or_else(|| EditorError::Document(format!("Could not get layer metadata for {path:?}")))?;
let layer = self.document_legacy.layer(&path)?;
let entry = LayerPanelEntry::new(&data, self.document_legacy.multiply_transforms(&path)?, layer, path, render_data);
let entry = LayerPanelEntry::new(&data, layer, path);
Ok(entry)
}
/// Returns a list of `LayerPanelEntry`s intended for display purposes. These don't contain
/// any actual data, but rather attributes such as visibility and names of the layers.
pub fn layer_panel(&mut self, path: &[LayerId], render_data: &RenderData) -> Result<Vec<LayerPanelEntry>, EditorError> {
let folder = self.document_legacy.folder(path)?;
let paths: Vec<Vec<LayerId>> = folder.layer_ids.iter().map(|id| [path, &[*id]].concat()).collect();
let entries = paths.iter().rev().filter_map(|path| self.layer_panel_entry_from_path(path, render_data)).collect();
Ok(entries)
}
pub fn layer_panel_entry_from_path(&self, path: &[LayerId], render_data: &RenderData) -> Option<LayerPanelEntry> {
pub fn layer_panel_entry_from_path(&self, path: &[LayerId]) -> Option<LayerPanelEntry> {
let layer_metadata = self.layer_metadata(path);
let transform = self.document_legacy.generate_transform_across_scope(path, Some(self.metadata().document_to_viewport.inverse())).ok()?;
let layer = self.document_legacy.layer(path).ok()?;
Some(LayerPanelEntry::new(layer_metadata, transform, layer, path.to_vec(), render_data))
Some(LayerPanelEntry::new(layer_metadata, layer, path.to_vec()))
}
/// When working with an insert index, deleting the layers may cause the insert index to point to a different location (if the layer being deleted was located before the insert index).
@@ -1297,11 +1141,6 @@ impl DocumentMessageHandler {
Ok(new_insert_index)
}
/// Calculates the bounding box of all layers in the document
pub fn all_layer_bounds(&self, render_data: &RenderData) -> Option<[DVec2; 2]> {
self.document_legacy.viewport_bounding_box(&[], render_data).ok().flatten()
}
/// Calculate the path that new layers should be inserted to.
/// Depends on the selected layers as well as their types (Folder/Non-Folder)
pub fn get_path_for_new_layer(&self) -> Vec<u64> {

View File

@@ -103,7 +103,6 @@ impl MessageHandler<NavigationMessage, (&Document, Option<[DVec2; 2]>, &InputPre
}
responses.add(BroadcastEvent::DocumentIsDirty);
responses.add(DocumentMessage::DirtyRenderDocumentInOutlineView);
responses.add(PortfolioMessage::UpdateDocumentWidgets);
self.create_document_transform(ipp.viewport_bounds.center(), responses);
}
@@ -235,7 +234,6 @@ impl MessageHandler<NavigationMessage, (&Document, Option<[DVec2; 2]>, &InputPre
self.zoom = zoom_factor.clamp(VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_SCALE_MAX);
self.zoom *= Self::clamp_zoom(self.zoom, document_bounds, old_zoom, ipp);
responses.add(BroadcastEvent::DocumentIsDirty);
responses.add(DocumentMessage::DirtyRenderDocumentInOutlineView);
responses.add(PortfolioMessage::UpdateDocumentWidgets);
self.create_document_transform(ipp.viewport_bounds.center(), responses);
}

View File

@@ -15,7 +15,7 @@ use graphene_core::{Artboard, Color};
use glam::{DAffine2, DVec2, IVec2};
pub type LayerIdentifier = Vec<document_legacy::LayerId>;
pub type LayerIdentifier = Vec<document_legacy::document::LayerId>;
#[impl_message(Message, DocumentMessage, GraphOperation)]
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]

View File

@@ -3,8 +3,8 @@ use crate::messages::prelude::*;
use bezier_rs::Subpath;
use document_legacy::document::Document;
use document_legacy::document::LayerId;
use document_legacy::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
use document_legacy::{LayerId, Operation};
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{generate_uuid, DocumentNode, NodeId, NodeInput, NodeNetwork, NodeOutput};
use graphene_core::raster::{BlendMode, ImageFrame};
@@ -315,13 +315,13 @@ impl<'a> ModifyInputsContext<'a> {
}
self.node_graph.network.clear();
self.responses.add(PropertiesPanelMessage::ResendActiveProperties);
self.responses.add(PropertiesPanelMessage::Refresh);
let layer_path = self.layer.to_vec();
if !skip_rerender {
self.responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path });
} else {
self.responses.add(DocumentMessage::FrameClear);
// Code was removed from here which cleared the frame
}
if existing_node_id.is_none() {
self.responses.add(NodeGraphMessage::SendGraph { should_rerender: false });
@@ -340,13 +340,13 @@ impl<'a> ModifyInputsContext<'a> {
self.modify_existing_node_inputs(existing_node_id, &mut update_input);
}
self.responses.add(PropertiesPanelMessage::ResendActiveProperties);
self.responses.add(PropertiesPanelMessage::Refresh);
let layer_path = self.layer.to_vec();
if !skip_rerender {
self.responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path });
} else {
self.responses.add(DocumentMessage::FrameClear);
// Code was removed from here which cleared the frame
}
}
@@ -582,8 +582,6 @@ impl MessageHandler<GraphOperationMessage, (&mut Document, &mut Vec<LayerNodeIde
GraphOperationMessage::FillSet { layer, fill } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(&layer, document, node_graph, responses) {
modify_inputs.fill_set(fill);
} else {
responses.add(Operation::SetLayerFill { path: layer, fill });
}
}
GraphOperationMessage::OpacitySet { layer, opacity } => {
@@ -604,8 +602,6 @@ impl MessageHandler<GraphOperationMessage, (&mut Document, &mut Vec<LayerNodeIde
GraphOperationMessage::StrokeSet { layer, stroke } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(&layer, document, node_graph, responses) {
modify_inputs.stroke_set(stroke);
} else {
responses.add(Operation::SetLayerStroke { path: layer, stroke });
}
}
GraphOperationMessage::TransformChange {

View File

@@ -1,6 +1,6 @@
use crate::messages::prelude::*;
use document_legacy::LayerId;
use document_legacy::document::LayerId;
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{DocumentNode, NodeId, NodeInput};
@@ -10,7 +10,6 @@ pub enum NodeGraphMessage {
// Messages
Init,
SelectedNodesUpdated,
CloseNodeGraph,
ConnectNodesByLink {
output_node: u64,
output_node_connector_index: usize,
@@ -57,9 +56,6 @@ pub enum NodeGraphMessage {
displacement_x: i32,
displacement_y: i32,
},
OpenNodeGraph {
layer_path: Vec<document_legacy::LayerId>,
},
PasteNodes {
serialized_nodes: String,
},

View File

@@ -6,8 +6,8 @@ use crate::messages::prelude::*;
use crate::node_graph_executor::GraphIdentifier;
use document_legacy::document::Document;
use document_legacy::document::LayerId;
use document_legacy::document_metadata::LayerNodeIdentifier;
use document_legacy::LayerId;
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{DocumentNode, NodeId, NodeInput, NodeNetwork, NodeOutput};
use graphene_core::*;
@@ -271,7 +271,7 @@ impl NodeGraphMessageHandler {
}
fn send_graph(network: &NodeNetwork, layer_path: &Option<Vec<LayerId>>, graph_view_overlay_open: bool, responses: &mut VecDeque<Message>) {
responses.add(PropertiesPanelMessage::ResendActiveProperties);
responses.add(PropertiesPanelMessage::Refresh);
if !graph_view_overlay_open {
return;
@@ -489,7 +489,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
}
responses.add(NodeGraphMessage::RunDocumentGraph);
}
NodeGraphMessage::CloseNodeGraph => {}
NodeGraphMessage::ConnectNodesByLink {
output_node,
output_node_connector_index,
@@ -687,7 +686,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
let should_rerender = network.connected_to_output(node_id);
responses.add(NodeGraphMessage::SendGraph { should_rerender });
responses.add(PropertiesPanelMessage::ResendActiveProperties);
responses.add(PropertiesPanelMessage::Refresh);
}
NodeGraphMessage::InsertNode { node_id, document_node } => {
if let Some(network) = document.document_network.nested_network_mut(&self.network) {
@@ -707,19 +706,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
}
Self::send_graph(network, &self.layer_path, graph_view_overlay_open, responses);
}
NodeGraphMessage::OpenNodeGraph { layer_path } => {
self.layer_path = Some(layer_path);
if let Some(network) = document.document_network.nested_network(&self.network) {
responses.add(document.metadata.clear_selected_nodes());
Self::send_graph(network, &self.layer_path, graph_view_overlay_open, responses);
let node_types = document_node_types::collect_node_types();
responses.add(FrontendMessage::UpdateNodeTypes { node_types });
}
self.update_selected(document, responses);
}
NodeGraphMessage::PasteNodes { serialized_nodes } => {
let Some(network) = document.document_network.nested_network(&self.network) else {
warn!("No network");
@@ -776,7 +762,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
}
NodeGraphMessage::SelectedNodesSet { nodes } => {
responses.add(document.metadata.set_selected_nodes(nodes));
responses.add(PropertiesPanelMessage::ResendActiveProperties);
responses.add(PropertiesPanelMessage::Refresh);
}
NodeGraphMessage::SendGraph { should_rerender } => {
if let Some(network) = document.document_network.nested_network(&self.network) {
@@ -797,7 +783,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
let input = NodeInput::Value { tagged_value: value, exposed: false };
responses.add(NodeGraphMessage::SetNodeInput { node_id, input_index, input });
responses.add(PropertiesPanelMessage::ResendActiveProperties);
responses.add(PropertiesPanelMessage::Refresh);
if (node.name != "Imaginate" || input_index == 0) && network.connected_to_output(node_id) {
if let Some(layer_path) = self.layer_path.clone() {
responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path });

View File

@@ -71,7 +71,7 @@ pub struct NodePropertiesContext<'a> {
pub persistent_data: &'a crate::messages::portfolio::utility_types::PersistentData,
pub document: &'a document_legacy::document::Document,
pub responses: &'a mut VecDeque<crate::messages::prelude::Message>,
pub layer_path: &'a [document_legacy::LayerId],
pub layer_path: &'a [document_legacy::document::LayerId],
pub nested_path: &'a [NodeId],
pub executor: &'a mut NodeGraphExecutor,
pub network: &'a NodeNetwork,

View File

@@ -5,8 +5,7 @@ use super::FrontendGraphDataType;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::prelude::*;
use document_legacy::{layers::layer_info::LayerDataTypeDiscriminant, Operation};
use graph_craft::concrete;
use document_legacy::layers::layer_info::LayerDataTypeDiscriminant;
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{DocumentNode, NodeId, NodeInput};
use graph_craft::imaginate_input::{ImaginateMaskStartingFill, ImaginateSamplingMethod, ImaginateServerStatus, ImaginateStatus};
@@ -867,34 +866,10 @@ pub fn load_image_properties(document_node: &DocumentNode, node_id: NodeId, _con
vec![LayoutGroup::Row { widgets: url }]
}
pub fn output_properties(_document_node: &DocumentNode, _node_id: NodeId, context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
let output_type = context.executor.previous_output_type(context.layer_path);
let disabled = match output_type {
Some(output_type) => output_type != concrete!(ImageFrame<Color>),
None => true,
};
pub fn output_properties(_document_node: &DocumentNode, _node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
let label = TextLabel::new("Graphics fed into the Output are drawn in the viewport").widget_holder();
let layer_path_1 = context.layer_path.to_vec();
let layer_path_2 = context.layer_path.to_vec();
let label = TextLabel::new("The graph's output is drawn in the layer").widget_holder();
let download_button = TextButton::new("Download Render Output")
.tooltip("Download the rendered image output as a PNG file")
.disabled(disabled)
.on_update(move |_| DocumentMessage::DownloadLayerImageOutput { layer_path: layer_path_1.clone() }.into())
.widget_holder();
let copy_button = TextButton::new("Copy Render Output")
.tooltip("Copy the rendered image output to the clipboard")
.disabled(disabled)
.on_update(move |_| DocumentMessage::CopyToClipboardLayerImageOutput { layer_path: layer_path_2.clone() }.into())
.widget_holder();
vec![
LayoutGroup::Row { widgets: vec![label] },
LayoutGroup::Row {
widgets: vec![download_button, Separator::new(SeparatorType::Related).widget_holder(), copy_button],
},
]
vec![LayoutGroup::Row { widgets: vec![label] }]
}
pub fn mask_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
@@ -1738,10 +1713,10 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
LayoutGroup::Row { widgets }.with_tooltip("Seed determines the random outcome, enabling limitless unique variations")
};
let transform = context
.executor
.introspect_node_in_network(context.network, &imaginate_node, |network| network.inputs.first().copied(), |frame: &ImageFrame<Color>| frame.transform)
.unwrap_or_default();
// let transform = context
// .executor
// .introspect_node_in_network(context.network, &imaginate_node, |network| network.inputs.first().copied(), |frame: &ImageFrame<Color>| frame.transform)
// .unwrap_or_default();
let image_size = context
.executor
.introspect_node_in_network(
@@ -1770,18 +1745,11 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
let dimensions_is_auto = vec2.is_none();
let vec2 = vec2.unwrap_or_else(|| round((image_size.0 as f64, image_size.1 as f64).into()));
let layer_path = context.layer_path.to_vec();
widgets.extend_from_slice(&[
Separator::new(SeparatorType::Unrelated).widget_holder(),
IconButton::new("Rescale", 24)
.tooltip("Set the layer dimensions to this resolution")
.on_update(move |_| {
Operation::SetLayerScaleAroundPivot {
path: layer_path.clone(),
new_scale: vec2.into(),
}
.into()
})
.on_update(move |_| DialogMessage::RequestComingSoonDialog { issue: None }.into())
.widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(),
CheckboxInput::new(!dimensions_is_auto || transform_not_connected)

View File

@@ -1,9 +1,6 @@
use super::utility_functions::overlay_canvas_element;
use super::utility_types::{OverlayContext, OverlayProvider};
use super::utility_types::OverlayProvider;
use crate::messages::prelude::*;
use wasm_bindgen::JsCast;
#[derive(Debug, Clone, Default)]
pub struct OverlaysMessageHandler {
pub overlay_providers: HashSet<OverlayProvider>,
@@ -16,6 +13,10 @@ impl MessageHandler<OverlaysMessage, (bool, &InputPreprocessorMessageHandler)> f
match message {
#[cfg(target_arch = "wasm32")]
OverlaysMessage::Draw => {
use super::utility_functions::overlay_canvas_element;
use super::utility_types::OverlayContext;
use wasm_bindgen::JsCast;
let canvas = self.canvas.get_or_insert_with(|| overlay_canvas_element().expect("Failed to get canvas element"));
let context = self.context.get_or_insert_with(|| {
@@ -36,7 +37,10 @@ impl MessageHandler<OverlaysMessage, (bool, &InputPreprocessorMessageHandler)> f
}
#[cfg(not(target_arch = "wasm32"))]
OverlaysMessage::Draw => {
warn!("Cannot render overlays on non-Wasm targets {overlays_visible} {ipp:?}.");
warn!(
"Cannot render overlays on non-Wasm targets.\n{responses:?} {overlays_visible} {ipp:?} {:?} {:?}",
self.canvas, self.context
);
}
OverlaysMessage::AddProvider(message) => {
self.overlay_providers.insert(message);

View File

@@ -1,7 +1,6 @@
mod properties_panel_message;
mod properties_panel_message_handler;
pub mod utility_functions;
pub mod utility_types;
#[doc(inline)]

View File

@@ -1,10 +1,5 @@
use super::utility_types::TransformOp;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::prelude::*;
use document_legacy::layers::style::{Fill, Stroke};
use document_legacy::LayerId;
use serde::{Deserialize, Serialize};
#[remain::sorted]
@@ -12,17 +7,6 @@ use serde::{Deserialize, Serialize};
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
pub enum PropertiesPanelMessage {
// Messages
CheckSelectedWasDeleted { path: Vec<LayerId> },
CheckSelectedWasUpdated { path: Vec<LayerId> },
ClearSelection,
Deactivate,
Init,
ModifyFill { fill: Fill },
ModifyPreserveAspect { preserve_aspect: bool },
ModifyStroke { stroke: Stroke },
ModifyTransform { value: f64, transform_op: TransformOp },
ResendActiveProperties,
SetActiveLayers { paths: Vec<Vec<LayerId>> },
SetPivot { new_position: PivotPosition },
UpdateSelectedDocumentProperties,
Clear,
Refresh,
}

View File

@@ -1,20 +1,13 @@
use super::utility_functions::{register_artwork_layer_properties, register_document_graph_properties};
use super::utility_types::PropertiesPanelMessageHandlerData;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::properties_panel::utility_functions::apply_transform_operation;
use crate::messages::portfolio::document::node_graph::NodePropertiesContext;
use crate::messages::portfolio::utility_types::PersistentData;
use crate::messages::prelude::*;
use document_legacy::layers::layer_info::LayerDataTypeDiscriminant;
use document_legacy::layers::style::{RenderData, ViewMode};
use document_legacy::{LayerId, Operation};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct PropertiesPanelMessageHandler {
active_selection: Option<Vec<LayerId>>, // TODO: Delete this if it's indeed dead code?
}
pub struct PropertiesPanelMessageHandler;
impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPanelMessageHandlerData<'a>)> for PropertiesPanelMessageHandler {
#[remain::check]
@@ -24,42 +17,13 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
let PropertiesPanelMessageHandlerData {
document_name,
artwork_document,
selected_layers,
node_graph_message_handler,
executor,
..
} = data;
let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::Normal, None);
match message {
SetActiveLayers { paths } => {
if paths.len() != 1 {
// TODO: Allow for multiple selected layers
responses.add(PropertiesPanelMessage::ClearSelection);
responses.add(NodeGraphMessage::CloseNodeGraph);
} else {
let path = paths.into_iter().next().unwrap();
if self.active_selection.as_ref() != Some(&path) {
// Update the layer visibility
if artwork_document
.layer(&path)
.ok()
.filter(|layer| LayerDataTypeDiscriminant::from(&layer.data) == LayerDataTypeDiscriminant::Layer)
.is_some()
{
responses.add(NodeGraphMessage::OpenNodeGraph { layer_path: path.clone() });
} else {
responses.add(NodeGraphMessage::CloseNodeGraph);
}
self.active_selection = Some(path);
responses.add(PropertiesPanelMessage::ResendActiveProperties);
}
}
}
ClearSelection => {
// This causes the Properties panel to change, so this needs to happen before the following lines clear the Properties panel
responses.add(NodeGraphMessage::CloseNodeGraph);
Clear => {
responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout::new(vec![])),
layout_target: LayoutTarget::PropertiesOptions,
@@ -68,84 +32,41 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
layout: Layout::WidgetLayout(WidgetLayout::new(vec![])),
layout_target: LayoutTarget::PropertiesSections,
});
self.active_selection = None;
}
Deactivate => responses.add(BroadcastMessage::UnsubscribeEvent {
on: BroadcastEvent::SelectionChanged,
message: Box::new(PropertiesPanelMessage::UpdateSelectedDocumentProperties.into()),
}),
Init => responses.add(BroadcastMessage::SubscribeEvent {
on: BroadcastEvent::SelectionChanged,
send: Box::new(PropertiesPanelMessage::UpdateSelectedDocumentProperties.into()),
}),
ModifyTransform { value, transform_op } => {
let path = self.active_selection.as_ref().expect("Received update for properties panel with no active layer");
let layer = artwork_document.layer(path).unwrap();
Refresh => {
let mut context = NodePropertiesContext {
persistent_data,
document: artwork_document,
responses,
nested_path: &node_graph_message_handler.network,
layer_path: &[],
executor,
network: &artwork_document.document_network,
};
let transform = apply_transform_operation(layer, transform_op, value, &render_data);
let properties_sections = node_graph_message_handler.collate_properties(&mut context);
self.create_document_operation(Operation::SetLayerTransform { path: path.clone(), transform }, true, responses);
}
ModifyPreserveAspect { preserve_aspect } => {
let layer_path = self.active_selection.clone().expect("Received update for properties panel with no active layer");
self.create_document_operation(Operation::SetLayerPreserveAspect { layer_path, preserve_aspect }, true, responses);
}
ModifyFill { fill } => {
let path = self.active_selection.clone().expect("Received update for properties panel with no active layer");
self.create_document_operation(Operation::SetLayerFill { path, fill }, true, responses);
}
ModifyStroke { stroke } => {
let path = self.active_selection.clone().expect("Received update for properties panel with no active layer");
self.create_document_operation(Operation::SetLayerStroke { path, stroke }, true, responses);
}
SetPivot { new_position } => {
let layer = self.active_selection.clone().expect("Received update for properties panel with no active layer");
let position: Option<glam::DVec2> = new_position.into();
let pivot = position.unwrap();
let options_bar = vec![LayoutGroup::Row {
widgets: vec![
IconLabel::new("File").tooltip("Document").widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(),
TextInput::new(document_name)
.on_update(|text_input| DocumentMessage::RenameDocument { new_name: text_input.value.clone() }.into())
.widget_holder(),
Separator::new(SeparatorType::Related).widget_holder(),
PopoverButton::new("Additional Options", "Coming soon").widget_holder(),
],
}];
responses.add(DocumentMessage::StartTransaction);
responses.add(GraphOperationMessage::TransformSetPivot { layer, pivot });
context.responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout::new(options_bar)),
layout_target: LayoutTarget::PropertiesOptions,
});
context.responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout::new(properties_sections)),
layout_target: LayoutTarget::PropertiesSections,
});
}
CheckSelectedWasUpdated { path } => {
if self.matches_selected(&path) {
responses.add(PropertiesPanelMessage::ResendActiveProperties)
}
}
CheckSelectedWasDeleted { path } => {
if self.matches_selected(&path) {
self.active_selection = None;
responses.add(LayoutMessage::SendLayout {
layout_target: LayoutTarget::PropertiesOptions,
layout: Layout::WidgetLayout(WidgetLayout::default()),
});
responses.add(LayoutMessage::SendLayout {
layout_target: LayoutTarget::PropertiesSections,
layout: Layout::WidgetLayout(WidgetLayout::default()),
});
responses.add(NodeGraphMessage::CloseNodeGraph);
}
}
ResendActiveProperties => {
if let Some(path) = self.active_selection.clone() {
// TODO: Remove this conditional now that the document graph is the only form of graph? (Also any other related code.)
let layer = artwork_document.layer(&path).unwrap();
register_artwork_layer_properties(artwork_document, path, layer, responses, persistent_data, node_graph_message_handler, executor);
} else {
let context = crate::messages::portfolio::document::node_graph::NodePropertiesContext {
persistent_data,
document: artwork_document,
responses,
nested_path: &node_graph_message_handler.network,
layer_path: &[],
executor,
network: &artwork_document.document_network,
};
register_document_graph_properties(context, node_graph_message_handler, document_name);
}
}
UpdateSelectedDocumentProperties => responses.add(PropertiesPanelMessage::SetActiveLayers {
paths: selected_layers.map(|path| path.to_vec()).collect(),
}),
}
}
@@ -153,21 +74,3 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
actions!(PropertiesMessageDiscriminant;)
}
}
impl PropertiesPanelMessageHandler {
fn matches_selected(&self, path: &[LayerId]) -> bool {
let last_active_path_id = self.active_selection.as_ref().and_then(|v| v.last().copied());
let last_modified = path.last().copied();
matches!((last_active_path_id, last_modified), (Some(active_last), Some(modified_last)) if active_last == modified_last)
}
fn create_document_operation(&self, operation: Operation, commit_history: bool, responses: &mut VecDeque<Message>) {
// Commit to history before the modification
if commit_history {
responses.add(DocumentMessage::StartTransaction);
}
// Dispatch the relevant operation to the main document
responses.add(DocumentMessage::DispatchOperation(Box::new(operation)));
}
}

View File

@@ -1,793 +0,0 @@
use super::utility_types::TransformOp;
use crate::application::generate_uuid;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::node_graph::NodePropertiesContext;
use crate::messages::portfolio::utility_types::PersistentData;
use crate::messages::prelude::*;
use crate::node_graph_executor::NodeGraphExecutor;
use document_legacy::document::Document;
use document_legacy::layers::layer_info::{LegacyLayer, LegacyLayerType};
use document_legacy::layers::style::{Fill, Gradient, GradientType, LineCap, LineJoin, RenderData, Stroke, ViewMode};
use graphene_core::raster::color::Color;
use glam::{DAffine2, DVec2};
use std::f64::consts::PI;
use std::sync::Arc;
pub fn apply_transform_operation(layer: &LegacyLayer, transform_op: TransformOp, value: f64, render_data: &RenderData) -> [f64; 6] {
let transformation = match transform_op {
TransformOp::X => DAffine2::update_x,
TransformOp::Y => DAffine2::update_y,
TransformOp::ScaleX | TransformOp::Width => DAffine2::update_scale_x,
TransformOp::ScaleY | TransformOp::Height => DAffine2::update_scale_y,
TransformOp::Rotation => DAffine2::update_rotation,
};
let scale = match transform_op {
TransformOp::Width => layer.bounding_transform(render_data).scale_x() / layer.transform.scale_x(),
TransformOp::Height => layer.bounding_transform(render_data).scale_y() / layer.transform.scale_y(),
_ => 1.,
};
// Apply the operation
let transform = transformation(layer.transform, value / scale);
// Return this transform if it is not a dimensions change
if !matches!(transform_op, TransformOp::ScaleX | TransformOp::Width | TransformOp::ScaleY | TransformOp::Height) {
return transform.to_cols_array();
}
// Find the layerspace pivot
let pivot = DAffine2::from_translation(layer.transform.transform_point2(layer.layerspace_pivot(render_data)));
// Find the delta transform
let mut delta = layer.transform.inverse() * transform;
if !delta.is_finite() {
return layer.transform.to_cols_array();
}
// Preserve aspect ratio
if matches!(transform_op, TransformOp::ScaleX | TransformOp::Width) && layer.preserve_aspect {
let scale_x = layer.transform.scale_x();
if scale_x != 0. {
delta = DAffine2::from_scale((1., (value / scale) / scale_x).into()) * delta;
}
} else if layer.preserve_aspect {
let scale_y = layer.transform.scale_y();
if scale_y != 0. {
delta = DAffine2::from_scale(((value / scale) / scale_y, 1.).into()) * delta;
}
}
// Transform around pivot
((pivot * delta * pivot.inverse()) * layer.transform).to_cols_array()
}
pub fn register_artwork_layer_properties(
document: &Document,
layer_path: Vec<document_legacy::LayerId>,
layer: &LegacyLayer,
responses: &mut VecDeque<Message>,
persistent_data: &PersistentData,
node_graph_message_handler: &NodeGraphMessageHandler,
executor: &mut NodeGraphExecutor,
) {
let options_bar = vec![LayoutGroup::Row {
widgets: vec![
match &layer.data {
LegacyLayerType::Folder(_) => IconLabel::new("Folder").tooltip("Folder").widget_holder(),
LegacyLayerType::Shape(_) => IconLabel::new("NodeShape").tooltip("Shape").widget_holder(),
LegacyLayerType::Layer(_) => IconLabel::new("Layer").tooltip("Layer").widget_holder(),
},
Separator::new(SeparatorType::Unrelated).widget_holder(),
TextInput::new(layer.name.clone().unwrap_or_else(|| "Untitled Layer".to_string()))
.on_update(|_text_input: &TextInput| panic!("This is presumed to be dead code, but if you are seeing this crash, please file a bug report."))
.widget_holder(),
Separator::new(SeparatorType::Related).widget_holder(),
PopoverButton::new("Additional Options", "Coming soon").widget_holder(),
],
}];
let properties_body = match &layer.data {
LegacyLayerType::Shape(shape) => {
if let Some(fill_layout) = node_section_fill(shape.style.fill()) {
vec![
node_section_transform(layer, persistent_data),
fill_layout,
node_section_stroke(&shape.style.stroke().unwrap_or_default()),
]
} else {
vec![node_section_transform(layer, persistent_data), node_section_stroke(&shape.style.stroke().unwrap_or_default())]
}
}
LegacyLayerType::Layer(layer) => {
let mut context = NodePropertiesContext {
persistent_data,
document,
responses,
nested_path: &node_graph_message_handler.network,
layer_path: &layer_path,
executor,
network: &layer.network,
};
let properties_sections = node_graph_message_handler.collate_properties(&mut context);
properties_sections
}
LegacyLayerType::Folder(_) => {
vec![node_section_transform(layer, persistent_data)]
}
};
responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout::new(options_bar)),
layout_target: LayoutTarget::PropertiesOptions,
});
responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout::new(properties_body)),
layout_target: LayoutTarget::PropertiesSections,
});
}
pub fn register_document_graph_properties(mut context: NodePropertiesContext, node_graph_message_handler: &NodeGraphMessageHandler, document_name: &str) {
let properties_sections = node_graph_message_handler.collate_properties(&mut context);
let options_bar = vec![LayoutGroup::Row {
widgets: vec![
IconLabel::new("File").tooltip("Document").widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(),
TextInput::new(document_name)
.on_update(|text_input| DocumentMessage::RenameDocument { new_name: text_input.value.clone() }.into())
.widget_holder(),
Separator::new(SeparatorType::Related).widget_holder(),
PopoverButton::new("Additional Options", "Coming soon").widget_holder(),
],
}];
context.responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout::new(options_bar)),
layout_target: LayoutTarget::PropertiesOptions,
});
context.responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout::new(properties_sections)),
layout_target: LayoutTarget::PropertiesSections,
});
}
fn node_section_transform(layer: &LegacyLayer, persistent_data: &PersistentData) -> LayoutGroup {
let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::default(), None);
let pivot = layer.transform.transform_vector2(layer.layerspace_pivot(&render_data));
LayoutGroup::Section {
name: "Transform".into(),
layout: vec![
LayoutGroup::Row {
widgets: vec![
TextLabel::new("Location").widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(),
PivotInput::new(layer.pivot.into())
.on_update(|pivot_input: &PivotInput| PropertiesPanelMessage::SetPivot { new_position: pivot_input.position }.into())
.widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(),
NumberInput::new(Some(layer.transform.x() + pivot.x))
.label("X")
.unit(" px")
.min(-((1u64 << std::f64::MANTISSA_DIGITS) as f64))
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
.on_update(move |number_input: &NumberInput| {
PropertiesPanelMessage::ModifyTransform {
value: number_input.value.unwrap() - pivot.x,
transform_op: TransformOp::X,
}
.into()
})
.widget_holder(),
Separator::new(SeparatorType::Related).widget_holder(),
NumberInput::new(Some(layer.transform.y() + pivot.y))
.label("Y")
.unit(" px")
.min(-((1u64 << std::f64::MANTISSA_DIGITS) as f64))
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
.on_update(move |number_input: &NumberInput| {
PropertiesPanelMessage::ModifyTransform {
value: number_input.value.unwrap() - pivot.y,
transform_op: TransformOp::Y,
}
.into()
})
.widget_holder(),
],
},
LayoutGroup::Row {
widgets: vec![
TextLabel::new("Rotation").widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: These three separators add up to 24px,
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: which is the width of the Assist area.
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: Remove these when we have proper entry row formatting that includes room for Assists.
Separator::new(SeparatorType::Unrelated).widget_holder(),
NumberInput::new(Some(layer.transform.rotation() * 180. / PI))
.unit("°")
.mode(NumberInputMode::Range)
.range_min(Some(-180.))
.range_max(Some(180.))
.on_update(|number_input: &NumberInput| {
PropertiesPanelMessage::ModifyTransform {
value: number_input.value.unwrap() / 180. * PI,
transform_op: TransformOp::Rotation,
}
.into()
})
.widget_holder(),
],
},
LayoutGroup::Row {
widgets: vec![
TextLabel::new("Scale").widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(),
Separator::new(SeparatorType::Related).widget_holder(),
CheckboxInput::new(layer.preserve_aspect)
.icon("Link")
.tooltip("Preserve Aspect Ratio")
.on_update(|input: &CheckboxInput| PropertiesPanelMessage::ModifyPreserveAspect { preserve_aspect: input.checked }.into())
.widget_holder(),
Separator::new(SeparatorType::Related).widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(),
NumberInput::new(Some(layer.transform.scale_x()))
.label("X")
.unit("")
.min(0.)
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
.on_update(|number_input: &NumberInput| {
PropertiesPanelMessage::ModifyTransform {
value: number_input.value.unwrap(),
transform_op: TransformOp::ScaleX,
}
.into()
})
.widget_holder(),
Separator::new(SeparatorType::Related).widget_holder(),
NumberInput::new(Some(layer.transform.scale_y()))
.label("Y")
.unit("")
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
.on_update(|number_input: &NumberInput| {
PropertiesPanelMessage::ModifyTransform {
value: number_input.value.unwrap(),
transform_op: TransformOp::ScaleY,
}
.into()
})
.widget_holder(),
],
},
LayoutGroup::Row {
widgets: vec![
TextLabel::new("Dimensions").widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: These three separators add up to 24px,
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: which is the width of the Assist area.
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: Remove these when we have proper entry row formatting that includes room for Assists.
Separator::new(SeparatorType::Unrelated).widget_holder(),
NumberInput::new(Some(layer.bounding_transform(&render_data).scale_x()))
.label("W")
.unit(" px")
.max((1u64 << f64::MANTISSA_DIGITS) as f64)
.on_update(|number_input: &NumberInput| {
PropertiesPanelMessage::ModifyTransform {
value: number_input.value.unwrap(),
transform_op: TransformOp::Width,
}
.into()
})
.widget_holder(),
Separator::new(SeparatorType::Related).widget_holder(),
NumberInput::new(Some(layer.bounding_transform(&render_data).scale_y()))
.label("H")
.unit(" px")
.max((1u64 << f64::MANTISSA_DIGITS) as f64)
.on_update(|number_input: &NumberInput| {
PropertiesPanelMessage::ModifyTransform {
value: number_input.value.unwrap(),
transform_op: TransformOp::Height,
}
.into()
})
.widget_holder(),
],
},
],
}
}
fn node_gradient_type(gradient: &Gradient) -> LayoutGroup {
let selected_index = match gradient.gradient_type {
GradientType::Linear => 0,
GradientType::Radial => 1,
};
let mut cloned_gradient_linear = gradient.clone();
cloned_gradient_linear.gradient_type = GradientType::Linear;
let mut cloned_gradient_radial = gradient.clone();
cloned_gradient_radial.gradient_type = GradientType::Radial;
LayoutGroup::Row {
widgets: vec![
TextLabel::new("Gradient Type").widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: These three separators add up to 24px,
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: which is the width of the Assist area.
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: Remove these when we have proper entry row formatting that includes room for Assists.
Separator::new(SeparatorType::Unrelated).widget_holder(),
RadioInput::new(vec![
RadioEntryData::new("linear")
.label("Linear")
.tooltip("Linear gradient changes colors from one side to the other along a line")
.on_update(move |_| {
PropertiesPanelMessage::ModifyFill {
fill: Fill::Gradient(cloned_gradient_linear.clone()),
}
.into()
}),
RadioEntryData::new("radial")
.label("Radial")
.tooltip("Radial gradient changes colors from the inside to the outside of a circular area")
.on_update(move |_| {
PropertiesPanelMessage::ModifyFill {
fill: Fill::Gradient(cloned_gradient_radial.clone()),
}
.into()
}),
])
.selected_index(Some(selected_index))
.widget_holder(),
],
}
}
fn node_gradient_color(gradient: &Gradient, position: usize) -> LayoutGroup {
let gradient_clone = Arc::new(gradient.clone());
let gradient_2 = gradient_clone.clone();
let gradient_3 = gradient_clone.clone();
let send_fill_message = move |new_gradient: Gradient| PropertiesPanelMessage::ModifyFill { fill: Fill::Gradient(new_gradient) }.into();
let value = format!("Gradient: {:.0}%", gradient_clone.positions[position].0 * 100.);
let mut widgets = vec![
TextLabel::new(value)
.tooltip("Adjustable by dragging the gradient stops in the viewport with the Gradient tool active")
.widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: These three separators add up to 24px,
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: which is the width of the Assist area.
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: Remove these when we have proper entry row formatting that includes room for Assists.
Separator::new(SeparatorType::Unrelated).widget_holder(),
ColorButton::new(gradient_clone.positions[position].1)
.on_update(move |text_input: &ColorButton| {
let mut new_gradient = (*gradient_clone).clone();
new_gradient.positions[position].1 = text_input.value;
send_fill_message(new_gradient)
})
.widget_holder(),
];
let mut skip_separator = false;
// Remove button
if gradient.positions.len() != position + 1 && position != 0 {
let on_update = move |_: &IconButton| {
let mut new_gradient = (*gradient_3).clone();
new_gradient.positions.remove(position);
send_fill_message(new_gradient)
};
skip_separator = true;
widgets.push(Separator::new(SeparatorType::Related).widget_holder());
widgets.push(IconButton::new("Remove", 16).tooltip("Remove this gradient stop").on_update(on_update).widget_holder());
}
// Add button
if gradient.positions.len() != position + 1 {
let on_update = move |_: &IconButton| {
let mut gradient = (*gradient_2).clone();
let get_color = |index: usize| match (gradient.positions[index].1, gradient.positions.get(index + 1).and_then(|x| x.1)) {
(Some(a), Some(b)) => Color::from_rgbaf32((a.r() + b.r()) / 2., (a.g() + b.g()) / 2., (a.b() + b.b()) / 2., ((a.a() + b.a()) / 2.).clamp(0., 1.)),
(Some(v), _) | (_, Some(v)) => Some(v),
_ => Some(Color::WHITE),
};
let get_pos = |index: usize| (gradient.positions[index].0 + gradient.positions.get(index + 1).map(|v| v.0).unwrap_or(1.)) / 2.;
gradient.positions.push((get_pos(position), get_color(position)));
gradient.positions.sort_unstable_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
send_fill_message(gradient)
};
if !skip_separator {
widgets.push(Separator::new(SeparatorType::Related).widget_holder());
}
widgets.push(IconButton::new("Add", 16).tooltip("Add a gradient stop after this").on_update(on_update).widget_holder());
}
LayoutGroup::Row { widgets }
}
fn node_section_fill(fill: &Fill) -> Option<LayoutGroup> {
let initial_color = if let Fill::Solid(color) = fill { *color } else { Color::BLACK };
match fill {
Fill::Solid(_) | Fill::None => Some(LayoutGroup::Section {
name: "Fill".into(),
layout: vec![
LayoutGroup::Row {
widgets: vec![
TextLabel::new("Color").widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: These three separators add up to 24px,
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: which is the width of the Assist area.
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: Remove these when we have proper entry row formatting that includes room for Assists.
Separator::new(SeparatorType::Unrelated).widget_holder(),
ColorButton::new(if let Fill::Solid(color) = fill { Some(*color) } else { None })
.on_update(|text_input: &ColorButton| {
let fill = if let Some(value) = text_input.value { Fill::Solid(value) } else { Fill::None };
PropertiesPanelMessage::ModifyFill { fill }.into()
})
.widget_holder(),
],
},
LayoutGroup::Row {
widgets: vec![
TextLabel::new("").widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: These three separators add up to 24px,
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: which is the width of the Assist area.
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: Remove these when we have proper entry row formatting that includes room for Assists.
Separator::new(SeparatorType::Unrelated).widget_holder(),
TextButton::new("Use Gradient")
.tooltip("Change this fill from a solid color to a gradient")
.on_update(move |_: &TextButton| {
let (r, g, b, _) = initial_color.components();
let opposite_color = Color::from_rgbaf32(1. - r, 1. - g, 1. - b, 1.).unwrap();
PropertiesPanelMessage::ModifyFill {
fill: Fill::Gradient(Gradient::new(
DVec2::new(0., 0.5),
initial_color,
DVec2::new(1., 0.5),
opposite_color,
DAffine2::IDENTITY,
generate_uuid(),
GradientType::Linear,
)),
}
.into()
})
.widget_holder(),
],
},
],
}),
Fill::Gradient(gradient) => Some(LayoutGroup::Section {
name: "Fill".into(),
layout: {
let cloned_gradient = gradient.clone();
let first_color = gradient.positions.get(0).unwrap_or(&(0., None)).1;
let mut layout = vec![node_gradient_type(gradient)];
layout.extend((0..gradient.positions.len()).map(|pos| node_gradient_color(gradient, pos)));
layout.push(LayoutGroup::Row {
widgets: vec![
TextLabel::new("").widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: These three separators add up to 24px,
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: which is the width of the Assist area.
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: Remove these when we have proper entry row formatting that includes room for Assists.
Separator::new(SeparatorType::Unrelated).widget_holder(),
TextButton::new("Invert")
.icon(Some("Swap".into()))
.tooltip("Reverse the order of each color stop")
.on_update(move |_: &TextButton| {
let mut new_gradient = cloned_gradient.clone();
new_gradient.positions = new_gradient.positions.iter().map(|(distance, color)| (1. - distance, *color)).collect();
new_gradient.positions.reverse();
PropertiesPanelMessage::ModifyFill { fill: Fill::Gradient(new_gradient) }.into()
})
.widget_holder(),
],
});
layout.push(LayoutGroup::Row {
widgets: vec![
TextLabel::new("").widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: These three separators add up to 24px,
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: which is the width of the Assist area.
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: Remove these when we have proper entry row formatting that includes room for Assists.
Separator::new(SeparatorType::Unrelated).widget_holder(),
TextButton::new("Use Solid Color")
.tooltip("Change this fill from a gradient to a solid color, keeping the 0% stop color")
.on_update(move |_: &TextButton| {
PropertiesPanelMessage::ModifyFill {
fill: Fill::Solid(first_color.unwrap_or_default()),
}
.into()
})
.widget_holder(),
],
});
layout
},
}),
}
}
fn node_section_stroke(stroke: &Stroke) -> LayoutGroup {
// We have to make multiple variables because they get moved into different closures.
let internal_stroke1 = stroke.clone();
let internal_stroke2 = stroke.clone();
let internal_stroke3 = stroke.clone();
let internal_stroke4 = stroke.clone();
let internal_stroke5 = stroke.clone();
let internal_stroke6 = stroke.clone();
let internal_stroke7 = stroke.clone();
let internal_stroke8 = stroke.clone();
let internal_stroke9 = stroke.clone();
let internal_stroke10 = stroke.clone();
let internal_stroke11 = stroke.clone();
LayoutGroup::Section {
name: "Stroke".into(),
layout: vec![
LayoutGroup::Row {
widgets: vec![
TextLabel::new("Color").widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: These three separators add up to 24px,
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: which is the width of the Assist area.
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: Remove these when we have proper entry row formatting that includes room for Assists.
Separator::new(SeparatorType::Unrelated).widget_holder(),
ColorButton::new(stroke.color())
.on_update(move |text_input: &ColorButton| {
internal_stroke1
.clone()
.with_color(&text_input.value)
.map_or(PropertiesPanelMessage::ResendActiveProperties.into(), |stroke| PropertiesPanelMessage::ModifyStroke { stroke }.into())
})
.widget_holder(),
],
},
LayoutGroup::Row {
widgets: vec![
TextLabel::new("Weight").widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: These three separators add up to 24px,
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: which is the width of the Assist area.
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: Remove these when we have proper entry row formatting that includes room for Assists.
Separator::new(SeparatorType::Unrelated).widget_holder(),
NumberInput::new(Some(stroke.weight()))
.is_integer(false)
.min(0.)
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
.unit(" px")
.on_update(move |number_input: &NumberInput| {
PropertiesPanelMessage::ModifyStroke {
stroke: internal_stroke2.clone().with_weight(number_input.value.unwrap()),
}
.into()
})
.widget_holder(),
],
},
LayoutGroup::Row {
widgets: vec![
TextLabel::new("Dash Lengths").widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: These three separators add up to 24px,
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: which is the width of the Assist area.
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: Remove these when we have proper entry row formatting that includes room for Assists.
Separator::new(SeparatorType::Unrelated).widget_holder(),
TextInput::new(stroke.dash_lengths())
.centered(true)
.on_update(move |text_input: &TextInput| {
internal_stroke3
.clone()
.with_dash_lengths(&text_input.value)
.map_or(PropertiesPanelMessage::ResendActiveProperties.into(), |stroke| PropertiesPanelMessage::ModifyStroke { stroke }.into())
})
.widget_holder(),
],
},
LayoutGroup::Row {
widgets: vec![
TextLabel::new("Dash Offset").widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: These three separators add up to 24px,
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: which is the width of the Assist area.
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: Remove these when we have proper entry row formatting that includes room for Assists.
Separator::new(SeparatorType::Unrelated).widget_holder(),
NumberInput::new(Some(stroke.dash_offset()))
.is_integer(true)
.min(0.)
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
.unit(" px")
.on_update(move |number_input: &NumberInput| {
PropertiesPanelMessage::ModifyStroke {
stroke: internal_stroke4.clone().with_dash_offset(number_input.value.unwrap()),
}
.into()
})
.widget_holder(),
],
},
LayoutGroup::Row {
widgets: vec![
TextLabel::new("Line Cap").widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: These three separators add up to 24px,
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: which is the width of the Assist area.
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: Remove these when we have proper entry row formatting that includes room for Assists.
Separator::new(SeparatorType::Unrelated).widget_holder(),
RadioInput::new(vec![
RadioEntryData::new("Butt").on_update(move |_| {
PropertiesPanelMessage::ModifyStroke {
stroke: internal_stroke6.clone().with_line_cap(LineCap::Butt),
}
.into()
}),
RadioEntryData::new("Round").on_update(move |_| {
PropertiesPanelMessage::ModifyStroke {
stroke: internal_stroke7.clone().with_line_cap(LineCap::Round),
}
.into()
}),
RadioEntryData::new("Square").on_update(move |_| {
PropertiesPanelMessage::ModifyStroke {
stroke: internal_stroke8.clone().with_line_cap(LineCap::Square),
}
.into()
}),
])
.selected_index(Some(stroke.line_cap_index()))
.widget_holder(),
],
},
LayoutGroup::Row {
widgets: vec![
TextLabel::new("Line Join").widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: These three separators add up to 24px,
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: which is the width of the Assist area.
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: Remove these when we have proper entry row formatting that includes room for Assists.
Separator::new(SeparatorType::Unrelated).widget_holder(),
RadioInput::new(vec![
RadioEntryData::new("Miter").on_update(move |_| {
PropertiesPanelMessage::ModifyStroke {
stroke: internal_stroke9.clone().with_line_join(LineJoin::Miter),
}
.into()
}),
RadioEntryData::new("Bevel").on_update(move |_| {
PropertiesPanelMessage::ModifyStroke {
stroke: internal_stroke10.clone().with_line_join(LineJoin::Bevel),
}
.into()
}),
RadioEntryData::new("Round").on_update(move |_| {
PropertiesPanelMessage::ModifyStroke {
stroke: internal_stroke11.clone().with_line_join(LineJoin::Round),
}
.into()
}),
])
.selected_index(Some(stroke.line_join_index()))
.widget_holder(),
],
},
// TODO: Gray out this row when Line Join isn't set to Miter
LayoutGroup::Row {
widgets: vec![
TextLabel::new("Miter Limit").widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(),
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: These three separators add up to 24px,
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: which is the width of the Assist area.
Separator::new(SeparatorType::Unrelated).widget_holder(), // TODO: Remove these when we have proper entry row formatting that includes room for Assists.
Separator::new(SeparatorType::Unrelated).widget_holder(),
NumberInput::new(Some(stroke.line_join_miter_limit() as f64))
.is_integer(true)
.min(0.)
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
.unit("")
.on_update(move |number_input: &NumberInput| {
PropertiesPanelMessage::ModifyStroke {
stroke: internal_stroke5.clone().with_line_join_miter_limit(number_input.value.unwrap()),
}
.into()
})
.widget_holder(),
],
},
],
}
}
trait DAffine2Utils {
fn scale_x(&self) -> f64;
fn update_scale_x(self, new_width: f64) -> Self;
fn scale_y(&self) -> f64;
fn update_scale_y(self, new_height: f64) -> Self;
fn x(&self) -> f64;
fn update_x(self, new_x: f64) -> Self;
fn y(&self) -> f64;
fn update_y(self, new_y: f64) -> Self;
fn rotation(&self) -> f64;
fn update_rotation(self, new_rotation: f64) -> Self;
}
impl DAffine2Utils for DAffine2 {
fn scale_x(&self) -> f64 {
self.transform_vector2((1., 0.).into()).length()
}
fn update_scale_x(self, new_width: f64) -> Self {
let scale_x = self.scale_x();
if scale_x != 0. {
self * DAffine2::from_scale((new_width / scale_x, 1.).into())
} else {
self
}
}
fn scale_y(&self) -> f64 {
self.transform_vector2((0., 1.).into()).length()
}
fn update_scale_y(self, new_height: f64) -> Self {
let scale_y = self.scale_y();
if scale_y != 0. {
self * DAffine2::from_scale((1., new_height / scale_y).into())
} else {
self
}
}
fn x(&self) -> f64 {
self.translation.x
}
fn update_x(mut self, new_x: f64) -> Self {
self.translation.x = new_x;
self
}
fn y(&self) -> f64 {
self.translation.y
}
fn update_y(mut self, new_y: f64) -> Self {
self.translation.y = new_y;
self
}
fn rotation(&self) -> f64 {
if self.scale_x() != 0. {
let cos = self.matrix2.col(0).x / self.scale_x();
let sin = self.matrix2.col(0).y / self.scale_x();
sin.atan2(cos)
} else if self.scale_y() != 0. {
let sin = -self.matrix2.col(1).x / self.scale_y();
let cos = self.matrix2.col(1).y / self.scale_y();
sin.atan2(cos)
} else {
// Rotation information does not exists anymore in the matrix
// return 0 for user experience.
0.
}
}
fn update_rotation(self, new_rotation: f64) -> Self {
let width = self.scale_x();
let height = self.scale_y();
let half_width = width / 2.;
let half_height = height / 2.;
let angle_translation_offset = |angle: f64| DVec2::new(-half_width * angle.cos() + half_height * angle.sin(), -half_width * angle.sin() - half_height * angle.cos());
let angle_translation_adjustment = angle_translation_offset(new_rotation) - angle_translation_offset(self.rotation());
DAffine2::from_scale_angle_translation((width, height).into(), new_rotation, self.translation + angle_translation_adjustment)
}
}

View File

@@ -1,7 +1,5 @@
use document_legacy::document::Document as DocumentLegacy;
use document_legacy::LayerId;
use serde::{Deserialize, Serialize};
use document_legacy::document::LayerId;
use crate::{messages::prelude::NodeGraphMessageHandler, node_graph_executor::NodeGraphExecutor};
@@ -12,14 +10,3 @@ pub struct PropertiesPanelMessageHandlerData<'a> {
pub node_graph_message_handler: &'a NodeGraphMessageHandler,
pub executor: &'a mut NodeGraphExecutor,
}
#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize, specta::Type)]
pub enum TransformOp {
X,
Y,
ScaleX,
ScaleY,
Width,
Height,
Rotation,
}

View File

@@ -1,8 +1,6 @@
use document_legacy::layers::layer_info::{LayerData, LayerDataTypeDiscriminant, LegacyLayer};
use document_legacy::layers::style::RenderData;
use document_legacy::LayerId;
use document_legacy::document::LayerId;
use document_legacy::layers::layer_info::{LayerDataTypeDiscriminant, LegacyLayer};
use glam::{DAffine2, DVec2};
use serde::ser::SerializeStruct;
use serde::{Deserialize, Serialize};
@@ -60,45 +58,15 @@ pub struct LayerPanelEntry {
impl LayerPanelEntry {
// TODO: Deprecate this because it's using document-legacy layer data which is no longer linked to data from the node graph,
// TODO: so this doesn't feed `name` (that's fed elsewhere) or `visible` (that's broken entirely), etc.
pub fn new(layer_metadata: &LayerMetadata, transform: DAffine2, layer: &LegacyLayer, path: Vec<LayerId>, render_data: &RenderData) -> Self {
let name = layer.name.clone().unwrap_or_else(|| String::from(""));
let mut tooltip = name.clone();
if cfg!(debug_assertions) {
tooltip += "\nLayer Path: ";
tooltip += &path.iter().map(|id| id.to_string()).collect::<Vec<_>>().join(" / ");
tooltip = tooltip.trim().to_string();
}
let arr = layer.data.bounding_box(transform, render_data).unwrap_or([DVec2::ZERO, DVec2::ZERO]);
let arr = arr.iter().map(|x| (*x).into()).collect::<Vec<(f64, f64)>>();
let mut thumbnail = String::new();
let mut svg_defs = String::new();
layer.data.clone().render(&mut thumbnail, &mut svg_defs, &mut vec![transform], render_data);
let transform = transform.to_cols_array().iter().map(ToString::to_string).collect::<Vec<_>>().join(",");
let thumbnail = if let [(x_min, y_min), (x_max, y_max)] = arr.as_slice() {
format!(
r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="{} {} {} {}"><defs>{}</defs><g transform="matrix({})">{}</g></svg>"#,
x_min,
y_min,
x_max - x_min,
y_max - y_min,
svg_defs,
transform,
thumbnail,
)
} else {
String::new()
};
LayerPanelEntry {
name,
tooltip,
pub fn new(layer_metadata: &LayerMetadata, layer: &LegacyLayer, path: Vec<LayerId>) -> Self {
Self {
name: "".to_string(), // Replaced before it gets used
tooltip: "".to_string(), // Replaced before it gets used
visible: layer.visible,
layer_type: (&layer.data).into(),
layer_metadata: *layer_metadata,
path,
thumbnail,
thumbnail: r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 0 0"></svg>"#.to_string(),
}
}
}

View File

@@ -1,6 +1,6 @@
pub use super::layer_panel::{LayerMetadata, LayerPanelEntry};
use document_legacy::document::Document as DocumentLegacy;
use document_legacy::LayerId;
use document_legacy::document::LayerId;
use graphene_core::raster::color::Color;
use serde::{Deserialize, Serialize};

View File

@@ -1,9 +1,8 @@
use crate::messages::frontend::utility_types::{ExportBounds, FileType};
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
use crate::messages::prelude::*;
use document_legacy::document::LayerId;
use document_legacy::document_metadata::LayerNodeIdentifier;
use document_legacy::LayerId;
use graph_craft::document::NodeId;
use graphene_core::text::Font;
use serde::{Deserialize, Serialize};
@@ -103,13 +102,6 @@ pub enum PortfolioMessage {
SetActiveDocument {
document_id: u64,
},
SetImageBlobUrl {
document_id: u64,
layer_path: Vec<LayerId>,
node_id: Option<NodeId>,
blob_url: String,
resolution: (f64, f64),
},
SubmitDocumentExport {
file_name: String,
file_type: FileType,

View File

@@ -11,7 +11,6 @@ use crate::messages::prelude::*;
use crate::messages::tool::utility_types::{HintData, HintGroup};
use crate::node_graph_executor::{ExportConfig, NodeGraphExecutor};
use document_legacy::layers::style::RenderData;
use graph_craft::document::NodeId;
use graphene_core::text::Font;
@@ -105,12 +104,11 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
}
PortfolioMessage::CloseAllDocuments => {
if self.active_document_id.is_some() {
responses.add(PropertiesPanelMessage::Deactivate);
responses.add(BroadcastEvent::ToolAbort);
responses.add(ToolMessage::DeactivateTools);
// Clear relevant UI layouts if there are no documents
responses.add(PropertiesPanelMessage::ClearSelection);
responses.add(PropertiesPanelMessage::Clear);
responses.add(DocumentMessage::ClearLayerTree);
let hint_data = HintData(vec![HintGroup(vec![])]);
responses.add(FrontendMessage::UpdateInputHints { hint_data });
@@ -134,7 +132,7 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
// Is this the last document?
if self.documents.len() == 1 && self.document_ids[0] == document_id {
// Clear UI layouts that assume the existence of a document
responses.add(PropertiesPanelMessage::ClearSelection);
responses.add(PropertiesPanelMessage::Clear);
responses.add(DocumentMessage::ClearLayerTree);
let hint_data = HintData(vec![HintGroup(vec![])]);
responses.add(FrontendMessage::UpdateInputHints { hint_data });
@@ -306,12 +304,12 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
})
}
if &server_status != self.persistent_data.imaginate.server_status() {
responses.add(PropertiesPanelMessage::ResendActiveProperties);
responses.add(PropertiesPanelMessage::Refresh);
}
}
PortfolioMessage::ImaginatePollServerStatus => {
self.persistent_data.imaginate.poll_server_check();
responses.add(PropertiesPanelMessage::ResendActiveProperties);
responses.add(PropertiesPanelMessage::Refresh);
}
PortfolioMessage::ImaginatePreferences => self.executor.update_imaginate_preferences(preferences.get_imaginate_preferences()),
PortfolioMessage::ImaginateServerHostname => {
@@ -492,25 +490,6 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
self.active_document_id = Some(document_id);
responses.add(MenuBarMessage::SendLayout);
}
PortfolioMessage::SetImageBlobUrl {
document_id,
layer_path,
node_id,
blob_url,
resolution,
} => {
if let Some(node_id) = node_id {
self.executor.insert_thumbnail_blob_url(blob_url, node_id, responses);
return;
}
let message = DocumentMessage::SetImageBlobUrl {
layer_path,
blob_url,
resolution,
document_id,
};
responses.add(PortfolioMessage::DocumentPassMessage { document_id, message });
}
PortfolioMessage::SubmitDocumentExport {
file_name,
file_type,
@@ -668,15 +647,13 @@ impl PortfolioMessageHandler {
// TODO: Fix how this doesn't preserve tab order upon loading new document from *File > Load*
fn load_document(&mut self, new_document: DocumentMessageHandler, document_id: u64, responses: &mut VecDeque<Message>) {
let render_data = RenderData::new(&self.persistent_data.font_cache, new_document.view_mode, None);
self.document_ids.push(document_id);
responses.extend(
new_document
.layer_metadata
.keys()
.filter_map(|path| new_document.layer_panel_entry_from_path(path, &render_data))
.filter_map(|path| new_document.layer_panel_entry_from_path(path))
.map(|entry| FrontendMessage::UpdateDocumentLayerDetails { data: entry }.into())
.collect::<Vec<_>>(),
);
@@ -685,7 +662,6 @@ impl PortfolioMessageHandler {
self.documents.insert(document_id, new_document);
if self.active_document().is_some() {
responses.add(PropertiesPanelMessage::Deactivate);
responses.add(BroadcastEvent::ToolAbort);
responses.add(ToolMessage::DeactivateTools);
}
@@ -696,12 +672,10 @@ impl PortfolioMessageHandler {
responses.add(PortfolioMessage::UpdateDocumentWidgets);
responses.add(PortfolioMessage::GraphViewOverlay { open: self.graph_view_overlay_open });
responses.add(ToolMessage::InitTools);
responses.add(PropertiesPanelMessage::Init);
responses.add(NodeGraphMessage::Init);
responses.add(NavigationMessage::TranslateCanvas { delta: (0., 0.).into() });
responses.add(DocumentMessage::DocumentStructureChanged);
responses.add(PropertiesPanelMessage::ClearSelection);
responses.add(PropertiesPanelMessage::UpdateSelectedDocumentProperties);
responses.add(PropertiesPanelMessage::Clear);
responses.add(NodeGraphMessage::UpdateNewNodeGraph);
}

View File

@@ -2,7 +2,7 @@ use crate::messages::portfolio::document::node_graph::VectorDataModification;
use crate::messages::prelude::*;
use bezier_rs::{ManipulatorGroup, Subpath};
use document_legacy::{document::Document, document_metadata::LayerNodeIdentifier, LayerId, Operation};
use document_legacy::{document::Document, document_metadata::LayerNodeIdentifier};
use graph_craft::document::{value::TaggedValue, DocumentNode, NodeId, NodeInput, NodeNetwork};
use graphene_core::raster::{BlendMode, ImageFrame};
use graphene_core::text::Font;
@@ -10,7 +10,7 @@ use graphene_core::uuid::ManipulatorGroupId;
use graphene_core::vector::style::{FillType, Gradient};
use graphene_core::Color;
use glam::{DAffine2, DVec2};
use glam::DVec2;
use std::collections::VecDeque;
/// Create a new vector layer from a vector of [`bezier_rs::Subpath`].
@@ -34,18 +34,6 @@ pub fn new_image_layer(image_frame: ImageFrame<Color>, id: NodeId, parent: Layer
LayerNodeIdentifier::new_unchecked(id)
}
/// Create a legacy node graph frame TODO: remove
pub fn new_custom_layer(network: NodeNetwork, layer_path: Vec<LayerId>, responses: &mut VecDeque<Message>) {
responses.add(DocumentMessage::DeselectAllLayers);
responses.add(Operation::AddFrame {
path: layer_path.clone(),
insert_index: -1,
transform: DAffine2::ZERO.to_cols_array(),
network,
});
responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path });
}
/// Batch set all of the manipulator groups to mirror on a specific layer
pub fn set_manipulator_mirror_angle(manipulator_groups: &[ManipulatorGroup<ManipulatorGroupId>], layer: LayerNodeIdentifier, mirror_angle: bool, responses: &mut VecDeque<Message>) {
for manipulator_group in manipulator_groups {
@@ -77,12 +65,6 @@ pub fn get_pivot(layer: LayerNodeIdentifier, document: &Document) -> Option<DVec
}
}
pub fn get_document_pivot(layer: LayerNodeIdentifier, document: &Document) -> DVec2 {
let [min, max] = document.metadata.nonzero_bounding_box(layer);
let pivot = get_pivot(layer, document).unwrap_or(DVec2::splat(0.5));
document.metadata.transform_to_document(layer).transform_point2(min + (max - min) * pivot)
}
pub fn get_viewport_pivot(layer: LayerNodeIdentifier, document: &Document) -> DVec2 {
let [min, max] = document.metadata.nonzero_bounding_box(layer);
let pivot = get_pivot(layer, document).unwrap_or(DVec2::splat(0.5));
@@ -234,25 +216,6 @@ impl<'a> NodeGraphLayer<'a> {
})
}
/// Get the nearest layer node from the path and the document
pub fn new_from_path(layer: &[LayerId], document: &'a document_legacy::document::Document) -> Option<Self> {
let node_graph = &document.document_network;
let outwards_links = document.document_network.collect_outwards_links();
let Some(mut layer_node) = layer.last().copied() else {
error!("Tried to modify root layer");
return None;
};
while !node_graph.nodes.get(&layer_node)?.is_layer() {
layer_node = outwards_links.get(&layer_node)?.first().copied()?;
}
Some(Self {
node_graph,
_outwards_links: outwards_links,
layer_node,
})
}
/// Return an iterator up the primary flow of the layer
pub fn primary_layer_flow(&self) -> impl Iterator<Item = (&'a DocumentNode, u64)> {
self.node_graph.upstream_flow_back_from_nodes(vec![self.layer_node], true)

View File

@@ -4,7 +4,6 @@ use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::snapping::SnapManager;
use document_legacy::document_metadata::LayerNodeIdentifier;
use document_legacy::layers::style::RenderData;
use glam::{DAffine2, DVec2, Vec2Swizzles};
@@ -17,16 +16,16 @@ pub struct Resize {
impl Resize {
/// Starts a resize, assigning the snap targets and snapping the starting position.
pub fn start(&mut self, responses: &mut VecDeque<Message>, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, render_data: &RenderData) {
self.snap_manager.start_snap(document, input, document.bounding_boxes(None, None, render_data), true, true);
pub fn start(&mut self, responses: &mut VecDeque<Message>, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler) {
self.snap_manager.start_snap(document, input, document.bounding_boxes(), true, true);
self.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
let root_transform = document.metadata().document_to_viewport;
self.drag_start = root_transform.inverse().transform_point2(self.snap_manager.snap_position(responses, document, input.mouse.position));
}
/// Recalculates snap targets without snapping the starting position.
pub fn recalculate_snaps(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, render_data: &RenderData) {
self.snap_manager.start_snap(document, input, document.bounding_boxes(None, None, render_data), true, true);
pub fn recalculate_snaps(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler) {
self.snap_manager.start_snap(document, input, document.bounding_boxes(), true, true);
self.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
}

View File

@@ -2,10 +2,8 @@ use super::shape_editor::ManipulatorPointInfo;
use crate::consts::{SNAP_AXIS_TOLERANCE, SNAP_POINT_TOLERANCE};
use crate::messages::prelude::*;
use document_legacy::document_metadata::LayerNodeIdentifier;
use document_legacy::document::LayerId;
use document_legacy::layers::layer_info::LegacyLayer;
use document_legacy::LayerId;
use graphene_core::vector::{ManipulatorPointId, SelectedType};
use glam::DVec2;
@@ -109,42 +107,44 @@ impl SnapManager {
/// This should be called after start_snap
pub fn add_snap_path(
&mut self,
document_message_handler: &DocumentMessageHandler,
input: &InputPreprocessorMessageHandler,
layer: &LegacyLayer,
path: &[LayerId],
include_handles: bool,
ignore_points: &[ManipulatorPointInfo],
_document_message_handler: &DocumentMessageHandler,
_input: &InputPreprocessorMessageHandler,
_layer: &LegacyLayer,
_path: &[LayerId],
_include_handles: bool,
_ignore_points: &[ManipulatorPointInfo],
) {
let Some(vector_data) = &layer.as_vector_data() else { return };
todo!();
if !document_message_handler.snapping_state.node_snapping {
return;
};
// let Some(vector_data) = &layer.as_vector_data() else { return };
let transform = document_message_handler.document_legacy.multiply_transforms(path).unwrap();
let snap_points = vector_data
.manipulator_groups()
.flat_map(|group| {
if include_handles {
[
Some((ManipulatorPointId::new(group.id, SelectedType::Anchor), group.anchor)),
group.in_handle.map(|pos| (ManipulatorPointId::new(group.id, SelectedType::InHandle), pos)),
group.out_handle.map(|pos| (ManipulatorPointId::new(group.id, SelectedType::OutHandle), pos)),
]
} else {
[Some((ManipulatorPointId::new(group.id, SelectedType::Anchor), group.anchor)), None, None]
}
})
.flatten()
.filter(|&(point_id, _)| {
!ignore_points.contains(&ManipulatorPointInfo {
layer: LayerNodeIdentifier::from_path(path, document_message_handler.network()),
point_id,
})
})
.map(|(_, pos)| transform.transform_point2(pos));
self.add_snap_points(document_message_handler, input, snap_points);
// if !document_message_handler.snapping_state.node_snapping {
// return;
// };
// let transform = document_message_handler.document_legacy.multiply_transforms(path).unwrap();
// let snap_points = vector_data
// .manipulator_groups()
// .flat_map(|group| {
// if include_handles {
// [
// Some((ManipulatorPointId::new(group.id, SelectedType::Anchor), group.anchor)),
// group.in_handle.map(|pos| (ManipulatorPointId::new(group.id, SelectedType::InHandle), pos)),
// group.out_handle.map(|pos| (ManipulatorPointId::new(group.id, SelectedType::OutHandle), pos)),
// ]
// } else {
// [Some((ManipulatorPointId::new(group.id, SelectedType::Anchor), group.anchor)), None, None]
// }
// })
// .flatten()
// .filter(|&(point_id, _)| {
// !ignore_points.contains(&ManipulatorPointInfo {
// layer: LayerNodeIdentifier::from_path(path, document_message_handler.network()),
// point_id,
// })
// })
// .map(|(_, pos)| transform.transform_point2(pos));
// self.add_snap_points(document_message_handler, input, snap_points);
}
/// Adds all of the shape handles in the document, including bézier handles of the points specified

View File

@@ -7,7 +7,6 @@ use crate::messages::prelude::*;
use crate::messages::tool::utility_types::ToolType;
use crate::node_graph_executor::NodeGraphExecutor;
use document_legacy::layers::style::RenderData;
use graphene_core::raster::color::Color;
#[derive(Debug, Default)]
@@ -25,7 +24,7 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, u64, &InputPreprocess
responses: &mut VecDeque<Message>,
(document, document_id, input, persistent_data, node_graph): (&DocumentMessageHandler, u64, &InputPreprocessorMessageHandler, &PersistentData, &NodeGraphExecutor),
) {
let render_data = RenderData::new(&persistent_data.font_cache, document.view_mode, None);
let font_cache = &persistent_data.font_cache;
#[remain::sorted]
match message {
@@ -89,7 +88,7 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, u64, &InputPreprocess
document_id,
global_tool_data: &self.tool_state.document_tool_data,
input,
render_data: &render_data,
font_cache,
shape_editor: &mut self.shape_editor,
node_graph,
};
@@ -171,7 +170,7 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, u64, &InputPreprocess
document_id,
global_tool_data: &self.tool_state.document_tool_data,
input,
render_data: &render_data,
font_cache,
shape_editor: &mut self.shape_editor,
node_graph,
};
@@ -241,7 +240,7 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, u64, &InputPreprocess
document_id,
global_tool_data: &self.tool_state.document_tool_data,
input,
render_data: &render_data,
font_cache,
shape_editor: &mut self.shape_editor,
node_graph,
};

View File

@@ -6,7 +6,6 @@ use crate::messages::tool::common_functionality::snapping::SnapManager;
use crate::messages::tool::common_functionality::transformation_cage::*;
use document_legacy::document_metadata::LayerNodeIdentifier;
use document_legacy::layers::RenderData;
use glam::{IVec2, Vec2Swizzles};
@@ -117,13 +116,11 @@ impl ArtboardToolData {
Some(edges)
}
fn start_resizing(&mut self, selected_edges: (bool, bool, bool, bool), document: &DocumentMessageHandler, render_data: &RenderData, input: &InputPreprocessorMessageHandler) {
fn start_resizing(&mut self, selected_edges: (bool, bool, bool, bool), document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler) {
let snap_x = selected_edges.2 || selected_edges.3;
let snap_y = selected_edges.0 || selected_edges.1;
let artboard = self.selected_artboard.unwrap();
self.snap_manager
.start_snap(document, input, document.bounding_boxes(None, Some(artboard.to_node()), render_data), snap_x, snap_y);
self.snap_manager.start_snap(document, input, document.bounding_boxes(), snap_x, snap_y);
self.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
if let Some(bounds) = &mut self.bounding_box_manager {
@@ -131,7 +128,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, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) -> bool {
responses.add(DocumentMessage::StartTransaction);
let mut intersections = document
@@ -143,15 +140,14 @@ impl ArtboardToolData {
if let Some(intersection) = intersections.next() {
self.selected_artboard = Some(intersection);
self.snap_manager
.start_snap(document, input, document.bounding_boxes(None, Some(intersection.to_node()), render_data), true, true);
self.snap_manager.start_snap(document, input, document.bounding_boxes(), true, true);
self.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
true
} else {
self.selected_artboard = None;
responses.add(PropertiesPanelMessage::ClearSelection);
responses.add(PropertiesPanelMessage::Clear);
false
}
@@ -183,7 +179,7 @@ impl Fsm for ArtboardToolFsmState {
type ToolOptions = ();
fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionHandlerData, _tool_options: &(), responses: &mut VecDeque<Message>) -> Self {
let ToolActionHandlerData { document, input, render_data, .. } = tool_action_data;
let ToolActionHandlerData { document, input, .. } = tool_action_data;
let ToolMessage::Artboard(event) = event else {
return self;
@@ -210,10 +206,10 @@ impl Fsm for ArtboardToolFsmState {
if let Some(selected_edges) = tool_data.check_dragging_bounds(input.mouse.position) {
responses.add(DocumentMessage::StartTransaction);
tool_data.start_resizing(selected_edges, document, render_data, input);
tool_data.start_resizing(selected_edges, document, input);
ArtboardToolFsmState::ResizingBounds
} else if tool_data.select_artboard(document, render_data, input, responses) {
} else if tool_data.select_artboard(document, input, responses) {
ArtboardToolFsmState::Dragging
} else {
ArtboardToolFsmState::Drawing
@@ -284,7 +280,7 @@ impl Fsm for ArtboardToolFsmState {
let id = generate_uuid();
tool_data.selected_artboard = Some(LayerNodeIdentifier::new_unchecked(id));
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(None, Some(id), render_data), true, true);
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(), true, true);
tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
responses.add(GraphOperationMessage::NewArtboard {
@@ -367,11 +363,6 @@ impl Fsm for ArtboardToolFsmState {
ArtboardToolFsmState::Ready
}
(_, ArtboardToolMessage::Abort) => {
// Register properties when switching back to other tools
responses.add(PropertiesPanelMessage::SetActiveLayers {
paths: document.selected_layers().map(|path| path.to_vec()).collect(),
});
tool_data.snap_manager.cleanup(responses);
responses.add(OverlaysMessage::Draw);
ArtboardToolFsmState::Ready

View File

@@ -185,11 +185,7 @@ impl Fsm for EllipseToolFsmState {
fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionHandlerData, tool_options: &Self::ToolOptions, responses: &mut VecDeque<Message>) -> Self {
let ToolActionHandlerData {
document,
global_tool_data,
input,
render_data,
..
document, global_tool_data, input, ..
} = tool_action_data;
let shape_data = &mut tool_data.data;
@@ -199,11 +195,11 @@ impl Fsm for EllipseToolFsmState {
};
match (self, event) {
(EllipseToolFsmState::Drawing, EllipseToolMessage::CanvasTransformed) => {
tool_data.data.recalculate_snaps(document, input, render_data);
tool_data.data.recalculate_snaps(document, input);
self
}
(EllipseToolFsmState::Ready, EllipseToolMessage::DragStart) => {
shape_data.start(responses, document, input, render_data);
shape_data.start(responses, document, input);
responses.add(DocumentMessage::StartTransaction);
// Create a new ellipse vector shape

View File

@@ -1,5 +1,6 @@
use super::tool_prelude::*;
use document_legacy::layers::style::Fill;
use graphene_core::vector::style::Fill;
#[derive(Default)]
pub struct FillTool {
@@ -80,9 +81,6 @@ impl Fsm for FillToolFsmState {
let fill = Fill::Solid(color);
responses.add(DocumentMessage::StartTransaction);
responses.add(DocumentMessage::SetSelectedLayers {
replacement_selected_layers: vec![layer.clone()],
});
responses.add(GraphOperationMessage::FillSet { layer, fill });
responses.add(DocumentMessage::CommitTransaction);

View File

@@ -6,8 +6,8 @@ use crate::messages::tool::common_functionality::graph_modification_utils::get_g
use crate::messages::tool::common_functionality::snapping::SnapManager;
use document_legacy::document_metadata::LayerNodeIdentifier;
use document_legacy::layers::style::{Fill, Gradient, GradientType, RenderData};
use graphene_core::raster::color::Color;
use graphene_core::vector::style::{Fill, Gradient, GradientType};
#[derive(Default)]
pub struct GradientTool {
@@ -267,8 +267,8 @@ struct GradientToolData {
drag_start: DVec2,
}
pub fn start_snap(snap_manager: &mut SnapManager, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, render_data: &RenderData) {
snap_manager.start_snap(document, input, document.bounding_boxes(None, None, render_data), true, true);
pub fn start_snap(snap_manager: &mut SnapManager, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler) {
snap_manager.start_snap(document, input, document.bounding_boxes(), true, true);
snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
}
@@ -278,11 +278,7 @@ impl Fsm for GradientToolFsmState {
fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionHandlerData, tool_options: &Self::ToolOptions, responses: &mut VecDeque<Message>) -> Self {
let ToolActionHandlerData {
document,
global_tool_data,
input,
render_data,
..
document, global_tool_data, input, ..
} = tool_action_data;
let ToolMessage::Gradient(event) = event else {
@@ -434,7 +430,7 @@ impl Fsm for GradientToolFsmState {
let pos = transform.transform_point2(pos);
if pos.distance_squared(mouse) < tolerance {
dragging = true;
start_snap(&mut tool_data.snap_manager, document, input, render_data);
start_snap(&mut tool_data.snap_manager, document, input);
tool_data.selected_gradient = Some(SelectedGradient {
layer,
transform,
@@ -479,7 +475,7 @@ impl Fsm for GradientToolFsmState {
tool_data.selected_gradient = Some(selected_gradient);
start_snap(&mut tool_data.snap_manager, document, input, render_data);
start_snap(&mut tool_data.snap_manager, document, input);
GradientToolFsmState::Drawing
} else {

View File

@@ -3,9 +3,7 @@ use crate::messages::portfolio::document::node_graph::{self, IMAGINATE_NODE};
use crate::messages::tool::common_functionality::resize::Resize;
use document_legacy::document_metadata::LayerNodeIdentifier;
use document_legacy::Operation;
use glam::DAffine2;
use serde::{Deserialize, Serialize};
#[derive(Default)]
@@ -105,7 +103,7 @@ impl Fsm for ImaginateToolFsmState {
self,
event: ToolMessage,
tool_data: &mut Self::ToolData,
ToolActionHandlerData { document, input, render_data, .. }: &mut ToolActionHandlerData,
ToolActionHandlerData { document, input, .. }: &mut ToolActionHandlerData,
_tool_options: &Self::ToolOptions,
responses: &mut VecDeque<Message>,
) -> Self {
@@ -116,12 +114,12 @@ impl Fsm for ImaginateToolFsmState {
};
match (self, event) {
(_, ImaginateToolMessage::DocumentIsDirty | ImaginateToolMessage::SelectionChanged) => {
//tool_data.path_outlines.update_selected(document.document_legacy.selected_visible_layers(), document, responses, render_data);
//tool_data.path_outlines.update_selected(document.document_legacy.selected_visible_layers(), document, responses, font_cache);
self
}
(ImaginateToolFsmState::Ready, ImaginateToolMessage::DragStart) => {
shape_data.start(responses, document, input, render_data);
shape_data.start(responses, document, input);
responses.add(DocumentMessage::StartTransaction);
shape_data.layer = Some(LayerNodeIdentifier::new(generate_uuid(), document.network()));
responses.add(DocumentMessage::DeselectAllLayers);
@@ -156,16 +154,16 @@ impl Fsm for ImaginateToolFsmState {
imaginate_node_id,
imaginate_node_type.to_document_node_default_inputs([Some(graph_craft::document::NodeInput::node(transform_node_id, 0))], next_pos()),
);
// Add a layer with a frame to the document
responses.add(Operation::AddFrame {
path: shape_data.layer.unwrap().to_path(),
insert_index: -1,
transform: DAffine2::ZERO.to_cols_array(),
network,
});
responses.add(NodeGraphMessage::ShiftNode { node_id: imaginate_node_id });
// // Add a layer with a frame to the document
// responses.add(Operation::AddFrame {
// path: shape_data.layer.unwrap().to_path(),
// insert_index: -1,
// transform: DAffine2::ZERO.to_cols_array(),
// network,
// });
ImaginateToolFsmState::Drawing
}
(state, ImaginateToolMessage::Resize { center, lock_ratio }) => {

View File

@@ -166,11 +166,7 @@ impl Fsm for LineToolFsmState {
fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionHandlerData, tool_options: &Self::ToolOptions, responses: &mut VecDeque<Message>) -> Self {
let ToolActionHandlerData {
document,
global_tool_data,
input,
render_data,
..
document, global_tool_data, input, ..
} = tool_action_data;
let ToolMessage::Line(event) = event else {
@@ -178,7 +174,7 @@ impl Fsm for LineToolFsmState {
};
match (self, event) {
(LineToolFsmState::Ready, LineToolMessage::DragStart) => {
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(None, None, render_data), true, true);
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(), true, true);
tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
let viewport_start = tool_data.snap_manager.snap_position(responses, document, input.mouse.position);

View File

@@ -257,7 +257,7 @@ impl PathToolData {
//self
// .snap_manager
// .start_snap(document, input, document.bounding_boxes(Some(&selected_layers), None, render_data), true, true);
// .start_snap(document, input, document.bounding_boxes(Some(&selected_layers), None, font_cache), true, true);
// Do not snap against handles when anchor is selected
let mut additional_selected_points = Vec::new();

View File

@@ -543,7 +543,6 @@ impl Fsm for PenToolFsmState {
document,
global_tool_data,
input,
render_data,
shape_editor,
..
} = tool_action_data;
@@ -568,7 +567,7 @@ impl Fsm for PenToolFsmState {
};
match (self, event) {
(_, PenToolMessage::CanvasTransformed) => {
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(None, None, render_data), true, true);
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(), true, true);
self
}
(_, PenToolMessage::SelectionChanged) => {
@@ -591,7 +590,7 @@ impl Fsm for PenToolFsmState {
responses.add(DocumentMessage::StartTransaction);
// Initialize snapping
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(None, None, render_data), true, true);
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(), true, true);
tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
// Disable this tool's mirroring

View File

@@ -225,11 +225,7 @@ impl Fsm for PolygonToolFsmState {
fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionHandlerData, tool_options: &Self::ToolOptions, responses: &mut VecDeque<Message>) -> Self {
let ToolActionHandlerData {
document,
global_tool_data,
input,
render_data,
..
document, global_tool_data, input, ..
} = tool_action_data;
let polygon_data = &mut tool_data.data;
@@ -239,11 +235,11 @@ impl Fsm for PolygonToolFsmState {
};
match (self, event) {
(PolygonToolFsmState::Drawing, PolygonToolMessage::CanvasTransformed) => {
tool_data.data.recalculate_snaps(document, input, render_data);
tool_data.data.recalculate_snaps(document, input);
self
}
(PolygonToolFsmState::Ready, PolygonToolMessage::DragStart) => {
polygon_data.start(responses, document, input, render_data);
polygon_data.start(responses, document, input);
responses.add(DocumentMessage::StartTransaction);
let subpath = match tool_options.primitive_shape_type {

View File

@@ -189,11 +189,7 @@ impl Fsm for RectangleToolFsmState {
event: ToolMessage,
tool_data: &mut Self::ToolData,
ToolActionHandlerData {
document,
global_tool_data,
input,
render_data,
..
document, global_tool_data, input, ..
}: &mut ToolActionHandlerData,
tool_options: &Self::ToolOptions,
responses: &mut VecDeque<Message>,
@@ -209,11 +205,11 @@ impl Fsm for RectangleToolFsmState {
match (self, event) {
(Drawing, CanvasTransformed) => {
tool_data.data.recalculate_snaps(document, input, render_data);
tool_data.data.recalculate_snaps(document, input);
self
}
(Ready, DragStart) => {
shape_data.start(responses, document, input, render_data);
shape_data.start(responses, document, input);
let subpath = bezier_rs::Subpath::new_rect(DVec2::ZERO, DVec2::ONE);

View File

@@ -382,7 +382,7 @@ impl Fsm for SelectToolFsmState {
type ToolOptions = ();
fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionHandlerData, _tool_options: &(), responses: &mut VecDeque<Message>) -> Self {
let ToolActionHandlerData { document, input, render_data, .. } = tool_action_data;
let ToolActionHandlerData { document, input, .. } = tool_action_data;
let ToolMessage::Select(event) = event else {
return self;
@@ -482,7 +482,7 @@ impl Fsm for SelectToolFsmState {
let state = if tool_data.pivot.is_over(input.mouse.position) {
responses.add(DocumentMessage::StartTransaction);
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(None, None, render_data), true, true);
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(), true, true);
tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
SelectToolFsmState::DraggingPivot
@@ -494,7 +494,7 @@ impl Fsm for SelectToolFsmState {
//
// tool_data
// .snap_manager
// .start_snap(document, input, document.bounding_boxes(Some(&selected), None, render_data), snap_x, snap_y);
// .start_snap(document, input, document.bounding_boxes(Some(&selected), None, font_cache), snap_x, snap_y);
// tool_data
// .snap_manager
// .add_all_document_handles(document, input, &[], &selected.iter().map(|x| x.as_slice()).collect::<Vec<_>>(), &[]);
@@ -550,7 +550,7 @@ impl Fsm for SelectToolFsmState {
// tool_data
// .snap_manager
// .start_snap(document, input, document.bounding_boxes(Some(&tool_data.layers_dragging), None, render_data), true, true);
// .start_snap(document, input, document.bounding_boxes(Some(&tool_data.layers_dragging), None, font_cache), true, true);
SelectToolFsmState::Dragging
} else {
@@ -922,7 +922,7 @@ fn drag_shallowest_manipulation(responses: &mut VecDeque<Message>, selected: Vec
});
// tool_data
// .snap_manager
// .start_snap(document, input, document.bounding_boxes(Some(&tool_data.layers_dragging), None, render_data), true, true);
// .start_snap(document, input, document.bounding_boxes(Some(&tool_data.layers_dragging), None, font_cache), true, true);
}
fn drag_deepest_manipulation(responses: &mut VecDeque<Message>, mut selected: Vec<LayerNodeIdentifier>, tool_data: &mut SelectToolData) {
@@ -932,7 +932,7 @@ fn drag_deepest_manipulation(responses: &mut VecDeque<Message>, mut selected: Ve
});
// tool_data
// .snap_manager
// .start_snap(document, input, document.bounding_boxes(Some(&tool_data.layers_dragging), None, render_data), true, true);
// .start_snap(document, input, document.bounding_boxes(Some(&tool_data.layers_dragging), None, font_cache), true, true);
}
fn edit_layer_shallowest_manipulation(document: &DocumentMessageHandler, layer: LayerNodeIdentifier, responses: &mut VecDeque<Message>) {

View File

@@ -196,11 +196,7 @@ impl Fsm for SplineToolFsmState {
fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionHandlerData, tool_options: &Self::ToolOptions, responses: &mut VecDeque<Message>) -> Self {
let ToolActionHandlerData {
document,
global_tool_data,
input,
render_data,
..
document, global_tool_data, input, ..
} = tool_action_data;
let ToolMessage::Spline(event) = event else {
@@ -208,7 +204,7 @@ impl Fsm for SplineToolFsmState {
};
match (self, event) {
(_, SplineToolMessage::CanvasTransformed) => {
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(None, None, render_data), true, true);
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(), true, true);
self
}
(SplineToolFsmState::Ready, SplineToolMessage::DragStart) => {
@@ -218,7 +214,7 @@ impl Fsm for SplineToolFsmState {
let parent = document.new_layer_parent();
let transform = document.metadata().transform_to_viewport(parent);
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(None, None, render_data), true, true);
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(), true, true);
tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
let snapped_position = tool_data.snap_manager.snap_position(responses, document, input.mouse.position);

View File

@@ -7,10 +7,10 @@ use crate::messages::tool::common_functionality::color_selector::{ToolColorOptio
use crate::messages::tool::common_functionality::graph_modification_utils::{self, is_layer_fed_by_node_of_name};
use document_legacy::document_metadata::LayerNodeIdentifier;
use document_legacy::layers::style::{Fill, RenderData};
use graph_craft::document::value::TaggedValue;
use graphene_core::renderer::Quad;
use graphene_core::text::{load_face, Font};
use graphene_core::text::{load_face, Font, FontCache};
use graphene_core::vector::style::Fill;
use graphene_core::Color;
#[derive(Default)]
@@ -224,7 +224,7 @@ struct TextToolData {
impl TextToolData {
/// Set the editing state of the currently modifying layer
fn set_editing(&self, editable: bool, render_data: &RenderData, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
fn set_editing(&self, editable: bool, font_cache: &FontCache, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
if let Some(node_id) = graph_modification_utils::get_fill_id(self.layer, &document.document_legacy) {
responses.add(NodeGraphMessage::SetHidden { node_id, hidden: editable });
}
@@ -235,7 +235,7 @@ impl TextToolData {
line_width: None,
font_size: editing_text.font_size,
color: editing_text.color.unwrap_or(Color::BLACK),
url: render_data.font_cache.get_preview_url(&editing_text.font).cloned().unwrap_or_default(),
url: font_cache.get_preview_url(&editing_text.font).cloned().unwrap_or_default(),
transform: editing_text.transform.to_cols_array(),
});
} else {
@@ -258,9 +258,9 @@ impl TextToolData {
Some(())
}
fn start_editing_layer(&mut self, layer: LayerNodeIdentifier, tool_state: TextToolFsmState, document: &DocumentMessageHandler, render_data: &RenderData, responses: &mut VecDeque<Message>) {
fn start_editing_layer(&mut self, layer: LayerNodeIdentifier, tool_state: TextToolFsmState, document: &DocumentMessageHandler, font_cache: &FontCache, responses: &mut VecDeque<Message>) {
if tool_state == TextToolFsmState::Editing {
self.set_editing(false, render_data, document, responses);
self.set_editing(false, font_cache, document, responses);
}
self.layer = layer;
@@ -268,19 +268,19 @@ impl TextToolData {
responses.add(DocumentMessage::StartTransaction);
self.set_editing(true, render_data, document, responses);
self.set_editing(true, font_cache, document, responses);
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![self.layer.to_node()] });
}
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, font_cache: &FontCache, responses: &mut VecDeque<Message>) -> TextToolFsmState {
// Check if the user has selected an existing text layer
if let Some(clicked_text_layer_path) = document
.document_legacy
.click(mouse, document.network())
.filter(|&layer| is_layer_fed_by_node_of_name(layer, &document.document_legacy, "Text"))
{
self.start_editing_layer(clicked_text_layer_path, state, document, render_data, responses);
self.start_editing_layer(clicked_text_layer_path, state, document, font_cache, responses);
TextToolFsmState::Editing
}
@@ -309,32 +309,32 @@ impl TextToolData {
skip_rerender: true,
});
self.set_editing(true, render_data, document, responses);
self.set_editing(true, font_cache, document, responses);
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: self.layer.to_path() });
TextToolFsmState::Editing
} else {
// Removing old text as editable
self.set_editing(false, render_data, document, responses);
self.set_editing(false, font_cache, document, responses);
TextToolFsmState::Ready
}
}
fn get_bounds(&self, text: &str, render_data: &RenderData) -> Option<[DVec2; 2]> {
fn get_bounds(&self, text: &str, font_cache: &FontCache) -> Option<[DVec2; 2]> {
let editing_text = self.editing_text.as_ref()?;
let buzz_face = render_data.font_cache.get(&editing_text.font).map(|data| load_face(data));
let buzz_face = font_cache.get(&editing_text.font).map(|data| load_face(data));
let subpaths = graphene_core::text::to_path(text, buzz_face, editing_text.font_size, None);
let bounds = subpaths.iter().filter_map(|subpath| subpath.bounding_box());
let combined_bounds = bounds.reduce(|a, b| [a[0].min(b[0]), a[1].max(b[1])]).unwrap_or_default();
Some(combined_bounds)
}
fn fix_text_bounds(&self, new_text: &str, _document: &DocumentMessageHandler, render_data: &RenderData, responses: &mut VecDeque<Message>) -> Option<()> {
fn fix_text_bounds(&self, new_text: &str, _document: &DocumentMessageHandler, font_cache: &FontCache, responses: &mut VecDeque<Message>) -> Option<()> {
let layer = self.layer.to_path();
let old_bounds = self.get_bounds(&self.editing_text.as_ref()?.text, render_data)?;
let new_bounds = self.get_bounds(new_text, render_data)?;
let old_bounds = self.get_bounds(&self.editing_text.as_ref()?.text, font_cache)?;
let new_bounds = self.get_bounds(new_text, font_cache)?;
responses.add(GraphOperationMessage::UpdateBounds { layer, old_bounds, new_bounds });
Some(())
@@ -366,7 +366,7 @@ impl Fsm for TextToolFsmState {
document,
global_tool_data,
input,
render_data,
font_cache,
..
} = transition_data;
let ToolMessage::Text(event) = event else {
@@ -378,7 +378,7 @@ impl Fsm for TextToolFsmState {
transform: document.metadata().transform_to_viewport(tool_data.layer).to_cols_array(),
});
if let Some(editing_text) = tool_data.editing_text.as_ref() {
let buzz_face = render_data.font_cache.get(&editing_text.font).map(|data| load_face(data));
let buzz_face = font_cache.get(&editing_text.font).map(|data| load_face(data));
let far = graphene_core::text::bounding_box(&tool_data.new_text, buzz_face, editing_text.font_size, None);
if far.x != 0. && far.y != 0. {
let quad = Quad::from_box([DVec2::ZERO, far]);
@@ -394,7 +394,7 @@ impl Fsm for TextToolFsmState {
let Some((text, font, font_size)) = graph_modification_utils::get_text(layer, &document.document_legacy) else {
continue;
};
let buzz_face = render_data.font_cache.get(font).map(|data| load_face(data));
let buzz_face = font_cache.get(font).map(|data| load_face(data));
let far = graphene_core::text::bounding_box(text, buzz_face, font_size, None);
let quad = Quad::from_box([DVec2::ZERO, far]);
let multiplied = document.metadata().transform_to_viewport(layer) * quad;
@@ -413,11 +413,11 @@ impl Fsm for TextToolFsmState {
});
tool_data.new_text = String::new();
tool_data.interact(state, input.mouse.position, document, render_data, responses)
tool_data.interact(state, input.mouse.position, document, font_cache, responses)
}
(state, TextToolMessage::EditSelected) => {
if let Some(layer) = can_edit_selected(document) {
tool_data.start_editing_layer(layer, state, document, render_data, responses);
tool_data.start_editing_layer(layer, state, document, font_cache, responses);
return TextToolFsmState::Editing;
}
@@ -425,7 +425,7 @@ impl Fsm for TextToolFsmState {
}
(state, TextToolMessage::Abort) => {
if state == TextToolFsmState::Editing {
tool_data.set_editing(false, render_data, document, responses);
tool_data.set_editing(false, font_cache, document, responses);
}
TextToolFsmState::Ready
@@ -436,7 +436,7 @@ impl Fsm for TextToolFsmState {
TextToolFsmState::Editing
}
(TextToolFsmState::Editing, TextToolMessage::TextChange { new_text }) => {
tool_data.fix_text_bounds(&new_text, document, render_data, responses);
tool_data.fix_text_bounds(&new_text, document, font_cache, responses);
responses.add(NodeGraphMessage::SetQualifiedInputValue {
layer_path: Vec::new(),
node_path: vec![graph_modification_utils::get_text_id(tool_data.layer, &document.document_legacy).unwrap()],
@@ -444,7 +444,7 @@ impl Fsm for TextToolFsmState {
value: TaggedValue::String(new_text),
});
tool_data.set_editing(false, render_data, document, responses);
tool_data.set_editing(false, font_cache, document, responses);
TextToolFsmState::Ready
}

View File

@@ -12,8 +12,8 @@ use crate::messages::portfolio::document::overlays::utility_types::OverlayProvid
use crate::messages::prelude::*;
use crate::node_graph_executor::NodeGraphExecutor;
use document_legacy::layers::style::RenderData;
use graphene_core::raster::color::Color;
use graphene_std::text::FontCache;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Debug};
@@ -23,7 +23,7 @@ pub struct ToolActionHandlerData<'a> {
pub document_id: u64,
pub global_tool_data: &'a DocumentToolData,
pub input: &'a InputPreprocessorMessageHandler,
pub render_data: &'a RenderData<'a>,
pub font_cache: &'a FontCache,
pub shape_editor: &'a mut ShapeState,
pub node_graph: &'a NodeGraphExecutor,
}
@@ -33,7 +33,7 @@ impl<'a> ToolActionHandlerData<'a> {
document_id: u64,
global_tool_data: &'a DocumentToolData,
input: &'a InputPreprocessorMessageHandler,
render_data: &'a RenderData<'a>,
font_cache: &'a FontCache,
shape_editor: &'a mut ShapeState,
node_graph: &'a NodeGraphExecutor,
) -> Self {
@@ -42,7 +42,7 @@ impl<'a> ToolActionHandlerData<'a> {
document_id,
global_tool_data,
input,
render_data,
font_cache,
shape_editor,
node_graph,
}

View File

@@ -6,9 +6,9 @@ use crate::messages::portfolio::document::utility_types::misc::{LayerMetadata, L
use crate::messages::prelude::*;
use document_legacy::document::Document as DocumentLegacy;
use document_legacy::document::LayerId;
use document_legacy::document_metadata::LayerNodeIdentifier;
use document_legacy::layers::layer_info::{LayerDataTypeDiscriminant, LegacyLayerType};
use document_legacy::{LayerId, Operation};
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{generate_uuid, DocumentNodeImplementation, NodeId, NodeNetwork};
use graph_craft::graphene_compiler::Compiler;
@@ -626,9 +626,7 @@ impl NodeGraphExecutor {
name: document.document_network.nodes.get(&node_id).map(|node| node.alias.clone()).unwrap_or_default(),
tooltip: if cfg!(debug_assertions) { format!("Layer ID: {node_id}") } else { "".into() },
visible: !document.document_network.disabled.contains(&layer.to_node()),
layer_type: if document.metadata.is_artboard(layer) {
LayerDataTypeDiscriminant::Artboard
} else if document.metadata.is_folder(layer) {
layer_type: if document.metadata.is_folder(layer) {
LayerDataTypeDiscriminant::Folder
} else {
LayerDataTypeDiscriminant::Layer
@@ -653,12 +651,9 @@ impl NodeGraphExecutor {
responses.add(DocumentMessage::RenderDocument);
responses.add(DocumentMessage::DocumentStructureChanged);
responses.add(BroadcastEvent::DocumentIsDirty);
responses.add(DocumentMessage::DirtyRenderDocument);
responses.add(OverlaysMessage::Draw);
}
NodeGraphUpdate::NodeGraphUpdateMessage(NodeGraphUpdateMessage::ImaginateStatusUpdate) => {
responses.add(DocumentMessage::PropertiesPanel(PropertiesPanelMessage::ResendActiveProperties))
}
NodeGraphUpdate::NodeGraphUpdateMessage(NodeGraphUpdateMessage::ImaginateStatusUpdate) => responses.add(DocumentMessage::PropertiesPanel(PropertiesPanelMessage::Refresh)),
}
}
Ok(())
@@ -685,10 +680,8 @@ impl NodeGraphExecutor {
fn process_node_graph_output(&mut self, node_graph_output: TaggedValue, layer_path: Vec<LayerId>, transform: DAffine2, responses: &mut VecDeque<Message>) -> Result<(), String> {
self.last_output_type.insert(layer_path.clone(), Some(node_graph_output.ty()));
match node_graph_output {
TaggedValue::SurfaceFrame(SurfaceFrame { surface_id, transform }) => {
let transform = transform.to_cols_array();
responses.add(Operation::SetLayerTransform { path: layer_path.clone(), transform });
responses.add(Operation::SetSurface { path: layer_path, surface_id });
TaggedValue::SurfaceFrame(SurfaceFrame { surface_id: _, transform: _ }) => {
// TODO: Reimplement this now that document-legacy is gone
}
TaggedValue::RenderOutput(graphene_std::wasm_application_io::RenderOutput::Svg(svg)) => {
// Send to frontend