mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-12-23 10:11:54 +00:00
Fix artboard tool and remove old artboard code
This commit is contained in:
parent
9a39c4a0cc
commit
b52f831b21
27 changed files with 196 additions and 553 deletions
|
|
@ -81,36 +81,53 @@ impl DocumentMetadata {
|
|||
self.document_to_viewport * self.transform_from_document(layer)
|
||||
}
|
||||
|
||||
/// Runs an intersection test with all layers and a document space quad
|
||||
pub fn intersect_quad(&self, viewport_quad: Quad) -> Option<(LayerNodeIdentifier, &Vec<ClickTarget>)> {
|
||||
/// Runs an intersection test with all layers and a viewport space quad
|
||||
pub fn intersect_quad(&self, viewport_quad: Quad) -> Option<LayerNodeIdentifier> {
|
||||
let document_quad = self.document_to_viewport.inverse() * viewport_quad;
|
||||
self.root()
|
||||
.decendants(self)
|
||||
.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))))
|
||||
.map(|(layer, _)| layer)
|
||||
}
|
||||
|
||||
/// Find the layer that has been clicked on from a document space location
|
||||
pub fn click(&self, viewport_location: DVec2) -> Option<(LayerNodeIdentifier, &Vec<ClickTarget>)> {
|
||||
/// Find all of the layers that were clicked on from a viewport space location
|
||||
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);
|
||||
self.root()
|
||||
.decendants(self)
|
||||
.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
|
||||
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
|
||||
.get(&layer)?
|
||||
.iter()
|
||||
.filter_map(|click_target| click_target.subpath.bounding_box_with_transform(transform))
|
||||
.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
|
||||
#[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);
|
||||
|
||||
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 {
|
||||
const ROOT: Self = LayerNodeIdentifier::new_unchecked(0);
|
||||
|
||||
|
|
|
|||
|
|
@ -33,9 +33,6 @@ pub struct DispatcherMessageHandlers {
|
|||
const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[
|
||||
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::RenderDocument)),
|
||||
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::PropertiesPanel(
|
||||
PropertiesPanelMessageDiscriminant::ResendActiveProperties,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use super::simple_dialogs::{self, AboutGraphiteDialog, ComingSoonDialog, DemoArtworkDialog, LicensesDialog};
|
||||
use crate::messages::layout::utility_types::widget_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`.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
|
|
@ -67,23 +68,19 @@ impl MessageHandler<DialogMessage, (&PortfolioMessageHandler, &PreferencesMessag
|
|||
}
|
||||
DialogMessage::RequestExportDialog => {
|
||||
if let Some(document) = portfolio.active_document() {
|
||||
let artboard_handler = &document.artboard_message_handler;
|
||||
let mut index = 0;
|
||||
let artboards = artboard_handler
|
||||
.artboard_ids
|
||||
.iter()
|
||||
.rev()
|
||||
.filter_map(|&artboard| artboard_handler.artboards_document.layer(&[artboard]).ok().map(|layer| (artboard, layer)))
|
||||
.map(|(artboard, layer)| {
|
||||
let artboards = document
|
||||
.document_legacy
|
||||
.metadata
|
||||
.all_layers()
|
||||
.filter(|&layer| is_artboard(layer, &document.document_legacy))
|
||||
.map(|layer| {
|
||||
(
|
||||
artboard,
|
||||
format!(
|
||||
"Artboard: {}",
|
||||
layer.name.clone().unwrap_or_else(|| {
|
||||
index += 1;
|
||||
format!("Untitled {index}")
|
||||
})
|
||||
),
|
||||
layer,
|
||||
format!("Artboard: {}", {
|
||||
index += 1;
|
||||
format!("Untitled {index}")
|
||||
}),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use crate::messages::frontend::utility_types::{ExportBounds, FileType};
|
|||
use crate::messages::layout::utility_types::widget_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.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
|
|
@ -11,7 +11,7 @@ pub struct ExportDialogMessageHandler {
|
|||
pub scale_factor: f64,
|
||||
pub bounds: ExportBounds,
|
||||
pub transparent_background: bool,
|
||||
pub artboards: HashMap<LayerId, String>,
|
||||
pub artboards: HashMap<LayerNodeIdentifier, String>,
|
||||
pub has_selection: bool,
|
||||
}
|
||||
|
||||
|
|
@ -86,7 +86,20 @@ impl LayoutHolder for ExportDialogMessageHandler {
|
|||
.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![
|
||||
(ExportBounds::AllArtwork, "All Artwork".to_string(), false),
|
||||
(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 {
|
||||
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 {
|
||||
id,
|
||||
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 serde::{Deserialize, Serialize};
|
||||
|
||||
|
|
@ -63,5 +64,5 @@ pub enum ExportBounds {
|
|||
#[default]
|
||||
AllArtwork,
|
||||
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>),
|
||||
#[remain::unsorted]
|
||||
#[child]
|
||||
Artboard(ArtboardMessage),
|
||||
#[remain::unsorted]
|
||||
#[child]
|
||||
Navigation(NavigationMessage),
|
||||
#[remain::unsorted]
|
||||
#[child]
|
||||
|
|
@ -51,7 +48,6 @@ pub enum DocumentMessage {
|
|||
},
|
||||
BackupDocument {
|
||||
document: DocumentLegacy,
|
||||
artboard: Box<ArtboardMessageHandler>,
|
||||
layer_metadata: HashMap<Vec<LayerId>, LayerMetadata>,
|
||||
},
|
||||
ClearLayerTree,
|
||||
|
|
|
|||
|
|
@ -62,7 +62,6 @@ pub struct DocumentMessageHandler {
|
|||
navigation_handler: NavigationMessageHandler,
|
||||
#[serde(skip)]
|
||||
overlays_message_handler: OverlaysMessageHandler,
|
||||
pub artboard_message_handler: ArtboardMessageHandler,
|
||||
properties_panel_message_handler: PropertiesPanelMessageHandler,
|
||||
#[serde(skip)]
|
||||
node_graph_handler: NodeGraphMessageHandler,
|
||||
|
|
@ -95,7 +94,6 @@ impl Default for DocumentMessageHandler {
|
|||
|
||||
navigation_handler: NavigationMessageHandler::default(),
|
||||
overlays_message_handler: OverlaysMessageHandler::default(),
|
||||
artboard_message_handler: ArtboardMessageHandler::default(),
|
||||
properties_panel_message_handler: PropertiesPanelMessageHandler::default(),
|
||||
node_graph_handler: Default::default(),
|
||||
}
|
||||
|
|
@ -176,10 +174,6 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
|
|||
}
|
||||
}
|
||||
#[remain::unsorted]
|
||||
Artboard(message) => {
|
||||
self.artboard_message_handler.process_message(message, responses, persistent_data);
|
||||
}
|
||||
#[remain::unsorted]
|
||||
Navigation(message) => {
|
||||
let document_bounds = self.document_bounds(&render_data);
|
||||
self.navigation_handler.process_message(
|
||||
|
|
@ -197,7 +191,6 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
|
|||
let properties_panel_message_handler_data = PropertiesPanelMessageHandlerData {
|
||||
document_name: self.name.as_str(),
|
||||
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())),
|
||||
node_graph_message_handler: &self.node_graph_handler,
|
||||
executor,
|
||||
|
|
@ -275,7 +268,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
|
|||
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 => {
|
||||
// Send an empty layer tree
|
||||
let data_buffer: RawBuffer = Self::default().serialize_root().as_slice().into();
|
||||
|
|
@ -375,13 +368,13 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
|
|||
bounds,
|
||||
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
|
||||
let bounds = match bounds {
|
||||
ExportBounds::AllArtwork => self.all_layer_bounds(&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();
|
||||
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);
|
||||
|
||||
self.restore_document_transform(old_transforms);
|
||||
self.restore_document_transform(old_artwork_transform);
|
||||
|
||||
let file_suffix = &format!(".{file_type:?}").to_lowercase();
|
||||
let name = match file_name.ends_with(FILE_SAVE_SUFFIX) {
|
||||
|
|
@ -681,7 +674,6 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
|
|||
responses.add(FrontendMessage::UpdateDocumentArtwork {
|
||||
svg: self.document_legacy.render_root(&render_data),
|
||||
});
|
||||
responses.add(ArtboardMessage::RenderArtboards);
|
||||
|
||||
let document_transform_scale = self.navigation_handler.snapped_scale();
|
||||
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_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 {
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
pub(crate) fn remove_document_transform(&mut self) -> [DAffine2; 2] {
|
||||
let old_artwork_transform = self.document_legacy.root.transform;
|
||||
self.document_legacy.root.transform = DAffine2::IDENTITY;
|
||||
pub(crate) fn remove_document_transform(&mut self) -> DAffine2 {
|
||||
let old_artwork_transform = self.document_legacy.metadata.document_to_viewport;
|
||||
self.document_legacy.metadata.document_to_viewport = DAffine2::IDENTITY;
|
||||
DocumentLegacy::mark_children_as_dirty(&mut self.document_legacy.root);
|
||||
|
||||
let old_artboard_transform = self.artboard_message_handler.artboards_document.root.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]
|
||||
old_artwork_transform
|
||||
}
|
||||
|
||||
/// 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]) {
|
||||
self.document_legacy.root.transform = old_artwork_transform;
|
||||
pub(crate) fn restore_document_transform(&mut self, old_artwork_transform: DAffine2) {
|
||||
self.document_legacy.metadata.document_to_viewport = old_artwork_transform;
|
||||
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 {
|
||||
|
|
@ -1079,13 +1064,10 @@ impl DocumentMessageHandler {
|
|||
DocumentRenderMode::LayerCutout(layer_path, background) => (self.document_legacy.render_layer(layer_path, &render_data).unwrap(), Some(background)),
|
||||
};
|
||||
let artboards = match transparent_background {
|
||||
false => self.artboard_message_handler.artboards_document.render_root(&render_data),
|
||||
false => "<!--artboards-->",
|
||||
true => "".into(),
|
||||
};
|
||||
let outside_artboards_color = outside.map_or_else(
|
||||
|| if self.artboard_message_handler.artboard_ids.is_empty() { "ffffff" } else { "222222" }.to_string(),
|
||||
|col| col.rgba_hex(),
|
||||
);
|
||||
let outside_artboards_color = outside.map_or_else(|| if false { "ffffff" } else { "222222" }.to_string(), |col| col.rgba_hex());
|
||||
let outside_artboards = match transparent_background {
|
||||
false => format!(r##"<rect x="0" y="0" width="100%" height="100%" fill="#{}" />"##, outside_artboards_color),
|
||||
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 starting_root_transform = document.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.size() / 2.);
|
||||
document.document_legacy.metadata.document_to_viewport = starting_root_transform;
|
||||
document.artboard_message_handler.artboards_document.root.transform = starting_root_transform;
|
||||
let transform = document.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.size() / 2.);
|
||||
document.document_legacy.metadata.document_to_viewport = transform;
|
||||
responses.add(DocumentMessage::UpdateDocumentTransform { transform });
|
||||
|
||||
document
|
||||
}
|
||||
|
|
@ -1170,10 +1152,6 @@ impl DocumentMessageHandler {
|
|||
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]> {
|
||||
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.
|
||||
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 {
|
||||
/// Returns the bounding boxes for all visible layers, optionally excluding any paths.
|
||||
pub fn bounding_boxes<'a>(&'a self, ignore_document: Option<&'a Vec<Vec<LayerId>>>, _ignore_artboard: Option<LayerId>, render_data: &'a RenderData) -> impl Iterator<Item = [DVec2; 2]> + 'a {
|
||||
self.visible_layers()
|
||||
.filter(move |path| ignore_document.map_or(true, |ignore_document| !ignore_document.iter().any(|ig| ig.as_slice() == *path)))
|
||||
.filter_map(|path| self.document_legacy.viewport_bounding_box(path, render_data).ok()?)
|
||||
.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>) {
|
||||
|
|
@ -1348,9 +1319,9 @@ impl DocumentMessageHandler {
|
|||
}
|
||||
|
||||
/// 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_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 {
|
||||
self.document_undo_history.pop_front();
|
||||
}
|
||||
|
|
@ -1361,14 +1332,13 @@ impl DocumentMessageHandler {
|
|||
|
||||
/// Copies the entire document into the history system
|
||||
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
|
||||
pub fn backup_nonmut(&self, responses: &mut VecDeque<Message>) {
|
||||
responses.add(DocumentMessage::BackupDocument {
|
||||
document: self.document_legacy.clone(),
|
||||
artboard: Box::new(self.artboard_message_handler.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.
|
||||
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
|
||||
let old_root = self.document_legacy.root.transform;
|
||||
let old_artboard_root = self.artboard_message_handler.artboards_document.root.transform;
|
||||
let old_root = self.document_legacy.metadata.document_to_viewport;
|
||||
let document = std::mem::replace(&mut self.document_legacy, document);
|
||||
let artboard = std::mem::replace(&mut self.artboard_message_handler, artboard);
|
||||
self.document_legacy.root.transform = old_root;
|
||||
self.artboard_message_handler.artboards_document.root.transform = old_artboard_root;
|
||||
self.document_legacy.metadata.document_to_viewport = old_root;
|
||||
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);
|
||||
|
||||
DocumentSave { document, artboard, layer_metadata }
|
||||
DocumentSave { document, layer_metadata }
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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
|
||||
// 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();
|
||||
|
|
@ -1412,7 +1378,7 @@ impl DocumentMessageHandler {
|
|||
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);
|
||||
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();
|
||||
|
||||
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
|
||||
// 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();
|
||||
|
|
@ -1447,7 +1413,7 @@ impl DocumentMessageHandler {
|
|||
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);
|
||||
if self.document_undo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN {
|
||||
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> {
|
||||
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()?;
|
||||
|
||||
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))
|
||||
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)
|
||||
} else {
|
||||
self.artboard_message_handler.artboards_document.viewport_bounding_box(&[], render_data).ok().flatten()
|
||||
}
|
||||
self.all_layer_bounds(render_data)
|
||||
}
|
||||
|
||||
/// Calculate the path that new layers should be inserted to.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
mod document_message;
|
||||
mod document_message_handler;
|
||||
|
||||
pub mod artboard;
|
||||
pub mod navigation;
|
||||
pub mod node_graph;
|
||||
pub mod overlays;
|
||||
|
|
|
|||
|
|
@ -378,37 +378,42 @@ impl<'a> ModifyInputsContext<'a> {
|
|||
}
|
||||
|
||||
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");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut new_input = None;
|
||||
let post_node = self.outwards_links.get(&id).and_then(|links| links.first().copied());
|
||||
let mut delete_nodes = vec![id];
|
||||
let mut is_artboard = false;
|
||||
for (node, id) in self.network.primary_flow_from_opt(Some(id)) {
|
||||
delete_nodes.push(id);
|
||||
if node.name == "Artboard" {
|
||||
new_input = Some(node.inputs[0].clone());
|
||||
is_artboard = true;
|
||||
break;
|
||||
LayerNodeIdentifier::new(id, self.network).delete(self.document_metadata);
|
||||
|
||||
let new_input = node.inputs[7].clone();
|
||||
|
||||
for post_node in self.outwards_links.get(&id).unwrap_or(&Vec::new()) {
|
||||
let Some(node) = self.network.nodes.get_mut(post_node) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
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 {
|
||||
self.network.nodes.remove(&node_id);
|
||||
}
|
||||
|
||||
if let (Some(new_input), Some(post_node)) = (new_input, post_node) {
|
||||
if let Some(node) = self.network.nodes.get_mut(&post_node) {
|
||||
node.inputs[0] = new_input;
|
||||
}
|
||||
}
|
||||
self.responses.add(DocumentMessage::DocumentStructureChanged);
|
||||
self.responses.add(NodeGraphMessage::SendGraph { should_rerender: true });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use super::utility_types::TransformOp;
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::portfolio::document::utility_types::misc::TargetDocument;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use document_legacy::layers::style::{Fill, Stroke};
|
||||
|
|
@ -24,7 +23,7 @@ pub enum PropertiesPanelMessage {
|
|||
ModifyStroke { stroke: Stroke },
|
||||
ModifyTransform { value: f64, transform_op: TransformOp },
|
||||
ResendActiveProperties,
|
||||
SetActiveLayers { paths: Vec<Vec<LayerId>>, document: TargetDocument },
|
||||
SetActiveLayers { paths: Vec<Vec<LayerId>> },
|
||||
SetPivot { new_position: PivotPosition },
|
||||
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 crate::messages::layout::utility_types::widget_prelude::*;
|
||||
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::prelude::*;
|
||||
|
||||
|
|
@ -14,7 +13,7 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct PropertiesPanelMessageHandler {
|
||||
active_selection: Option<(Vec<LayerId>, TargetDocument)>,
|
||||
active_selection: Option<Vec<LayerId>>,
|
||||
}
|
||||
|
||||
impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPanelMessageHandlerData<'a>)> for PropertiesPanelMessageHandler {
|
||||
|
|
@ -25,28 +24,23 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
|
|||
let PropertiesPanelMessageHandlerData {
|
||||
document_name,
|
||||
artwork_document,
|
||||
artboard_document,
|
||||
selected_layers,
|
||||
node_graph_message_handler,
|
||||
executor,
|
||||
} = 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);
|
||||
|
||||
match message {
|
||||
SetActiveLayers { paths, document } => {
|
||||
SetActiveLayers { paths } => {
|
||||
if paths.len() != 1 {
|
||||
// TODO: Allow for multiple selected layers
|
||||
responses.add(PropertiesPanelMessage::ClearSelection);
|
||||
responses.add(NodeGraphMessage::CloseNodeGraph);
|
||||
} else {
|
||||
let path = paths.into_iter().next().unwrap();
|
||||
if Some((path.clone(), document)) != self.active_selection {
|
||||
if Some(&path) != self.active_selection.as_ref() {
|
||||
// Update the layer visibility
|
||||
if get_document(document)
|
||||
if artwork_document
|
||||
.layer(&path)
|
||||
.ok()
|
||||
.filter(|layer| LayerDataTypeDiscriminant::from(&layer.data) == LayerDataTypeDiscriminant::Layer)
|
||||
|
|
@ -57,7 +51,7 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
|
|||
responses.add(NodeGraphMessage::CloseNodeGraph);
|
||||
}
|
||||
|
||||
self.active_selection = Some((path, document));
|
||||
self.active_selection = Some(path);
|
||||
responses.add(PropertiesPanelMessage::ResendActiveProperties);
|
||||
}
|
||||
}
|
||||
|
|
@ -85,31 +79,31 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
|
|||
send: Box::new(PropertiesPanelMessage::UpdateSelectedDocumentProperties.into()),
|
||||
}),
|
||||
ModifyTransform { value, transform_op } => {
|
||||
let (path, target_document) = 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 path = self.active_selection.as_ref().expect("Received update for properties panel with no active layer");
|
||||
let layer = artwork_document.layer(path).unwrap();
|
||||
|
||||
let transform = apply_transform_operation(layer, transform_op, value, &render_data);
|
||||
|
||||
self.create_document_operation(Operation::SetLayerTransform { path: path.clone(), transform }, true, responses);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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 pivot = position.unwrap();
|
||||
|
||||
|
|
@ -136,13 +130,9 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
|
|||
}
|
||||
}
|
||||
ResendActiveProperties => {
|
||||
if let Some((path, target_document)) = self.active_selection.clone() {
|
||||
let document = get_document(target_document);
|
||||
let layer = document.layer(&path).unwrap();
|
||||
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),
|
||||
}
|
||||
if let Some(path) = self.active_selection.clone() {
|
||||
let layer = artwork_document.layer(&path).unwrap();
|
||||
register_artwork_layer_properties(artwork_document, path, layer, responses, persistent_data, node_graph_message_handler, executor);
|
||||
} else {
|
||||
let context = crate::messages::portfolio::document::node_graph::NodePropertiesContext {
|
||||
persistent_data,
|
||||
|
|
@ -158,7 +148,6 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
|
|||
}
|
||||
UpdateSelectedDocumentProperties => responses.add(PropertiesPanelMessage::SetActiveLayers {
|
||||
paths: selected_layers.map(|path| path.to_vec()).collect(),
|
||||
document: TargetDocument::Artwork,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
|
@ -170,29 +159,18 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
|
|||
|
||||
impl PropertiesPanelMessageHandler {
|
||||
fn matches_selected(&self, path: &[LayerId]) -> bool {
|
||||
let last_active_path_id = self.active_selection.as_ref().and_then(|(v, _)| v.last().copied());
|
||||
let last_active_path_id = self.active_selection.as_ref().and_then(|v| v.last().copied());
|
||||
let last_modified = path.last().copied();
|
||||
matches!((last_active_path_id, last_modified), (Some(active_last), Some(modified_last)) if active_last == modified_last)
|
||||
}
|
||||
|
||||
fn create_document_operation(&self, operation: Operation, commit_history: bool, responses: &mut VecDeque<Message>) {
|
||||
let (_, target_document) = self.active_selection.as_ref().unwrap();
|
||||
match *target_document {
|
||||
TargetDocument::Artboard => {
|
||||
// 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)));
|
||||
}
|
||||
// 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)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,139 +64,6 @@ pub fn apply_transform_operation(layer: &Layer, transform_op: TransformOp, value
|
|||
((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(
|
||||
document: &Document,
|
||||
layer_path: Vec<document_legacy::LayerId>,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ use crate::{messages::prelude::NodeGraphMessageHandler, node_graph_executor::Nod
|
|||
pub struct PropertiesPanelMessageHandlerData<'a> {
|
||||
pub document_name: &'a str,
|
||||
pub artwork_document: &'a DocumentLegacy,
|
||||
pub artboard_document: &'a DocumentLegacy,
|
||||
pub selected_layers: &'a mut dyn Iterator<Item = &'a [LayerId]>,
|
||||
pub node_graph_message_handler: &'a NodeGraphMessageHandler,
|
||||
pub executor: &'a mut NodeGraphExecutor,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
pub use super::layer_panel::{LayerMetadata, LayerPanelEntry};
|
||||
use crate::messages::prelude::ArtboardMessageHandler;
|
||||
|
||||
use document_legacy::document::Document as DocumentLegacy;
|
||||
use document_legacy::LayerId;
|
||||
use graphene_core::raster::color::Color;
|
||||
|
|
@ -12,7 +10,6 @@ use std::fmt;
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct DocumentSave {
|
||||
pub document: DocumentLegacy,
|
||||
pub artboard: ArtboardMessageHandler,
|
||||
pub layer_metadata: HashMap<Vec<LayerId>, LayerMetadata>,
|
||||
}
|
||||
|
||||
|
|
@ -36,12 +33,6 @@ pub enum AlignAggregate {
|
|||
Average,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
pub enum TargetDocument {
|
||||
Artboard,
|
||||
Artwork,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
pub enum DocumentMode {
|
||||
DesignMode,
|
||||
|
|
|
|||
|
|
@ -249,7 +249,7 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||
no_active_document,
|
||||
MenuBarEntryChildren(vec![vec![MenuBarEntry {
|
||||
label: "Clear Artboards".into(),
|
||||
action: MenuBarEntry::create_action(|_| ArtboardMessage::ClearArtboards.into()),
|
||||
action: MenuBarEntry::create_action(|_| DialogMessage::RequestComingSoonDialog { issue: None }.into()),
|
||||
disabled: no_active_document,
|
||||
..MenuBarEntry::default()
|
||||
}]]),
|
||||
|
|
|
|||
|
|
@ -299,7 +299,7 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
|
|||
}
|
||||
}
|
||||
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();
|
||||
if self.active_document().is_some() {
|
||||
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_preprocessor::{InputPreprocessorMessage, InputPreprocessorMessageDiscriminant, InputPreprocessorMessageHandler};
|
||||
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::node_graph::{GraphOperationMessage, GraphOperationMessageDiscriminant, GraphOperationMessageHandler};
|
||||
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
|
||||
pub fn get_manipulator_groups(subpaths: &[Subpath<ManipulatorGroupId>]) -> impl Iterator<Item = &bezier_rs::ManipulatorGroup<ManipulatorGroupId>> + DoubleEndedIterator {
|
||||
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))
|
||||
}
|
||||
|
||||
/// 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
|
||||
pub fn find_node_inputs(&self, node_name: &str) -> Option<&'a Vec<NodeInput>> {
|
||||
for (node, _id) in self.primary_layer_flow() {
|
||||
if node.name == node_name {
|
||||
return Some(&node.inputs);
|
||||
}
|
||||
}
|
||||
None
|
||||
self.primary_layer_flow().find(|(node, _id)| node.name == node_name).map(|(node, _id)| &node.inputs)
|
||||
}
|
||||
|
||||
/// Find a specific input of a node within the layer's primary flow
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
use super::tool_prelude::*;
|
||||
use crate::application::generate_uuid;
|
||||
use crate::consts::SELECTION_TOLERANCE;
|
||||
use crate::messages::portfolio::document::utility_types::misc::TargetDocument;
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils::is_artboard;
|
||||
use crate::messages::tool::common_functionality::snapping::SnapManager;
|
||||
use crate::messages::tool::common_functionality::transformation_cage::*;
|
||||
|
||||
use document_legacy::intersection::Quad;
|
||||
use document_legacy::LayerId;
|
||||
use document_legacy::document_metadata::LayerNodeIdentifier;
|
||||
|
||||
use document_legacy::layers::RenderData;
|
||||
use glam::{IVec2, Vec2Swizzles};
|
||||
|
|
@ -96,7 +94,7 @@ enum ArtboardToolFsmState {
|
|||
#[derive(Clone, Debug, Default)]
|
||||
struct ArtboardToolData {
|
||||
bounding_box_overlays: Option<BoundingBoxOverlays>,
|
||||
selected_artboard: Option<LayerId>,
|
||||
selected_artboard: Option<LayerNodeIdentifier>,
|
||||
snap_manager: SnapManager,
|
||||
cursor: MouseCursorIcon,
|
||||
drag_start: DVec2,
|
||||
|
|
@ -104,15 +102,15 @@ struct ArtboardToolData {
|
|||
}
|
||||
|
||||
impl ArtboardToolData {
|
||||
fn refresh_overlays(&mut self, document: &DocumentMessageHandler, render_data: &RenderData, responses: &mut VecDeque<Message>) {
|
||||
let current_artboard = self.selected_artboard.and_then(|path| document.artboard_bounding_box_and_transform(&[path], render_data));
|
||||
fn refresh_overlays(&mut self, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
|
||||
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()) {
|
||||
(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));
|
||||
|
||||
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);
|
||||
|
||||
|
|
@ -130,6 +128,7 @@ impl ArtboardToolData {
|
|||
let (top, bottom, left, right) = edges;
|
||||
let selected_edges = SelectedEdges::new(top, bottom, left, right, bounding_box.bounds);
|
||||
bounding_box.opposite_pivot = selected_edges.calculate_pivot();
|
||||
bounding_box.selected_edges = Some(selected_edges);
|
||||
|
||||
Some(edges)
|
||||
}
|
||||
|
|
@ -140,29 +139,29 @@ impl ArtboardToolData {
|
|||
|
||||
let artboard = self.selected_artboard.unwrap();
|
||||
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, &[], &[], &[]);
|
||||
|
||||
if let Some(bounds) = &mut self.bounding_box_overlays {
|
||||
let pivot = document.artboard_message_handler.artboards_document.pivot(&[artboard], render_data).unwrap_or_default();
|
||||
let root = document.document_legacy.metadata.document_to_viewport;
|
||||
let pivot = root.inverse().transform_point2(pivot);
|
||||
bounds.center_of_transformation = pivot;
|
||||
bounds.center_of_transformation = (bounds.bounds[0] + bounds.bounds[1]) / 2.;
|
||||
}
|
||||
}
|
||||
|
||||
fn select_artboard(&mut self, document: &DocumentMessageHandler, render_data: &RenderData, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) -> bool {
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
let tolerance = DVec2::splat(SELECTION_TOLERANCE);
|
||||
let quad = Quad::from_box([input.mouse.position - tolerance, input.mouse.position + tolerance]);
|
||||
let intersection = document.artboard_message_handler.artboards_document.intersects_quad_root(quad, render_data);
|
||||
|
||||
let mut intersections = document
|
||||
.document_legacy
|
||||
.metadata
|
||||
.click_xray(input.mouse.position)
|
||||
.filter(|&layer| is_artboard(layer, &document.document_legacy));
|
||||
|
||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||
if let Some(intersection) = intersection.last() {
|
||||
self.selected_artboard = Some(intersection[0]);
|
||||
if let Some(intersection) = intersections.next() {
|
||||
self.selected_artboard = Some(intersection);
|
||||
|
||||
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, &[], &[], &[]);
|
||||
|
||||
true
|
||||
|
|
@ -186,13 +185,8 @@ impl ArtboardToolData {
|
|||
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);
|
||||
responses.add(ArtboardMessage::ResizeArtboard {
|
||||
artboard: self.selected_artboard.unwrap(),
|
||||
position: position.round().into(),
|
||||
size: size.round().into(),
|
||||
});
|
||||
responses.add(GraphOperationMessage::ResizeArtboard {
|
||||
id: self.selected_artboard.unwrap(),
|
||||
id: self.selected_artboard.unwrap().to_node(),
|
||||
location: position.round().as_ivec2(),
|
||||
dimensions: size.round().as_ivec2(),
|
||||
});
|
||||
|
|
@ -214,7 +208,7 @@ impl Fsm for ArtboardToolFsmState {
|
|||
|
||||
match (self, event) {
|
||||
(state, ArtboardToolMessage::DocumentIsDirty) if state != ArtboardToolFsmState::Drawing => {
|
||||
tool_data.refresh_overlays(document, render_data, responses);
|
||||
tool_data.refresh_overlays(document, responses);
|
||||
|
||||
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);
|
||||
|
||||
responses.add(ArtboardMessage::ResizeArtboard {
|
||||
artboard: tool_data.selected_artboard.unwrap(),
|
||||
position: position.round().into(),
|
||||
size: size.round().into(),
|
||||
});
|
||||
responses.add(GraphOperationMessage::ResizeArtboard {
|
||||
id: tool_data.selected_artboard.unwrap(),
|
||||
id: tool_data.selected_artboard.unwrap().to_node(),
|
||||
location: position.round().as_ivec2(),
|
||||
dimensions: size.round().as_ivec2(),
|
||||
});
|
||||
|
|
@ -294,28 +283,18 @@ impl Fsm for ArtboardToolFsmState {
|
|||
let size = root_transform.transform_vector2(size);
|
||||
|
||||
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 {
|
||||
id: tool_data.selected_artboard.unwrap(),
|
||||
id: artboard.to_node(),
|
||||
location: start.round().as_ivec2(),
|
||||
dimensions: size.round().as_ivec2(),
|
||||
});
|
||||
} else {
|
||||
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.add_all_document_handles(document, input, &[], &[], &[]);
|
||||
|
||||
responses.add(ArtboardMessage::AddArtboard {
|
||||
id: Some(id),
|
||||
position: start.round().into(),
|
||||
size: (1., 1.),
|
||||
});
|
||||
responses.add(GraphOperationMessage::NewArtboard {
|
||||
id,
|
||||
artboard: graphene_core::Artboard {
|
||||
|
|
@ -376,8 +355,8 @@ impl Fsm for ArtboardToolFsmState {
|
|||
}
|
||||
(_, ArtboardToolMessage::DeleteSelected) => {
|
||||
if let Some(artboard) = tool_data.selected_artboard.take() {
|
||||
responses.add(ArtboardMessage::DeleteArtboard { artboard });
|
||||
responses.add(GraphOperationMessage::DeleteLayer { id: artboard });
|
||||
let id = artboard.to_node();
|
||||
responses.add(GraphOperationMessage::DeleteLayer { id });
|
||||
|
||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||
}
|
||||
|
|
@ -385,13 +364,8 @@ impl Fsm for ArtboardToolFsmState {
|
|||
}
|
||||
(_, ArtboardToolMessage::NudgeSelected { delta_x, delta_y }) => {
|
||||
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 {
|
||||
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(),
|
||||
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
|
||||
responses.add(PropertiesPanelMessage::SetActiveLayers {
|
||||
paths: document.selected_layers().map(|path| path.to_vec()).collect(),
|
||||
document: TargetDocument::Artwork,
|
||||
});
|
||||
|
||||
tool_data.snap_manager.cleanup(responses);
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ impl Fsm for FillToolFsmState {
|
|||
let ToolMessage::Fill(event) = event else {
|
||||
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;
|
||||
};
|
||||
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)
|
||||
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 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);
|
||||
|
||||
// Apply the gradient to the selected layer
|
||||
if let Some((layer, _)) = selected_layer {
|
||||
if let Some(layer) = selected_layer {
|
||||
// let is_bitmap = document
|
||||
// .document_legacy
|
||||
// .layer(&layer)
|
||||
|
|
|
|||
|
|
@ -242,7 +242,7 @@ impl PathToolData {
|
|||
PathToolFsmState::Dragging
|
||||
}
|
||||
// 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
|
||||
let layer_list = vec![layer.to_path()];
|
||||
if shift {
|
||||
|
|
|
|||
|
|
@ -484,7 +484,6 @@ impl NodeGraphExecutor {
|
|||
affected_layer_path: execution_context.layer_path,
|
||||
});
|
||||
responses.add(DocumentMessage::RenderDocument);
|
||||
responses.add(ArtboardMessage::RenderArtboards);
|
||||
responses.add(DocumentMessage::DocumentStructureChanged);
|
||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||
responses.add(DocumentMessage::DirtyRenderDocument);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue