mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-19 02:48:12 +08:00
Move layouts definitions to backend and fix Firefox overlay scrollbars (#647)
* Fix two-axis scrollbars in scrollable regions on Firefox * Move Document Mode dropdown to the backend; and related code cleanup * Port the Layer Tree options bar layout to the backend * Port the tool shelf to the backend * Clean up initialization and wasm wrapper * Fix crash * Fix missing document bar * Remove unused functions in api.rs * Code review * Tool initalisation * Remove some frontend functions * Initalise -> Init so en-US/GB doesn't have to matter :) * Remove blend_mode and opacity from LayerPanelEntry Co-authored-by: 0hypercube <0hypercube@gmail.com>
This commit is contained in:
@@ -1,16 +1,17 @@
|
||||
use super::clipboards::Clipboard;
|
||||
use super::layer_panel::{layer_panel_entry, LayerDataTypeDiscriminant, LayerMetadata, LayerPanelEntry, RawBuffer};
|
||||
use super::properties_panel_message_handler::PropertiesPanelMessageHandlerData;
|
||||
use super::utility_types::TargetDocument;
|
||||
use super::utility_types::{AlignAggregate, AlignAxis, DocumentSave, FlipAxis};
|
||||
use super::utility_types::{DocumentMode, TargetDocument};
|
||||
use super::{vectorize_layer_metadata, PropertiesPanelMessageHandler};
|
||||
use super::{ArtboardMessageHandler, MovementMessageHandler, OverlaysMessageHandler, TransformLayerMessageHandler};
|
||||
use crate::consts::{ASYMPTOTIC_EFFECT, DEFAULT_DOCUMENT_NAME, FILE_SAVE_SUFFIX, GRAPHITE_DOCUMENT_VERSION, SCALE_EFFECT, SCROLLBAR_SPACING, VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR};
|
||||
use crate::frontend::utility_types::{FileType, FrontendImageData};
|
||||
use crate::input::InputPreprocessorMessageHandler;
|
||||
use crate::layout::layout_message::LayoutTarget;
|
||||
use crate::layout::widgets::{
|
||||
IconButton, LayoutRow, NumberInput, NumberInputIncrementBehavior, OptionalInput, PopoverButton, PropertyHolder, RadioEntryData, RadioInput, Separator, SeparatorDirection, SeparatorType, Widget,
|
||||
WidgetCallback, WidgetHolder, WidgetLayout,
|
||||
DropdownEntryData, DropdownInput, IconButton, LayoutRow, NumberInput, NumberInputIncrementBehavior, OptionalInput, PopoverButton, RadioEntryData, RadioInput, Separator, SeparatorDirection,
|
||||
SeparatorType, Widget, WidgetCallback, WidgetHolder, WidgetLayout,
|
||||
};
|
||||
use crate::message_prelude::*;
|
||||
use crate::viewport_tools::vector_editor::vector_shape::VectorShape;
|
||||
@@ -18,6 +19,7 @@ use crate::EditorError;
|
||||
|
||||
use graphene::color::Color;
|
||||
use graphene::document::Document as GrapheneDocument;
|
||||
use graphene::layers::blend_mode::BlendMode;
|
||||
use graphene::layers::folder_layer::FolderLayer;
|
||||
use graphene::layers::layer_info::LayerDataType;
|
||||
use graphene::layers::style::{Fill, ViewMode};
|
||||
@@ -32,15 +34,24 @@ use std::collections::VecDeque;
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct DocumentMessageHandler {
|
||||
pub graphene_document: GrapheneDocument,
|
||||
pub saved_document_identifier: u64,
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
|
||||
pub document_mode: DocumentMode,
|
||||
pub view_mode: ViewMode,
|
||||
pub snapping_enabled: bool,
|
||||
pub overlays_visible: bool,
|
||||
|
||||
#[serde(skip)]
|
||||
pub document_undo_history: Vec<DocumentSave>,
|
||||
#[serde(skip)]
|
||||
pub document_redo_history: Vec<DocumentSave>,
|
||||
pub saved_document_identifier: u64,
|
||||
pub name: String,
|
||||
|
||||
#[serde(with = "vectorize_layer_metadata")]
|
||||
pub layer_metadata: HashMap<Vec<LayerId>, LayerMetadata>,
|
||||
layer_range_selection_reference: Vec<LayerId>,
|
||||
|
||||
movement_handler: MovementMessageHandler,
|
||||
#[serde(skip)]
|
||||
overlays_message_handler: OverlaysMessageHandler,
|
||||
@@ -48,31 +59,32 @@ pub struct DocumentMessageHandler {
|
||||
#[serde(skip)]
|
||||
transform_layer_handler: TransformLayerMessageHandler,
|
||||
properties_panel_message_handler: PropertiesPanelMessageHandler,
|
||||
pub overlays_visible: bool,
|
||||
pub snapping_enabled: bool,
|
||||
pub view_mode: ViewMode,
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
impl Default for DocumentMessageHandler {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
graphene_document: GrapheneDocument::default(),
|
||||
document_undo_history: Vec::new(),
|
||||
document_redo_history: Vec::new(),
|
||||
saved_document_identifier: 0,
|
||||
name: String::from("Untitled Document"),
|
||||
version: GRAPHITE_DOCUMENT_VERSION.to_string(),
|
||||
|
||||
document_mode: DocumentMode::DesignMode,
|
||||
view_mode: ViewMode::default(),
|
||||
snapping_enabled: true,
|
||||
overlays_visible: true,
|
||||
|
||||
document_undo_history: Vec::new(),
|
||||
document_redo_history: Vec::new(),
|
||||
|
||||
layer_metadata: vec![(vec![], LayerMetadata::new(true))].into_iter().collect(),
|
||||
layer_range_selection_reference: Vec::new(),
|
||||
|
||||
movement_handler: MovementMessageHandler::default(),
|
||||
overlays_message_handler: OverlaysMessageHandler::default(),
|
||||
artboard_message_handler: ArtboardMessageHandler::default(),
|
||||
transform_layer_handler: TransformLayerMessageHandler::default(),
|
||||
properties_panel_message_handler: PropertiesPanelMessageHandler::default(),
|
||||
snapping_enabled: true,
|
||||
overlays_visible: true,
|
||||
view_mode: ViewMode::default(),
|
||||
version: GRAPHITE_DOCUMENT_VERSION.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,7 +142,7 @@ impl DocumentMessageHandler {
|
||||
if let Some(layer) = self.layer_metadata.get_mut(path) {
|
||||
layer.selected = true;
|
||||
let data = self.layer_panel_entry(path.to_vec()).ok()?;
|
||||
(!path.is_empty()).then(|| FrontendMessage::UpdateDocumentLayer { data }.into())
|
||||
(!path.is_empty()).then(|| FrontendMessage::UpdateDocumentLayerDetails { data }.into())
|
||||
} else {
|
||||
log::warn!("Tried to select non existing layer {:?}", path);
|
||||
None
|
||||
@@ -516,11 +528,9 @@ impl DocumentMessageHandler {
|
||||
responses.push_back(FrontendMessage::TriggerFontLoadDefault.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PropertyHolder for DocumentMessageHandler {
|
||||
fn properties(&self) -> WidgetLayout {
|
||||
WidgetLayout::new(vec![LayoutRow::Row {
|
||||
pub fn update_document_widgets(&self, responses: &mut VecDeque<Message>) {
|
||||
let document_bar_layout = WidgetLayout::new(vec![LayoutRow::Row {
|
||||
widgets: vec![
|
||||
WidgetHolder::new(Widget::OptionalInput(OptionalInput {
|
||||
checked: self.snapping_enabled,
|
||||
@@ -600,11 +610,11 @@ impl PropertyHolder for DocumentMessageHandler {
|
||||
})),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
unit: "°".into(),
|
||||
value: self.movement_handler.tilt / (std::f64::consts::PI / 180.),
|
||||
value: Some(self.movement_handler.tilt / (std::f64::consts::PI / 180.)),
|
||||
increment_factor: 15.,
|
||||
on_update: WidgetCallback::new(|number_input: &NumberInput| {
|
||||
MovementMessage::SetCanvasRotation {
|
||||
angle_radians: number_input.value * (std::f64::consts::PI / 180.),
|
||||
angle_radians: number_input.value.unwrap() * (std::f64::consts::PI / 180.),
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
@@ -641,12 +651,12 @@ impl PropertyHolder for DocumentMessageHandler {
|
||||
})),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
unit: "%".into(),
|
||||
value: self.movement_handler.zoom * 100.,
|
||||
value: Some(self.movement_handler.zoom * 100.),
|
||||
min: Some(0.000001),
|
||||
max: Some(1000000.),
|
||||
on_update: WidgetCallback::new(|number_input: &NumberInput| {
|
||||
MovementMessage::SetCanvasZoom {
|
||||
zoom_factor: number_input.value / 100.,
|
||||
zoom_factor: number_input.value.unwrap() / 100.,
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
@@ -656,7 +666,163 @@ impl PropertyHolder for DocumentMessageHandler {
|
||||
..NumberInput::default()
|
||||
})),
|
||||
],
|
||||
}])
|
||||
}]);
|
||||
|
||||
let document_mode_layout = WidgetLayout::new(vec![LayoutRow::Row {
|
||||
widgets: vec![WidgetHolder::new(Widget::DropdownInput(DropdownInput {
|
||||
entries: vec![vec![
|
||||
DropdownEntryData {
|
||||
label: DocumentMode::DesignMode.to_string(),
|
||||
icon: DocumentMode::DesignMode.icon_name(),
|
||||
..DropdownEntryData::default()
|
||||
},
|
||||
DropdownEntryData {
|
||||
label: DocumentMode::SelectMode.to_string(),
|
||||
icon: DocumentMode::SelectMode.icon_name(),
|
||||
on_update: WidgetCallback::new(|_| DialogMessage::RequestComingSoonDialog { issue: Some(330) }.into()),
|
||||
..DropdownEntryData::default()
|
||||
},
|
||||
DropdownEntryData {
|
||||
label: DocumentMode::GuideMode.to_string(),
|
||||
icon: DocumentMode::GuideMode.icon_name(),
|
||||
on_update: WidgetCallback::new(|_| DialogMessage::RequestComingSoonDialog { issue: Some(331) }.into()),
|
||||
..DropdownEntryData::default()
|
||||
},
|
||||
]],
|
||||
selected_index: Some(self.document_mode as u32),
|
||||
draw_icon: true,
|
||||
..Default::default()
|
||||
}))],
|
||||
}]);
|
||||
|
||||
responses.push_back(
|
||||
LayoutMessage::SendLayout {
|
||||
layout: document_bar_layout,
|
||||
layout_target: LayoutTarget::DocumentBar,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
|
||||
responses.push_back(
|
||||
LayoutMessage::SendLayout {
|
||||
layout: document_mode_layout,
|
||||
layout_target: LayoutTarget::DocumentMode,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn update_layer_tree_options_bar_widgets(&self, responses: &mut VecDeque<Message>) {
|
||||
let mut opacity = None;
|
||||
let mut opacity_is_mixed = false;
|
||||
|
||||
let mut blend_mode = None;
|
||||
let mut blend_mode_is_mixed = false;
|
||||
|
||||
self.layer_metadata
|
||||
.keys()
|
||||
.filter_map(|path| self.layer_panel_entry_from_path(path))
|
||||
.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| {
|
||||
match opacity {
|
||||
None => opacity = Some(layer.opacity),
|
||||
Some(opacity) => {
|
||||
if (opacity - layer.opacity).abs() > (1. / 1_000_000.) {
|
||||
opacity_is_mixed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match blend_mode {
|
||||
None => blend_mode = Some(layer.blend_mode),
|
||||
Some(blend_mode) => {
|
||||
if blend_mode != layer.blend_mode {
|
||||
blend_mode_is_mixed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if opacity_is_mixed {
|
||||
opacity = None;
|
||||
}
|
||||
if blend_mode_is_mixed {
|
||||
blend_mode = None;
|
||||
}
|
||||
|
||||
let blend_mode_menu_entries = BlendMode::list_modes_in_groups()
|
||||
.iter()
|
||||
.map(|modes| {
|
||||
modes
|
||||
.iter()
|
||||
.map(|mode| DropdownEntryData {
|
||||
label: mode.to_string(),
|
||||
value: mode.to_string(),
|
||||
on_update: WidgetCallback::new(|_| DocumentMessage::SetBlendModeForSelectedLayers { blend_mode: *mode }.into()),
|
||||
..Default::default()
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
|
||||
let layer_tree_options = WidgetLayout::new(vec![LayoutRow::Row {
|
||||
widgets: vec![
|
||||
WidgetHolder::new(Widget::DropdownInput(DropdownInput {
|
||||
entries: blend_mode_menu_entries,
|
||||
selected_index: blend_mode.map(|blend_mode| blend_mode as u32),
|
||||
disabled: blend_mode.is_none() && !blend_mode_is_mixed,
|
||||
draw_icon: false,
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Related,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
label: "Opacity".into(),
|
||||
unit: "%".into(),
|
||||
display_decimal_places: 2,
|
||||
disabled: opacity.is_none() && !opacity_is_mixed,
|
||||
value: opacity.map(|opacity| opacity * 100.),
|
||||
min: Some(0.),
|
||||
max: Some(100.),
|
||||
on_update: WidgetCallback::new(|number_input: &NumberInput| {
|
||||
if let Some(value) = number_input.value {
|
||||
DocumentMessage::SetOpacityForSelectedLayers { opacity: value / 100. }.into()
|
||||
} else {
|
||||
Message::NoOp
|
||||
}
|
||||
}),
|
||||
..NumberInput::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Section,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "NodeFolder".into(),
|
||||
tooltip: "New Folder (Ctrl+Shift+N)".into(), // TODO: Customize this tooltip for the Mac version of the keyboard shortcut
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| DocumentMessage::CreateEmptyFolder { container_path: vec![] }.into()),
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||
icon: "Trash".into(),
|
||||
tooltip: "Delete Selected (Del)".into(), // TODO: Customize this tooltip for the Mac version of the keyboard shortcut
|
||||
size: 24,
|
||||
on_update: WidgetCallback::new(|_| DocumentMessage::DeleteSelectedLayers.into()),
|
||||
..Default::default()
|
||||
})),
|
||||
],
|
||||
}]);
|
||||
|
||||
responses.push_back(
|
||||
LayoutMessage::SendLayout {
|
||||
layout: layer_tree_options,
|
||||
layout_target: LayoutTarget::LayerTreeOptions,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -757,6 +923,8 @@ impl MessageHandler<DocumentMessage, &InputPreprocessorMessageHandler> for Docum
|
||||
// TODO: Correctly update layer panel in clear_selection instead of here
|
||||
responses.push_back(FolderChanged { affected_folder_path: vec![] }.into());
|
||||
responses.push_back(DocumentMessage::SelectionChanged.into());
|
||||
|
||||
self.update_layer_tree_options_bar_widgets(responses);
|
||||
}
|
||||
AlignSelectedLayers { axis, aggregate } => {
|
||||
self.backup(responses);
|
||||
@@ -796,7 +964,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessorMessageHandler> for Docum
|
||||
}
|
||||
}
|
||||
BooleanOperation(op) => {
|
||||
// convert Vec<&[LayerId]> to Vec<Vec<&LayerId>> because Vec<&[LayerId]> does not implement several traits (Debug, Serialize, Deserialize, ...) required by DocumentOperation enum
|
||||
// Convert Vec<&[LayerId]> to Vec<Vec<&LayerId>> because Vec<&[LayerId]> does not implement several traits (Debug, Serialize, Deserialize, ...) required by DocumentOperation enum
|
||||
responses.push_back(StartTransaction.into());
|
||||
responses.push_back(
|
||||
DocumentOperation::BooleanOperation {
|
||||
@@ -974,9 +1142,10 @@ impl MessageHandler<DocumentMessage, &InputPreprocessorMessageHandler> for Docum
|
||||
}
|
||||
LayerChanged { affected_layer_path } => {
|
||||
if let Ok(layer_entry) = self.layer_panel_entry(affected_layer_path.clone()) {
|
||||
responses.push_back(FrontendMessage::UpdateDocumentLayer { data: layer_entry }.into());
|
||||
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 } => {
|
||||
if !self.graphene_document.font_cache.loaded_font(&font) {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use graphene::document::FontCache;
|
||||
use graphene::layers::blend_mode::BlendMode;
|
||||
use graphene::layers::layer_info::{Layer, LayerData, LayerDataType};
|
||||
use graphene::layers::style::ViewMode;
|
||||
use graphene::LayerId;
|
||||
@@ -48,8 +47,6 @@ pub fn layer_panel_entry(layer_metadata: &LayerMetadata, transform: DAffine2, la
|
||||
LayerPanelEntry {
|
||||
name,
|
||||
visible: layer.visible,
|
||||
blend_mode: layer.blend_mode,
|
||||
opacity: layer.opacity,
|
||||
layer_type: (&layer.data).into(),
|
||||
layer_metadata: *layer_metadata,
|
||||
path,
|
||||
@@ -88,8 +85,6 @@ impl Serialize for RawBuffer {
|
||||
pub struct LayerPanelEntry {
|
||||
pub name: String,
|
||||
pub visible: bool,
|
||||
pub blend_mode: BlendMode,
|
||||
pub opacity: f64,
|
||||
pub layer_type: LayerDataTypeDiscriminant,
|
||||
pub layer_metadata: LayerMetadata,
|
||||
pub path: Vec<LayerId>,
|
||||
|
||||
@@ -153,10 +153,9 @@ impl MessageHandler<MovementMessage, (&Document, &InputPreprocessorMessageHandle
|
||||
self.zoom = 1.
|
||||
}
|
||||
|
||||
responses.push_back(FrontendMessage::UpdateCanvasZoom { factor: self.zoom }.into());
|
||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
||||
responses.push_back(DocumentMessage::DirtyRenderDocumentInOutlineView.into());
|
||||
responses.push_back(PortfolioMessage::UpdateDocumentBar.into());
|
||||
responses.push_back(PortfolioMessage::UpdateDocumentWidgets.into());
|
||||
self.create_document_transform(&ipp.viewport_bounds, responses);
|
||||
}
|
||||
IncreaseCanvasZoom { center_on_mouse } => {
|
||||
@@ -245,15 +244,13 @@ impl MessageHandler<MovementMessage, (&Document, &InputPreprocessorMessageHandle
|
||||
self.tilt = angle_radians;
|
||||
self.create_document_transform(&ipp.viewport_bounds, responses);
|
||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
||||
responses.push_back(FrontendMessage::UpdateCanvasRotation { angle_radians: self.snapped_angle() }.into());
|
||||
responses.push_back(PortfolioMessage::UpdateDocumentBar.into());
|
||||
responses.push_back(PortfolioMessage::UpdateDocumentWidgets.into());
|
||||
}
|
||||
SetCanvasZoom { zoom_factor } => {
|
||||
self.zoom = zoom_factor.clamp(VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_SCALE_MAX);
|
||||
responses.push_back(FrontendMessage::UpdateCanvasZoom { factor: self.snapped_scale() }.into());
|
||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
||||
responses.push_back(DocumentMessage::DirtyRenderDocumentInOutlineView.into());
|
||||
responses.push_back(PortfolioMessage::UpdateDocumentBar.into());
|
||||
responses.push_back(PortfolioMessage::UpdateDocumentWidgets.into());
|
||||
self.create_document_transform(&ipp.viewport_bounds, responses);
|
||||
}
|
||||
TransformCanvasEnd => {
|
||||
|
||||
@@ -67,6 +67,6 @@ pub enum PortfolioMessage {
|
||||
SetActiveDocument {
|
||||
document_id: u64,
|
||||
},
|
||||
UpdateDocumentBar,
|
||||
UpdateDocumentWidgets,
|
||||
UpdateOpenDocumentsList,
|
||||
}
|
||||
|
||||
@@ -73,9 +73,10 @@ impl PortfolioMessageHandler {
|
||||
.layer_metadata
|
||||
.keys()
|
||||
.filter_map(|path| new_document.layer_panel_entry_from_path(path))
|
||||
.map(|entry| FrontendMessage::UpdateDocumentLayer { data: entry }.into())
|
||||
.map(|entry| FrontendMessage::UpdateDocumentLayerDetails { data: entry }.into())
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
new_document.update_layer_tree_options_bar_widgets(responses);
|
||||
|
||||
new_document.load_image_data(responses, &new_document.graphene_document.root.data, Vec::new());
|
||||
new_document.load_default_font(responses);
|
||||
@@ -432,14 +433,14 @@ impl MessageHandler<PortfolioMessage, &InputPreprocessorMessageHandler> for Port
|
||||
responses.push_back(DocumentMessage::LayerChanged { affected_layer_path: layer.clone() }.into());
|
||||
}
|
||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
||||
responses.push_back(PortfolioMessage::UpdateDocumentBar.into());
|
||||
responses.push_back(PortfolioMessage::UpdateDocumentWidgets.into());
|
||||
}
|
||||
SetActiveDocument { document_id } => {
|
||||
self.active_document_id = document_id;
|
||||
}
|
||||
UpdateDocumentBar => {
|
||||
UpdateDocumentWidgets => {
|
||||
let active_document = self.active_document();
|
||||
active_document.register_properties(responses, LayoutTarget::DocumentBar)
|
||||
active_document.update_document_widgets(responses);
|
||||
}
|
||||
UpdateOpenDocumentsList => {
|
||||
// Send the list of document tab names
|
||||
|
||||
@@ -137,14 +137,14 @@ impl<'a> MessageHandler<PropertiesPanelMessage, PropertiesPanelMessageHandlerDat
|
||||
responses.push_back(
|
||||
LayoutMessage::SendLayout {
|
||||
layout: WidgetLayout::new(vec![]),
|
||||
layout_target: LayoutTarget::PropertiesOptionsPanel,
|
||||
layout_target: LayoutTarget::PropertiesOptions,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
responses.push_back(
|
||||
LayoutMessage::SendLayout {
|
||||
layout: WidgetLayout::new(vec![]),
|
||||
layout_target: LayoutTarget::PropertiesSectionsPanel,
|
||||
layout_target: LayoutTarget::PropertiesSections,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
@@ -217,14 +217,14 @@ impl<'a> MessageHandler<PropertiesPanelMessage, PropertiesPanelMessageHandlerDat
|
||||
self.active_selection = None;
|
||||
responses.push_back(
|
||||
LayoutMessage::SendLayout {
|
||||
layout_target: LayoutTarget::PropertiesOptionsPanel,
|
||||
layout_target: LayoutTarget::PropertiesOptions,
|
||||
layout: WidgetLayout::default(),
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
responses.push_back(
|
||||
LayoutMessage::SendLayout {
|
||||
layout_target: LayoutTarget::PropertiesSectionsPanel,
|
||||
layout_target: LayoutTarget::PropertiesSections,
|
||||
layout: WidgetLayout::default(),
|
||||
}
|
||||
.into(),
|
||||
@@ -308,12 +308,12 @@ fn register_artboard_layer_properties(layer: &Layer, responses: &mut VecDeque<Me
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
value: layer.transform.x(),
|
||||
value: Some(layer.transform.x()),
|
||||
label: "X".into(),
|
||||
unit: " px".into(),
|
||||
on_update: WidgetCallback::new(|number_input: &NumberInput| {
|
||||
PropertiesPanelMessage::ModifyTransform {
|
||||
value: number_input.value,
|
||||
value: number_input.value.unwrap(),
|
||||
transform_op: TransformOp::X,
|
||||
}
|
||||
.into()
|
||||
@@ -325,12 +325,12 @@ fn register_artboard_layer_properties(layer: &Layer, responses: &mut VecDeque<Me
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
value: layer.transform.y(),
|
||||
value: Some(layer.transform.y()),
|
||||
label: "Y".into(),
|
||||
unit: " px".into(),
|
||||
on_update: WidgetCallback::new(|number_input: &NumberInput| {
|
||||
PropertiesPanelMessage::ModifyTransform {
|
||||
value: number_input.value,
|
||||
value: number_input.value.unwrap(),
|
||||
transform_op: TransformOp::Y,
|
||||
}
|
||||
.into()
|
||||
@@ -350,12 +350,12 @@ fn register_artboard_layer_properties(layer: &Layer, responses: &mut VecDeque<Me
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
value: layer.bounding_transform(font_cache).scale_x(),
|
||||
value: Some(layer.bounding_transform(font_cache).scale_x()),
|
||||
label: "W".into(),
|
||||
unit: " px".into(),
|
||||
on_update: WidgetCallback::new(|number_input: &NumberInput| {
|
||||
PropertiesPanelMessage::ModifyTransform {
|
||||
value: number_input.value,
|
||||
value: number_input.value.unwrap(),
|
||||
transform_op: TransformOp::Width,
|
||||
}
|
||||
.into()
|
||||
@@ -367,12 +367,12 @@ fn register_artboard_layer_properties(layer: &Layer, responses: &mut VecDeque<Me
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
value: layer.bounding_transform(font_cache).scale_y(),
|
||||
value: Some(layer.bounding_transform(font_cache).scale_y()),
|
||||
label: "H".into(),
|
||||
unit: " px".into(),
|
||||
on_update: WidgetCallback::new(|number_input: &NumberInput| {
|
||||
PropertiesPanelMessage::ModifyTransform {
|
||||
value: number_input.value,
|
||||
value: number_input.value.unwrap(),
|
||||
transform_op: TransformOp::Height,
|
||||
}
|
||||
.into()
|
||||
@@ -416,14 +416,14 @@ fn register_artboard_layer_properties(layer: &Layer, responses: &mut VecDeque<Me
|
||||
responses.push_back(
|
||||
LayoutMessage::SendLayout {
|
||||
layout: WidgetLayout::new(options_bar),
|
||||
layout_target: LayoutTarget::PropertiesOptionsPanel,
|
||||
layout_target: LayoutTarget::PropertiesOptions,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
responses.push_back(
|
||||
LayoutMessage::SendLayout {
|
||||
layout: WidgetLayout::new(properties_body),
|
||||
layout_target: LayoutTarget::PropertiesSectionsPanel,
|
||||
layout_target: LayoutTarget::PropertiesSections,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
@@ -504,14 +504,14 @@ fn register_artwork_layer_properties(layer: &Layer, responses: &mut VecDeque<Mes
|
||||
responses.push_back(
|
||||
LayoutMessage::SendLayout {
|
||||
layout: WidgetLayout::new(options_bar),
|
||||
layout_target: LayoutTarget::PropertiesOptionsPanel,
|
||||
layout_target: LayoutTarget::PropertiesOptions,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
responses.push_back(
|
||||
LayoutMessage::SendLayout {
|
||||
layout: WidgetLayout::new(properties_body),
|
||||
layout_target: LayoutTarget::PropertiesSectionsPanel,
|
||||
layout_target: LayoutTarget::PropertiesSections,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
@@ -532,12 +532,12 @@ fn node_section_transform(layer: &Layer, font_cache: &FontCache) -> LayoutRow {
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
value: layer.transform.x(),
|
||||
value: Some(layer.transform.x()),
|
||||
label: "X".into(),
|
||||
unit: " px".into(),
|
||||
on_update: WidgetCallback::new(|number_input: &NumberInput| {
|
||||
PropertiesPanelMessage::ModifyTransform {
|
||||
value: number_input.value,
|
||||
value: number_input.value.unwrap(),
|
||||
transform_op: TransformOp::X,
|
||||
}
|
||||
.into()
|
||||
@@ -549,12 +549,12 @@ fn node_section_transform(layer: &Layer, font_cache: &FontCache) -> LayoutRow {
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
value: layer.transform.y(),
|
||||
value: Some(layer.transform.y()),
|
||||
label: "Y".into(),
|
||||
unit: " px".into(),
|
||||
on_update: WidgetCallback::new(|number_input: &NumberInput| {
|
||||
PropertiesPanelMessage::ModifyTransform {
|
||||
value: number_input.value,
|
||||
value: number_input.value.unwrap(),
|
||||
transform_op: TransformOp::Y,
|
||||
}
|
||||
.into()
|
||||
@@ -574,12 +574,12 @@ fn node_section_transform(layer: &Layer, font_cache: &FontCache) -> LayoutRow {
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
value: layer.transform.rotation() * 180. / PI,
|
||||
value: Some(layer.transform.rotation() * 180. / PI),
|
||||
label: "".into(),
|
||||
unit: "°".into(),
|
||||
on_update: WidgetCallback::new(|number_input: &NumberInput| {
|
||||
PropertiesPanelMessage::ModifyTransform {
|
||||
value: number_input.value / 180. * PI,
|
||||
value: number_input.value.unwrap() / 180. * PI,
|
||||
transform_op: TransformOp::Rotation,
|
||||
}
|
||||
.into()
|
||||
@@ -599,12 +599,12 @@ fn node_section_transform(layer: &Layer, font_cache: &FontCache) -> LayoutRow {
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
value: layer.transform.scale_x(),
|
||||
value: Some(layer.transform.scale_x()),
|
||||
label: "X".into(),
|
||||
unit: "".into(),
|
||||
on_update: WidgetCallback::new(|number_input: &NumberInput| {
|
||||
PropertiesPanelMessage::ModifyTransform {
|
||||
value: number_input.value,
|
||||
value: number_input.value.unwrap(),
|
||||
transform_op: TransformOp::ScaleX,
|
||||
}
|
||||
.into()
|
||||
@@ -616,12 +616,12 @@ fn node_section_transform(layer: &Layer, font_cache: &FontCache) -> LayoutRow {
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
value: layer.transform.scale_y(),
|
||||
value: Some(layer.transform.scale_y()),
|
||||
label: "Y".into(),
|
||||
unit: "".into(),
|
||||
on_update: WidgetCallback::new(|number_input: &NumberInput| {
|
||||
PropertiesPanelMessage::ModifyTransform {
|
||||
value: number_input.value,
|
||||
value: number_input.value.unwrap(),
|
||||
transform_op: TransformOp::ScaleY,
|
||||
}
|
||||
.into()
|
||||
@@ -641,12 +641,12 @@ fn node_section_transform(layer: &Layer, font_cache: &FontCache) -> LayoutRow {
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
value: layer.bounding_transform(font_cache).scale_x(),
|
||||
value: Some(layer.bounding_transform(font_cache).scale_x()),
|
||||
label: "W".into(),
|
||||
unit: " px".into(),
|
||||
on_update: WidgetCallback::new(|number_input: &NumberInput| {
|
||||
PropertiesPanelMessage::ModifyTransform {
|
||||
value: number_input.value,
|
||||
value: number_input.value.unwrap(),
|
||||
transform_op: TransformOp::Width,
|
||||
}
|
||||
.into()
|
||||
@@ -658,12 +658,12 @@ fn node_section_transform(layer: &Layer, font_cache: &FontCache) -> LayoutRow {
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
value: layer.bounding_transform(font_cache).scale_y(),
|
||||
value: Some(layer.bounding_transform(font_cache).scale_y()),
|
||||
label: "H".into(),
|
||||
unit: " px".into(),
|
||||
on_update: WidgetCallback::new(|number_input: &NumberInput| {
|
||||
PropertiesPanelMessage::ModifyTransform {
|
||||
value: number_input.value,
|
||||
value: number_input.value.unwrap(),
|
||||
transform_op: TransformOp::Height,
|
||||
}
|
||||
.into()
|
||||
@@ -765,7 +765,7 @@ fn node_section_font(layer: &TextLayer) -> LayoutRow {
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
value: layer.size,
|
||||
value: Some(layer.size),
|
||||
min: Some(1.),
|
||||
unit: " px".into(),
|
||||
on_update: WidgetCallback::new(move |number_input: &NumberInput| {
|
||||
@@ -773,7 +773,7 @@ fn node_section_font(layer: &TextLayer) -> LayoutRow {
|
||||
font_family: font_family.clone(),
|
||||
font_style: font_style.clone(),
|
||||
font_file: font_file.clone(),
|
||||
size: number_input.value,
|
||||
size: number_input.value.unwrap(),
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
@@ -954,13 +954,13 @@ fn node_section_stroke(stroke: &Stroke) -> LayoutRow {
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
value: stroke.weight() as f64,
|
||||
value: Some(stroke.weight() as f64),
|
||||
is_integer: false,
|
||||
min: Some(0.),
|
||||
unit: " px".into(),
|
||||
on_update: WidgetCallback::new(move |number_input: &NumberInput| {
|
||||
PropertiesPanelMessage::ModifyStroke {
|
||||
stroke: internal_stroke2.clone().with_weight(number_input.value),
|
||||
stroke: internal_stroke2.clone().with_weight(number_input.value.unwrap()),
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
@@ -1000,13 +1000,13 @@ fn node_section_stroke(stroke: &Stroke) -> LayoutRow {
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
value: stroke.dash_offset() as f64,
|
||||
value: Some(stroke.dash_offset() as f64),
|
||||
is_integer: true,
|
||||
min: Some(0.),
|
||||
unit: " px".into(),
|
||||
on_update: WidgetCallback::new(move |number_input: &NumberInput| {
|
||||
PropertiesPanelMessage::ModifyStroke {
|
||||
stroke: internal_stroke4.clone().with_dash_offset(number_input.value),
|
||||
stroke: internal_stroke4.clone().with_dash_offset(number_input.value.unwrap()),
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
@@ -1120,13 +1120,13 @@ fn node_section_stroke(stroke: &Stroke) -> LayoutRow {
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
value: stroke.line_join_miter_limit() as f64,
|
||||
value: Some(stroke.line_join_miter_limit() as f64),
|
||||
is_integer: true,
|
||||
min: Some(0.),
|
||||
unit: "".into(),
|
||||
on_update: WidgetCallback::new(move |number_input: &NumberInput| {
|
||||
PropertiesPanelMessage::ModifyStroke {
|
||||
stroke: internal_stroke5.clone().with_line_join_miter_limit(number_input.value),
|
||||
stroke: internal_stroke5.clone().with_line_join_miter_limit(number_input.value.unwrap()),
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
|
||||
@@ -4,6 +4,7 @@ use graphene::LayerId;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
|
||||
pub type DocumentSave = (GrapheneDocument, HashMap<Vec<LayerId>, LayerMetadata>);
|
||||
|
||||
@@ -32,3 +33,31 @@ pub enum TargetDocument {
|
||||
Artboard,
|
||||
Artwork,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
pub enum DocumentMode {
|
||||
DesignMode,
|
||||
SelectMode,
|
||||
GuideMode,
|
||||
}
|
||||
|
||||
impl fmt::Display for DocumentMode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let text = match self {
|
||||
DocumentMode::DesignMode => "Design Mode".to_string(),
|
||||
DocumentMode::SelectMode => "Select Mode".to_string(),
|
||||
DocumentMode::GuideMode => "Guide Mode".to_string(),
|
||||
};
|
||||
write!(f, "{}", text)
|
||||
}
|
||||
}
|
||||
|
||||
impl DocumentMode {
|
||||
pub fn icon_name(&self) -> String {
|
||||
match self {
|
||||
DocumentMode::DesignMode => "ViewportDesignMode".to_string(),
|
||||
DocumentMode::SelectMode => "ViewportSelectMode".to_string(),
|
||||
DocumentMode::GuideMode => "ViewportGuideMode".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user