mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-17 15:28:04 +08:00
Refactor font loading from per-document to the portfolio (#659)
* Cleanup default font loading * Refactor fonts * Fix menulist mouse navigation * Format * Formatting * Move default font into consts.rs Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
committed by
Keavon Chambers
parent
d4539bc304
commit
8923b68e30
@@ -3,6 +3,7 @@ use crate::message_prelude::*;
|
||||
use graphene::color::Color;
|
||||
use graphene::document::Document as GrapheneDocument;
|
||||
use graphene::layers::style::{self, Fill, ViewMode};
|
||||
use graphene::layers::text_layer::FontCache;
|
||||
use graphene::DocumentResponse;
|
||||
use graphene::Operation as DocumentOperation;
|
||||
|
||||
@@ -22,16 +23,16 @@ impl ArtboardMessageHandler {
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageHandler<ArtboardMessage, ()> for ArtboardMessageHandler {
|
||||
impl MessageHandler<ArtboardMessage, &FontCache> for ArtboardMessageHandler {
|
||||
#[remain::check]
|
||||
fn process_action(&mut self, message: ArtboardMessage, _: (), responses: &mut VecDeque<Message>) {
|
||||
fn process_action(&mut self, message: ArtboardMessage, font_cache: &FontCache, responses: &mut VecDeque<Message>) {
|
||||
use ArtboardMessage::*;
|
||||
|
||||
#[remain::sorted]
|
||||
match message {
|
||||
// Sub-messages
|
||||
#[remain::unsorted]
|
||||
DispatchOperation(operation) => match self.artboards_graphene_document.handle_operation(*operation) {
|
||||
DispatchOperation(operation) => match self.artboards_graphene_document.handle_operation(*operation, font_cache) {
|
||||
Ok(Some(document_responses)) => {
|
||||
for response in document_responses {
|
||||
match &response {
|
||||
@@ -86,7 +87,7 @@ impl MessageHandler<ArtboardMessage, ()> for ArtboardMessageHandler {
|
||||
} else {
|
||||
responses.push_back(
|
||||
FrontendMessage::UpdateDocumentArtboards {
|
||||
svg: self.artboards_graphene_document.render_root(ViewMode::Normal),
|
||||
svg: self.artboards_graphene_document.render_root(ViewMode::Normal, font_cache),
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
|
||||
@@ -72,18 +72,10 @@ pub enum DocumentMessage {
|
||||
FolderChanged {
|
||||
affected_folder_path: Vec<LayerId>,
|
||||
},
|
||||
FontLoaded {
|
||||
font_file_url: String,
|
||||
data: Vec<u8>,
|
||||
is_default: bool,
|
||||
},
|
||||
GroupSelectedLayers,
|
||||
LayerChanged {
|
||||
affected_layer_path: Vec<LayerId>,
|
||||
},
|
||||
LoadFont {
|
||||
font_file_url: String,
|
||||
},
|
||||
MoveSelectedLayersTo {
|
||||
folder_path: Vec<LayerId>,
|
||||
insert_index: isize,
|
||||
|
||||
@@ -23,6 +23,7 @@ use graphene::layers::blend_mode::BlendMode;
|
||||
use graphene::layers::folder_layer::FolderLayer;
|
||||
use graphene::layers::layer_info::LayerDataType;
|
||||
use graphene::layers::style::{Fill, ViewMode};
|
||||
use graphene::layers::text_layer::FontCache;
|
||||
use graphene::{DocumentError, DocumentResponse, LayerId, Operation as DocumentOperation};
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
@@ -136,12 +137,12 @@ impl DocumentMessageHandler {
|
||||
&& self.name.starts_with(DEFAULT_DOCUMENT_NAME)
|
||||
}
|
||||
|
||||
fn select_layer(&mut self, path: &[LayerId]) -> Option<Message> {
|
||||
fn select_layer(&mut self, path: &[LayerId], font_cache: &FontCache) -> Option<Message> {
|
||||
println!("Select_layer fail: {:?}", self.all_layers_sorted());
|
||||
|
||||
if let Some(layer) = self.layer_metadata.get_mut(path) {
|
||||
layer.selected = true;
|
||||
let data = self.layer_panel_entry(path.to_vec()).ok()?;
|
||||
let data = self.layer_panel_entry(path.to_vec(), font_cache).ok()?;
|
||||
(!path.is_empty()).then(|| FrontendMessage::UpdateDocumentLayerDetails { data }.into())
|
||||
} else {
|
||||
log::warn!("Tried to select non existing layer {:?}", path);
|
||||
@@ -149,17 +150,17 @@ impl DocumentMessageHandler {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn selected_visible_layers_bounding_box(&self) -> Option<[DVec2; 2]> {
|
||||
pub fn selected_visible_layers_bounding_box(&self, font_cache: &FontCache) -> Option<[DVec2; 2]> {
|
||||
let paths = self.selected_visible_layers();
|
||||
self.graphene_document.combined_viewport_bounding_box(paths)
|
||||
self.graphene_document.combined_viewport_bounding_box(paths, font_cache)
|
||||
}
|
||||
|
||||
pub fn artboard_bounding_box_and_transform(&self, path: &[LayerId]) -> Option<([DVec2; 2], DAffine2)> {
|
||||
self.artboard_message_handler.artboards_graphene_document.bounding_box_and_transform(path).unwrap_or(None)
|
||||
pub fn artboard_bounding_box_and_transform(&self, path: &[LayerId], font_cache: &FontCache) -> Option<([DVec2; 2], DAffine2)> {
|
||||
self.artboard_message_handler.artboards_graphene_document.bounding_box_and_transform(path, font_cache).unwrap_or(None)
|
||||
}
|
||||
|
||||
/// Create a new vector shape representation with the underlying kurbo data, VectorManipulatorShape
|
||||
pub fn selected_visible_layers_vector_shapes(&self, responses: &mut VecDeque<Message>) -> Vec<VectorShape> {
|
||||
pub fn selected_visible_layers_vector_shapes(&self, responses: &mut VecDeque<Message>, font_cache: &FontCache) -> Vec<VectorShape> {
|
||||
let shapes = self.selected_layers().filter_map(|path_to_shape| {
|
||||
let viewport_transform = self.graphene_document.generate_transform_relative_to_viewport(path_to_shape).ok()?;
|
||||
let layer = self.graphene_document.layer(path_to_shape);
|
||||
@@ -172,13 +173,7 @@ impl DocumentMessageHandler {
|
||||
// TODO: Create VectorManipulatorShape when creating a kurbo shape as a stopgap, rather than on each new selection
|
||||
match &layer.ok()?.data {
|
||||
LayerDataType::Shape(shape) => Some(VectorShape::new(path_to_shape.to_vec(), viewport_transform, &shape.path, shape.closed, responses)),
|
||||
LayerDataType::Text(text) => Some(VectorShape::new(
|
||||
path_to_shape.to_vec(),
|
||||
viewport_transform,
|
||||
&text.to_bez_path_nonmut(&self.graphene_document.font_cache),
|
||||
true,
|
||||
responses,
|
||||
)),
|
||||
LayerDataType::Text(text) => Some(VectorShape::new(path_to_shape.to_vec(), viewport_transform, &text.to_bez_path_nonmut(font_cache), true, responses)),
|
||||
_ => None,
|
||||
}
|
||||
});
|
||||
@@ -230,16 +225,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>) -> impl Iterator<Item = [DVec2; 2]> + 'a {
|
||||
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 {
|
||||
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.graphene_document.viewport_bounding_box(path).ok()?)
|
||||
.filter_map(|path| self.graphene_document.viewport_bounding_box(path, font_cache).ok()?)
|
||||
.chain(
|
||||
self.artboard_message_handler
|
||||
.artboard_ids
|
||||
.iter()
|
||||
.filter(move |&&id| Some(id) != ignore_artboard)
|
||||
.filter_map(|&path| self.artboard_message_handler.artboards_graphene_document.viewport_bounding_box(&[path]).ok()?),
|
||||
.filter_map(|&path| self.artboard_message_handler.artboards_graphene_document.viewport_bounding_box(&[path], font_cache).ok()?),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -431,26 +426,26 @@ 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>) -> Result<LayerPanelEntry, EditorError> {
|
||||
pub fn layer_panel_entry(&mut self, path: Vec<LayerId>, font_cache: &FontCache) -> 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.graphene_document.layer(&path)?;
|
||||
let entry = layer_panel_entry(&data, self.graphene_document.multiply_transforms(&path)?, layer, path, &self.graphene_document.font_cache);
|
||||
let entry = layer_panel_entry(&data, self.graphene_document.multiply_transforms(&path)?, layer, path, font_cache);
|
||||
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]) -> Result<Vec<LayerPanelEntry>, EditorError> {
|
||||
pub fn layer_panel(&mut self, path: &[LayerId], font_cache: &FontCache) -> Result<Vec<LayerPanelEntry>, EditorError> {
|
||||
let folder = self.graphene_document.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)).collect();
|
||||
let entries = paths.iter().rev().filter_map(|path| self.layer_panel_entry_from_path(path, font_cache)).collect();
|
||||
Ok(entries)
|
||||
}
|
||||
|
||||
pub fn layer_panel_entry_from_path(&self, path: &[LayerId]) -> Option<LayerPanelEntry> {
|
||||
pub fn layer_panel_entry_from_path(&self, path: &[LayerId], font_cache: &FontCache) -> Option<LayerPanelEntry> {
|
||||
let layer_metadata = self.layer_metadata(path);
|
||||
let transform = self
|
||||
.graphene_document
|
||||
@@ -458,7 +453,7 @@ impl DocumentMessageHandler {
|
||||
.ok()?;
|
||||
let layer = self.graphene_document.layer(path).ok()?;
|
||||
|
||||
Some(layer_panel_entry(layer_metadata, transform, layer, path.to_vec(), &self.graphene_document.font_cache))
|
||||
Some(layer_panel_entry(layer_metadata, transform, layer, path.to_vec(), font_cache))
|
||||
}
|
||||
|
||||
/// 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).
|
||||
@@ -473,16 +468,16 @@ impl DocumentMessageHandler {
|
||||
}
|
||||
|
||||
/// Calculates the bounding box of all layers in the document
|
||||
pub fn all_layer_bounds(&self) -> Option<[DVec2; 2]> {
|
||||
self.graphene_document.viewport_bounding_box(&[]).ok().flatten()
|
||||
pub fn all_layer_bounds(&self, font_cache: &FontCache) -> Option<[DVec2; 2]> {
|
||||
self.graphene_document.viewport_bounding_box(&[], font_cache).ok().flatten()
|
||||
}
|
||||
|
||||
/// Calculates the document bounds used for scrolling and centring (the layer bounds or the artboard (if applicable))
|
||||
pub fn document_bounds(&self) -> Option<[DVec2; 2]> {
|
||||
pub fn document_bounds(&self, font_cache: &FontCache) -> Option<[DVec2; 2]> {
|
||||
if self.artboard_message_handler.is_infinite_canvas() {
|
||||
self.all_layer_bounds()
|
||||
self.all_layer_bounds(font_cache)
|
||||
} else {
|
||||
self.artboard_message_handler.artboards_graphene_document.viewport_bounding_box(&[]).ok().flatten()
|
||||
self.artboard_message_handler.artboards_graphene_document.viewport_bounding_box(&[], font_cache).ok().flatten()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -523,13 +518,6 @@ impl DocumentMessageHandler {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Loading the default font should happen on a per-application basis, not a per-document basis
|
||||
pub fn load_default_font(&self, responses: &mut VecDeque<Message>) {
|
||||
if !self.graphene_document.font_cache.has_default() {
|
||||
responses.push_back(FrontendMessage::TriggerFontLoadDefault.into())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_document_widgets(&self, responses: &mut VecDeque<Message>) {
|
||||
let document_bar_layout = WidgetLayout::new(vec![LayoutRow::Row {
|
||||
widgets: vec![
|
||||
@@ -719,7 +707,7 @@ impl DocumentMessageHandler {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn update_layer_tree_options_bar_widgets(&self, responses: &mut VecDeque<Message>) {
|
||||
pub fn update_layer_tree_options_bar_widgets(&self, responses: &mut VecDeque<Message>, font_cache: &FontCache) {
|
||||
let mut opacity = None;
|
||||
let mut opacity_is_mixed = false;
|
||||
|
||||
@@ -728,7 +716,7 @@ impl DocumentMessageHandler {
|
||||
|
||||
self.layer_metadata
|
||||
.keys()
|
||||
.filter_map(|path| self.layer_panel_entry_from_path(path))
|
||||
.filter_map(|path| self.layer_panel_entry_from_path(path, font_cache))
|
||||
.filter(|layer_panel_entry| layer_panel_entry.layer_metadata.selected)
|
||||
.flat_map(|layer_panel_entry| self.graphene_document.layer(layer_panel_entry.path.as_slice()))
|
||||
.for_each(|layer| {
|
||||
@@ -834,16 +822,16 @@ impl DocumentMessageHandler {
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageHandler<DocumentMessage, &InputPreprocessorMessageHandler> for DocumentMessageHandler {
|
||||
impl MessageHandler<DocumentMessage, (&InputPreprocessorMessageHandler, &FontCache)> for DocumentMessageHandler {
|
||||
#[remain::check]
|
||||
fn process_action(&mut self, message: DocumentMessage, ipp: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
|
||||
fn process_action(&mut self, message: DocumentMessage, (ipp, font_cache): (&InputPreprocessorMessageHandler, &FontCache), responses: &mut VecDeque<Message>) {
|
||||
use DocumentMessage::*;
|
||||
|
||||
#[remain::sorted]
|
||||
match message {
|
||||
// Sub-messages
|
||||
#[remain::unsorted]
|
||||
DispatchOperation(op) => match self.graphene_document.handle_operation(*op) {
|
||||
DispatchOperation(op) => match self.graphene_document.handle_operation(*op, font_cache) {
|
||||
Ok(Some(document_responses)) => {
|
||||
for response in document_responses {
|
||||
match &response {
|
||||
@@ -877,7 +865,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessorMessageHandler> for Docum
|
||||
},
|
||||
#[remain::unsorted]
|
||||
Artboard(message) => {
|
||||
self.artboard_message_handler.process_action(message, (), responses);
|
||||
self.artboard_message_handler.process_action(message, font_cache, responses);
|
||||
}
|
||||
#[remain::unsorted]
|
||||
Movement(message) => {
|
||||
@@ -885,13 +873,13 @@ impl MessageHandler<DocumentMessage, &InputPreprocessorMessageHandler> for Docum
|
||||
}
|
||||
#[remain::unsorted]
|
||||
Overlays(message) => {
|
||||
self.overlays_message_handler.process_action(message, self.overlays_visible, responses);
|
||||
self.overlays_message_handler.process_action(message, (self.overlays_visible, font_cache), responses);
|
||||
// responses.push_back(OverlaysMessage::RenderOverlays.into());
|
||||
}
|
||||
#[remain::unsorted]
|
||||
TransformLayers(message) => {
|
||||
self.transform_layer_handler
|
||||
.process_action(message, (&mut self.layer_metadata, &mut self.graphene_document, ipp), responses);
|
||||
.process_action(message, (&mut self.layer_metadata, &mut self.graphene_document, ipp, font_cache), responses);
|
||||
}
|
||||
#[remain::unsorted]
|
||||
PropertiesPanel(message) => {
|
||||
@@ -900,6 +888,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessorMessageHandler> for Docum
|
||||
PropertiesPanelMessageHandlerData {
|
||||
artwork_document: &self.graphene_document,
|
||||
artboard_document: &self.artboard_message_handler.artboards_graphene_document,
|
||||
font_cache,
|
||||
},
|
||||
responses,
|
||||
);
|
||||
@@ -912,7 +901,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessorMessageHandler> for Docum
|
||||
}
|
||||
AddSelectedLayers { additional_layers } => {
|
||||
for layer_path in &additional_layers {
|
||||
responses.extend(self.select_layer(layer_path));
|
||||
responses.extend(self.select_layer(layer_path, font_cache));
|
||||
}
|
||||
|
||||
let selected_paths: Vec<Vec<u64>> = self.selected_layers().map(|path| path.to_vec()).collect();
|
||||
@@ -932,13 +921,13 @@ impl MessageHandler<DocumentMessage, &InputPreprocessorMessageHandler> for Docum
|
||||
responses.push_back(FolderChanged { affected_folder_path: vec![] }.into());
|
||||
responses.push_back(DocumentMessage::SelectionChanged.into());
|
||||
|
||||
self.update_layer_tree_options_bar_widgets(responses);
|
||||
self.update_layer_tree_options_bar_widgets(responses, font_cache);
|
||||
}
|
||||
AlignSelectedLayers { axis, aggregate } => {
|
||||
self.backup(responses);
|
||||
let (paths, boxes): (Vec<_>, Vec<_>) = self
|
||||
.selected_layers()
|
||||
.filter_map(|path| self.graphene_document.viewport_bounding_box(path).ok()?.map(|b| (path, b)))
|
||||
.filter_map(|path| self.graphene_document.viewport_bounding_box(path, font_cache).ok()?.map(|b| (path, b)))
|
||||
.unzip();
|
||||
|
||||
let axis = match axis {
|
||||
@@ -946,7 +935,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessorMessageHandler> for Docum
|
||||
AlignAxis::Y => DVec2::Y,
|
||||
};
|
||||
let lerp = |bbox: &[DVec2; 2]| bbox[0].lerp(bbox[1], 0.5);
|
||||
if let Some(combined_box) = self.graphene_document.combined_viewport_bounding_box(self.selected_layers()) {
|
||||
if let Some(combined_box) = self.graphene_document.combined_viewport_bounding_box(self.selected_layers(), font_cache) {
|
||||
let aggregated = match aggregate {
|
||||
AlignAggregate::Min => combined_box[0],
|
||||
AlignAggregate::Max => combined_box[1],
|
||||
@@ -1054,13 +1043,13 @@ impl MessageHandler<DocumentMessage, &InputPreprocessorMessageHandler> for Docum
|
||||
|
||||
// Calculates the bounding box of the region to be exported
|
||||
let bbox = match bounds {
|
||||
crate::frontend::utility_types::ExportBounds::AllArtwork => self.all_layer_bounds(),
|
||||
crate::frontend::utility_types::ExportBounds::AllArtwork => self.all_layer_bounds(font_cache),
|
||||
crate::frontend::utility_types::ExportBounds::Artboard(id) => self
|
||||
.artboard_message_handler
|
||||
.artboards_graphene_document
|
||||
.layer(&[id])
|
||||
.ok()
|
||||
.and_then(|layer| layer.aabounding_box(&self.graphene_document.font_cache)),
|
||||
.and_then(|layer| layer.aabounding_box(font_cache)),
|
||||
}
|
||||
.unwrap_or_default();
|
||||
let size = bbox[1] - bbox[0];
|
||||
@@ -1071,7 +1060,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessorMessageHandler> for Docum
|
||||
false => file_name + file_suffix,
|
||||
};
|
||||
|
||||
let rendered = self.graphene_document.render_root(self.view_mode);
|
||||
let rendered = self.graphene_document.render_root(self.view_mode, font_cache);
|
||||
let document = format!(
|
||||
r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="{} {} {} {}" width="{}px" height="{}">{}{}</svg>"#,
|
||||
bbox[0].x, bbox[0].y, size.x, size.y, size.x, size.y, "\n", rendered
|
||||
@@ -1094,7 +1083,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessorMessageHandler> for Docum
|
||||
FlipAxis::X => DVec2::new(-1., 1.),
|
||||
FlipAxis::Y => DVec2::new(1., -1.),
|
||||
};
|
||||
if let Some([min, max]) = self.graphene_document.combined_viewport_bounding_box(self.selected_layers()) {
|
||||
if let Some([min, max]) = self.graphene_document.combined_viewport_bounding_box(self.selected_layers(), font_cache) {
|
||||
let center = (max + min) / 2.;
|
||||
let bbox_trans = DAffine2::from_translation(-center);
|
||||
for path in self.selected_layers() {
|
||||
@@ -1111,14 +1100,10 @@ impl MessageHandler<DocumentMessage, &InputPreprocessorMessageHandler> for Docum
|
||||
}
|
||||
}
|
||||
FolderChanged { affected_folder_path } => {
|
||||
let _ = self.graphene_document.render_root(self.view_mode);
|
||||
let _ = self.graphene_document.render_root(self.view_mode, font_cache);
|
||||
let affected_layer_path = affected_folder_path;
|
||||
responses.extend([LayerChanged { affected_layer_path }.into(), DocumentStructureChanged.into()]);
|
||||
}
|
||||
FontLoaded { font_file_url, data, is_default } => {
|
||||
self.graphene_document.font_cache.insert(font_file_url, data, is_default);
|
||||
responses.push_back(DocumentMessage::DirtyRenderDocument.into());
|
||||
}
|
||||
GroupSelectedLayers => {
|
||||
let mut new_folder_path = self.graphene_document.shallowest_common_folder(self.selected_layers()).unwrap_or(&[]).to_vec();
|
||||
|
||||
@@ -1149,16 +1134,11 @@ impl MessageHandler<DocumentMessage, &InputPreprocessorMessageHandler> for Docum
|
||||
);
|
||||
}
|
||||
LayerChanged { affected_layer_path } => {
|
||||
if let Ok(layer_entry) = self.layer_panel_entry(affected_layer_path.clone()) {
|
||||
if let Ok(layer_entry) = self.layer_panel_entry(affected_layer_path.clone(), font_cache) {
|
||||
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);
|
||||
}
|
||||
LoadFont { font_file_url } => {
|
||||
if !self.graphene_document.font_cache.loaded_font(&font_file_url) {
|
||||
responses.push_front(FrontendMessage::TriggerFontLoad { font_file_url }.into());
|
||||
}
|
||||
self.update_layer_tree_options_bar_widgets(responses, font_cache);
|
||||
}
|
||||
MoveSelectedLayersTo {
|
||||
folder_path,
|
||||
@@ -1240,7 +1220,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessorMessageHandler> for Docum
|
||||
RenderDocument => {
|
||||
responses.push_back(
|
||||
FrontendMessage::UpdateDocumentArtwork {
|
||||
svg: self.graphene_document.render_root(self.view_mode),
|
||||
svg: self.graphene_document.render_root(self.view_mode, font_cache),
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
@@ -1250,7 +1230,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessorMessageHandler> for Docum
|
||||
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().unwrap_or([viewport_mid; 2]);
|
||||
let [bounds1, bounds2] = self.document_bounds(font_cache).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);
|
||||
@@ -1423,7 +1403,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessorMessageHandler> for Docum
|
||||
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) {
|
||||
if let Some(layer) = self.layer_panel_entry_from_path(&layer_path, font_cache) {
|
||||
// Only save the history state if the name actually changed to something different
|
||||
if layer.name != name {
|
||||
self.backup(responses);
|
||||
@@ -1532,7 +1512,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessorMessageHandler> for Docum
|
||||
self.layer_metadata.insert(layer_path, layer_metadata);
|
||||
}
|
||||
ZoomCanvasToFitAll => {
|
||||
if let Some(bounds) = self.document_bounds() {
|
||||
if let Some(bounds) = self.document_bounds(font_cache) {
|
||||
responses.push_back(
|
||||
MovementMessage::FitViewportToBounds {
|
||||
bounds,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use graphene::document::FontCache;
|
||||
use graphene::layers::layer_info::{Layer, LayerData, LayerDataType};
|
||||
use graphene::layers::style::ViewMode;
|
||||
use graphene::layers::text_layer::FontCache;
|
||||
use graphene::LayerId;
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
@@ -8,7 +8,7 @@ use serde::ser::SerializeStruct;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Copy)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Copy)]
|
||||
pub struct LayerMetadata {
|
||||
pub selected: bool,
|
||||
pub expanded: bool,
|
||||
@@ -54,7 +54,7 @@ pub fn layer_panel_entry(layer_metadata: &LayerMetadata, transform: DAffine2, la
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq)]
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
|
||||
pub struct RawBuffer(Vec<u8>);
|
||||
|
||||
impl From<Vec<u64>> for RawBuffer {
|
||||
@@ -81,7 +81,7 @@ impl Serialize for RawBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct LayerPanelEntry {
|
||||
pub name: String,
|
||||
pub visible: bool,
|
||||
|
||||
@@ -2,22 +2,23 @@ use crate::message_prelude::*;
|
||||
|
||||
use graphene::document::Document as GrapheneDocument;
|
||||
use graphene::layers::style::ViewMode;
|
||||
use graphene::layers::text_layer::FontCache;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct OverlaysMessageHandler {
|
||||
pub overlays_graphene_document: GrapheneDocument,
|
||||
}
|
||||
|
||||
impl MessageHandler<OverlaysMessage, bool> for OverlaysMessageHandler {
|
||||
impl MessageHandler<OverlaysMessage, (bool, &FontCache)> for OverlaysMessageHandler {
|
||||
#[remain::check]
|
||||
fn process_action(&mut self, message: OverlaysMessage, overlays_visible: bool, responses: &mut VecDeque<Message>) {
|
||||
fn process_action(&mut self, message: OverlaysMessage, (overlays_visible, font_cache): (bool, &FontCache), responses: &mut VecDeque<Message>) {
|
||||
use OverlaysMessage::*;
|
||||
|
||||
#[remain::sorted]
|
||||
match message {
|
||||
// Sub-messages
|
||||
#[remain::unsorted]
|
||||
DispatchOperation(operation) => match self.overlays_graphene_document.handle_operation(*operation) {
|
||||
DispatchOperation(operation) => match self.overlays_graphene_document.handle_operation(*operation, font_cache) {
|
||||
Ok(_) => responses.push_back(OverlaysMessage::Rerender.into()),
|
||||
Err(e) => log::error!("OverlaysError: {:?}", e),
|
||||
},
|
||||
@@ -30,7 +31,7 @@ impl MessageHandler<OverlaysMessage, bool> for OverlaysMessageHandler {
|
||||
responses.push_back(
|
||||
FrontendMessage::UpdateDocumentOverlays {
|
||||
svg: if overlays_visible {
|
||||
self.overlays_graphene_document.render_root(ViewMode::Normal)
|
||||
self.overlays_graphene_document.render_root(ViewMode::Normal, font_cache)
|
||||
} else {
|
||||
String::from("")
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use super::clipboards::Clipboard;
|
||||
use crate::message_prelude::*;
|
||||
|
||||
use graphene::layers::text_layer::Font;
|
||||
use graphene::LayerId;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -33,6 +34,17 @@ pub enum PortfolioMessage {
|
||||
Cut {
|
||||
clipboard: Clipboard,
|
||||
},
|
||||
FontLoaded {
|
||||
font_family: String,
|
||||
font_style: String,
|
||||
preview_url: String,
|
||||
data: Vec<u8>,
|
||||
is_default: bool,
|
||||
},
|
||||
LoadFont {
|
||||
font: Font,
|
||||
is_default: bool,
|
||||
},
|
||||
NewDocument,
|
||||
NewDocumentWithName {
|
||||
name: String,
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::layout::layout_message::LayoutTarget;
|
||||
use crate::layout::widgets::PropertyHolder;
|
||||
use crate::{dialog, message_prelude::*};
|
||||
|
||||
use graphene::layers::text_layer::{Font, FontCache};
|
||||
use graphene::Operation as DocumentOperation;
|
||||
|
||||
use log::warn;
|
||||
@@ -18,6 +19,7 @@ pub struct PortfolioMessageHandler {
|
||||
document_ids: Vec<u64>,
|
||||
active_document_id: u64,
|
||||
copy_buffer: [Vec<CopyBufferEntry>; INTERNAL_CLIPBOARD_COUNT as usize],
|
||||
font_cache: FontCache,
|
||||
}
|
||||
|
||||
impl PortfolioMessageHandler {
|
||||
@@ -72,15 +74,13 @@ impl PortfolioMessageHandler {
|
||||
new_document
|
||||
.layer_metadata
|
||||
.keys()
|
||||
.filter_map(|path| new_document.layer_panel_entry_from_path(path))
|
||||
.filter_map(|path| new_document.layer_panel_entry_from_path(path, &self.font_cache))
|
||||
.map(|entry| FrontendMessage::UpdateDocumentLayerDetails { data: entry }.into())
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
new_document.update_layer_tree_options_bar_widgets(responses);
|
||||
new_document.update_layer_tree_options_bar_widgets(responses, &self.font_cache);
|
||||
|
||||
new_document.load_image_data(responses, &new_document.graphene_document.root.data, Vec::new());
|
||||
// TODO: Loading the default font should happen on a per-application basis, not a per-document basis
|
||||
new_document.load_default_font(responses);
|
||||
|
||||
self.documents.insert(document_id, new_document);
|
||||
|
||||
@@ -110,6 +110,10 @@ impl PortfolioMessageHandler {
|
||||
fn document_index(&self, document_id: u64) -> usize {
|
||||
self.document_ids.iter().position(|id| id == &document_id).expect("Active document is missing from document ids")
|
||||
}
|
||||
|
||||
pub fn font_cache(&self) -> &FontCache {
|
||||
&self.font_cache
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PortfolioMessageHandler {
|
||||
@@ -125,6 +129,7 @@ impl Default for PortfolioMessageHandler {
|
||||
document_ids: vec![starting_key],
|
||||
copy_buffer: [EMPTY_VEC; INTERNAL_CLIPBOARD_COUNT as usize],
|
||||
active_document_id: starting_key,
|
||||
font_cache: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,7 +144,7 @@ impl MessageHandler<PortfolioMessage, &InputPreprocessorMessageHandler> for Port
|
||||
match message {
|
||||
// Sub-messages
|
||||
#[remain::unsorted]
|
||||
Document(message) => self.active_document_mut().process_action(message, ipp, responses),
|
||||
Document(message) => self.documents.get_mut(&self.active_document_id).unwrap().process_action(message, (ipp, &self.font_cache), responses),
|
||||
|
||||
// Messages
|
||||
AutoSaveActiveDocument => responses.push_back(PortfolioMessage::AutoSaveDocument { document_id: self.active_document_id }.into()),
|
||||
@@ -263,6 +268,21 @@ impl MessageHandler<PortfolioMessage, &InputPreprocessorMessageHandler> for Port
|
||||
responses.push_back(Copy { clipboard }.into());
|
||||
responses.push_back(DeleteSelectedLayers.into());
|
||||
}
|
||||
FontLoaded {
|
||||
font_family,
|
||||
font_style,
|
||||
preview_url,
|
||||
data,
|
||||
is_default,
|
||||
} => {
|
||||
self.font_cache.insert(Font::new(font_family, font_style), preview_url, data, is_default);
|
||||
responses.push_back(DocumentMessage::DirtyRenderDocument.into());
|
||||
}
|
||||
LoadFont { font, is_default } => {
|
||||
if !self.font_cache.loaded_font(&font) {
|
||||
responses.push_front(FrontendMessage::TriggerFontLoad { font, is_default }.into());
|
||||
}
|
||||
}
|
||||
NewDocument => {
|
||||
let name = self.generate_new_document_name();
|
||||
let new_document = DocumentMessageHandler::with_name(name, ipp);
|
||||
|
||||
@@ -9,43 +9,20 @@ use serde::{Deserialize, Serialize};
|
||||
#[impl_message(Message, DocumentMessage, PropertiesPanel)]
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum PropertiesPanelMessage {
|
||||
CheckSelectedWasDeleted {
|
||||
path: Vec<LayerId>,
|
||||
},
|
||||
CheckSelectedWasUpdated {
|
||||
path: Vec<LayerId>,
|
||||
},
|
||||
CheckSelectedWasDeleted { path: Vec<LayerId> },
|
||||
CheckSelectedWasUpdated { path: Vec<LayerId> },
|
||||
ClearSelection,
|
||||
ModifyFill {
|
||||
fill: Fill,
|
||||
},
|
||||
ModifyFont {
|
||||
font_family: String,
|
||||
font_style: String,
|
||||
font_file: Option<String>,
|
||||
size: f64,
|
||||
},
|
||||
ModifyName {
|
||||
name: String,
|
||||
},
|
||||
ModifyStroke {
|
||||
stroke: Stroke,
|
||||
},
|
||||
ModifyText {
|
||||
new_text: String,
|
||||
},
|
||||
ModifyTransform {
|
||||
value: f64,
|
||||
transform_op: TransformOp,
|
||||
},
|
||||
ModifyFill { fill: Fill },
|
||||
ModifyFont { font_family: String, font_style: String, size: f64 },
|
||||
ModifyName { name: String },
|
||||
ModifyStroke { stroke: Stroke },
|
||||
ModifyText { new_text: String },
|
||||
ModifyTransform { value: f64, transform_op: TransformOp },
|
||||
ResendActiveProperties,
|
||||
SetActiveLayers {
|
||||
paths: Vec<Vec<LayerId>>,
|
||||
document: TargetDocument,
|
||||
},
|
||||
SetActiveLayers { paths: Vec<Vec<LayerId>>, document: TargetDocument },
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
pub enum TransformOp {
|
||||
X,
|
||||
Y,
|
||||
|
||||
@@ -9,10 +9,10 @@ use crate::layout::widgets::{
|
||||
use crate::message_prelude::*;
|
||||
|
||||
use graphene::color::Color;
|
||||
use graphene::document::{Document as GrapheneDocument, FontCache};
|
||||
use graphene::document::Document as GrapheneDocument;
|
||||
use graphene::layers::layer_info::{Layer, LayerDataType};
|
||||
use graphene::layers::style::{Fill, Gradient, GradientType, LineCap, LineJoin, Stroke};
|
||||
use graphene::layers::text_layer::TextLayer;
|
||||
use graphene::layers::text_layer::{FontCache, TextLayer};
|
||||
use graphene::{LayerId, Operation};
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
@@ -111,12 +111,17 @@ impl PropertiesPanelMessageHandler {
|
||||
pub struct PropertiesPanelMessageHandlerData<'a> {
|
||||
pub artwork_document: &'a GrapheneDocument,
|
||||
pub artboard_document: &'a GrapheneDocument,
|
||||
pub font_cache: &'a FontCache,
|
||||
}
|
||||
|
||||
impl<'a> MessageHandler<PropertiesPanelMessage, PropertiesPanelMessageHandlerData<'a>> for PropertiesPanelMessageHandler {
|
||||
#[remain::check]
|
||||
fn process_action(&mut self, message: PropertiesPanelMessage, data: PropertiesPanelMessageHandlerData, responses: &mut VecDeque<Message>) {
|
||||
let PropertiesPanelMessageHandlerData { artwork_document, artboard_document } = data;
|
||||
let PropertiesPanelMessageHandlerData {
|
||||
artwork_document,
|
||||
artboard_document,
|
||||
font_cache,
|
||||
} = data;
|
||||
let get_document = |document_selector: TargetDocument| match document_selector {
|
||||
TargetDocument::Artboard => artboard_document,
|
||||
TargetDocument::Artwork => artwork_document,
|
||||
@@ -150,21 +155,10 @@ impl<'a> MessageHandler<PropertiesPanelMessage, PropertiesPanelMessageHandlerDat
|
||||
);
|
||||
self.active_selection = None;
|
||||
}
|
||||
ModifyFont {
|
||||
font_family,
|
||||
font_style,
|
||||
font_file,
|
||||
size,
|
||||
} => {
|
||||
ModifyFont { font_family, font_style, size } => {
|
||||
let (path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
||||
|
||||
responses.push_back(self.create_document_operation(Operation::ModifyFont {
|
||||
path,
|
||||
font_family,
|
||||
font_style,
|
||||
font_file,
|
||||
size,
|
||||
}));
|
||||
responses.push_back(self.create_document_operation(Operation::ModifyFont { path, font_family, font_style, size }));
|
||||
responses.push_back(ResendActiveProperties.into());
|
||||
}
|
||||
ModifyTransform { value, transform_op } => {
|
||||
@@ -181,8 +175,8 @@ impl<'a> MessageHandler<PropertiesPanelMessage, PropertiesPanelMessageHandlerDat
|
||||
};
|
||||
|
||||
let scale = match transform_op {
|
||||
Width => layer.bounding_transform(&get_document(*target_document).font_cache).scale_x() / layer.transform.scale_x(),
|
||||
Height => layer.bounding_transform(&get_document(*target_document).font_cache).scale_y() / layer.transform.scale_y(),
|
||||
Width => layer.bounding_transform(font_cache).scale_x() / layer.transform.scale_x(),
|
||||
Height => layer.bounding_transform(font_cache).scale_y() / layer.transform.scale_y(),
|
||||
_ => 1.,
|
||||
};
|
||||
|
||||
@@ -235,8 +229,8 @@ impl<'a> MessageHandler<PropertiesPanelMessage, PropertiesPanelMessageHandlerDat
|
||||
if let Some((path, target_document)) = self.active_selection.clone() {
|
||||
let layer = get_document(target_document).layer(&path).unwrap();
|
||||
match target_document {
|
||||
TargetDocument::Artboard => register_artboard_layer_properties(layer, responses, &get_document(target_document).font_cache),
|
||||
TargetDocument::Artwork => register_artwork_layer_properties(layer, responses, &get_document(target_document).font_cache),
|
||||
TargetDocument::Artboard => register_artboard_layer_properties(layer, responses, font_cache),
|
||||
TargetDocument::Artwork => register_artwork_layer_properties(layer, responses, font_cache),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -677,9 +671,7 @@ fn node_section_transform(layer: &Layer, font_cache: &FontCache) -> LayoutRow {
|
||||
}
|
||||
|
||||
fn node_section_font(layer: &TextLayer) -> LayoutRow {
|
||||
let font_family = layer.font_family.clone();
|
||||
let font_style = layer.font_style.clone();
|
||||
let font_file = layer.font_file.clone();
|
||||
let font = layer.font.clone();
|
||||
let size = layer.size;
|
||||
LayoutRow::Section {
|
||||
name: "Font".into(),
|
||||
@@ -712,14 +704,12 @@ fn node_section_font(layer: &TextLayer) -> LayoutRow {
|
||||
})),
|
||||
WidgetHolder::new(Widget::FontInput(FontInput {
|
||||
is_style_picker: false,
|
||||
font_family: layer.font_family.clone(),
|
||||
font_style: layer.font_style.clone(),
|
||||
font_file_url: String::new(),
|
||||
font_family: layer.font.font_family.clone(),
|
||||
font_style: layer.font.font_style.clone(),
|
||||
on_update: WidgetCallback::new(move |font_input: &FontInput| {
|
||||
PropertiesPanelMessage::ModifyFont {
|
||||
font_family: font_input.font_family.clone(),
|
||||
font_style: font_input.font_style.clone(),
|
||||
font_file: Some(font_input.font_file_url.clone()),
|
||||
size,
|
||||
}
|
||||
.into()
|
||||
@@ -739,14 +729,12 @@ fn node_section_font(layer: &TextLayer) -> LayoutRow {
|
||||
})),
|
||||
WidgetHolder::new(Widget::FontInput(FontInput {
|
||||
is_style_picker: true,
|
||||
font_family: layer.font_family.clone(),
|
||||
font_style: layer.font_style.clone(),
|
||||
font_file_url: String::new(),
|
||||
font_family: layer.font.font_family.clone(),
|
||||
font_style: layer.font.font_style.clone(),
|
||||
on_update: WidgetCallback::new(move |font_input: &FontInput| {
|
||||
PropertiesPanelMessage::ModifyFont {
|
||||
font_family: font_input.font_family.clone(),
|
||||
font_style: font_input.font_style.clone(),
|
||||
font_file: Some(font_input.font_file_url.clone()),
|
||||
size,
|
||||
}
|
||||
.into()
|
||||
@@ -770,9 +758,8 @@ fn node_section_font(layer: &TextLayer) -> LayoutRow {
|
||||
unit: " px".into(),
|
||||
on_update: WidgetCallback::new(move |number_input: &NumberInput| {
|
||||
PropertiesPanelMessage::ModifyFont {
|
||||
font_family: font_family.clone(),
|
||||
font_style: font_style.clone(),
|
||||
font_file: font_file.clone(),
|
||||
font_family: font.font_family.clone(),
|
||||
font_style: font.font_style.clone(),
|
||||
size: number_input.value.unwrap(),
|
||||
}
|
||||
.into()
|
||||
|
||||
@@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
#[remain::sorted]
|
||||
#[impl_message(Message, DocumentMessage, TransformLayers)]
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum TransformLayerMessage {
|
||||
ApplyTransformOperation,
|
||||
BeginGrab,
|
||||
|
||||
@@ -8,6 +8,7 @@ use crate::message_prelude::*;
|
||||
use graphene::document::Document;
|
||||
|
||||
use glam::DVec2;
|
||||
use graphene::layers::text_layer::FontCache;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq)]
|
||||
@@ -25,18 +26,12 @@ pub struct TransformLayerMessageHandler {
|
||||
pivot: DVec2,
|
||||
}
|
||||
|
||||
impl MessageHandler<TransformLayerMessage, (&mut HashMap<Vec<LayerId>, LayerMetadata>, &mut Document, &InputPreprocessorMessageHandler)> for TransformLayerMessageHandler {
|
||||
type TransformData<'a> = (&'a mut HashMap<Vec<LayerId>, LayerMetadata>, &'a mut Document, &'a InputPreprocessorMessageHandler, &'a FontCache);
|
||||
impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformLayerMessageHandler {
|
||||
#[remain::check]
|
||||
fn process_action(
|
||||
&mut self,
|
||||
message: TransformLayerMessage,
|
||||
data: (&mut HashMap<Vec<LayerId>, LayerMetadata>, &mut Document, &InputPreprocessorMessageHandler),
|
||||
responses: &mut VecDeque<Message>,
|
||||
) {
|
||||
fn process_action(&mut self, message: TransformLayerMessage, (layer_metadata, document, ipp, font_cache): TransformData, responses: &mut VecDeque<Message>) {
|
||||
use TransformLayerMessage::*;
|
||||
|
||||
let (layer_metadata, document, ipp) = data;
|
||||
|
||||
let selected_layers = layer_metadata.iter().filter_map(|(layer_path, data)| data.selected.then(|| layer_path)).collect::<Vec<_>>();
|
||||
let mut selected = Selected::new(&mut self.original_transforms, &mut self.pivot, &selected_layers, responses, document);
|
||||
|
||||
@@ -45,7 +40,7 @@ impl MessageHandler<TransformLayerMessage, (&mut HashMap<Vec<LayerId>, LayerMeta
|
||||
selected.revert_operation();
|
||||
typing.clear();
|
||||
} else {
|
||||
*selected.pivot = selected.calculate_pivot(&document.font_cache);
|
||||
*selected.pivot = selected.calculate_pivot(font_cache);
|
||||
}
|
||||
|
||||
*mouse_position = ipp.mouse.position;
|
||||
@@ -128,7 +123,7 @@ impl MessageHandler<TransformLayerMessage, (&mut HashMap<Vec<LayerId>, LayerMeta
|
||||
self.transform_operation.apply_transform_operation(&mut selected, self.snap);
|
||||
}
|
||||
TransformOperation::Rotating(rotation) => {
|
||||
let selected_pivot = selected.calculate_pivot(&document.font_cache);
|
||||
let selected_pivot = selected.calculate_pivot(font_cache);
|
||||
let angle = {
|
||||
let start_offset = self.mouse_position - selected_pivot;
|
||||
let end_offset = ipp.mouse.position - selected_pivot;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use crate::consts::{ROTATE_SNAP_ANGLE, SCALE_SNAP_INTERVAL};
|
||||
use crate::message_prelude::*;
|
||||
|
||||
use graphene::document::{Document, FontCache};
|
||||
use graphene::document::Document;
|
||||
use graphene::layers::text_layer::FontCache;
|
||||
use graphene::Operation as DocumentOperation;
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
@@ -9,7 +10,7 @@ use std::collections::{HashMap, VecDeque};
|
||||
|
||||
pub type OriginalTransforms = HashMap<Vec<LayerId>, DAffine2>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Copy)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||
pub enum Axis {
|
||||
Both,
|
||||
X,
|
||||
@@ -270,7 +271,7 @@ impl<'a> Selected<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
pub struct Typing {
|
||||
pub digits: Vec<u8>,
|
||||
pub contains_decimal: bool,
|
||||
|
||||
@@ -8,19 +8,19 @@ use std::fmt;
|
||||
|
||||
pub type DocumentSave = (GrapheneDocument, HashMap<Vec<LayerId>, LayerMetadata>);
|
||||
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, Hash)]
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize, Hash)]
|
||||
pub enum FlipAxis {
|
||||
X,
|
||||
Y,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, Hash)]
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize, Hash)]
|
||||
pub enum AlignAxis {
|
||||
X,
|
||||
Y,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, Hash)]
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize, Hash)]
|
||||
pub enum AlignAggregate {
|
||||
Min,
|
||||
Max,
|
||||
@@ -28,13 +28,13 @@ pub enum AlignAggregate {
|
||||
Average,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
pub enum TargetDocument {
|
||||
Artboard,
|
||||
Artwork,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
pub enum DocumentMode {
|
||||
DesignMode,
|
||||
SelectMode,
|
||||
|
||||
Reference in New Issue
Block a user