Fix artboard tool and remove old artboard code
This commit is contained in:
parent
9a39c4a0cc
commit
b52f831b21
|
|
@ -81,36 +81,53 @@ impl DocumentMetadata {
|
||||||
self.document_to_viewport * self.transform_from_document(layer)
|
self.document_to_viewport * self.transform_from_document(layer)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs an intersection test with all layers and a document space quad
|
/// Runs an intersection test with all layers and a viewport space quad
|
||||||
pub fn intersect_quad(&self, viewport_quad: Quad) -> Option<(LayerNodeIdentifier, &Vec<ClickTarget>)> {
|
pub fn intersect_quad(&self, viewport_quad: Quad) -> Option<LayerNodeIdentifier> {
|
||||||
let document_quad = self.document_to_viewport.inverse() * viewport_quad;
|
let document_quad = self.document_to_viewport.inverse() * viewport_quad;
|
||||||
self.root()
|
self.root()
|
||||||
.decendants(self)
|
.decendants(self)
|
||||||
.filter_map(|layer| self.click_targets.get(&layer).map(|targets| (layer, targets)))
|
.filter_map(|layer| self.click_targets.get(&layer).map(|targets| (layer, targets)))
|
||||||
.find(|(layer, target)| target.iter().any(|target| target.intersect_rectangle(document_quad, self.transform_from_document(*layer))))
|
.find(|(layer, target)| target.iter().any(|target| target.intersect_rectangle(document_quad, self.transform_from_document(*layer))))
|
||||||
|
.map(|(layer, _)| layer)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find the layer that has been clicked on from a document space location
|
/// Find all of the layers that were clicked on from a viewport space location
|
||||||
pub fn click(&self, viewport_location: DVec2) -> Option<(LayerNodeIdentifier, &Vec<ClickTarget>)> {
|
pub fn click_xray<'a>(&'a self, viewport_location: DVec2) -> impl Iterator<Item = LayerNodeIdentifier> + 'a {
|
||||||
let point = self.document_to_viewport.inverse().transform_point2(viewport_location);
|
let point = self.document_to_viewport.inverse().transform_point2(viewport_location);
|
||||||
self.root()
|
self.root()
|
||||||
.decendants(self)
|
.decendants(self)
|
||||||
.filter_map(|layer| self.click_targets.get(&layer).map(|targets| (layer, targets)))
|
.filter_map(|layer| self.click_targets.get(&layer).map(|targets| (layer, targets)))
|
||||||
.find(|(layer, target)| target.iter().any(|target: &ClickTarget| target.intersect_point(point, self.transform_from_document(*layer))))
|
.filter(move |(layer, target)| target.iter().any(|target: &ClickTarget| target.intersect_point(point, self.transform_from_document(*layer))))
|
||||||
|
.map(|(layer, _)| layer)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find the layer that has been clicked on from a viewport space location
|
||||||
|
pub fn click(&self, viewport_location: DVec2) -> Option<LayerNodeIdentifier> {
|
||||||
|
self.click_xray(viewport_location).next()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the bounding box of the click target of the specified layer in the specified transform space
|
/// Get the bounding box of the click target of the specified layer in the specified transform space
|
||||||
pub fn bounding_box(&self, layer: LayerNodeIdentifier, transform: DAffine2) -> Option<[DVec2; 2]> {
|
pub fn bounding_box_with_transform(&self, layer: LayerNodeIdentifier, transform: DAffine2) -> Option<[DVec2; 2]> {
|
||||||
self.click_targets
|
self.click_targets
|
||||||
.get(&layer)?
|
.get(&layer)?
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|click_target| click_target.subpath.bounding_box_with_transform(transform))
|
.filter_map(|click_target| click_target.subpath.bounding_box_with_transform(transform))
|
||||||
.reduce(Quad::combine_bounds)
|
.reduce(Quad::combine_bounds)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the bounding box of the click target of the specified layer in document space
|
||||||
|
pub fn bounding_box_document(&self, layer: LayerNodeIdentifier) -> Option<[DVec2; 2]> {
|
||||||
|
self.bounding_box_with_transform(layer, self.transform_from_document(layer))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the bounding box of the click target of the specified layer in viewport space
|
||||||
|
pub fn bounding_box_viewport(&self, layer: LayerNodeIdentifier) -> Option<[DVec2; 2]> {
|
||||||
|
self.bounding_box_with_transform(layer, self.transform_from_viewport(layer))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Id of a layer node
|
/// Id of a layer node
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
pub struct LayerNodeIdentifier(NonZeroU64);
|
pub struct LayerNodeIdentifier(NonZeroU64);
|
||||||
|
|
||||||
impl Default for LayerNodeIdentifier {
|
impl Default for LayerNodeIdentifier {
|
||||||
|
|
@ -119,6 +136,18 @@ impl Default for LayerNodeIdentifier {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Debug for LayerNodeIdentifier {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_tuple("LayerNodeIdentifier").field(&self.to_node()).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Display for LayerNodeIdentifier {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_fmt(format_args!("Layer(node_id={})", self.to_node()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl LayerNodeIdentifier {
|
impl LayerNodeIdentifier {
|
||||||
const ROOT: Self = LayerNodeIdentifier::new_unchecked(0);
|
const ROOT: Self = LayerNodeIdentifier::new_unchecked(0);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,9 +33,6 @@ pub struct DispatcherMessageHandlers {
|
||||||
const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[
|
const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[
|
||||||
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::RenderDocument)),
|
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::RenderDocument)),
|
||||||
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::Overlays(OverlaysMessageDiscriminant::Rerender))),
|
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::Overlays(OverlaysMessageDiscriminant::Rerender))),
|
||||||
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::Artboard(
|
|
||||||
ArtboardMessageDiscriminant::RenderArtboards,
|
|
||||||
))),
|
|
||||||
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::NodeGraph(NodeGraphMessageDiscriminant::SendGraph))),
|
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::NodeGraph(NodeGraphMessageDiscriminant::SendGraph))),
|
||||||
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::PropertiesPanel(
|
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::PropertiesPanel(
|
||||||
PropertiesPanelMessageDiscriminant::ResendActiveProperties,
|
PropertiesPanelMessageDiscriminant::ResendActiveProperties,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use super::simple_dialogs::{self, AboutGraphiteDialog, ComingSoonDialog, DemoArtworkDialog, LicensesDialog};
|
use super::simple_dialogs::{self, AboutGraphiteDialog, ComingSoonDialog, DemoArtworkDialog, LicensesDialog};
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
use crate::messages::tool::common_functionality::graph_modification_utils::is_artboard;
|
||||||
|
|
||||||
/// Stores the dialogs which require state. These are the ones that have their own message handlers, and are not the ones defined in `simple_dialogs`.
|
/// Stores the dialogs which require state. These are the ones that have their own message handlers, and are not the ones defined in `simple_dialogs`.
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
|
|
@ -67,23 +68,19 @@ impl MessageHandler<DialogMessage, (&PortfolioMessageHandler, &PreferencesMessag
|
||||||
}
|
}
|
||||||
DialogMessage::RequestExportDialog => {
|
DialogMessage::RequestExportDialog => {
|
||||||
if let Some(document) = portfolio.active_document() {
|
if let Some(document) = portfolio.active_document() {
|
||||||
let artboard_handler = &document.artboard_message_handler;
|
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
let artboards = artboard_handler
|
let artboards = document
|
||||||
.artboard_ids
|
.document_legacy
|
||||||
.iter()
|
.metadata
|
||||||
.rev()
|
.all_layers()
|
||||||
.filter_map(|&artboard| artboard_handler.artboards_document.layer(&[artboard]).ok().map(|layer| (artboard, layer)))
|
.filter(|&layer| is_artboard(layer, &document.document_legacy))
|
||||||
.map(|(artboard, layer)| {
|
.map(|layer| {
|
||||||
(
|
(
|
||||||
artboard,
|
layer,
|
||||||
format!(
|
format!("Artboard: {}", {
|
||||||
"Artboard: {}",
|
index += 1;
|
||||||
layer.name.clone().unwrap_or_else(|| {
|
format!("Untitled {index}")
|
||||||
index += 1;
|
}),
|
||||||
format!("Untitled {index}")
|
|
||||||
})
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use crate::messages::frontend::utility_types::{ExportBounds, FileType};
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
use document_legacy::LayerId;
|
use document_legacy::document_metadata::LayerNodeIdentifier;
|
||||||
|
|
||||||
/// A dialog to allow users to customize their file export.
|
/// A dialog to allow users to customize their file export.
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
|
|
@ -11,7 +11,7 @@ pub struct ExportDialogMessageHandler {
|
||||||
pub scale_factor: f64,
|
pub scale_factor: f64,
|
||||||
pub bounds: ExportBounds,
|
pub bounds: ExportBounds,
|
||||||
pub transparent_background: bool,
|
pub transparent_background: bool,
|
||||||
pub artboards: HashMap<LayerId, String>,
|
pub artboards: HashMap<LayerNodeIdentifier, String>,
|
||||||
pub has_selection: bool,
|
pub has_selection: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,7 +86,20 @@ impl LayoutHolder for ExportDialogMessageHandler {
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
];
|
];
|
||||||
|
|
||||||
let artboards = self.artboards.iter().map(|(&val, name)| (ExportBounds::Artboard(val), name.to_string(), false));
|
let resolution = vec![
|
||||||
|
TextLabel::new("Scale Factor").table_align(true).min_width(100).widget_holder(),
|
||||||
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
|
NumberInput::new(Some(self.scale_factor))
|
||||||
|
.unit("")
|
||||||
|
.min(0.)
|
||||||
|
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
|
||||||
|
.disabled(self.file_type == FileType::Svg)
|
||||||
|
.on_update(|number_input: &NumberInput| ExportDialogMessage::ScaleFactor(number_input.value.unwrap()).into())
|
||||||
|
.min_width(200)
|
||||||
|
.widget_holder(),
|
||||||
|
];
|
||||||
|
|
||||||
|
let artboards = self.artboards.iter().map(|(&layer, name)| (ExportBounds::Artboard(layer), name.to_string(), false));
|
||||||
let mut export_area_options = vec![
|
let mut export_area_options = vec![
|
||||||
(ExportBounds::AllArtwork, "All Artwork".to_string(), false),
|
(ExportBounds::AllArtwork, "All Artwork".to_string(), false),
|
||||||
(ExportBounds::Selection, "Selection".to_string(), !self.has_selection),
|
(ExportBounds::Selection, "Selection".to_string(), !self.has_selection),
|
||||||
|
|
|
||||||
|
|
@ -26,11 +26,6 @@ impl MessageHandler<NewDocumentDialogMessage, ()> for NewDocumentDialogMessageHa
|
||||||
|
|
||||||
if !self.infinite && self.dimensions.x > 0 && self.dimensions.y > 0 {
|
if !self.infinite && self.dimensions.x > 0 && self.dimensions.y > 0 {
|
||||||
let id = generate_uuid();
|
let id = generate_uuid();
|
||||||
responses.add(ArtboardMessage::AddArtboard {
|
|
||||||
id: Some(id),
|
|
||||||
position: (0., 0.),
|
|
||||||
size: (self.dimensions.x as f64, self.dimensions.y as f64),
|
|
||||||
});
|
|
||||||
responses.add(GraphOperationMessage::NewArtboard {
|
responses.add(GraphOperationMessage::NewArtboard {
|
||||||
id,
|
id,
|
||||||
artboard: graphene_core::Artboard::new(IVec2::ZERO, self.dimensions.as_ivec2()),
|
artboard: graphene_core::Artboard::new(IVec2::ZERO, self.dimensions.as_ivec2()),
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use document_legacy::document_metadata::LayerNodeIdentifier;
|
||||||
use document_legacy::LayerId;
|
use document_legacy::LayerId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
|
@ -63,5 +64,5 @@ pub enum ExportBounds {
|
||||||
#[default]
|
#[default]
|
||||||
AllArtwork,
|
AllArtwork,
|
||||||
Selection,
|
Selection,
|
||||||
Artboard(LayerId),
|
Artboard(LayerNodeIdentifier),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
use crate::messages::prelude::*;
|
|
||||||
|
|
||||||
use document_legacy::LayerId;
|
|
||||||
use document_legacy::Operation as DocumentOperation;
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[remain::sorted]
|
|
||||||
#[impl_message(Message, DocumentMessage, Artboard)]
|
|
||||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub enum ArtboardMessage {
|
|
||||||
// Sub-messages
|
|
||||||
#[remain::unsorted]
|
|
||||||
DispatchOperation(Box<DocumentOperation>),
|
|
||||||
|
|
||||||
// Messages
|
|
||||||
AddArtboard {
|
|
||||||
id: Option<LayerId>,
|
|
||||||
position: (f64, f64),
|
|
||||||
size: (f64, f64),
|
|
||||||
},
|
|
||||||
ClearArtboards,
|
|
||||||
DeleteArtboard {
|
|
||||||
artboard: LayerId,
|
|
||||||
},
|
|
||||||
RenderArtboards,
|
|
||||||
ResizeArtboard {
|
|
||||||
artboard: LayerId,
|
|
||||||
position: (f64, f64),
|
|
||||||
size: (f64, f64),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<DocumentOperation> for ArtboardMessage {
|
|
||||||
fn from(operation: DocumentOperation) -> Self {
|
|
||||||
Self::DispatchOperation(Box::new(operation))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,119 +0,0 @@
|
||||||
use crate::application::generate_uuid;
|
|
||||||
use crate::messages::portfolio::utility_types::PersistentData;
|
|
||||||
use crate::messages::prelude::*;
|
|
||||||
|
|
||||||
use document_legacy::document::Document as DocumentLegacy;
|
|
||||||
use document_legacy::layers::style::{self, Fill};
|
|
||||||
use document_legacy::DocumentResponse;
|
|
||||||
use document_legacy::LayerId;
|
|
||||||
use document_legacy::Operation as DocumentOperation;
|
|
||||||
use graphene_core::raster::color::Color;
|
|
||||||
|
|
||||||
use glam::DAffine2;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
|
|
||||||
pub struct ArtboardMessageHandler {
|
|
||||||
pub artboards_document: DocumentLegacy,
|
|
||||||
pub artboard_ids: Vec<LayerId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MessageHandler<ArtboardMessage, &PersistentData> for ArtboardMessageHandler {
|
|
||||||
#[remain::check]
|
|
||||||
fn process_message(&mut self, message: ArtboardMessage, responses: &mut VecDeque<Message>, _persistent_data: &PersistentData) {
|
|
||||||
use ArtboardMessage::*;
|
|
||||||
|
|
||||||
#[remain::sorted]
|
|
||||||
match message {
|
|
||||||
// Sub-messages
|
|
||||||
#[remain::unsorted]
|
|
||||||
DispatchOperation(operation) => match self.artboards_document.handle_operation(*operation) {
|
|
||||||
Ok(Some(document_responses)) => {
|
|
||||||
for response in document_responses {
|
|
||||||
match &response {
|
|
||||||
DocumentResponse::LayerChanged { path } => responses.add(PropertiesPanelMessage::CheckSelectedWasUpdated { path: path.clone() }),
|
|
||||||
DocumentResponse::DeletedLayer { path } => responses.add(PropertiesPanelMessage::CheckSelectedWasDeleted { path: path.clone() }),
|
|
||||||
DocumentResponse::DocumentChanged => responses.add(ArtboardMessage::RenderArtboards),
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(None) => {}
|
|
||||||
Err(e) => error!("Artboard Error: {:?}", e),
|
|
||||||
},
|
|
||||||
|
|
||||||
// Messages
|
|
||||||
AddArtboard { id, position, size } => {
|
|
||||||
let artboard_id = id.unwrap_or_else(generate_uuid);
|
|
||||||
self.artboard_ids.push(artboard_id);
|
|
||||||
|
|
||||||
responses.add(ArtboardMessage::DispatchOperation(
|
|
||||||
DocumentOperation::AddRect {
|
|
||||||
path: vec![artboard_id],
|
|
||||||
insert_index: -1,
|
|
||||||
transform: DAffine2::from_scale_angle_translation(size.into(), 0., position.into()).to_cols_array(),
|
|
||||||
style: style::PathStyle::new(None, Fill::solid(Color::WHITE)),
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
));
|
|
||||||
|
|
||||||
responses.add(DocumentMessage::RenderDocument);
|
|
||||||
}
|
|
||||||
ClearArtboards => {
|
|
||||||
// TODO: Make this remove the artboard layers from the graph (and cleanly reconnect the artwork)
|
|
||||||
responses.add(DialogMessage::RequestComingSoonDialog { issue: None });
|
|
||||||
// for &artboard in self.artboard_ids.iter() {
|
|
||||||
// responses.add_front(ArtboardMessage::DeleteArtboard { artboard });
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
DeleteArtboard { artboard } => {
|
|
||||||
self.artboard_ids.retain(|&id| id != artboard);
|
|
||||||
|
|
||||||
responses.add(ArtboardMessage::DispatchOperation(Box::new(DocumentOperation::DeleteLayer { path: vec![artboard] })));
|
|
||||||
|
|
||||||
responses.add(DocumentMessage::RenderDocument);
|
|
||||||
}
|
|
||||||
RenderArtboards => {
|
|
||||||
// Render an infinite canvas if there are no artboards
|
|
||||||
if self.artboard_ids.is_empty() {
|
|
||||||
responses.add(FrontendMessage::UpdateDocumentArtboards {
|
|
||||||
svg: r##"<rect width="100%" height="100%" fill="#ffffff" />"##.to_string(),
|
|
||||||
})
|
|
||||||
// TODO: Delete this whole legacy code path when cleaning up/removing the old (non-node based) artboard implementation
|
|
||||||
// TODO: The below code was used to draw the non-node based artboards, but we still need the above code to draw the infinite canvas until the refactor is complete and all this code can be removed
|
|
||||||
// } else {
|
|
||||||
// let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::Normal, None);
|
|
||||||
// responses.add(FrontendMessage::UpdateDocumentArtboards {
|
|
||||||
// svg: self.artboards_document.render_root(&render_data),
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ResizeArtboard { artboard, position, mut size } => {
|
|
||||||
if size.0.abs() == 0. {
|
|
||||||
size.0 = size.0.signum();
|
|
||||||
}
|
|
||||||
if size.1.abs() == 0. {
|
|
||||||
size.1 = size.1.signum();
|
|
||||||
}
|
|
||||||
|
|
||||||
responses.add(ArtboardMessage::DispatchOperation(Box::new(DocumentOperation::SetLayerTransform {
|
|
||||||
path: vec![artboard],
|
|
||||||
transform: DAffine2::from_scale_angle_translation(size.into(), 0., position.into()).to_cols_array(),
|
|
||||||
})));
|
|
||||||
|
|
||||||
responses.add(DocumentMessage::RenderDocument);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn actions(&self) -> ActionList {
|
|
||||||
actions!(ArtboardMessageDiscriminant;)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ArtboardMessageHandler {
|
|
||||||
pub fn is_infinite_canvas(&self) -> bool {
|
|
||||||
self.artboard_ids.is_empty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
mod artboard_message;
|
|
||||||
mod artboard_message_handler;
|
|
||||||
|
|
||||||
#[doc(inline)]
|
|
||||||
pub use artboard_message::{ArtboardMessage, ArtboardMessageDiscriminant};
|
|
||||||
#[doc(inline)]
|
|
||||||
pub use artboard_message_handler::ArtboardMessageHandler;
|
|
||||||
|
|
@ -23,9 +23,6 @@ pub enum DocumentMessage {
|
||||||
DispatchOperation(Box<DocumentOperation>),
|
DispatchOperation(Box<DocumentOperation>),
|
||||||
#[remain::unsorted]
|
#[remain::unsorted]
|
||||||
#[child]
|
#[child]
|
||||||
Artboard(ArtboardMessage),
|
|
||||||
#[remain::unsorted]
|
|
||||||
#[child]
|
|
||||||
Navigation(NavigationMessage),
|
Navigation(NavigationMessage),
|
||||||
#[remain::unsorted]
|
#[remain::unsorted]
|
||||||
#[child]
|
#[child]
|
||||||
|
|
@ -51,7 +48,6 @@ pub enum DocumentMessage {
|
||||||
},
|
},
|
||||||
BackupDocument {
|
BackupDocument {
|
||||||
document: DocumentLegacy,
|
document: DocumentLegacy,
|
||||||
artboard: Box<ArtboardMessageHandler>,
|
|
||||||
layer_metadata: HashMap<Vec<LayerId>, LayerMetadata>,
|
layer_metadata: HashMap<Vec<LayerId>, LayerMetadata>,
|
||||||
},
|
},
|
||||||
ClearLayerTree,
|
ClearLayerTree,
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,6 @@ pub struct DocumentMessageHandler {
|
||||||
navigation_handler: NavigationMessageHandler,
|
navigation_handler: NavigationMessageHandler,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
overlays_message_handler: OverlaysMessageHandler,
|
overlays_message_handler: OverlaysMessageHandler,
|
||||||
pub artboard_message_handler: ArtboardMessageHandler,
|
|
||||||
properties_panel_message_handler: PropertiesPanelMessageHandler,
|
properties_panel_message_handler: PropertiesPanelMessageHandler,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
node_graph_handler: NodeGraphMessageHandler,
|
node_graph_handler: NodeGraphMessageHandler,
|
||||||
|
|
@ -95,7 +94,6 @@ impl Default for DocumentMessageHandler {
|
||||||
|
|
||||||
navigation_handler: NavigationMessageHandler::default(),
|
navigation_handler: NavigationMessageHandler::default(),
|
||||||
overlays_message_handler: OverlaysMessageHandler::default(),
|
overlays_message_handler: OverlaysMessageHandler::default(),
|
||||||
artboard_message_handler: ArtboardMessageHandler::default(),
|
|
||||||
properties_panel_message_handler: PropertiesPanelMessageHandler::default(),
|
properties_panel_message_handler: PropertiesPanelMessageHandler::default(),
|
||||||
node_graph_handler: Default::default(),
|
node_graph_handler: Default::default(),
|
||||||
}
|
}
|
||||||
|
|
@ -176,10 +174,6 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[remain::unsorted]
|
#[remain::unsorted]
|
||||||
Artboard(message) => {
|
|
||||||
self.artboard_message_handler.process_message(message, responses, persistent_data);
|
|
||||||
}
|
|
||||||
#[remain::unsorted]
|
|
||||||
Navigation(message) => {
|
Navigation(message) => {
|
||||||
let document_bounds = self.document_bounds(&render_data);
|
let document_bounds = self.document_bounds(&render_data);
|
||||||
self.navigation_handler.process_message(
|
self.navigation_handler.process_message(
|
||||||
|
|
@ -197,7 +191,6 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
|
||||||
let properties_panel_message_handler_data = PropertiesPanelMessageHandlerData {
|
let properties_panel_message_handler_data = PropertiesPanelMessageHandlerData {
|
||||||
document_name: self.name.as_str(),
|
document_name: self.name.as_str(),
|
||||||
artwork_document: &self.document_legacy,
|
artwork_document: &self.document_legacy,
|
||||||
artboard_document: &self.artboard_message_handler.artboards_document,
|
|
||||||
selected_layers: &mut self.layer_metadata.iter().filter_map(|(path, data)| data.selected.then_some(path.as_slice())),
|
selected_layers: &mut self.layer_metadata.iter().filter_map(|(path, data)| data.selected.then_some(path.as_slice())),
|
||||||
node_graph_message_handler: &self.node_graph_handler,
|
node_graph_message_handler: &self.node_graph_handler,
|
||||||
executor,
|
executor,
|
||||||
|
|
@ -275,7 +268,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
|
||||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BackupDocument { document, artboard, layer_metadata } => self.backup_with_document(document, *artboard, layer_metadata, responses),
|
BackupDocument { document, layer_metadata } => self.backup_with_document(document, layer_metadata, responses),
|
||||||
ClearLayerTree => {
|
ClearLayerTree => {
|
||||||
// Send an empty layer tree
|
// Send an empty layer tree
|
||||||
let data_buffer: RawBuffer = Self::default().serialize_root().as_slice().into();
|
let data_buffer: RawBuffer = Self::default().serialize_root().as_slice().into();
|
||||||
|
|
@ -375,13 +368,13 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
|
||||||
bounds,
|
bounds,
|
||||||
transparent_background,
|
transparent_background,
|
||||||
} => {
|
} => {
|
||||||
let old_transforms = self.remove_document_transform();
|
let old_artwork_transform = self.remove_document_transform();
|
||||||
|
|
||||||
// Calculate the bounding box of the region to be exported
|
// Calculate the bounding box of the region to be exported
|
||||||
let bounds = match bounds {
|
let bounds = match bounds {
|
||||||
ExportBounds::AllArtwork => self.all_layer_bounds(&render_data),
|
ExportBounds::AllArtwork => self.all_layer_bounds(&render_data),
|
||||||
ExportBounds::Selection => self.selected_visible_layers_bounding_box(&render_data),
|
ExportBounds::Selection => self.selected_visible_layers_bounding_box(&render_data),
|
||||||
ExportBounds::Artboard(id) => self.artboard_message_handler.artboards_document.layer(&[id]).ok().and_then(|layer| layer.aabb(&render_data)),
|
ExportBounds::Artboard(id) => self.document_legacy.metadata.bounding_box_document(id),
|
||||||
}
|
}
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let size = bounds[1] - bounds[0];
|
let size = bounds[1] - bounds[0];
|
||||||
|
|
@ -389,7 +382,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
|
||||||
|
|
||||||
let document = self.render_document(size, transform, transparent_background, persistent_data, DocumentRenderMode::Root);
|
let document = self.render_document(size, transform, transparent_background, persistent_data, DocumentRenderMode::Root);
|
||||||
|
|
||||||
self.restore_document_transform(old_transforms);
|
self.restore_document_transform(old_artwork_transform);
|
||||||
|
|
||||||
let file_suffix = &format!(".{file_type:?}").to_lowercase();
|
let file_suffix = &format!(".{file_type:?}").to_lowercase();
|
||||||
let name = match file_name.ends_with(FILE_SAVE_SUFFIX) {
|
let name = match file_name.ends_with(FILE_SAVE_SUFFIX) {
|
||||||
|
|
@ -681,7 +674,6 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
|
||||||
responses.add(FrontendMessage::UpdateDocumentArtwork {
|
responses.add(FrontendMessage::UpdateDocumentArtwork {
|
||||||
svg: self.document_legacy.render_root(&render_data),
|
svg: self.document_legacy.render_root(&render_data),
|
||||||
});
|
});
|
||||||
responses.add(ArtboardMessage::RenderArtboards);
|
|
||||||
|
|
||||||
let document_transform_scale = self.navigation_handler.snapped_scale();
|
let document_transform_scale = self.navigation_handler.snapped_scale();
|
||||||
let scale = 0.5 + ASYMPTOTIC_EFFECT + document_transform_scale * SCALE_EFFECT;
|
let scale = 0.5 + ASYMPTOTIC_EFFECT + document_transform_scale * SCALE_EFFECT;
|
||||||
|
|
@ -699,7 +691,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
|
||||||
let ruler_interval = if log < 0. { 100. * 2_f64.powf(-log.ceil()) } else { 100. / 2_f64.powf(log.ceil()) };
|
let ruler_interval = if log < 0. { 100. * 2_f64.powf(-log.ceil()) } else { 100. / 2_f64.powf(log.ceil()) };
|
||||||
let ruler_spacing = ruler_interval * document_transform_scale;
|
let ruler_spacing = ruler_interval * document_transform_scale;
|
||||||
|
|
||||||
let ruler_origin = self.document_legacy.root.transform.transform_point2(DVec2::ZERO);
|
let ruler_origin = self.document_legacy.metadata.document_to_viewport.transform_point2(DVec2::ZERO);
|
||||||
|
|
||||||
responses.add(FrontendMessage::UpdateDocumentScrollbars {
|
responses.add(FrontendMessage::UpdateDocumentScrollbars {
|
||||||
position: scrollbar_position.into(),
|
position: scrollbar_position.into(),
|
||||||
|
|
@ -1020,7 +1012,7 @@ impl DocumentMessageHandler {
|
||||||
|
|
||||||
// If the Input Frame node is connected upstream, rasterize the artwork below this layer by calling into JS
|
// If the Input Frame node is connected upstream, rasterize the artwork below this layer by calling into JS
|
||||||
let response = if input_frame_connected_to_graph_output {
|
let response = if input_frame_connected_to_graph_output {
|
||||||
let old_transforms = self.remove_document_transform();
|
let old_artwork_transform = self.remove_document_transform();
|
||||||
|
|
||||||
// Calculate the size of the region to be exported and generate an SVG of the artwork below this layer within that region
|
// Calculate the size of the region to be exported and generate an SVG of the artwork below this layer within that region
|
||||||
let transform = self.document_legacy.multiply_transforms(&layer_path).unwrap();
|
let transform = self.document_legacy.multiply_transforms(&layer_path).unwrap();
|
||||||
|
|
@ -1028,7 +1020,7 @@ impl DocumentMessageHandler {
|
||||||
// TODO: Test if this would be better to have a transparent background
|
// TODO: Test if this would be better to have a transparent background
|
||||||
let svg = self.render_document(size, transform.inverse(), false, persistent_data, DocumentRenderMode::OnlyBelowLayerInFolder(&layer_path));
|
let svg = self.render_document(size, transform.inverse(), false, persistent_data, DocumentRenderMode::OnlyBelowLayerInFolder(&layer_path));
|
||||||
|
|
||||||
self.restore_document_transform(old_transforms);
|
self.restore_document_transform(old_artwork_transform);
|
||||||
|
|
||||||
// Once JS asynchronously rasterizes the SVG, it will call the `PortfolioMessage::RenderGraphUsingRasterizedRegionBelowLayer` message with the rasterized image data
|
// Once JS asynchronously rasterizes the SVG, it will call the `PortfolioMessage::RenderGraphUsingRasterizedRegionBelowLayer` message with the rasterized image data
|
||||||
FrontendMessage::TriggerRasterizeRegionBelowLayer { document_id, layer_path, svg, size }.into()
|
FrontendMessage::TriggerRasterizeRegionBelowLayer { document_id, layer_path, svg, size }.into()
|
||||||
|
|
@ -1047,25 +1039,18 @@ impl DocumentMessageHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove the artwork and artboard pan/tilt/zoom to render it without the user's viewport navigation, and save it to be restored at the end
|
/// Remove the artwork and artboard pan/tilt/zoom to render it without the user's viewport navigation, and save it to be restored at the end
|
||||||
pub(crate) fn remove_document_transform(&mut self) -> [DAffine2; 2] {
|
pub(crate) fn remove_document_transform(&mut self) -> DAffine2 {
|
||||||
let old_artwork_transform = self.document_legacy.root.transform;
|
let old_artwork_transform = self.document_legacy.metadata.document_to_viewport;
|
||||||
self.document_legacy.root.transform = DAffine2::IDENTITY;
|
self.document_legacy.metadata.document_to_viewport = DAffine2::IDENTITY;
|
||||||
DocumentLegacy::mark_children_as_dirty(&mut self.document_legacy.root);
|
DocumentLegacy::mark_children_as_dirty(&mut self.document_legacy.root);
|
||||||
|
|
||||||
let old_artboard_transform = self.artboard_message_handler.artboards_document.root.transform;
|
old_artwork_transform
|
||||||
self.artboard_message_handler.artboards_document.root.transform = DAffine2::IDENTITY;
|
|
||||||
DocumentLegacy::mark_children_as_dirty(&mut self.artboard_message_handler.artboards_document.root);
|
|
||||||
|
|
||||||
[old_artwork_transform, old_artboard_transform]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transform the artwork and artboard back to their original scales
|
/// Transform the artwork and artboard back to their original scales
|
||||||
pub(crate) fn restore_document_transform(&mut self, [old_artwork_transform, old_artboard_transform]: [DAffine2; 2]) {
|
pub(crate) fn restore_document_transform(&mut self, old_artwork_transform: DAffine2) {
|
||||||
self.document_legacy.root.transform = old_artwork_transform;
|
self.document_legacy.metadata.document_to_viewport = old_artwork_transform;
|
||||||
DocumentLegacy::mark_children_as_dirty(&mut self.document_legacy.root);
|
DocumentLegacy::mark_children_as_dirty(&mut self.document_legacy.root);
|
||||||
|
|
||||||
self.artboard_message_handler.artboards_document.root.transform = old_artboard_transform;
|
|
||||||
DocumentLegacy::mark_children_as_dirty(&mut self.artboard_message_handler.artboards_document.root);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_document(&mut self, size: DVec2, transform: DAffine2, transparent_background: bool, persistent_data: &PersistentData, render_mode: DocumentRenderMode) -> String {
|
pub fn render_document(&mut self, size: DVec2, transform: DAffine2, transparent_background: bool, persistent_data: &PersistentData, render_mode: DocumentRenderMode) -> String {
|
||||||
|
|
@ -1079,13 +1064,10 @@ impl DocumentMessageHandler {
|
||||||
DocumentRenderMode::LayerCutout(layer_path, background) => (self.document_legacy.render_layer(layer_path, &render_data).unwrap(), Some(background)),
|
DocumentRenderMode::LayerCutout(layer_path, background) => (self.document_legacy.render_layer(layer_path, &render_data).unwrap(), Some(background)),
|
||||||
};
|
};
|
||||||
let artboards = match transparent_background {
|
let artboards = match transparent_background {
|
||||||
false => self.artboard_message_handler.artboards_document.render_root(&render_data),
|
false => "<!--artboards-->",
|
||||||
true => "".into(),
|
true => "".into(),
|
||||||
};
|
};
|
||||||
let outside_artboards_color = outside.map_or_else(
|
let outside_artboards_color = outside.map_or_else(|| if false { "ffffff" } else { "222222" }.to_string(), |col| col.rgba_hex());
|
||||||
|| if self.artboard_message_handler.artboard_ids.is_empty() { "ffffff" } else { "222222" }.to_string(),
|
|
||||||
|col| col.rgba_hex(),
|
|
||||||
);
|
|
||||||
let outside_artboards = match transparent_background {
|
let outside_artboards = match transparent_background {
|
||||||
false => format!(r##"<rect x="0" y="0" width="100%" height="100%" fill="#{}" />"##, outside_artboards_color),
|
false => format!(r##"<rect x="0" y="0" width="100%" height="100%" fill="#{}" />"##, outside_artboards_color),
|
||||||
true => "".into(),
|
true => "".into(),
|
||||||
|
|
@ -1123,11 +1105,11 @@ impl DocumentMessageHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_name(name: String, ipp: &InputPreprocessorMessageHandler) -> Self {
|
pub fn with_name(name: String, ipp: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) -> Self {
|
||||||
let mut document = Self { name, ..Self::default() };
|
let mut document = Self { name, ..Self::default() };
|
||||||
let starting_root_transform = document.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.size() / 2.);
|
let transform = document.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.size() / 2.);
|
||||||
document.document_legacy.metadata.document_to_viewport = starting_root_transform;
|
document.document_legacy.metadata.document_to_viewport = transform;
|
||||||
document.artboard_message_handler.artboards_document.root.transform = starting_root_transform;
|
responses.add(DocumentMessage::UpdateDocumentTransform { transform });
|
||||||
|
|
||||||
document
|
document
|
||||||
}
|
}
|
||||||
|
|
@ -1170,10 +1152,6 @@ impl DocumentMessageHandler {
|
||||||
self.document_legacy.combined_viewport_bounding_box(paths, render_data)
|
self.document_legacy.combined_viewport_bounding_box(paths, render_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn artboard_bounding_box_and_transform(&self, path: &[LayerId], render_data: &RenderData) -> Option<([DVec2; 2], DAffine2)> {
|
|
||||||
self.artboard_message_handler.artboards_document.bounding_box_and_transform(path, render_data).unwrap_or(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn selected_layers(&self) -> impl Iterator<Item = &[LayerId]> {
|
pub fn selected_layers(&self) -> impl Iterator<Item = &[LayerId]> {
|
||||||
self.layer_metadata.iter().filter_map(|(path, data)| data.selected.then_some(path.as_slice()))
|
self.layer_metadata.iter().filter_map(|(path, data)| data.selected.then_some(path.as_slice()))
|
||||||
}
|
}
|
||||||
|
|
@ -1216,18 +1194,11 @@ impl DocumentMessageHandler {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the bounding boxes for all visible layers and artboards, optionally excluding any paths.
|
/// 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 {
|
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()
|
self.visible_layers()
|
||||||
.filter(move |path| ignore_document.map_or(true, |ignore_document| !ignore_document.iter().any(|ig| ig.as_slice() == *path)))
|
.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()?)
|
.filter_map(|path| self.document_legacy.viewport_bounding_box(path, render_data).ok()?)
|
||||||
.chain(
|
|
||||||
self.artboard_message_handler
|
|
||||||
.artboard_ids
|
|
||||||
.iter()
|
|
||||||
.filter(move |&&id| Some(id) != ignore_artboard)
|
|
||||||
.filter_map(|&path| self.artboard_message_handler.artboards_document.viewport_bounding_box(&[path], render_data).ok()?),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_structure(&self, folder: LayerNodeIdentifier, structure: &mut Vec<u64>, data: &mut Vec<LayerId>, path: &mut Vec<LayerId>) {
|
fn serialize_structure(&self, folder: LayerNodeIdentifier, structure: &mut Vec<u64>, data: &mut Vec<LayerId>, path: &mut Vec<LayerId>) {
|
||||||
|
|
@ -1348,9 +1319,9 @@ impl DocumentMessageHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Places a document into the history system
|
/// Places a document into the history system
|
||||||
fn backup_with_document(&mut self, document: DocumentLegacy, artboard: ArtboardMessageHandler, layer_metadata: HashMap<Vec<LayerId>, LayerMetadata>, responses: &mut VecDeque<Message>) {
|
fn backup_with_document(&mut self, document: DocumentLegacy, layer_metadata: HashMap<Vec<LayerId>, LayerMetadata>, responses: &mut VecDeque<Message>) {
|
||||||
self.document_redo_history.clear();
|
self.document_redo_history.clear();
|
||||||
self.document_undo_history.push_back(DocumentSave { document, artboard, layer_metadata });
|
self.document_undo_history.push_back(DocumentSave { document, layer_metadata });
|
||||||
if self.document_undo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN {
|
if self.document_undo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN {
|
||||||
self.document_undo_history.pop_front();
|
self.document_undo_history.pop_front();
|
||||||
}
|
}
|
||||||
|
|
@ -1361,14 +1332,13 @@ impl DocumentMessageHandler {
|
||||||
|
|
||||||
/// Copies the entire document into the history system
|
/// Copies the entire document into the history system
|
||||||
pub fn backup(&mut self, responses: &mut VecDeque<Message>) {
|
pub fn backup(&mut self, responses: &mut VecDeque<Message>) {
|
||||||
self.backup_with_document(self.document_legacy.clone(), self.artboard_message_handler.clone(), self.layer_metadata.clone(), responses);
|
self.backup_with_document(self.document_legacy.clone(), self.layer_metadata.clone(), responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Push a message backing up the document in its current state
|
/// Push a message backing up the document in its current state
|
||||||
pub fn backup_nonmut(&self, responses: &mut VecDeque<Message>) {
|
pub fn backup_nonmut(&self, responses: &mut VecDeque<Message>) {
|
||||||
responses.add(DocumentMessage::BackupDocument {
|
responses.add(DocumentMessage::BackupDocument {
|
||||||
document: self.document_legacy.clone(),
|
document: self.document_legacy.clone(),
|
||||||
artboard: Box::new(self.artboard_message_handler.clone()),
|
|
||||||
layer_metadata: self.layer_metadata.clone(),
|
layer_metadata: self.layer_metadata.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -1380,20 +1350,16 @@ impl DocumentMessageHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Replace the document with a new document save, returning the document save.
|
/// Replace the document with a new document save, returning the document save.
|
||||||
pub fn replace_document(&mut self, DocumentSave { document, artboard, layer_metadata }: DocumentSave) -> DocumentSave {
|
pub fn replace_document(&mut self, DocumentSave { document, layer_metadata }: DocumentSave) -> DocumentSave {
|
||||||
// Keeping the root is required if the bounds of the viewport have changed during the operation
|
// Keeping the root is required if the bounds of the viewport have changed during the operation
|
||||||
let old_root = self.document_legacy.root.transform;
|
let old_root = self.document_legacy.metadata.document_to_viewport;
|
||||||
let old_artboard_root = self.artboard_message_handler.artboards_document.root.transform;
|
|
||||||
let document = std::mem::replace(&mut self.document_legacy, document);
|
let document = std::mem::replace(&mut self.document_legacy, document);
|
||||||
let artboard = std::mem::replace(&mut self.artboard_message_handler, artboard);
|
self.document_legacy.metadata.document_to_viewport = old_root;
|
||||||
self.document_legacy.root.transform = old_root;
|
|
||||||
self.artboard_message_handler.artboards_document.root.transform = old_artboard_root;
|
|
||||||
self.document_legacy.root.cache_dirty = true;
|
self.document_legacy.root.cache_dirty = true;
|
||||||
self.artboard_message_handler.artboards_document.root.cache_dirty = true;
|
|
||||||
|
|
||||||
let layer_metadata = std::mem::replace(&mut self.layer_metadata, layer_metadata);
|
let layer_metadata = std::mem::replace(&mut self.layer_metadata, layer_metadata);
|
||||||
|
|
||||||
DocumentSave { document, artboard, layer_metadata }
|
DocumentSave { document, layer_metadata }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn undo(&mut self, responses: &mut VecDeque<Message>) -> Result<(), EditorError> {
|
pub fn undo(&mut self, responses: &mut VecDeque<Message>) -> Result<(), EditorError> {
|
||||||
|
|
@ -1403,7 +1369,7 @@ impl DocumentMessageHandler {
|
||||||
let selected_paths: Vec<Vec<LayerId>> = self.selected_layers().map(|path| path.to_vec()).collect();
|
let selected_paths: Vec<Vec<LayerId>> = self.selected_layers().map(|path| path.to_vec()).collect();
|
||||||
|
|
||||||
match self.document_undo_history.pop_back() {
|
match self.document_undo_history.pop_back() {
|
||||||
Some(DocumentSave { document, artboard, layer_metadata }) => {
|
Some(DocumentSave { document, layer_metadata }) => {
|
||||||
// Update the currently displayed layer on the Properties panel if the selection changes after an undo action
|
// Update the currently displayed layer on the Properties panel if the selection changes after an undo action
|
||||||
// Also appropriately update the Properties panel if an undo action results in a layer being deleted
|
// Also appropriately update the Properties panel if an undo action results in a layer being deleted
|
||||||
let prev_selected_paths: Vec<Vec<LayerId>> = layer_metadata.iter().filter_map(|(layer_id, metadata)| metadata.selected.then_some(layer_id.clone())).collect();
|
let prev_selected_paths: Vec<Vec<LayerId>> = layer_metadata.iter().filter_map(|(layer_id, metadata)| metadata.selected.then_some(layer_id.clone())).collect();
|
||||||
|
|
@ -1412,7 +1378,7 @@ impl DocumentMessageHandler {
|
||||||
responses.add(BroadcastEvent::SelectionChanged);
|
responses.add(BroadcastEvent::SelectionChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
let document_save = self.replace_document(DocumentSave { document, artboard, layer_metadata });
|
let document_save = self.replace_document(DocumentSave { document, layer_metadata });
|
||||||
|
|
||||||
self.document_redo_history.push_back(document_save);
|
self.document_redo_history.push_back(document_save);
|
||||||
if self.document_redo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN {
|
if self.document_redo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN {
|
||||||
|
|
@ -1438,7 +1404,7 @@ impl DocumentMessageHandler {
|
||||||
let selected_paths: Vec<Vec<LayerId>> = self.selected_layers().map(|path| path.to_vec()).collect();
|
let selected_paths: Vec<Vec<LayerId>> = self.selected_layers().map(|path| path.to_vec()).collect();
|
||||||
|
|
||||||
match self.document_redo_history.pop_back() {
|
match self.document_redo_history.pop_back() {
|
||||||
Some(DocumentSave { document, artboard, layer_metadata }) => {
|
Some(DocumentSave { document, layer_metadata }) => {
|
||||||
// Update currently displayed layer on property panel if selection changes after redo action
|
// Update currently displayed layer on property panel if selection changes after redo action
|
||||||
// Also appropriately update property panel if redo action results in a layer being added
|
// Also appropriately update property panel if redo action results in a layer being added
|
||||||
let next_selected_paths: Vec<Vec<LayerId>> = layer_metadata.iter().filter_map(|(layer_id, metadata)| metadata.selected.then_some(layer_id.clone())).collect();
|
let next_selected_paths: Vec<Vec<LayerId>> = layer_metadata.iter().filter_map(|(layer_id, metadata)| metadata.selected.then_some(layer_id.clone())).collect();
|
||||||
|
|
@ -1447,7 +1413,7 @@ impl DocumentMessageHandler {
|
||||||
responses.add(BroadcastEvent::SelectionChanged);
|
responses.add(BroadcastEvent::SelectionChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
let document_save = self.replace_document(DocumentSave { document, artboard, layer_metadata });
|
let document_save = self.replace_document(DocumentSave { document, layer_metadata });
|
||||||
self.document_undo_history.push_back(document_save);
|
self.document_undo_history.push_back(document_save);
|
||||||
if self.document_undo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN {
|
if self.document_undo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN {
|
||||||
self.document_undo_history.pop_front();
|
self.document_undo_history.pop_front();
|
||||||
|
|
@ -1521,7 +1487,10 @@ impl DocumentMessageHandler {
|
||||||
|
|
||||||
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], render_data: &RenderData) -> Option<LayerPanelEntry> {
|
||||||
let layer_metadata = self.layer_metadata(path);
|
let layer_metadata = self.layer_metadata(path);
|
||||||
let transform = self.document_legacy.generate_transform_across_scope(path, Some(self.document_legacy.root.transform.inverse())).ok()?;
|
let transform = self
|
||||||
|
.document_legacy
|
||||||
|
.generate_transform_across_scope(path, Some(self.document_legacy.metadata.document_to_viewport.inverse()))
|
||||||
|
.ok()?;
|
||||||
let layer = self.document_legacy.layer(path).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, transform, layer, path.to_vec(), render_data))
|
||||||
|
|
@ -1545,11 +1514,7 @@ impl DocumentMessageHandler {
|
||||||
|
|
||||||
/// Calculates the document bounds used for scrolling and centring (the layer bounds or the artboard (if applicable))
|
/// Calculates the document bounds used for scrolling and centring (the layer bounds or the artboard (if applicable))
|
||||||
pub fn document_bounds(&self, render_data: &RenderData) -> Option<[DVec2; 2]> {
|
pub fn document_bounds(&self, render_data: &RenderData) -> Option<[DVec2; 2]> {
|
||||||
if self.artboard_message_handler.is_infinite_canvas() {
|
self.all_layer_bounds(render_data)
|
||||||
self.all_layer_bounds(render_data)
|
|
||||||
} else {
|
|
||||||
self.artboard_message_handler.artboards_document.viewport_bounding_box(&[], render_data).ok().flatten()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the path that new layers should be inserted to.
|
/// Calculate the path that new layers should be inserted to.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
mod document_message;
|
mod document_message;
|
||||||
mod document_message_handler;
|
mod document_message_handler;
|
||||||
|
|
||||||
pub mod artboard;
|
|
||||||
pub mod navigation;
|
pub mod navigation;
|
||||||
pub mod node_graph;
|
pub mod node_graph;
|
||||||
pub mod overlays;
|
pub mod overlays;
|
||||||
|
|
|
||||||
|
|
@ -378,37 +378,42 @@ impl<'a> ModifyInputsContext<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_layer(&mut self, id: NodeId) {
|
fn delete_layer(&mut self, id: NodeId) {
|
||||||
if !self.network.nodes.contains_key(&id) {
|
let Some(node) = self.network.nodes.get(&id) else {
|
||||||
warn!("Deleting layer node that does not exist");
|
warn!("Deleting layer node that does not exist");
|
||||||
return;
|
return;
|
||||||
}
|
};
|
||||||
|
|
||||||
let mut new_input = None;
|
LayerNodeIdentifier::new(id, self.network).delete(self.document_metadata);
|
||||||
let post_node = self.outwards_links.get(&id).and_then(|links| links.first().copied());
|
|
||||||
let mut delete_nodes = vec![id];
|
let new_input = node.inputs[7].clone();
|
||||||
let mut is_artboard = false;
|
|
||||||
for (node, id) in self.network.primary_flow_from_opt(Some(id)) {
|
for post_node in self.outwards_links.get(&id).unwrap_or(&Vec::new()) {
|
||||||
delete_nodes.push(id);
|
let Some(node) = self.network.nodes.get_mut(post_node) else {
|
||||||
if node.name == "Artboard" {
|
continue;
|
||||||
new_input = Some(node.inputs[0].clone());
|
};
|
||||||
is_artboard = true;
|
|
||||||
break;
|
for input in &mut node.inputs {
|
||||||
|
if let NodeInput::Node { node_id, .. } = input {
|
||||||
|
if *node_id == id {
|
||||||
|
*input = new_input.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !is_artboard {
|
|
||||||
LayerNodeIdentifier::new(id, self.network).delete(self.document_metadata);
|
let mut delete_nodes = vec![id];
|
||||||
|
for (_node, id) in self.network.primary_flow_from_opt(Some(id)) {
|
||||||
|
if self.outwards_links.get(&id).is_some_and(|outwards| outwards.len() == 1) {
|
||||||
|
delete_nodes.push(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.responses.add(DocumentMessage::DocumentStructureChanged);
|
|
||||||
|
|
||||||
for node_id in delete_nodes {
|
for node_id in delete_nodes {
|
||||||
self.network.nodes.remove(&node_id);
|
self.network.nodes.remove(&node_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (Some(new_input), Some(post_node)) = (new_input, post_node) {
|
self.responses.add(DocumentMessage::DocumentStructureChanged);
|
||||||
if let Some(node) = self.network.nodes.get_mut(&post_node) {
|
self.responses.add(NodeGraphMessage::SendGraph { should_rerender: true });
|
||||||
node.inputs[0] = new_input;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
use super::utility_types::TransformOp;
|
use super::utility_types::TransformOp;
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::portfolio::document::utility_types::misc::TargetDocument;
|
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
use document_legacy::layers::style::{Fill, Stroke};
|
use document_legacy::layers::style::{Fill, Stroke};
|
||||||
|
|
@ -24,7 +23,7 @@ pub enum PropertiesPanelMessage {
|
||||||
ModifyStroke { stroke: Stroke },
|
ModifyStroke { stroke: Stroke },
|
||||||
ModifyTransform { value: f64, transform_op: TransformOp },
|
ModifyTransform { value: f64, transform_op: TransformOp },
|
||||||
ResendActiveProperties,
|
ResendActiveProperties,
|
||||||
SetActiveLayers { paths: Vec<Vec<LayerId>>, document: TargetDocument },
|
SetActiveLayers { paths: Vec<Vec<LayerId>> },
|
||||||
SetPivot { new_position: PivotPosition },
|
SetPivot { new_position: PivotPosition },
|
||||||
UpdateSelectedDocumentProperties,
|
UpdateSelectedDocumentProperties,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
use super::utility_functions::{register_artboard_layer_properties, register_artwork_layer_properties, register_document_graph_properties};
|
use super::utility_functions::{register_artwork_layer_properties, register_document_graph_properties};
|
||||||
use super::utility_types::PropertiesPanelMessageHandlerData;
|
use super::utility_types::PropertiesPanelMessageHandlerData;
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
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::properties_panel::utility_functions::apply_transform_operation;
|
||||||
use crate::messages::portfolio::document::utility_types::misc::TargetDocument;
|
|
||||||
use crate::messages::portfolio::utility_types::PersistentData;
|
use crate::messages::portfolio::utility_types::PersistentData;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
|
|
@ -14,7 +13,7 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||||
pub struct PropertiesPanelMessageHandler {
|
pub struct PropertiesPanelMessageHandler {
|
||||||
active_selection: Option<(Vec<LayerId>, TargetDocument)>,
|
active_selection: Option<Vec<LayerId>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPanelMessageHandlerData<'a>)> for PropertiesPanelMessageHandler {
|
impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPanelMessageHandlerData<'a>)> for PropertiesPanelMessageHandler {
|
||||||
|
|
@ -25,28 +24,23 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
|
||||||
let PropertiesPanelMessageHandlerData {
|
let PropertiesPanelMessageHandlerData {
|
||||||
document_name,
|
document_name,
|
||||||
artwork_document,
|
artwork_document,
|
||||||
artboard_document,
|
|
||||||
selected_layers,
|
selected_layers,
|
||||||
node_graph_message_handler,
|
node_graph_message_handler,
|
||||||
executor,
|
executor,
|
||||||
} = data;
|
} = data;
|
||||||
let get_document = |document_selector: TargetDocument| match document_selector {
|
|
||||||
TargetDocument::Artboard => artboard_document,
|
|
||||||
TargetDocument::Artwork => artwork_document,
|
|
||||||
};
|
|
||||||
let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::Normal, None);
|
let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::Normal, None);
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
SetActiveLayers { paths, document } => {
|
SetActiveLayers { paths } => {
|
||||||
if paths.len() != 1 {
|
if paths.len() != 1 {
|
||||||
// TODO: Allow for multiple selected layers
|
// TODO: Allow for multiple selected layers
|
||||||
responses.add(PropertiesPanelMessage::ClearSelection);
|
responses.add(PropertiesPanelMessage::ClearSelection);
|
||||||
responses.add(NodeGraphMessage::CloseNodeGraph);
|
responses.add(NodeGraphMessage::CloseNodeGraph);
|
||||||
} else {
|
} else {
|
||||||
let path = paths.into_iter().next().unwrap();
|
let path = paths.into_iter().next().unwrap();
|
||||||
if Some((path.clone(), document)) != self.active_selection {
|
if Some(&path) != self.active_selection.as_ref() {
|
||||||
// Update the layer visibility
|
// Update the layer visibility
|
||||||
if get_document(document)
|
if artwork_document
|
||||||
.layer(&path)
|
.layer(&path)
|
||||||
.ok()
|
.ok()
|
||||||
.filter(|layer| LayerDataTypeDiscriminant::from(&layer.data) == LayerDataTypeDiscriminant::Layer)
|
.filter(|layer| LayerDataTypeDiscriminant::from(&layer.data) == LayerDataTypeDiscriminant::Layer)
|
||||||
|
|
@ -57,7 +51,7 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
|
||||||
responses.add(NodeGraphMessage::CloseNodeGraph);
|
responses.add(NodeGraphMessage::CloseNodeGraph);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.active_selection = Some((path, document));
|
self.active_selection = Some(path);
|
||||||
responses.add(PropertiesPanelMessage::ResendActiveProperties);
|
responses.add(PropertiesPanelMessage::ResendActiveProperties);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -85,31 +79,31 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
|
||||||
send: Box::new(PropertiesPanelMessage::UpdateSelectedDocumentProperties.into()),
|
send: Box::new(PropertiesPanelMessage::UpdateSelectedDocumentProperties.into()),
|
||||||
}),
|
}),
|
||||||
ModifyTransform { value, transform_op } => {
|
ModifyTransform { value, transform_op } => {
|
||||||
let (path, target_document) = self.active_selection.as_ref().expect("Received update for properties panel with no active layer");
|
let path = self.active_selection.as_ref().expect("Received update for properties panel with no active layer");
|
||||||
let layer = get_document(*target_document).layer(path).unwrap();
|
let layer = artwork_document.layer(path).unwrap();
|
||||||
|
|
||||||
let transform = apply_transform_operation(layer, transform_op, value, &render_data);
|
let transform = apply_transform_operation(layer, transform_op, value, &render_data);
|
||||||
|
|
||||||
self.create_document_operation(Operation::SetLayerTransform { path: path.clone(), transform }, true, responses);
|
self.create_document_operation(Operation::SetLayerTransform { path: path.clone(), transform }, true, responses);
|
||||||
}
|
}
|
||||||
ModifyName { name } => {
|
ModifyName { name } => {
|
||||||
let (path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
let path = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
||||||
self.create_document_operation(Operation::SetLayerName { path, name }, true, responses);
|
self.create_document_operation(Operation::SetLayerName { path, name }, true, responses);
|
||||||
}
|
}
|
||||||
ModifyPreserveAspect { preserve_aspect } => {
|
ModifyPreserveAspect { preserve_aspect } => {
|
||||||
let (layer_path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
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);
|
self.create_document_operation(Operation::SetLayerPreserveAspect { layer_path, preserve_aspect }, true, responses);
|
||||||
}
|
}
|
||||||
ModifyFill { fill } => {
|
ModifyFill { fill } => {
|
||||||
let (path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
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);
|
self.create_document_operation(Operation::SetLayerFill { path, fill }, true, responses);
|
||||||
}
|
}
|
||||||
ModifyStroke { stroke } => {
|
ModifyStroke { stroke } => {
|
||||||
let (path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
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);
|
self.create_document_operation(Operation::SetLayerStroke { path, stroke }, true, responses);
|
||||||
}
|
}
|
||||||
SetPivot { new_position } => {
|
SetPivot { new_position } => {
|
||||||
let (layer, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
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 position: Option<glam::DVec2> = new_position.into();
|
||||||
let pivot = position.unwrap();
|
let pivot = position.unwrap();
|
||||||
|
|
||||||
|
|
@ -136,13 +130,9 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ResendActiveProperties => {
|
ResendActiveProperties => {
|
||||||
if let Some((path, target_document)) = self.active_selection.clone() {
|
if let Some(path) = self.active_selection.clone() {
|
||||||
let document = get_document(target_document);
|
let layer = artwork_document.layer(&path).unwrap();
|
||||||
let layer = document.layer(&path).unwrap();
|
register_artwork_layer_properties(artwork_document, path, layer, responses, persistent_data, node_graph_message_handler, executor);
|
||||||
match target_document {
|
|
||||||
TargetDocument::Artboard => register_artboard_layer_properties(layer, responses, persistent_data),
|
|
||||||
TargetDocument::Artwork => register_artwork_layer_properties(document, path, layer, responses, persistent_data, node_graph_message_handler, executor),
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
let context = crate::messages::portfolio::document::node_graph::NodePropertiesContext {
|
let context = crate::messages::portfolio::document::node_graph::NodePropertiesContext {
|
||||||
persistent_data,
|
persistent_data,
|
||||||
|
|
@ -158,7 +148,6 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
|
||||||
}
|
}
|
||||||
UpdateSelectedDocumentProperties => responses.add(PropertiesPanelMessage::SetActiveLayers {
|
UpdateSelectedDocumentProperties => responses.add(PropertiesPanelMessage::SetActiveLayers {
|
||||||
paths: selected_layers.map(|path| path.to_vec()).collect(),
|
paths: selected_layers.map(|path| path.to_vec()).collect(),
|
||||||
document: TargetDocument::Artwork,
|
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -170,29 +159,18 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
|
||||||
|
|
||||||
impl PropertiesPanelMessageHandler {
|
impl PropertiesPanelMessageHandler {
|
||||||
fn matches_selected(&self, path: &[LayerId]) -> bool {
|
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_active_path_id = self.active_selection.as_ref().and_then(|v| v.last().copied());
|
||||||
let last_modified = path.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)
|
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>) {
|
fn create_document_operation(&self, operation: Operation, commit_history: bool, responses: &mut VecDeque<Message>) {
|
||||||
let (_, target_document) = self.active_selection.as_ref().unwrap();
|
// Commit to history before the modification
|
||||||
match *target_document {
|
if commit_history {
|
||||||
TargetDocument::Artboard => {
|
responses.add(DocumentMessage::StartTransaction);
|
||||||
// Commit history is not respected as the artboard document is not saved in the history system.
|
|
||||||
|
|
||||||
// Dispatch the relevant operation to the artboard document
|
|
||||||
responses.add(ArtboardMessage::DispatchOperation(Box::new(operation)))
|
|
||||||
}
|
|
||||||
TargetDocument::Artwork => {
|
|
||||||
// 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)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dispatch the relevant operation to the main document
|
||||||
|
responses.add(DocumentMessage::DispatchOperation(Box::new(operation)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,139 +64,6 @@ pub fn apply_transform_operation(layer: &Layer, transform_op: TransformOp, value
|
||||||
((pivot * delta * pivot.inverse()) * layer.transform).to_cols_array()
|
((pivot * delta * pivot.inverse()) * layer.transform).to_cols_array()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_artboard_layer_properties(layer: &Layer, responses: &mut VecDeque<Message>, persistent_data: &PersistentData) {
|
|
||||||
let options_bar = vec![LayoutGroup::Row {
|
|
||||||
widgets: vec![
|
|
||||||
IconLabel::new("NodeArtboard").tooltip("Artboard").widget_holder(),
|
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
|
||||||
TextInput::new(layer.name.clone().unwrap_or_else(|| "Untitled Artboard".to_string()))
|
|
||||||
.on_update(|text_input: &TextInput| PropertiesPanelMessage::ModifyName { name: text_input.value.clone() }.into())
|
|
||||||
.widget_holder(),
|
|
||||||
Separator::new(SeparatorType::Related).widget_holder(),
|
|
||||||
PopoverButton::new("Additional Options", "Coming soon").widget_holder(),
|
|
||||||
],
|
|
||||||
}];
|
|
||||||
|
|
||||||
let properties_body = {
|
|
||||||
let LayerDataType::Shape(shape) = &layer.data else { panic!("Artboards can only be shapes") };
|
|
||||||
let Fill::Solid(color) = shape.style.fill() else { panic!("Artboard must have a solid fill") };
|
|
||||||
|
|
||||||
let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::default(), None);
|
|
||||||
let pivot = layer.transform.transform_vector2(layer.layerspace_pivot(&render_data));
|
|
||||||
|
|
||||||
vec![LayoutGroup::Section {
|
|
||||||
name: "Artboard".into(),
|
|
||||||
layout: vec![
|
|
||||||
LayoutGroup::Row {
|
|
||||||
widgets: vec![
|
|
||||||
TextLabel::new("Location").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.x() + pivot.x))
|
|
||||||
.label("X")
|
|
||||||
.unit(" px")
|
|
||||||
.min(1.)
|
|
||||||
.max(std::i32::MAX 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(1.)
|
|
||||||
.max(std::i32::MAX 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("Dimensions").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.bounding_transform(&render_data).scale_x()))
|
|
||||||
.label("W")
|
|
||||||
.unit(" px")
|
|
||||||
.is_integer(true)
|
|
||||||
.min(1.)
|
|
||||||
.max(std::i32::MAX 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")
|
|
||||||
.is_integer(true)
|
|
||||||
.min(1.)
|
|
||||||
.max(std::i32::MAX as f64)
|
|
||||||
.on_update(|number_input: &NumberInput| {
|
|
||||||
PropertiesPanelMessage::ModifyTransform {
|
|
||||||
value: number_input.value.unwrap(),
|
|
||||||
transform_op: TransformOp::Height,
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
})
|
|
||||||
.widget_holder(),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
LayoutGroup::Row {
|
|
||||||
widgets: vec![
|
|
||||||
TextLabel::new("Background").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(),
|
|
||||||
ColorInput::new(Some(*color))
|
|
||||||
.on_update(|text_input: &ColorInput| {
|
|
||||||
let fill = if let Some(value) = text_input.value { value } else { Color::TRANSPARENT };
|
|
||||||
PropertiesPanelMessage::ModifyFill { fill: Fill::Solid(fill) }.into()
|
|
||||||
})
|
|
||||||
.widget_holder(),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
|
|
||||||
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_artwork_layer_properties(
|
pub fn register_artwork_layer_properties(
|
||||||
document: &Document,
|
document: &Document,
|
||||||
layer_path: Vec<document_legacy::LayerId>,
|
layer_path: Vec<document_legacy::LayerId>,
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ use crate::{messages::prelude::NodeGraphMessageHandler, node_graph_executor::Nod
|
||||||
pub struct PropertiesPanelMessageHandlerData<'a> {
|
pub struct PropertiesPanelMessageHandlerData<'a> {
|
||||||
pub document_name: &'a str,
|
pub document_name: &'a str,
|
||||||
pub artwork_document: &'a DocumentLegacy,
|
pub artwork_document: &'a DocumentLegacy,
|
||||||
pub artboard_document: &'a DocumentLegacy,
|
|
||||||
pub selected_layers: &'a mut dyn Iterator<Item = &'a [LayerId]>,
|
pub selected_layers: &'a mut dyn Iterator<Item = &'a [LayerId]>,
|
||||||
pub node_graph_message_handler: &'a NodeGraphMessageHandler,
|
pub node_graph_message_handler: &'a NodeGraphMessageHandler,
|
||||||
pub executor: &'a mut NodeGraphExecutor,
|
pub executor: &'a mut NodeGraphExecutor,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
pub use super::layer_panel::{LayerMetadata, LayerPanelEntry};
|
pub use super::layer_panel::{LayerMetadata, LayerPanelEntry};
|
||||||
use crate::messages::prelude::ArtboardMessageHandler;
|
|
||||||
|
|
||||||
use document_legacy::document::Document as DocumentLegacy;
|
use document_legacy::document::Document as DocumentLegacy;
|
||||||
use document_legacy::LayerId;
|
use document_legacy::LayerId;
|
||||||
use graphene_core::raster::color::Color;
|
use graphene_core::raster::color::Color;
|
||||||
|
|
@ -12,7 +10,6 @@ use std::fmt;
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct DocumentSave {
|
pub struct DocumentSave {
|
||||||
pub document: DocumentLegacy,
|
pub document: DocumentLegacy,
|
||||||
pub artboard: ArtboardMessageHandler,
|
|
||||||
pub layer_metadata: HashMap<Vec<LayerId>, LayerMetadata>,
|
pub layer_metadata: HashMap<Vec<LayerId>, LayerMetadata>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -36,12 +33,6 @@ pub enum AlignAggregate {
|
||||||
Average,
|
Average,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
|
|
||||||
pub enum TargetDocument {
|
|
||||||
Artboard,
|
|
||||||
Artwork,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
|
#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
|
||||||
pub enum DocumentMode {
|
pub enum DocumentMode {
|
||||||
DesignMode,
|
DesignMode,
|
||||||
|
|
|
||||||
|
|
@ -249,7 +249,7 @@ impl LayoutHolder for MenuBarMessageHandler {
|
||||||
no_active_document,
|
no_active_document,
|
||||||
MenuBarEntryChildren(vec![vec![MenuBarEntry {
|
MenuBarEntryChildren(vec![vec![MenuBarEntry {
|
||||||
label: "Clear Artboards".into(),
|
label: "Clear Artboards".into(),
|
||||||
action: MenuBarEntry::create_action(|_| ArtboardMessage::ClearArtboards.into()),
|
action: MenuBarEntry::create_action(|_| DialogMessage::RequestComingSoonDialog { issue: None }.into()),
|
||||||
disabled: no_active_document,
|
disabled: no_active_document,
|
||||||
..MenuBarEntry::default()
|
..MenuBarEntry::default()
|
||||||
}]]),
|
}]]),
|
||||||
|
|
|
||||||
|
|
@ -299,7 +299,7 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PortfolioMessage::NewDocumentWithName { name } => {
|
PortfolioMessage::NewDocumentWithName { name } => {
|
||||||
let new_document = DocumentMessageHandler::with_name(name, ipp);
|
let new_document = DocumentMessageHandler::with_name(name, ipp, responses);
|
||||||
let document_id = generate_uuid();
|
let document_id = generate_uuid();
|
||||||
if self.active_document().is_some() {
|
if self.active_document().is_some() {
|
||||||
responses.add(BroadcastEvent::ToolAbort);
|
responses.add(BroadcastEvent::ToolAbort);
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ pub use crate::messages::input_mapper::key_mapping::{KeyMappingMessage, KeyMappi
|
||||||
pub use crate::messages::input_mapper::{InputMapperMessage, InputMapperMessageDiscriminant, InputMapperMessageHandler};
|
pub use crate::messages::input_mapper::{InputMapperMessage, InputMapperMessageDiscriminant, InputMapperMessageHandler};
|
||||||
pub use crate::messages::input_preprocessor::{InputPreprocessorMessage, InputPreprocessorMessageDiscriminant, InputPreprocessorMessageHandler};
|
pub use crate::messages::input_preprocessor::{InputPreprocessorMessage, InputPreprocessorMessageDiscriminant, InputPreprocessorMessageHandler};
|
||||||
pub use crate::messages::layout::{LayoutMessage, LayoutMessageDiscriminant, LayoutMessageHandler};
|
pub use crate::messages::layout::{LayoutMessage, LayoutMessageDiscriminant, LayoutMessageHandler};
|
||||||
pub use crate::messages::portfolio::document::artboard::{ArtboardMessage, ArtboardMessageDiscriminant, ArtboardMessageHandler};
|
|
||||||
pub use crate::messages::portfolio::document::navigation::{NavigationMessage, NavigationMessageDiscriminant, NavigationMessageHandler};
|
pub use crate::messages::portfolio::document::navigation::{NavigationMessage, NavigationMessageDiscriminant, NavigationMessageHandler};
|
||||||
pub use crate::messages::portfolio::document::node_graph::{GraphOperationMessage, GraphOperationMessageDiscriminant, GraphOperationMessageHandler};
|
pub use crate::messages::portfolio::document::node_graph::{GraphOperationMessage, GraphOperationMessageDiscriminant, GraphOperationMessageHandler};
|
||||||
pub use crate::messages::portfolio::document::node_graph::{NodeGraphMessage, NodeGraphMessageDiscriminant, NodeGraphMessageHandler};
|
pub use crate::messages::portfolio::document::node_graph::{NodeGraphMessage, NodeGraphMessageDiscriminant, NodeGraphMessageHandler};
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,11 @@ pub fn get_gradient(layer: LayerNodeIdentifier, document: &Document) -> Option<G
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is a specified layer an artboard?
|
||||||
|
pub fn is_artboard(layer: LayerNodeIdentifier, document: &Document) -> bool {
|
||||||
|
NodeGraphLayer::new(layer, document).is_some_and(|layer| layer.uses_node("Artboard"))
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert subpaths to an iterator of manipulator groups
|
/// Convert subpaths to an iterator of manipulator groups
|
||||||
pub fn get_manipulator_groups(subpaths: &[Subpath<ManipulatorGroupId>]) -> impl Iterator<Item = &bezier_rs::ManipulatorGroup<ManipulatorGroupId>> + DoubleEndedIterator {
|
pub fn get_manipulator_groups(subpaths: &[Subpath<ManipulatorGroupId>]) -> impl Iterator<Item = &bezier_rs::ManipulatorGroup<ManipulatorGroupId>> + DoubleEndedIterator {
|
||||||
subpaths.iter().flat_map(|subpath| subpath.manipulator_groups())
|
subpaths.iter().flat_map(|subpath| subpath.manipulator_groups())
|
||||||
|
|
@ -145,14 +150,14 @@ impl<'a> NodeGraphLayer<'a> {
|
||||||
self.node_graph.primary_flow_from_opt(Some(self.layer_node))
|
self.node_graph.primary_flow_from_opt(Some(self.layer_node))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Does a node exist in the layer's primary flow
|
||||||
|
pub fn uses_node(&self, node_name: &str) -> bool {
|
||||||
|
self.primary_layer_flow().any(|(node, _id)| node.name == node_name)
|
||||||
|
}
|
||||||
|
|
||||||
/// Find all of the inputs of a specific node within the layer's primary flow
|
/// Find all of the inputs of a specific node within the layer's primary flow
|
||||||
pub fn find_node_inputs(&self, node_name: &str) -> Option<&'a Vec<NodeInput>> {
|
pub fn find_node_inputs(&self, node_name: &str) -> Option<&'a Vec<NodeInput>> {
|
||||||
for (node, _id) in self.primary_layer_flow() {
|
self.primary_layer_flow().find(|(node, _id)| node.name == node_name).map(|(node, _id)| &node.inputs)
|
||||||
if node.name == node_name {
|
|
||||||
return Some(&node.inputs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find a specific input of a node within the layer's primary flow
|
/// Find a specific input of a node within the layer's primary flow
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
use super::tool_prelude::*;
|
use super::tool_prelude::*;
|
||||||
use crate::application::generate_uuid;
|
use crate::application::generate_uuid;
|
||||||
use crate::consts::SELECTION_TOLERANCE;
|
use crate::messages::tool::common_functionality::graph_modification_utils::is_artboard;
|
||||||
use crate::messages::portfolio::document::utility_types::misc::TargetDocument;
|
|
||||||
use crate::messages::tool::common_functionality::snapping::SnapManager;
|
use crate::messages::tool::common_functionality::snapping::SnapManager;
|
||||||
use crate::messages::tool::common_functionality::transformation_cage::*;
|
use crate::messages::tool::common_functionality::transformation_cage::*;
|
||||||
|
|
||||||
use document_legacy::intersection::Quad;
|
use document_legacy::document_metadata::LayerNodeIdentifier;
|
||||||
use document_legacy::LayerId;
|
|
||||||
|
|
||||||
use document_legacy::layers::RenderData;
|
use document_legacy::layers::RenderData;
|
||||||
use glam::{IVec2, Vec2Swizzles};
|
use glam::{IVec2, Vec2Swizzles};
|
||||||
|
|
@ -96,7 +94,7 @@ enum ArtboardToolFsmState {
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct ArtboardToolData {
|
struct ArtboardToolData {
|
||||||
bounding_box_overlays: Option<BoundingBoxOverlays>,
|
bounding_box_overlays: Option<BoundingBoxOverlays>,
|
||||||
selected_artboard: Option<LayerId>,
|
selected_artboard: Option<LayerNodeIdentifier>,
|
||||||
snap_manager: SnapManager,
|
snap_manager: SnapManager,
|
||||||
cursor: MouseCursorIcon,
|
cursor: MouseCursorIcon,
|
||||||
drag_start: DVec2,
|
drag_start: DVec2,
|
||||||
|
|
@ -104,15 +102,15 @@ struct ArtboardToolData {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ArtboardToolData {
|
impl ArtboardToolData {
|
||||||
fn refresh_overlays(&mut self, document: &DocumentMessageHandler, render_data: &RenderData, responses: &mut VecDeque<Message>) {
|
fn refresh_overlays(&mut self, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
|
||||||
let current_artboard = self.selected_artboard.and_then(|path| document.artboard_bounding_box_and_transform(&[path], render_data));
|
let current_artboard = self.selected_artboard.and_then(|layer| document.document_legacy.metadata.bounding_box_document(layer));
|
||||||
match (current_artboard, self.bounding_box_overlays.take()) {
|
match (current_artboard, self.bounding_box_overlays.take()) {
|
||||||
(None, Some(bounding_box_overlays)) => bounding_box_overlays.delete(responses),
|
(None, Some(bounding_box_overlays)) => bounding_box_overlays.delete(responses),
|
||||||
(Some((bounds, transform)), paths) => {
|
(Some(bounds), paths) => {
|
||||||
let mut bounding_box_overlays = paths.unwrap_or_else(|| BoundingBoxOverlays::new(responses));
|
let mut bounding_box_overlays = paths.unwrap_or_else(|| BoundingBoxOverlays::new(responses));
|
||||||
|
|
||||||
bounding_box_overlays.bounds = bounds;
|
bounding_box_overlays.bounds = bounds;
|
||||||
bounding_box_overlays.transform = transform;
|
bounding_box_overlays.transform = document.document_legacy.metadata.document_to_viewport;
|
||||||
|
|
||||||
bounding_box_overlays.transform(responses);
|
bounding_box_overlays.transform(responses);
|
||||||
|
|
||||||
|
|
@ -130,6 +128,7 @@ impl ArtboardToolData {
|
||||||
let (top, bottom, left, right) = edges;
|
let (top, bottom, left, right) = edges;
|
||||||
let selected_edges = SelectedEdges::new(top, bottom, left, right, bounding_box.bounds);
|
let selected_edges = SelectedEdges::new(top, bottom, left, right, bounding_box.bounds);
|
||||||
bounding_box.opposite_pivot = selected_edges.calculate_pivot();
|
bounding_box.opposite_pivot = selected_edges.calculate_pivot();
|
||||||
|
bounding_box.selected_edges = Some(selected_edges);
|
||||||
|
|
||||||
Some(edges)
|
Some(edges)
|
||||||
}
|
}
|
||||||
|
|
@ -140,29 +139,29 @@ impl ArtboardToolData {
|
||||||
|
|
||||||
let artboard = self.selected_artboard.unwrap();
|
let artboard = self.selected_artboard.unwrap();
|
||||||
self.snap_manager
|
self.snap_manager
|
||||||
.start_snap(document, input, document.bounding_boxes(None, Some(artboard), render_data), snap_x, snap_y);
|
.start_snap(document, input, document.bounding_boxes(None, Some(artboard.to_node()), render_data), snap_x, snap_y);
|
||||||
self.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
|
self.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
|
||||||
|
|
||||||
if let Some(bounds) = &mut self.bounding_box_overlays {
|
if let Some(bounds) = &mut self.bounding_box_overlays {
|
||||||
let pivot = document.artboard_message_handler.artboards_document.pivot(&[artboard], render_data).unwrap_or_default();
|
bounds.center_of_transformation = (bounds.bounds[0] + bounds.bounds[1]) / 2.;
|
||||||
let root = document.document_legacy.metadata.document_to_viewport;
|
|
||||||
let pivot = root.inverse().transform_point2(pivot);
|
|
||||||
bounds.center_of_transformation = pivot;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_artboard(&mut self, document: &DocumentMessageHandler, render_data: &RenderData, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) -> bool {
|
fn select_artboard(&mut self, document: &DocumentMessageHandler, render_data: &RenderData, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) -> bool {
|
||||||
responses.add(DocumentMessage::StartTransaction);
|
responses.add(DocumentMessage::StartTransaction);
|
||||||
let tolerance = DVec2::splat(SELECTION_TOLERANCE);
|
|
||||||
let quad = Quad::from_box([input.mouse.position - tolerance, input.mouse.position + tolerance]);
|
let mut intersections = document
|
||||||
let intersection = document.artboard_message_handler.artboards_document.intersects_quad_root(quad, render_data);
|
.document_legacy
|
||||||
|
.metadata
|
||||||
|
.click_xray(input.mouse.position)
|
||||||
|
.filter(|&layer| is_artboard(layer, &document.document_legacy));
|
||||||
|
|
||||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||||
if let Some(intersection) = intersection.last() {
|
if let Some(intersection) = intersections.next() {
|
||||||
self.selected_artboard = Some(intersection[0]);
|
self.selected_artboard = Some(intersection);
|
||||||
|
|
||||||
self.snap_manager
|
self.snap_manager
|
||||||
.start_snap(document, input, document.bounding_boxes(None, Some(intersection[0]), render_data), true, true);
|
.start_snap(document, input, document.bounding_boxes(None, Some(intersection.to_node()), render_data), true, true);
|
||||||
self.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
|
self.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
|
||||||
|
|
||||||
true
|
true
|
||||||
|
|
@ -186,13 +185,8 @@ impl ArtboardToolData {
|
||||||
let snapped_mouse_position: DVec2 = self.snap_manager.snap_position(responses, document, mouse_position);
|
let snapped_mouse_position: DVec2 = self.snap_manager.snap_position(responses, document, mouse_position);
|
||||||
|
|
||||||
let (position, size) = movement.new_size(snapped_mouse_position, bounds.transform, from_center, bounds.center_of_transformation, constrain_square);
|
let (position, size) = movement.new_size(snapped_mouse_position, bounds.transform, from_center, bounds.center_of_transformation, constrain_square);
|
||||||
responses.add(ArtboardMessage::ResizeArtboard {
|
|
||||||
artboard: self.selected_artboard.unwrap(),
|
|
||||||
position: position.round().into(),
|
|
||||||
size: size.round().into(),
|
|
||||||
});
|
|
||||||
responses.add(GraphOperationMessage::ResizeArtboard {
|
responses.add(GraphOperationMessage::ResizeArtboard {
|
||||||
id: self.selected_artboard.unwrap(),
|
id: self.selected_artboard.unwrap().to_node(),
|
||||||
location: position.round().as_ivec2(),
|
location: position.round().as_ivec2(),
|
||||||
dimensions: size.round().as_ivec2(),
|
dimensions: size.round().as_ivec2(),
|
||||||
});
|
});
|
||||||
|
|
@ -214,7 +208,7 @@ impl Fsm for ArtboardToolFsmState {
|
||||||
|
|
||||||
match (self, event) {
|
match (self, event) {
|
||||||
(state, ArtboardToolMessage::DocumentIsDirty) if state != ArtboardToolFsmState::Drawing => {
|
(state, ArtboardToolMessage::DocumentIsDirty) if state != ArtboardToolFsmState::Drawing => {
|
||||||
tool_data.refresh_overlays(document, render_data, responses);
|
tool_data.refresh_overlays(document, responses);
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
@ -255,13 +249,8 @@ impl Fsm for ArtboardToolFsmState {
|
||||||
|
|
||||||
let position = bounds.bounds[0] + bounds.transform.inverse().transform_vector2(mouse_position - tool_data.drag_current + closest_move);
|
let position = bounds.bounds[0] + bounds.transform.inverse().transform_vector2(mouse_position - tool_data.drag_current + closest_move);
|
||||||
|
|
||||||
responses.add(ArtboardMessage::ResizeArtboard {
|
|
||||||
artboard: tool_data.selected_artboard.unwrap(),
|
|
||||||
position: position.round().into(),
|
|
||||||
size: size.round().into(),
|
|
||||||
});
|
|
||||||
responses.add(GraphOperationMessage::ResizeArtboard {
|
responses.add(GraphOperationMessage::ResizeArtboard {
|
||||||
id: tool_data.selected_artboard.unwrap(),
|
id: tool_data.selected_artboard.unwrap().to_node(),
|
||||||
location: position.round().as_ivec2(),
|
location: position.round().as_ivec2(),
|
||||||
dimensions: size.round().as_ivec2(),
|
dimensions: size.round().as_ivec2(),
|
||||||
});
|
});
|
||||||
|
|
@ -294,28 +283,18 @@ impl Fsm for ArtboardToolFsmState {
|
||||||
let size = root_transform.transform_vector2(size);
|
let size = root_transform.transform_vector2(size);
|
||||||
|
|
||||||
if let Some(artboard) = tool_data.selected_artboard {
|
if let Some(artboard) = tool_data.selected_artboard {
|
||||||
responses.add(ArtboardMessage::ResizeArtboard {
|
|
||||||
artboard,
|
|
||||||
position: start.round().into(),
|
|
||||||
size: size.round().into(),
|
|
||||||
});
|
|
||||||
responses.add(GraphOperationMessage::ResizeArtboard {
|
responses.add(GraphOperationMessage::ResizeArtboard {
|
||||||
id: tool_data.selected_artboard.unwrap(),
|
id: artboard.to_node(),
|
||||||
location: start.round().as_ivec2(),
|
location: start.round().as_ivec2(),
|
||||||
dimensions: size.round().as_ivec2(),
|
dimensions: size.round().as_ivec2(),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let id = generate_uuid();
|
let id = generate_uuid();
|
||||||
tool_data.selected_artboard = Some(id);
|
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(None, Some(id), render_data), true, true);
|
||||||
tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
|
tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
|
||||||
|
|
||||||
responses.add(ArtboardMessage::AddArtboard {
|
|
||||||
id: Some(id),
|
|
||||||
position: start.round().into(),
|
|
||||||
size: (1., 1.),
|
|
||||||
});
|
|
||||||
responses.add(GraphOperationMessage::NewArtboard {
|
responses.add(GraphOperationMessage::NewArtboard {
|
||||||
id,
|
id,
|
||||||
artboard: graphene_core::Artboard {
|
artboard: graphene_core::Artboard {
|
||||||
|
|
@ -376,8 +355,8 @@ impl Fsm for ArtboardToolFsmState {
|
||||||
}
|
}
|
||||||
(_, ArtboardToolMessage::DeleteSelected) => {
|
(_, ArtboardToolMessage::DeleteSelected) => {
|
||||||
if let Some(artboard) = tool_data.selected_artboard.take() {
|
if let Some(artboard) = tool_data.selected_artboard.take() {
|
||||||
responses.add(ArtboardMessage::DeleteArtboard { artboard });
|
let id = artboard.to_node();
|
||||||
responses.add(GraphOperationMessage::DeleteLayer { id: artboard });
|
responses.add(GraphOperationMessage::DeleteLayer { id });
|
||||||
|
|
||||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||||
}
|
}
|
||||||
|
|
@ -385,13 +364,8 @@ impl Fsm for ArtboardToolFsmState {
|
||||||
}
|
}
|
||||||
(_, ArtboardToolMessage::NudgeSelected { delta_x, delta_y }) => {
|
(_, ArtboardToolMessage::NudgeSelected { delta_x, delta_y }) => {
|
||||||
if let Some(bounds) = &mut tool_data.bounding_box_overlays {
|
if let Some(bounds) = &mut tool_data.bounding_box_overlays {
|
||||||
responses.add(ArtboardMessage::ResizeArtboard {
|
|
||||||
artboard: tool_data.selected_artboard.unwrap(),
|
|
||||||
position: (bounds.bounds[0].x + delta_x, bounds.bounds[0].y + delta_y),
|
|
||||||
size: (bounds.bounds[1] - bounds.bounds[0]).round().into(),
|
|
||||||
});
|
|
||||||
responses.add(GraphOperationMessage::ResizeArtboard {
|
responses.add(GraphOperationMessage::ResizeArtboard {
|
||||||
id: tool_data.selected_artboard.unwrap(),
|
id: tool_data.selected_artboard.unwrap().to_node(),
|
||||||
location: DVec2::new(bounds.bounds[0].x + delta_x, bounds.bounds[0].y + delta_y).round().as_ivec2(),
|
location: DVec2::new(bounds.bounds[0].x + delta_x, bounds.bounds[0].y + delta_y).round().as_ivec2(),
|
||||||
dimensions: (bounds.bounds[1] - bounds.bounds[0]).round().as_ivec2(),
|
dimensions: (bounds.bounds[1] - bounds.bounds[0]).round().as_ivec2(),
|
||||||
});
|
});
|
||||||
|
|
@ -407,7 +381,6 @@ impl Fsm for ArtboardToolFsmState {
|
||||||
// Register properties when switching back to other tools
|
// Register properties when switching back to other tools
|
||||||
responses.add(PropertiesPanelMessage::SetActiveLayers {
|
responses.add(PropertiesPanelMessage::SetActiveLayers {
|
||||||
paths: document.selected_layers().map(|path| path.to_vec()).collect(),
|
paths: document.selected_layers().map(|path| path.to_vec()).collect(),
|
||||||
document: TargetDocument::Artwork,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
tool_data.snap_manager.cleanup(responses);
|
tool_data.snap_manager.cleanup(responses);
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ impl Fsm for FillToolFsmState {
|
||||||
let ToolMessage::Fill(event) = event else {
|
let ToolMessage::Fill(event) = event else {
|
||||||
return self;
|
return self;
|
||||||
};
|
};
|
||||||
let Some((layer_identifier, _)) = document.document_legacy.metadata.click(input.mouse.position) else {
|
let Some(layer_identifier) = document.document_legacy.metadata.click(input.mouse.position) else {
|
||||||
return self;
|
return self;
|
||||||
};
|
};
|
||||||
let layer = layer_identifier.to_path();
|
let layer = layer_identifier.to_path();
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,7 @@ enum GradientToolFsmState {
|
||||||
|
|
||||||
/// Computes the transform from gradient space to viewport space (where gradient space is 0..1)
|
/// Computes the transform from gradient space to viewport space (where gradient space is 0..1)
|
||||||
fn gradient_space_transform(layer: LayerNodeIdentifier, document: &DocumentMessageHandler) -> DAffine2 {
|
fn gradient_space_transform(layer: LayerNodeIdentifier, document: &DocumentMessageHandler) -> DAffine2 {
|
||||||
let bounds = document.document_legacy.metadata.bounding_box(layer, DAffine2::IDENTITY).unwrap();
|
let bounds = document.document_legacy.metadata.bounding_box_with_transform(layer, DAffine2::IDENTITY).unwrap();
|
||||||
let bound_transform = DAffine2::from_scale_angle_translation(bounds[1] - bounds[0], 0., bounds[0]);
|
let bound_transform = DAffine2::from_scale_angle_translation(bounds[1] - bounds[0], 0., bounds[0]);
|
||||||
|
|
||||||
let multiplied = document.document_legacy.metadata.transform_from_viewport(layer);
|
let multiplied = document.document_legacy.metadata.transform_from_viewport(layer);
|
||||||
|
|
@ -529,7 +529,7 @@ impl Fsm for GradientToolFsmState {
|
||||||
let selected_layer = document.document_legacy.metadata.click(input.mouse.position);
|
let selected_layer = document.document_legacy.metadata.click(input.mouse.position);
|
||||||
|
|
||||||
// Apply the gradient to the selected layer
|
// Apply the gradient to the selected layer
|
||||||
if let Some((layer, _)) = selected_layer {
|
if let Some(layer) = selected_layer {
|
||||||
// let is_bitmap = document
|
// let is_bitmap = document
|
||||||
// .document_legacy
|
// .document_legacy
|
||||||
// .layer(&layer)
|
// .layer(&layer)
|
||||||
|
|
|
||||||
|
|
@ -242,7 +242,7 @@ impl PathToolData {
|
||||||
PathToolFsmState::Dragging
|
PathToolFsmState::Dragging
|
||||||
}
|
}
|
||||||
// We didn't find a point nearby, so consider selecting the nearest shape instead
|
// We didn't find a point nearby, so consider selecting the nearest shape instead
|
||||||
else if let Some((layer, _)) = document.document_legacy.metadata.click(input.mouse.position) {
|
else if let Some(layer) = document.document_legacy.metadata.click(input.mouse.position) {
|
||||||
// TODO: Actual selection
|
// TODO: Actual selection
|
||||||
let layer_list = vec![layer.to_path()];
|
let layer_list = vec![layer.to_path()];
|
||||||
if shift {
|
if shift {
|
||||||
|
|
|
||||||
|
|
@ -484,7 +484,6 @@ impl NodeGraphExecutor {
|
||||||
affected_layer_path: execution_context.layer_path,
|
affected_layer_path: execution_context.layer_path,
|
||||||
});
|
});
|
||||||
responses.add(DocumentMessage::RenderDocument);
|
responses.add(DocumentMessage::RenderDocument);
|
||||||
responses.add(ArtboardMessage::RenderArtboards);
|
|
||||||
responses.add(DocumentMessage::DocumentStructureChanged);
|
responses.add(DocumentMessage::DocumentStructureChanged);
|
||||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||||
responses.add(DocumentMessage::DirtyRenderDocument);
|
responses.add(DocumentMessage::DirtyRenderDocument);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue