Refactor font_cache into render_data; delete image layer type

This commit is contained in:
Keavon Chambers
2023-01-29 20:31:14 -08:00
parent 7ab601c0c6
commit beab0f01c6
41 changed files with 395 additions and 554 deletions
@@ -1,10 +1,10 @@
use crate::application::generate_uuid;
use crate::messages::portfolio::utility_types::PersistentData;
use crate::messages::prelude::*;
use document_legacy::color::Color;
use document_legacy::document::Document as DocumentLegacy;
use document_legacy::layers::style::{self, Fill, RenderData, ViewMode};
use document_legacy::layers::text_layer::FontCache;
use document_legacy::DocumentResponse;
use document_legacy::LayerId;
use document_legacy::Operation as DocumentOperation;
@@ -18,30 +18,34 @@ pub struct ArtboardMessageHandler {
pub artboard_ids: Vec<LayerId>,
}
impl MessageHandler<ArtboardMessage, &FontCache> for ArtboardMessageHandler {
impl MessageHandler<ArtboardMessage, &PersistentData> for ArtboardMessageHandler {
#[remain::check]
fn process_message(&mut self, message: ArtboardMessage, responses: &mut VecDeque<Message>, font_cache: &FontCache) {
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, font_cache) {
Ok(Some(document_responses)) => {
for response in document_responses {
match &response {
DocumentResponse::LayerChanged { path } => responses.push_back(PropertiesPanelMessage::CheckSelectedWasUpdated { path: path.clone() }.into()),
DocumentResponse::DeletedLayer { path } => responses.push_back(PropertiesPanelMessage::CheckSelectedWasDeleted { path: path.clone() }.into()),
DocumentResponse::DocumentChanged => responses.push_back(ArtboardMessage::RenderArtboards.into()),
_ => {}
};
responses.push_back(BroadcastEvent::DocumentIsDirty.into());
DispatchOperation(operation) => {
let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::Normal, None);
match self.artboards_document.handle_operation(*operation, &render_data) {
Ok(Some(document_responses)) => {
for response in document_responses {
match &response {
DocumentResponse::LayerChanged { path } => responses.push_back(PropertiesPanelMessage::CheckSelectedWasUpdated { path: path.clone() }.into()),
DocumentResponse::DeletedLayer { path } => responses.push_back(PropertiesPanelMessage::CheckSelectedWasDeleted { path: path.clone() }.into()),
DocumentResponse::DocumentChanged => responses.push_back(ArtboardMessage::RenderArtboards.into()),
_ => {}
};
responses.push_back(BroadcastEvent::DocumentIsDirty.into());
}
}
Ok(None) => {}
Err(e) => error!("Artboard Error: {:?}", e),
}
Ok(None) => {}
Err(e) => error!("Artboard Error: {:?}", e),
},
}
// Messages
AddArtboard { id, position, size } => {
@@ -85,10 +89,10 @@ impl MessageHandler<ArtboardMessage, &FontCache> for ArtboardMessageHandler {
.into(),
)
} else {
let render_data = RenderData::new(ViewMode::Normal, font_cache, None);
let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::Normal, None);
responses.push_back(
FrontendMessage::UpdateDocumentArtboards {
svg: self.artboards_document.render_root(render_data),
svg: self.artboards_document.render_root(&render_data),
}
.into(),
);
@@ -29,7 +29,7 @@ use document_legacy::layers::blend_mode::BlendMode;
use document_legacy::layers::folder_layer::FolderLayer;
use document_legacy::layers::layer_info::{LayerDataType, LayerDataTypeDiscriminant};
use document_legacy::layers::style::{Fill, RenderData, ViewMode};
use document_legacy::layers::text_layer::{Font, FontCache};
use document_legacy::layers::text_layer::Font;
use document_legacy::{DocumentError, DocumentResponse, LayerId, Operation as DocumentOperation};
use graph_craft::document::NodeId;
use graphene_std::vector::subpath::Subpath;
@@ -114,70 +114,74 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
) {
use DocumentMessage::*;
let render_data = RenderData::new(&persistent_data.font_cache, self.view_mode, Some(ipp.document_bounds()));
#[remain::sorted]
match message {
// Sub-messages
#[remain::unsorted]
DispatchOperation(op) => match self.document_legacy.handle_operation(*op, &persistent_data.font_cache) {
Ok(Some(document_responses)) => {
for response in document_responses {
match &response {
DocumentResponse::FolderChanged { path } => responses.push_back(FolderChanged { affected_folder_path: path.clone() }.into()),
DocumentResponse::DeletedLayer { path } => {
self.layer_metadata.remove(path);
}
DocumentResponse::LayerChanged { path } => responses.push_back(LayerChanged { affected_layer_path: path.clone() }.into()),
DocumentResponse::CreatedLayer { path } => {
if self.layer_metadata.contains_key(path) {
warn!("CreatedLayer overrides existing layer metadata.");
DispatchOperation(op) => {
match self.document_legacy.handle_operation(*op, &render_data) {
Ok(Some(document_responses)) => {
for response in document_responses {
match &response {
DocumentResponse::FolderChanged { path } => responses.push_back(FolderChanged { affected_folder_path: path.clone() }.into()),
DocumentResponse::DeletedLayer { path } => {
self.layer_metadata.remove(path);
}
self.layer_metadata.insert(path.clone(), LayerMetadata::new(false));
DocumentResponse::LayerChanged { path } => responses.push_back(LayerChanged { affected_layer_path: path.clone() }.into()),
DocumentResponse::CreatedLayer { path } => {
if self.layer_metadata.contains_key(path) {
warn!("CreatedLayer overrides existing layer metadata.");
}
self.layer_metadata.insert(path.clone(), LayerMetadata::new(false));
responses.push_back(LayerChanged { affected_layer_path: path.clone() }.into());
self.layer_range_selection_reference = path.clone();
responses.push_back(
AddSelectedLayers {
additional_layers: vec![path.clone()],
}
.into(),
);
}
DocumentResponse::DocumentChanged => responses.push_back(RenderDocument.into()),
DocumentResponse::DeletedSelectedManipulatorPoints => {
// Clear Properties panel after deleting all points by updating backend widget state.
responses.push_back(
LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout::new(vec![])),
layout_target: LayoutTarget::PropertiesOptions,
}
.into(),
);
responses.push_back(
LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout::new(vec![])),
layout_target: LayoutTarget::PropertiesSections,
}
.into(),
);
}
};
responses.push_back(BroadcastEvent::DocumentIsDirty.into());
responses.push_back(LayerChanged { affected_layer_path: path.clone() }.into());
self.layer_range_selection_reference = path.clone();
responses.push_back(
AddSelectedLayers {
additional_layers: vec![path.clone()],
}
.into(),
);
}
DocumentResponse::DocumentChanged => responses.push_back(RenderDocument.into()),
DocumentResponse::DeletedSelectedManipulatorPoints => {
// Clear Properties panel after deleting all points by updating backend widget state.
responses.push_back(
LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout::new(vec![])),
layout_target: LayoutTarget::PropertiesOptions,
}
.into(),
);
responses.push_back(
LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout::new(vec![])),
layout_target: LayoutTarget::PropertiesSections,
}
.into(),
);
}
};
responses.push_back(BroadcastEvent::DocumentIsDirty.into());
}
}
// Display boolean operation error to the user (except if it is a nothing done error).
Err(DocumentError::BooleanOperationError(boolean_operation_error)) if boolean_operation_error != BooleanOperationError::NothingDone => responses.push_back(
DialogMessage::DisplayDialogError {
title: "Failed to calculate boolean operation".into(),
description: format!("Unfortunately, this feature not that robust yet.\n\nError: {boolean_operation_error:?}"),
}
.into(),
),
Err(e) => error!("DocumentError: {:?}", e),
Ok(_) => (),
}
// Display boolean operation error to the user (except if it is a nothing done error).
Err(DocumentError::BooleanOperationError(boolean_operation_error)) if boolean_operation_error != BooleanOperationError::NothingDone => responses.push_back(
DialogMessage::DisplayDialogError {
title: "Failed to calculate boolean operation".into(),
description: format!("Unfortunately, this feature not that robust yet.\n\nError: {boolean_operation_error:?}"),
}
.into(),
),
Err(e) => error!("DocumentError: {:?}", e),
Ok(_) => (),
},
}
#[remain::unsorted]
Artboard(message) => {
self.artboard_message_handler.process_message(message, responses, &persistent_data.font_cache);
self.artboard_message_handler.process_message(message, responses, persistent_data);
}
#[remain::unsorted]
Navigation(message) => {
@@ -185,13 +189,12 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
}
#[remain::unsorted]
Overlays(message) => {
self.overlays_message_handler
.process_message(message, responses, (self.overlays_visible, &persistent_data.font_cache, ipp));
self.overlays_message_handler.process_message(message, responses, (self.overlays_visible, persistent_data, ipp));
}
#[remain::unsorted]
TransformLayer(message) => {
self.transform_layer_handler
.process_message(message, responses, (&mut self.layer_metadata, &mut self.document_legacy, ipp, &persistent_data.font_cache));
.process_message(message, responses, (&mut self.layer_metadata, &mut self.document_legacy, ipp, &render_data));
}
#[remain::unsorted]
PropertiesPanel(message) => {
@@ -219,20 +222,20 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
}
AddSelectedLayers { additional_layers } => {
for layer_path in &additional_layers {
responses.extend(self.select_layer(layer_path, &persistent_data.font_cache));
responses.extend(self.select_layer(layer_path, &render_data));
}
// TODO: Correctly update layer panel in clear_selection instead of here
responses.push_back(FolderChanged { affected_folder_path: vec![] }.into());
responses.push_back(BroadcastEvent::SelectionChanged.into());
self.update_layer_tree_options_bar_widgets(responses, &persistent_data.font_cache);
self.update_layer_tree_options_bar_widgets(responses, &render_data);
}
AlignSelectedLayers { axis, aggregate } => {
self.backup(responses);
let (paths, boxes): (Vec<_>, Vec<_>) = self
.selected_layers()
.filter_map(|path| self.document_legacy.viewport_bounding_box(path, &persistent_data.font_cache).ok()?.map(|b| (path, b)))
.filter_map(|path| self.document_legacy.viewport_bounding_box(path, &render_data).ok()?.map(|b| (path, b)))
.unzip();
let axis = match axis {
@@ -240,7 +243,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
AlignAxis::Y => DVec2::Y,
};
let lerp = |bbox: &[DVec2; 2]| bbox[0].lerp(bbox[1], 0.5);
if let Some(combined_box) = self.document_legacy.combined_viewport_bounding_box(self.selected_layers(), &persistent_data.font_cache) {
if let Some(combined_box) = self.document_legacy.combined_viewport_bounding_box(self.selected_layers(), &render_data) {
let aggregated = match aggregate {
AlignAggregate::Min => combined_box[0],
AlignAggregate::Max => combined_box[1],
@@ -376,14 +379,9 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
// Calculate the bounding box of the region to be exported
let bounds = match bounds {
ExportBounds::AllArtwork => self.all_layer_bounds(&persistent_data.font_cache),
ExportBounds::Selection => self.selected_visible_layers_bounding_box(&persistent_data.font_cache),
ExportBounds::Artboard(id) => self
.artboard_message_handler
.artboards_document
.layer(&[id])
.ok()
.and_then(|layer| layer.aabb(&persistent_data.font_cache)),
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)),
}
.unwrap_or_default();
let size = bounds[1] - bounds[0];
@@ -412,7 +410,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
FlipAxis::X => DVec2::new(-1., 1.),
FlipAxis::Y => DVec2::new(1., -1.),
};
if let Some([min, max]) = self.document_legacy.combined_viewport_bounding_box(self.selected_layers(), &persistent_data.font_cache) {
if let Some([min, max]) = self.document_legacy.combined_viewport_bounding_box(self.selected_layers(), &render_data) {
let center = (max + min) / 2.;
let bbox_trans = DAffine2::from_translation(-center);
for path in self.selected_layers() {
@@ -483,11 +481,11 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
);
}
LayerChanged { affected_layer_path } => {
if let Ok(layer_entry) = self.layer_panel_entry(affected_layer_path.clone(), &persistent_data.font_cache) {
if let Ok(layer_entry) = self.layer_panel_entry(affected_layer_path.clone(), &render_data) {
responses.push_back(FrontendMessage::UpdateDocumentLayerDetails { data: layer_entry }.into());
}
responses.push_back(PropertiesPanelMessage::CheckSelectedWasUpdated { path: affected_layer_path }.into());
self.update_layer_tree_options_bar_widgets(responses, &persistent_data.font_cache);
self.update_layer_tree_options_bar_widgets(responses, &render_data);
}
MoveSelectedLayersTo {
folder_path,
@@ -632,10 +630,9 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
}
RenameLayer { layer_path, new_name } => responses.push_back(DocumentOperation::RenameLayer { layer_path, new_name }.into()),
RenderDocument => {
let render_data = RenderData::new(self.view_mode, &persistent_data.font_cache, Some(ipp.document_bounds()));
responses.push_back(
FrontendMessage::UpdateDocumentArtwork {
svg: self.document_legacy.render_root(render_data),
svg: self.document_legacy.render_root(&render_data),
}
.into(),
);
@@ -645,7 +642,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
let scale = 0.5 + ASYMPTOTIC_EFFECT + document_transform_scale * SCALE_EFFECT;
let viewport_size = ipp.viewport_bounds.size();
let viewport_mid = ipp.viewport_bounds.center();
let [bounds1, bounds2] = self.document_bounds(&persistent_data.font_cache).unwrap_or([viewport_mid; 2]);
let [bounds1, bounds2] = self.document_bounds(&render_data).unwrap_or([viewport_mid; 2]);
let bounds1 = bounds1.min(viewport_mid) - viewport_size * scale;
let bounds2 = bounds2.max(viewport_mid) + viewport_size * scale;
let bounds_length = (bounds2 - bounds1) * (1. + SCROLLBAR_SPACING);
@@ -782,7 +779,6 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
responses.push_back(FrontendMessage::TriggerRevokeBlobUrl { url: url.clone() }.into());
}
}
LayerDataType::Image(_) => {}
other => panic!(
"Setting blob URL for invalid layer type, which must be an `Imaginate`, `NodeGraphFrame` or `Image`. Found: `{:?}`",
other
@@ -803,7 +799,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
responses.push_back(LayerChanged { affected_layer_path: layer_path }.into())
}
SetLayerName { layer_path, name } => {
if let Some(layer) = self.layer_panel_entry_from_path(&layer_path, &persistent_data.font_cache) {
if let Some(layer) = self.layer_panel_entry_from_path(&layer_path, &render_data) {
// Only save the history state if the name actually changed to something different
if layer.name != name {
self.backup(responses);
@@ -926,7 +922,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
responses.push_front(NavigationMessage::SetCanvasZoom { zoom_factor: 2. }.into());
}
ZoomCanvasToFitAll => {
if let Some(bounds) = self.document_bounds(&persistent_data.font_cache) {
if let Some(bounds) = self.document_bounds(&render_data) {
responses.push_back(
NavigationMessage::FitViewportToBounds {
bounds,
@@ -1057,14 +1053,14 @@ impl DocumentMessageHandler {
pub fn render_document(&mut self, size: DVec2, transform: DAffine2, persistent_data: &PersistentData, render_mode: DocumentRenderMode) -> String {
// Render the document SVG code
let render_data = RenderData::new(ViewMode::Normal, &persistent_data.font_cache, None);
let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::Normal, None);
let (artwork, outside) = match render_mode {
DocumentRenderMode::Root => (self.document_legacy.render_root(render_data), None),
DocumentRenderMode::OnlyBelowLayerInFolder(below_layer_path) => (self.document_legacy.render_layers_below(below_layer_path, render_data).unwrap(), None),
DocumentRenderMode::LayerCutout(layer_path, background) => (self.document_legacy.render_layer(layer_path, render_data).unwrap(), Some(background)),
DocumentRenderMode::Root => (self.document_legacy.render_root(&render_data), None),
DocumentRenderMode::OnlyBelowLayerInFolder(below_layer_path) => (self.document_legacy.render_layers_below(below_layer_path, &render_data).unwrap(), None),
DocumentRenderMode::LayerCutout(layer_path, background) => (self.document_legacy.render_layer(layer_path, &render_data).unwrap(), Some(background)),
};
let artboards = self.artboard_message_handler.artboards_document.render_root(render_data);
let artboards = self.artboard_message_handler.artboards_document.render_root(&render_data);
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(),
@@ -1129,12 +1125,14 @@ impl DocumentMessageHandler {
&& self.name.starts_with(DEFAULT_DOCUMENT_NAME)
}
fn select_layer(&mut self, path: &[LayerId], font_cache: &FontCache) -> Option<Message> {
fn select_layer(&mut self, path: &[LayerId], render_data: &RenderData) -> Option<Message> {
println!("Select_layer fail: {:?}", self.all_layers_sorted());
if let Some(layer) = self.layer_metadata.get_mut(path) {
let render_data = RenderData::new(render_data.font_cache, self.view_mode, None);
layer.selected = true;
let data = self.layer_panel_entry(path.to_vec(), font_cache).ok()?;
let data = self.layer_panel_entry(path.to_vec(), &render_data).ok()?;
(!path.is_empty()).then(|| FrontendMessage::UpdateDocumentLayerDetails { data }.into())
} else {
warn!("Tried to select non existing layer {:?}", path);
@@ -1142,13 +1140,13 @@ impl DocumentMessageHandler {
}
}
pub fn selected_visible_layers_bounding_box(&self, font_cache: &FontCache) -> Option<[DVec2; 2]> {
pub fn selected_visible_layers_bounding_box(&self, render_data: &RenderData) -> Option<[DVec2; 2]> {
let paths = self.selected_visible_layers();
self.document_legacy.combined_viewport_bounding_box(paths, font_cache)
self.document_legacy.combined_viewport_bounding_box(paths, render_data)
}
pub fn artboard_bounding_box_and_transform(&self, path: &[LayerId], font_cache: &FontCache) -> Option<([DVec2; 2], DAffine2)> {
self.artboard_message_handler.artboards_document.bounding_box_and_transform(path, font_cache).unwrap_or(None)
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]> {
@@ -1220,16 +1218,16 @@ 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>, font_cache: &'a FontCache) -> 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()
.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, font_cache).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], font_cache).ok()?),
.filter_map(|&path| self.artboard_message_handler.artboards_document.viewport_bounding_box(&[path], render_data).ok()?),
)
}
@@ -1505,31 +1503,31 @@ impl DocumentMessageHandler {
}
// TODO: This should probably take a slice not a vec, also why does this even exist when `layer_panel_entry_from_path` also exists?
pub fn layer_panel_entry(&mut self, path: Vec<LayerId>, font_cache: &FontCache) -> Result<LayerPanelEntry, EditorError> {
pub fn layer_panel_entry(&mut self, path: Vec<LayerId>, render_data: &RenderData) -> Result<LayerPanelEntry, EditorError> {
let data: LayerMetadata = *self
.layer_metadata
.get_mut(&path)
.ok_or_else(|| EditorError::Document(format!("Could not get layer metadata for {:?}", path)))?;
let layer = self.document_legacy.layer(&path)?;
let entry = LayerPanelEntry::new(&data, self.document_legacy.multiply_transforms(&path)?, layer, path, font_cache);
let entry = LayerPanelEntry::new(&data, self.document_legacy.multiply_transforms(&path)?, layer, path, render_data);
Ok(entry)
}
/// Returns a list of `LayerPanelEntry`s intended for display purposes. These don't contain
/// any actual data, but rather attributes such as visibility and names of the layers.
pub fn layer_panel(&mut self, path: &[LayerId], font_cache: &FontCache) -> Result<Vec<LayerPanelEntry>, EditorError> {
pub fn layer_panel(&mut self, path: &[LayerId], render_data: &RenderData) -> Result<Vec<LayerPanelEntry>, EditorError> {
let folder = self.document_legacy.folder(path)?;
let paths: Vec<Vec<LayerId>> = folder.layer_ids.iter().map(|id| [path, &[*id]].concat()).collect();
let entries = paths.iter().rev().filter_map(|path| self.layer_panel_entry_from_path(path, font_cache)).collect();
let entries = paths.iter().rev().filter_map(|path| self.layer_panel_entry_from_path(path, render_data)).collect();
Ok(entries)
}
pub fn layer_panel_entry_from_path(&self, path: &[LayerId], font_cache: &FontCache) -> 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 transform = self.document_legacy.generate_transform_across_scope(path, Some(self.document_legacy.root.transform.inverse())).ok()?;
let layer = self.document_legacy.layer(path).ok()?;
Some(LayerPanelEntry::new(layer_metadata, transform, layer, path.to_vec(), font_cache))
Some(LayerPanelEntry::new(layer_metadata, transform, layer, path.to_vec(), render_data))
}
/// When working with an insert index, deleting the layers may cause the insert index to point to a different location (if the layer being deleted was located before the insert index).
@@ -1544,16 +1542,16 @@ impl DocumentMessageHandler {
}
/// Calculates the bounding box of all layers in the document
pub fn all_layer_bounds(&self, font_cache: &FontCache) -> Option<[DVec2; 2]> {
self.document_legacy.viewport_bounding_box(&[], font_cache).ok().flatten()
pub fn all_layer_bounds(&self, render_data: &RenderData) -> Option<[DVec2; 2]> {
self.document_legacy.viewport_bounding_box(&[], render_data).ok().flatten()
}
/// Calculates the document bounds used for scrolling and centring (the layer bounds or the artboard (if applicable))
pub fn document_bounds(&self, font_cache: &FontCache) -> 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(font_cache)
self.all_layer_bounds(render_data)
} else {
self.artboard_message_handler.artboards_document.viewport_bounding_box(&[], font_cache).ok().flatten()
self.artboard_message_handler.artboards_document.viewport_bounding_box(&[], render_data).ok().flatten()
}
}
@@ -1581,11 +1579,6 @@ impl DocumentMessageHandler {
LayerDataType::Text(text) => {
fonts.insert(text.font.clone());
}
LayerDataType::Image(image) => image_data.push(FrontendImageData {
path: path.clone(),
image_data: image.image_data.clone(),
mime: image.mime.clone(),
}),
LayerDataType::NodeGraphFrame(node_graph_frame) => {
if let Some(data) = &node_graph_frame.image_data {
image_data.push(FrontendImageData {
@@ -1829,7 +1822,7 @@ impl DocumentMessageHandler {
);
}
pub fn update_layer_tree_options_bar_widgets(&self, responses: &mut VecDeque<Message>, font_cache: &FontCache) {
pub fn update_layer_tree_options_bar_widgets(&self, responses: &mut VecDeque<Message>, render_data: &RenderData) {
let mut opacity = None;
let mut opacity_is_mixed = false;
@@ -1838,7 +1831,7 @@ impl DocumentMessageHandler {
self.layer_metadata
.keys()
.filter_map(|path| self.layer_panel_entry_from_path(path, font_cache))
.filter_map(|path| self.layer_panel_entry_from_path(path, render_data))
.filter(|layer_panel_entry| layer_panel_entry.layer_metadata.selected)
.flat_map(|layer_panel_entry| self.document_legacy.layer(layer_panel_entry.path.as_slice()))
.for_each(|layer| {
@@ -1,27 +1,31 @@
use crate::messages::portfolio::utility_types::PersistentData;
use crate::messages::prelude::*;
use document_legacy::document::Document as DocumentLegacy;
use document_legacy::layers::style::{RenderData, ViewMode};
use document_legacy::layers::text_layer::FontCache;
#[derive(Debug, Clone, Default)]
pub struct OverlaysMessageHandler {
pub overlays_document: DocumentLegacy,
}
impl MessageHandler<OverlaysMessage, (bool, &FontCache, &InputPreprocessorMessageHandler)> for OverlaysMessageHandler {
impl MessageHandler<OverlaysMessage, (bool, &PersistentData, &InputPreprocessorMessageHandler)> for OverlaysMessageHandler {
#[remain::check]
fn process_message(&mut self, message: OverlaysMessage, responses: &mut VecDeque<Message>, (overlays_visible, font_cache, ipp): (bool, &FontCache, &InputPreprocessorMessageHandler)) {
fn process_message(&mut self, message: OverlaysMessage, responses: &mut VecDeque<Message>, (overlays_visible, persistent_data, ipp): (bool, &PersistentData, &InputPreprocessorMessageHandler)) {
use OverlaysMessage::*;
#[remain::sorted]
match message {
// Sub-messages
#[remain::unsorted]
DispatchOperation(operation) => match self.overlays_document.handle_operation(*operation, font_cache) {
Ok(_) => responses.push_back(OverlaysMessage::Rerender.into()),
Err(e) => error!("OverlaysError: {:?}", e),
},
DispatchOperation(operation) => {
let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::Normal, Some(ipp.document_bounds()));
match self.overlays_document.handle_operation(*operation, &render_data) {
Ok(_) => responses.push_back(OverlaysMessage::Rerender.into()),
Err(e) => error!("OverlaysError: {:?}", e),
}
}
// Messages
ClearAllOverlays => {
@@ -33,8 +37,8 @@ impl MessageHandler<OverlaysMessage, (bool, &FontCache, &InputPreprocessorMessag
responses.push_back(
FrontendMessage::UpdateDocumentOverlays {
svg: if overlays_visible {
let render_data = RenderData::new(ViewMode::Normal, font_cache, Some(ipp.document_bounds()));
self.overlays_document.render_root(render_data)
let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::Normal, Some(ipp.document_bounds()));
self.overlays_document.render_root(&render_data)
} else {
String::from("")
},
@@ -8,6 +8,7 @@ use crate::messages::portfolio::utility_types::PersistentData;
use crate::messages::prelude::*;
use document_legacy::layers::layer_info::LayerDataTypeDiscriminant;
use document_legacy::layers::style::{RenderData, ViewMode};
use document_legacy::{LayerId, Operation};
use serde::{Deserialize, Serialize};
@@ -20,6 +21,8 @@ pub struct PropertiesPanelMessageHandler {
impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPanelMessageHandlerData<'a>)> for PropertiesPanelMessageHandler {
#[remain::check]
fn process_message(&mut self, message: PropertiesPanelMessage, responses: &mut VecDeque<Message>, (persistent_data, data): (&PersistentData, PropertiesPanelMessageHandlerData)) {
use PropertiesPanelMessage::*;
let PropertiesPanelMessageHandlerData {
artwork_document,
artboard_document,
@@ -30,7 +33,8 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
TargetDocument::Artboard => artboard_document,
TargetDocument::Artwork => artwork_document,
};
use PropertiesPanelMessage::*;
let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::Normal, None);
match message {
SetActiveLayers { paths, document } => {
if paths.len() != 1 {
@@ -99,7 +103,7 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
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 transform = apply_transform_operation(layer, transform_op, value, &persistent_data.font_cache);
let transform = apply_transform_operation(layer, transform_op, value, &render_data);
self.create_document_operation(Operation::SetLayerTransform { path: path.clone(), transform }, true, responses);
}
@@ -12,14 +12,14 @@ use crate::messages::prelude::*;
use document_legacy::color::Color;
use document_legacy::document::Document;
use document_legacy::layers::layer_info::{Layer, LayerDataType, LayerDataTypeDiscriminant};
use document_legacy::layers::style::{Fill, Gradient, GradientType, LineCap, LineJoin, Stroke};
use document_legacy::layers::text_layer::{FontCache, TextLayer};
use document_legacy::layers::style::{Fill, Gradient, GradientType, LineCap, LineJoin, RenderData, Stroke, ViewMode};
use document_legacy::layers::text_layer::TextLayer;
use glam::{DAffine2, DVec2};
use std::f64::consts::PI;
use std::sync::Arc;
pub fn apply_transform_operation(layer: &Layer, transform_op: TransformOp, value: f64, font_cache: &FontCache) -> [f64; 6] {
pub fn apply_transform_operation(layer: &Layer, transform_op: TransformOp, value: f64, render_data: &RenderData) -> [f64; 6] {
let transformation = match transform_op {
TransformOp::X => DAffine2::update_x,
TransformOp::Y => DAffine2::update_y,
@@ -29,8 +29,8 @@ pub fn apply_transform_operation(layer: &Layer, transform_op: TransformOp, value
};
let scale = match transform_op {
TransformOp::Width => layer.bounding_transform(font_cache).scale_x() / layer.transform.scale_x(),
TransformOp::Height => layer.bounding_transform(font_cache).scale_y() / layer.transform.scale_y(),
TransformOp::Width => layer.bounding_transform(render_data).scale_x() / layer.transform.scale_x(),
TransformOp::Height => layer.bounding_transform(render_data).scale_y() / layer.transform.scale_y(),
_ => 1.,
};
@@ -43,7 +43,7 @@ pub fn apply_transform_operation(layer: &Layer, transform_op: TransformOp, value
}
// Find the layerspace pivot
let pivot = DAffine2::from_translation(layer.transform.transform_point2(layer.layerspace_pivot(font_cache)));
let pivot = DAffine2::from_translation(layer.transform.transform_point2(layer.layerspace_pivot(render_data)));
// Find the delta transform
let mut delta = layer.transform.inverse() * transform;
@@ -94,17 +94,15 @@ pub fn register_artboard_layer_properties(layer: &Layer, responses: &mut VecDequ
}];
let properties_body = {
let shape = if let LayerDataType::Shape(shape) = &layer.data {
shape
} else {
let LayerDataType::Shape(shape) = &layer.data else {
panic!("Artboards can only be shapes")
};
let color = if let Fill::Solid(color) = shape.style.fill() {
color
} else {
let Fill::Solid(color) = shape.style.fill() else {
panic!("Artboard must have a solid fill")
};
let pivot = layer.transform.transform_vector2(layer.layerspace_pivot(&persistent_data.font_cache));
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(),
@@ -167,7 +165,7 @@ pub fn register_artboard_layer_properties(layer: &Layer, responses: &mut VecDequ
WidgetHolder::related_separator(),
WidgetHolder::unrelated_separator(),
WidgetHolder::new(Widget::NumberInput(NumberInput {
value: Some(layer.bounding_transform(&persistent_data.font_cache).scale_x()),
value: Some(layer.bounding_transform(&render_data).scale_x()),
label: "W".into(),
unit: " px".into(),
is_integer: true,
@@ -183,7 +181,7 @@ pub fn register_artboard_layer_properties(layer: &Layer, responses: &mut VecDequ
})),
WidgetHolder::related_separator(),
WidgetHolder::new(Widget::NumberInput(NumberInput {
value: Some(layer.bounding_transform(&persistent_data.font_cache).scale_y()),
value: Some(layer.bounding_transform(&render_data).scale_y()),
label: "H".into(),
unit: " px".into(),
is_integer: true,
@@ -267,11 +265,6 @@ pub fn register_artwork_layer_properties(
tooltip: "Text".into(),
..Default::default()
})),
LayerDataType::Image(_) => WidgetHolder::new(Widget::IconLabel(IconLabel {
icon: "NodeImage".into(),
tooltip: "Image".into(),
..Default::default()
})),
LayerDataType::NodeGraphFrame(_) => WidgetHolder::new(Widget::IconLabel(IconLabel {
icon: "NodeNodes".into(),
tooltip: "Node Graph Frame".into(),
@@ -321,9 +314,6 @@ pub fn register_artwork_layer_properties(
node_section_stroke(&text.path_style.stroke().unwrap_or_default()),
]
}
LayerDataType::Image(_) => {
vec![node_section_transform(layer, persistent_data)]
}
LayerDataType::NodeGraphFrame(node_graph_frame) => {
let mut properties_sections = vec![node_section_transform(layer, persistent_data)];
@@ -360,7 +350,8 @@ pub fn register_artwork_layer_properties(
}
fn node_section_transform(layer: &Layer, persistent_data: &PersistentData) -> LayoutGroup {
let pivot = layer.transform.transform_vector2(layer.layerspace_pivot(&persistent_data.font_cache));
let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::default(), None);
let pivot = layer.transform.transform_vector2(layer.layerspace_pivot(&render_data));
LayoutGroup::Section {
name: "Transform".into(),
layout: vec![
@@ -492,7 +483,7 @@ fn node_section_transform(layer: &Layer, persistent_data: &PersistentData) -> La
WidgetHolder::unrelated_separator(), // TODO: Remove these when we have proper entry row formatting that includes room for Assists.
WidgetHolder::unrelated_separator(),
WidgetHolder::new(Widget::NumberInput(NumberInput {
value: Some(layer.bounding_transform(&persistent_data.font_cache).scale_x()),
value: Some(layer.bounding_transform(&render_data).scale_x()),
label: "W".into(),
unit: " px".into(),
on_update: WidgetCallback::new(|number_input: &NumberInput| {
@@ -506,7 +497,7 @@ fn node_section_transform(layer: &Layer, persistent_data: &PersistentData) -> La
})),
WidgetHolder::related_separator(),
WidgetHolder::new(Widget::NumberInput(NumberInput {
value: Some(layer.bounding_transform(&persistent_data.font_cache).scale_y()),
value: Some(layer.bounding_transform(&render_data).scale_y()),
label: "H".into(),
unit: " px".into(),
on_update: WidgetCallback::new(|number_input: &NumberInput| {
@@ -5,9 +5,9 @@ use crate::messages::portfolio::document::utility_types::transformation::{Axis,
use crate::messages::prelude::*;
use document_legacy::document::Document;
use document_legacy::layers::style::RenderData;
use document_legacy::LayerId;
use document_legacy::layers::text_layer::FontCache;
use glam::DVec2;
#[derive(Debug, Clone, Default, PartialEq)]
@@ -25,10 +25,10 @@ pub struct TransformLayerMessageHandler {
pivot: DVec2,
}
type TransformData<'a> = (&'a mut HashMap<Vec<LayerId>, LayerMetadata>, &'a mut Document, &'a InputPreprocessorMessageHandler, &'a FontCache);
type TransformData<'a> = (&'a mut HashMap<Vec<LayerId>, LayerMetadata>, &'a mut Document, &'a InputPreprocessorMessageHandler, &'a RenderData<'a>);
impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformLayerMessageHandler {
#[remain::check]
fn process_message(&mut self, message: TransformLayerMessage, responses: &mut VecDeque<Message>, (layer_metadata, document, ipp, font_cache): TransformData) {
fn process_message(&mut self, message: TransformLayerMessage, responses: &mut VecDeque<Message>, (layer_metadata, document, ipp, render_data): TransformData) {
use TransformLayerMessage::*;
let selected_layers = layer_metadata.iter().filter_map(|(layer_path, data)| data.selected.then_some(layer_path)).collect::<Vec<_>>();
@@ -39,7 +39,7 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
selected.revert_operation();
typing.clear();
} else {
*selected.pivot = selected.mean_average_of_pivots(font_cache);
*selected.pivot = selected.mean_average_of_pivots(render_data);
}
*mouse_position = ipp.mouse.position;
@@ -137,7 +137,7 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
self.transform_operation.apply_transform_operation(&mut selected, self.snap);
}
TransformOperation::Rotating(rotation) => {
let selected_pivot = selected.mean_average_of_pivots(font_cache);
let selected_pivot = selected.mean_average_of_pivots(render_data);
let angle = {
let start_offset = self.mouse_position - selected_pivot;
let end_offset = ipp.mouse.position - selected_pivot;
@@ -1,6 +1,5 @@
use document_legacy::layers::layer_info::{Layer, LayerData, LayerDataTypeDiscriminant};
use document_legacy::layers::style::{RenderData, ViewMode};
use document_legacy::layers::text_layer::FontCache;
use document_legacy::layers::style::RenderData;
use document_legacy::LayerId;
use glam::{DAffine2, DVec2};
@@ -65,7 +64,7 @@ pub struct LayerPanelEntry {
}
impl LayerPanelEntry {
pub fn new(layer_metadata: &LayerMetadata, transform: DAffine2, layer: &Layer, path: Vec<LayerId>, font_cache: &FontCache) -> Self {
pub fn new(layer_metadata: &LayerMetadata, transform: DAffine2, layer: &Layer, path: Vec<LayerId>, render_data: &RenderData) -> Self {
let name = layer.name.clone().unwrap_or_else(|| String::from(""));
let mut tooltip = name.clone();
@@ -75,11 +74,10 @@ impl LayerPanelEntry {
tooltip = tooltip.trim().to_string();
}
let arr = layer.data.bounding_box(transform, font_cache).unwrap_or([DVec2::ZERO, DVec2::ZERO]);
let arr = layer.data.bounding_box(transform, render_data).unwrap_or([DVec2::ZERO, DVec2::ZERO]);
let arr = arr.iter().map(|x| (*x).into()).collect::<Vec<(f64, f64)>>();
let mut thumbnail = String::new();
let mut svg_defs = String::new();
let render_data = RenderData::new(ViewMode::Normal, font_cache, None);
layer.data.clone().render(&mut thumbnail, &mut svg_defs, &mut vec![transform], render_data);
let transform = transform.to_cols_array().iter().map(ToString::to_string).collect::<Vec<_>>().join(",");
let thumbnail = if let [(x_min, y_min), (x_max, y_max)] = arr.as_slice() {
@@ -2,7 +2,7 @@ use crate::consts::{ROTATE_SNAP_ANGLE, SCALE_SNAP_INTERVAL};
use crate::messages::prelude::*;
use document_legacy::document::Document;
use document_legacy::layers::text_layer::FontCache;
use document_legacy::layers::style::RenderData;
use document_legacy::LayerId;
use document_legacy::Operation as DocumentOperation;
@@ -206,20 +206,20 @@ impl<'a> Selected<'a> {
}
}
pub fn mean_average_of_pivots(&mut self, font_cache: &FontCache) -> DVec2 {
let xy_summation = self.selected.iter().filter_map(|path| self.document.pivot(path, font_cache)).reduce(|a, b| a + b).unwrap_or_default();
pub fn mean_average_of_pivots(&mut self, render_data: &RenderData) -> DVec2 {
let xy_summation = self.selected.iter().filter_map(|path| self.document.pivot(path, render_data)).reduce(|a, b| a + b).unwrap_or_default();
xy_summation / self.selected.len() as f64
}
pub fn center_of_aabb(&mut self, font_cache: &FontCache) -> DVec2 {
pub fn center_of_aabb(&mut self, render_data: &RenderData) -> DVec2 {
let [min, max] = self
.selected
.iter()
.filter_map(|path| {
let multiplied_transform = self.document.multiply_transforms(path).unwrap();
self.document.layer(path).unwrap().aabb_for_transform(multiplied_transform, font_cache)
self.document.layer(path).unwrap().aabb_for_transform(multiplied_transform, render_data)
})
.reduce(|a, b| [a[0].min(b[0]), a[1].max(b[1])])
.unwrap_or_default();