mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-04 06:38:11 +08:00
Event broadcasting system (#692)
* broadcast system implemented but not everywhere * unused types * code review with keavon * - optional signal mappings - tool.rs simplification * Cleanup * reduced code duplication in `tool.rs` * ran cargo fmt * code review changes * fix merge error Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
co-authored by
Keavon Chambers
parent
0ad6e41a59
commit
535b4fff85
@@ -0,0 +1,26 @@
|
|||||||
|
use crate::message_prelude::*;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[impl_message(Message, Broadcast)]
|
||||||
|
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub enum BroadcastMessage {
|
||||||
|
SubscribeSignal {
|
||||||
|
on: BroadcastSignal,
|
||||||
|
send: Box<Message>,
|
||||||
|
},
|
||||||
|
UnsubscribeSignal {
|
||||||
|
on: BroadcastSignal,
|
||||||
|
message: Box<Message>,
|
||||||
|
},
|
||||||
|
#[child]
|
||||||
|
TriggerSignal(BroadcastSignal),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize, Hash)]
|
||||||
|
#[impl_message(Message, BroadcastMessage, TriggerSignal)]
|
||||||
|
pub enum BroadcastSignal {
|
||||||
|
DocumentIsDirty,
|
||||||
|
ToolAbort,
|
||||||
|
SelectionChanged,
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
use crate::message_prelude::*;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct BroadcastMessageHandler {
|
||||||
|
listeners: HashMap<BroadcastSignal, Vec<Message>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageHandler<BroadcastMessage, ()> for BroadcastMessageHandler {
|
||||||
|
fn process_action(&mut self, action: BroadcastMessage, _data: (), responses: &mut VecDeque<Message>) {
|
||||||
|
use BroadcastMessage::*;
|
||||||
|
match action {
|
||||||
|
SubscribeSignal { on, send } => self.listeners.entry(on).or_default().push(*send),
|
||||||
|
UnsubscribeSignal { on, message } => self.listeners.entry(on).or_default().retain(|msg| *msg != *message),
|
||||||
|
TriggerSignal(signal) => {
|
||||||
|
for message in self.listeners.entry(signal).or_default() {
|
||||||
|
responses.push_front(message.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn actions(&self) -> ActionList {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use super::broadcast_message_handler::BroadcastMessageHandler;
|
||||||
use crate::document::PortfolioMessageHandler;
|
use crate::document::PortfolioMessageHandler;
|
||||||
use crate::global::GlobalMessageHandler;
|
use crate::global::GlobalMessageHandler;
|
||||||
use crate::input::{InputMapperMessageHandler, InputPreprocessorMessageHandler};
|
use crate::input::{InputMapperMessageHandler, InputPreprocessorMessageHandler};
|
||||||
@@ -18,6 +19,7 @@ pub struct Dispatcher {
|
|||||||
#[remain::sorted]
|
#[remain::sorted]
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
struct DispatcherMessageHandlers {
|
struct DispatcherMessageHandlers {
|
||||||
|
broadcast_message_handler: BroadcastMessageHandler,
|
||||||
dialog_message_handler: DialogMessageHandler,
|
dialog_message_handler: DialogMessageHandler,
|
||||||
global_message_handler: GlobalMessageHandler,
|
global_message_handler: GlobalMessageHandler,
|
||||||
input_mapper_message_handler: InputMapperMessageHandler,
|
input_mapper_message_handler: InputMapperMessageHandler,
|
||||||
@@ -43,7 +45,7 @@ const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[
|
|||||||
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::UpdateActiveDocument),
|
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::UpdateActiveDocument),
|
||||||
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::UpdateOpenDocumentsList),
|
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::UpdateOpenDocumentsList),
|
||||||
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::TriggerFontLoad),
|
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::TriggerFontLoad),
|
||||||
MessageDiscriminant::Tool(ToolMessageDiscriminant::DocumentIsDirty),
|
MessageDiscriminant::Broadcast(BroadcastMessageDiscriminant::TriggerSignal(BroadcastSignalDiscriminant::DocumentIsDirty)),
|
||||||
];
|
];
|
||||||
|
|
||||||
impl Dispatcher {
|
impl Dispatcher {
|
||||||
@@ -85,6 +87,7 @@ impl Dispatcher {
|
|||||||
match message {
|
match message {
|
||||||
#[remain::unsorted]
|
#[remain::unsorted]
|
||||||
NoOp => {}
|
NoOp => {}
|
||||||
|
Broadcast(message) => self.message_handlers.broadcast_message_handler.process_action(message, (), &mut queue),
|
||||||
Dialog(message) => {
|
Dialog(message) => {
|
||||||
self.message_handlers
|
self.message_handlers
|
||||||
.dialog_message_handler
|
.dialog_message_handler
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ pub enum Message {
|
|||||||
#[remain::unsorted]
|
#[remain::unsorted]
|
||||||
NoOp,
|
NoOp,
|
||||||
#[child]
|
#[child]
|
||||||
|
Broadcast(BroadcastMessage),
|
||||||
|
#[child]
|
||||||
Dialog(DialogMessage),
|
Dialog(DialogMessage),
|
||||||
#[child]
|
#[child]
|
||||||
Frontend(FrontendMessage),
|
Frontend(FrontendMessage),
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
pub mod broadcast_message;
|
||||||
|
pub mod broadcast_message_handler;
|
||||||
pub mod dispatcher;
|
pub mod dispatcher;
|
||||||
pub mod message;
|
pub mod message;
|
||||||
pub mod message_handler;
|
pub mod message_handler;
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ impl MessageHandler<ArtboardMessage, &FontCache> for ArtboardMessageHandler {
|
|||||||
DocumentResponse::DocumentChanged => responses.push_back(ArtboardMessage::RenderArtboards.into()),
|
DocumentResponse::DocumentChanged => responses.push_back(ArtboardMessage::RenderArtboards.into()),
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(None) => {}
|
Ok(None) => {}
|
||||||
|
|||||||
@@ -102,7 +102,6 @@ pub enum DocumentMessage {
|
|||||||
RollbackTransaction,
|
RollbackTransaction,
|
||||||
SaveDocument,
|
SaveDocument,
|
||||||
SelectAllLayers,
|
SelectAllLayers,
|
||||||
SelectionChanged,
|
|
||||||
SelectLayer {
|
SelectLayer {
|
||||||
layer_path: Vec<LayerId>,
|
layer_path: Vec<LayerId>,
|
||||||
ctrl: bool,
|
ctrl: bool,
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use super::clipboards::Clipboard;
|
use super::clipboards::Clipboard;
|
||||||
use super::layer_panel::{layer_panel_entry, LayerMetadata, LayerPanelEntry, RawBuffer};
|
use super::layer_panel::{layer_panel_entry, LayerMetadata, LayerPanelEntry, RawBuffer};
|
||||||
use super::properties_panel_message_handler::PropertiesPanelMessageHandlerData;
|
use super::properties_panel_message_handler::PropertiesPanelMessageHandlerData;
|
||||||
|
use super::utility_types::DocumentMode;
|
||||||
use super::utility_types::{AlignAggregate, AlignAxis, DocumentSave, FlipAxis};
|
use super::utility_types::{AlignAggregate, AlignAxis, DocumentSave, FlipAxis};
|
||||||
use super::utility_types::{DocumentMode, TargetDocument};
|
|
||||||
use super::{vectorize_layer_metadata, PropertiesPanelMessageHandler};
|
use super::{vectorize_layer_metadata, PropertiesPanelMessageHandler};
|
||||||
use super::{ArtboardMessageHandler, MovementMessageHandler, OverlaysMessageHandler, TransformLayerMessageHandler};
|
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::consts::{ASYMPTOTIC_EFFECT, DEFAULT_DOCUMENT_NAME, FILE_SAVE_SUFFIX, GRAPHITE_DOCUMENT_VERSION, SCALE_EFFECT, SCROLLBAR_SPACING, VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR};
|
||||||
@@ -864,7 +864,7 @@ impl MessageHandler<DocumentMessage, (&InputPreprocessorMessageHandler, &FontCac
|
|||||||
}
|
}
|
||||||
DocumentResponse::DocumentChanged => responses.push_back(RenderDocument.into()),
|
DocumentResponse::DocumentChanged => responses.push_back(RenderDocument.into()),
|
||||||
};
|
};
|
||||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => log::error!("DocumentError: {:?}", e),
|
Err(e) => log::error!("DocumentError: {:?}", e),
|
||||||
@@ -894,6 +894,7 @@ impl MessageHandler<DocumentMessage, (&InputPreprocessorMessageHandler, &FontCac
|
|||||||
PropertiesPanelMessageHandlerData {
|
PropertiesPanelMessageHandlerData {
|
||||||
artwork_document: &self.graphene_document,
|
artwork_document: &self.graphene_document,
|
||||||
artboard_document: &self.artboard_message_handler.artboards_graphene_document,
|
artboard_document: &self.artboard_message_handler.artboards_graphene_document,
|
||||||
|
selected_layers: &mut self.layer_metadata.iter().filter_map(|(path, data)| data.selected.then(|| path.as_slice())),
|
||||||
font_cache,
|
font_cache,
|
||||||
},
|
},
|
||||||
responses,
|
responses,
|
||||||
@@ -910,22 +911,9 @@ impl MessageHandler<DocumentMessage, (&InputPreprocessorMessageHandler, &FontCac
|
|||||||
responses.extend(self.select_layer(layer_path, font_cache));
|
responses.extend(self.select_layer(layer_path, font_cache));
|
||||||
}
|
}
|
||||||
|
|
||||||
let selected_paths: Vec<Vec<u64>> = self.selected_layers().map(|path| path.to_vec()).collect();
|
|
||||||
if selected_paths.is_empty() {
|
|
||||||
responses.push_back(PropertiesPanelMessage::ClearSelection.into())
|
|
||||||
} else {
|
|
||||||
responses.push_back(
|
|
||||||
PropertiesPanelMessage::SetActiveLayers {
|
|
||||||
paths: selected_paths,
|
|
||||||
document: TargetDocument::Artwork,
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Correctly update layer panel in clear_selection instead of here
|
// TODO: Correctly update layer panel in clear_selection instead of here
|
||||||
responses.push_back(FolderChanged { affected_folder_path: vec![] }.into());
|
responses.push_back(FolderChanged { affected_folder_path: vec![] }.into());
|
||||||
responses.push_back(DocumentMessage::SelectionChanged.into());
|
responses.push_back(BroadcastSignal::SelectionChanged.into());
|
||||||
|
|
||||||
self.update_layer_tree_options_bar_widgets(responses, font_cache);
|
self.update_layer_tree_options_bar_widgets(responses, font_cache);
|
||||||
}
|
}
|
||||||
@@ -963,7 +951,7 @@ impl MessageHandler<DocumentMessage, (&InputPreprocessorMessageHandler, &FontCac
|
|||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BooleanOperation(op) => {
|
BooleanOperation(op) => {
|
||||||
@@ -997,7 +985,7 @@ impl MessageHandler<DocumentMessage, (&InputPreprocessorMessageHandler, &FontCac
|
|||||||
}
|
}
|
||||||
DeleteLayer { layer_path } => {
|
DeleteLayer { layer_path } => {
|
||||||
responses.push_front(DocumentOperation::DeleteLayer { path: layer_path.clone() }.into());
|
responses.push_front(DocumentOperation::DeleteLayer { path: layer_path.clone() }.into());
|
||||||
responses.push_front(ToolMessage::AbortCurrentTool.into());
|
responses.push_front(BroadcastSignal::ToolAbort.into());
|
||||||
responses.push_back(PropertiesPanelMessage::CheckSelectedWasDeleted { path: layer_path }.into());
|
responses.push_back(PropertiesPanelMessage::CheckSelectedWasDeleted { path: layer_path }.into());
|
||||||
}
|
}
|
||||||
DeleteSelectedLayers => {
|
DeleteSelectedLayers => {
|
||||||
@@ -1007,7 +995,8 @@ impl MessageHandler<DocumentMessage, (&InputPreprocessorMessageHandler, &FontCac
|
|||||||
responses.push_front(DocumentMessage::DeleteLayer { layer_path: path.to_vec() }.into());
|
responses.push_front(DocumentMessage::DeleteLayer { layer_path: path.to_vec() }.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
responses.push_front(DocumentMessage::SelectionChanged.into());
|
responses.push_front(BroadcastSignal::SelectionChanged.into());
|
||||||
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
}
|
}
|
||||||
DeselectAllLayers => {
|
DeselectAllLayers => {
|
||||||
responses.push_front(SetSelectedLayers { replacement_selected_layers: vec![] }.into());
|
responses.push_front(SetSelectedLayers { replacement_selected_layers: vec![] }.into());
|
||||||
@@ -1106,7 +1095,7 @@ impl MessageHandler<DocumentMessage, (&InputPreprocessorMessageHandler, &FontCac
|
|||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FolderChanged { affected_folder_path } => {
|
FolderChanged { affected_folder_path } => {
|
||||||
@@ -1183,7 +1172,7 @@ impl MessageHandler<DocumentMessage, (&InputPreprocessorMessageHandler, &FontCac
|
|||||||
};
|
};
|
||||||
responses.push_back(operation.into());
|
responses.push_back(operation.into());
|
||||||
}
|
}
|
||||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
}
|
}
|
||||||
PasteImage { mime, image_data, mouse } => {
|
PasteImage { mime, image_data, mouse } => {
|
||||||
let path = vec![generate_uuid()];
|
let path = vec![generate_uuid()];
|
||||||
@@ -1221,7 +1210,7 @@ impl MessageHandler<DocumentMessage, (&InputPreprocessorMessageHandler, &FontCac
|
|||||||
Redo => {
|
Redo => {
|
||||||
responses.push_back(SelectToolMessage::Abort.into());
|
responses.push_back(SelectToolMessage::Abort.into());
|
||||||
responses.push_back(DocumentHistoryForward.into());
|
responses.push_back(DocumentHistoryForward.into());
|
||||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
responses.push_back(RenderDocument.into());
|
responses.push_back(RenderDocument.into());
|
||||||
responses.push_back(FolderChanged { affected_folder_path: vec![] }.into());
|
responses.push_back(FolderChanged { affected_folder_path: vec![] }.into());
|
||||||
}
|
}
|
||||||
@@ -1353,11 +1342,6 @@ impl MessageHandler<DocumentMessage, (&InputPreprocessorMessageHandler, &FontCac
|
|||||||
let all = self.all_layers().map(|path| path.to_vec()).collect();
|
let all = self.all_layers().map(|path| path.to_vec()).collect();
|
||||||
responses.push_front(SetSelectedLayers { replacement_selected_layers: all }.into());
|
responses.push_front(SetSelectedLayers { replacement_selected_layers: all }.into());
|
||||||
}
|
}
|
||||||
SelectionChanged => {
|
|
||||||
// TODO: Hoist this duplicated code into wider system
|
|
||||||
responses.push_back(ToolMessage::SelectionChanged.into());
|
|
||||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
|
||||||
}
|
|
||||||
SelectLayer { layer_path, ctrl, shift } => {
|
SelectLayer { layer_path, ctrl, shift } => {
|
||||||
let mut paths = vec![];
|
let mut paths = vec![];
|
||||||
let last_selection_exists = !self.layer_range_selection_reference.is_empty();
|
let last_selection_exists = !self.layer_range_selection_reference.is_empty();
|
||||||
@@ -1382,7 +1366,7 @@ impl MessageHandler<DocumentMessage, (&InputPreprocessorMessageHandler, &FontCac
|
|||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
responses.push_back(DocumentMessage::SelectionChanged.into());
|
responses.push_back(BroadcastSignal::SelectionChanged.into());
|
||||||
} else {
|
} else {
|
||||||
paths.push(layer_path.clone());
|
paths.push(layer_path.clone());
|
||||||
}
|
}
|
||||||
@@ -1476,12 +1460,12 @@ impl MessageHandler<DocumentMessage, (&InputPreprocessorMessageHandler, &FontCac
|
|||||||
}
|
}
|
||||||
ToggleLayerVisibility { layer_path } => {
|
ToggleLayerVisibility { layer_path } => {
|
||||||
responses.push_back(DocumentOperation::ToggleLayerVisibility { path: layer_path }.into());
|
responses.push_back(DocumentOperation::ToggleLayerVisibility { path: layer_path }.into());
|
||||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
}
|
}
|
||||||
Undo => {
|
Undo => {
|
||||||
responses.push_back(ToolMessage::AbortCurrentTool.into());
|
responses.push_back(BroadcastSignal::ToolAbort.into());
|
||||||
responses.push_back(DocumentHistoryBackward.into());
|
responses.push_back(DocumentHistoryBackward.into());
|
||||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
responses.push_back(RenderDocument.into());
|
responses.push_back(RenderDocument.into());
|
||||||
responses.push_back(FolderChanged { affected_folder_path: vec![] }.into());
|
responses.push_back(FolderChanged { affected_folder_path: vec![] }.into());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ impl MessageHandler<MovementMessage, (&Document, &InputPreprocessorMessageHandle
|
|||||||
self.zoom = 1.
|
self.zoom = 1.
|
||||||
}
|
}
|
||||||
|
|
||||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
responses.push_back(DocumentMessage::DirtyRenderDocumentInOutlineView.into());
|
responses.push_back(DocumentMessage::DirtyRenderDocumentInOutlineView.into());
|
||||||
responses.push_back(PortfolioMessage::UpdateDocumentWidgets.into());
|
responses.push_back(PortfolioMessage::UpdateDocumentWidgets.into());
|
||||||
self.create_document_transform(&ipp.viewport_bounds, responses);
|
self.create_document_transform(&ipp.viewport_bounds, responses);
|
||||||
@@ -243,12 +243,12 @@ impl MessageHandler<MovementMessage, (&Document, &InputPreprocessorMessageHandle
|
|||||||
SetCanvasRotation { angle_radians } => {
|
SetCanvasRotation { angle_radians } => {
|
||||||
self.tilt = angle_radians;
|
self.tilt = angle_radians;
|
||||||
self.create_document_transform(&ipp.viewport_bounds, responses);
|
self.create_document_transform(&ipp.viewport_bounds, responses);
|
||||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
responses.push_back(PortfolioMessage::UpdateDocumentWidgets.into());
|
responses.push_back(PortfolioMessage::UpdateDocumentWidgets.into());
|
||||||
}
|
}
|
||||||
SetCanvasZoom { zoom_factor } => {
|
SetCanvasZoom { zoom_factor } => {
|
||||||
self.zoom = zoom_factor.clamp(VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_SCALE_MAX);
|
self.zoom = zoom_factor.clamp(VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_SCALE_MAX);
|
||||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
responses.push_back(DocumentMessage::DirtyRenderDocumentInOutlineView.into());
|
responses.push_back(DocumentMessage::DirtyRenderDocumentInOutlineView.into());
|
||||||
responses.push_back(PortfolioMessage::UpdateDocumentWidgets.into());
|
responses.push_back(PortfolioMessage::UpdateDocumentWidgets.into());
|
||||||
self.create_document_transform(&ipp.viewport_bounds, responses);
|
self.create_document_transform(&ipp.viewport_bounds, responses);
|
||||||
@@ -256,7 +256,7 @@ impl MessageHandler<MovementMessage, (&Document, &InputPreprocessorMessageHandle
|
|||||||
TransformCanvasEnd => {
|
TransformCanvasEnd => {
|
||||||
self.tilt = self.snapped_angle();
|
self.tilt = self.snapped_angle();
|
||||||
self.zoom = self.snapped_scale();
|
self.zoom = self.snapped_scale();
|
||||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
responses.push_back(ToolMessage::UpdateCursor.into());
|
responses.push_back(ToolMessage::UpdateCursor.into());
|
||||||
responses.push_back(ToolMessage::UpdateHints.into());
|
responses.push_back(ToolMessage::UpdateHints.into());
|
||||||
self.snap_tilt = false;
|
self.snap_tilt = false;
|
||||||
@@ -270,7 +270,7 @@ impl MessageHandler<MovementMessage, (&Document, &InputPreprocessorMessageHandle
|
|||||||
let transformed_delta = document.root.transform.inverse().transform_vector2(delta);
|
let transformed_delta = document.root.transform.inverse().transform_vector2(delta);
|
||||||
|
|
||||||
self.pan += transformed_delta;
|
self.pan += transformed_delta;
|
||||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
self.create_document_transform(&ipp.viewport_bounds, responses);
|
self.create_document_transform(&ipp.viewport_bounds, responses);
|
||||||
}
|
}
|
||||||
TranslateCanvasBegin => {
|
TranslateCanvasBegin => {
|
||||||
@@ -284,7 +284,7 @@ impl MessageHandler<MovementMessage, (&Document, &InputPreprocessorMessageHandle
|
|||||||
let transformed_delta = document.root.transform.inverse().transform_vector2(delta * ipp.viewport_bounds.size());
|
let transformed_delta = document.root.transform.inverse().transform_vector2(delta * ipp.viewport_bounds.size());
|
||||||
|
|
||||||
self.pan += transformed_delta;
|
self.pan += transformed_delta;
|
||||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
self.create_document_transform(&ipp.viewport_bounds, responses);
|
self.create_document_transform(&ipp.viewport_bounds, responses);
|
||||||
}
|
}
|
||||||
WheelCanvasTranslate { use_y_as_x } => {
|
WheelCanvasTranslate { use_y_as_x } => {
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ impl PortfolioMessageHandler {
|
|||||||
fn load_document(&mut self, new_document: DocumentMessageHandler, document_id: u64, replace_first_empty: bool, responses: &mut VecDeque<Message>) {
|
fn load_document(&mut self, new_document: DocumentMessageHandler, document_id: u64, replace_first_empty: bool, responses: &mut VecDeque<Message>) {
|
||||||
// Special case when loading a document on an empty page
|
// Special case when loading a document on an empty page
|
||||||
if replace_first_empty && self.active_document().is_unmodified_default() {
|
if replace_first_empty && self.active_document().is_unmodified_default() {
|
||||||
responses.push_back(ToolMessage::AbortCurrentTool.into());
|
responses.push_back(BroadcastSignal::ToolAbort.into());
|
||||||
responses.push_back(PortfolioMessage::CloseDocument { document_id: self.active_document_id }.into());
|
responses.push_back(PortfolioMessage::CloseDocument { document_id: self.active_document_id }.into());
|
||||||
|
|
||||||
let active_document_index = self
|
let active_document_index = self
|
||||||
@@ -168,7 +168,7 @@ impl MessageHandler<PortfolioMessage, &InputPreprocessorMessageHandler> for Port
|
|||||||
self.document_ids.push(new_document_id);
|
self.document_ids.push(new_document_id);
|
||||||
self.active_document_id = new_document_id;
|
self.active_document_id = new_document_id;
|
||||||
|
|
||||||
responses.push_back(ToolMessage::AbortCurrentTool.into());
|
responses.push_back(BroadcastSignal::ToolAbort.into());
|
||||||
responses.push_back(PortfolioMessage::UpdateOpenDocumentsList.into());
|
responses.push_back(PortfolioMessage::UpdateOpenDocumentsList.into());
|
||||||
responses.push_back(PortfolioMessage::SelectDocument { document_id: new_document_id }.into())
|
responses.push_back(PortfolioMessage::SelectDocument { document_id: new_document_id }.into())
|
||||||
}
|
}
|
||||||
@@ -208,7 +208,7 @@ impl MessageHandler<PortfolioMessage, &InputPreprocessorMessageHandler> for Port
|
|||||||
CloseDocumentWithConfirmation { document_id } => {
|
CloseDocumentWithConfirmation { document_id } => {
|
||||||
let target_document = self.documents.get(&document_id).unwrap();
|
let target_document = self.documents.get(&document_id).unwrap();
|
||||||
if target_document.is_saved() {
|
if target_document.is_saved() {
|
||||||
responses.push_back(ToolMessage::AbortCurrentTool.into());
|
responses.push_back(BroadcastSignal::ToolAbort.into());
|
||||||
responses.push_back(PortfolioMessage::CloseDocument { document_id }.into());
|
responses.push_back(PortfolioMessage::CloseDocument { document_id }.into());
|
||||||
} else {
|
} else {
|
||||||
let dialog = dialog::CloseDocument {
|
let dialog = dialog::CloseDocument {
|
||||||
@@ -274,13 +274,13 @@ impl MessageHandler<PortfolioMessage, &InputPreprocessorMessageHandler> for Port
|
|||||||
let name = self.generate_new_document_name();
|
let name = self.generate_new_document_name();
|
||||||
let new_document = DocumentMessageHandler::with_name(name, ipp);
|
let new_document = DocumentMessageHandler::with_name(name, ipp);
|
||||||
let document_id = generate_uuid();
|
let document_id = generate_uuid();
|
||||||
responses.push_back(ToolMessage::AbortCurrentTool.into());
|
responses.push_back(BroadcastSignal::ToolAbort.into());
|
||||||
self.load_document(new_document, document_id, false, responses);
|
self.load_document(new_document, document_id, false, responses);
|
||||||
}
|
}
|
||||||
NewDocumentWithName { name } => {
|
NewDocumentWithName { name } => {
|
||||||
let new_document = DocumentMessageHandler::with_name(name, ipp);
|
let new_document = DocumentMessageHandler::with_name(name, ipp);
|
||||||
let document_id = generate_uuid();
|
let document_id = generate_uuid();
|
||||||
responses.push_back(ToolMessage::AbortCurrentTool.into());
|
responses.push_back(BroadcastSignal::ToolAbort.into());
|
||||||
self.load_document(new_document, document_id, false, responses);
|
self.load_document(new_document, document_id, false, responses);
|
||||||
}
|
}
|
||||||
NextDocument => {
|
NextDocument => {
|
||||||
@@ -431,7 +431,7 @@ impl MessageHandler<PortfolioMessage, &InputPreprocessorMessageHandler> for Port
|
|||||||
if !active_document.is_saved() {
|
if !active_document.is_saved() {
|
||||||
responses.push_back(PortfolioMessage::AutoSaveDocument { document_id: self.active_document_id }.into());
|
responses.push_back(PortfolioMessage::AutoSaveDocument { document_id: self.active_document_id }.into());
|
||||||
}
|
}
|
||||||
responses.push_back(ToolMessage::AbortCurrentTool.into());
|
responses.push_back(BroadcastSignal::ToolAbort.into());
|
||||||
responses.push_back(SetActiveDocument { document_id }.into());
|
responses.push_back(SetActiveDocument { document_id }.into());
|
||||||
|
|
||||||
responses.push_back(FrontendMessage::UpdateActiveDocument { document_id }.into());
|
responses.push_back(FrontendMessage::UpdateActiveDocument { document_id }.into());
|
||||||
@@ -440,7 +440,8 @@ impl MessageHandler<PortfolioMessage, &InputPreprocessorMessageHandler> for Port
|
|||||||
for layer in self.documents.get(&document_id).unwrap().layer_metadata.keys() {
|
for layer in self.documents.get(&document_id).unwrap().layer_metadata.keys() {
|
||||||
responses.push_back(DocumentMessage::LayerChanged { affected_layer_path: layer.clone() }.into());
|
responses.push_back(DocumentMessage::LayerChanged { affected_layer_path: layer.clone() }.into());
|
||||||
}
|
}
|
||||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
responses.push_back(BroadcastSignal::SelectionChanged.into());
|
||||||
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
responses.push_back(PortfolioMessage::UpdateDocumentWidgets.into());
|
responses.push_back(PortfolioMessage::UpdateDocumentWidgets.into());
|
||||||
}
|
}
|
||||||
SetActiveDocument { document_id } => {
|
SetActiveDocument { document_id } => {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ pub enum PropertiesPanelMessage {
|
|||||||
CheckSelectedWasDeleted { path: Vec<LayerId> },
|
CheckSelectedWasDeleted { path: Vec<LayerId> },
|
||||||
CheckSelectedWasUpdated { path: Vec<LayerId> },
|
CheckSelectedWasUpdated { path: Vec<LayerId> },
|
||||||
ClearSelection,
|
ClearSelection,
|
||||||
|
Init,
|
||||||
ModifyFill { fill: Fill },
|
ModifyFill { fill: Fill },
|
||||||
ModifyFont { font_family: String, font_style: String, size: f64 },
|
ModifyFont { font_family: String, font_style: String, size: f64 },
|
||||||
ModifyName { name: String },
|
ModifyName { name: String },
|
||||||
@@ -20,6 +21,7 @@ pub enum PropertiesPanelMessage {
|
|||||||
ModifyTransform { value: f64, transform_op: TransformOp },
|
ModifyTransform { value: f64, transform_op: TransformOp },
|
||||||
ResendActiveProperties,
|
ResendActiveProperties,
|
||||||
SetActiveLayers { paths: Vec<Vec<LayerId>>, document: TargetDocument },
|
SetActiveLayers { paths: Vec<Vec<LayerId>>, document: TargetDocument },
|
||||||
|
UpdateSelectedDocumentProperties,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
|
#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ impl PropertiesPanelMessageHandler {
|
|||||||
pub struct PropertiesPanelMessageHandlerData<'a> {
|
pub struct PropertiesPanelMessageHandlerData<'a> {
|
||||||
pub artwork_document: &'a GrapheneDocument,
|
pub artwork_document: &'a GrapheneDocument,
|
||||||
pub artboard_document: &'a GrapheneDocument,
|
pub artboard_document: &'a GrapheneDocument,
|
||||||
|
pub selected_layers: &'a mut dyn Iterator<Item = &'a [LayerId]>,
|
||||||
pub font_cache: &'a FontCache,
|
pub font_cache: &'a FontCache,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,6 +120,7 @@ impl<'a> MessageHandler<PropertiesPanelMessage, PropertiesPanelMessageHandlerDat
|
|||||||
let PropertiesPanelMessageHandlerData {
|
let PropertiesPanelMessageHandlerData {
|
||||||
artwork_document,
|
artwork_document,
|
||||||
artboard_document,
|
artboard_document,
|
||||||
|
selected_layers,
|
||||||
font_cache,
|
font_cache,
|
||||||
} = data;
|
} = data;
|
||||||
let get_document = |document_selector: TargetDocument| match document_selector {
|
let get_document = |document_selector: TargetDocument| match document_selector {
|
||||||
@@ -154,6 +156,13 @@ impl<'a> MessageHandler<PropertiesPanelMessage, PropertiesPanelMessageHandlerDat
|
|||||||
);
|
);
|
||||||
self.active_selection = None;
|
self.active_selection = None;
|
||||||
}
|
}
|
||||||
|
Init => responses.push_back(
|
||||||
|
BroadcastMessage::SubscribeSignal {
|
||||||
|
on: BroadcastSignal::SelectionChanged,
|
||||||
|
send: Box::new(PropertiesPanelMessage::UpdateSelectedDocumentProperties.into()),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
ModifyFont { font_family, font_style, size } => {
|
ModifyFont { font_family, font_style, size } => {
|
||||||
let (path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
let (path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
||||||
|
|
||||||
@@ -233,6 +242,13 @@ impl<'a> MessageHandler<PropertiesPanelMessage, PropertiesPanelMessageHandlerDat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
UpdateSelectedDocumentProperties => responses.push_back(
|
||||||
|
PropertiesPanelMessage::SetActiveLayers {
|
||||||
|
paths: selected_layers.map(|path| path.to_vec()).collect(),
|
||||||
|
document: TargetDocument::Artwork,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
|
|||||||
|
|
||||||
self.transform_operation = TransformOperation::None;
|
self.transform_operation = TransformOperation::None;
|
||||||
|
|
||||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
}
|
}
|
||||||
BeginGrab => {
|
BeginGrab => {
|
||||||
if let TransformOperation::Grabbing(_) = self.transform_operation {
|
if let TransformOperation::Grabbing(_) = self.transform_operation {
|
||||||
@@ -66,7 +66,7 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
|
|||||||
|
|
||||||
self.transform_operation = TransformOperation::Grabbing(Default::default());
|
self.transform_operation = TransformOperation::Grabbing(Default::default());
|
||||||
|
|
||||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
}
|
}
|
||||||
BeginRotate => {
|
BeginRotate => {
|
||||||
if let TransformOperation::Rotating(_) = self.transform_operation {
|
if let TransformOperation::Rotating(_) = self.transform_operation {
|
||||||
@@ -77,7 +77,7 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
|
|||||||
|
|
||||||
self.transform_operation = TransformOperation::Rotating(Default::default());
|
self.transform_operation = TransformOperation::Rotating(Default::default());
|
||||||
|
|
||||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
}
|
}
|
||||||
BeginScale => {
|
BeginScale => {
|
||||||
if let TransformOperation::Scaling(_) = self.transform_operation {
|
if let TransformOperation::Scaling(_) = self.transform_operation {
|
||||||
@@ -89,7 +89,7 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
|
|||||||
self.transform_operation = TransformOperation::Scaling(Default::default());
|
self.transform_operation = TransformOperation::Scaling(Default::default());
|
||||||
self.transform_operation.apply_transform_operation(&mut selected, self.snap);
|
self.transform_operation.apply_transform_operation(&mut selected, self.snap);
|
||||||
|
|
||||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
}
|
}
|
||||||
CancelTransformOperation => {
|
CancelTransformOperation => {
|
||||||
selected.revert_operation();
|
selected.revert_operation();
|
||||||
@@ -99,7 +99,7 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
|
|||||||
|
|
||||||
self.transform_operation = TransformOperation::None;
|
self.transform_operation = TransformOperation::None;
|
||||||
|
|
||||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
}
|
}
|
||||||
ConstrainX => self.transform_operation.constrain_axis(Axis::X, &mut selected, self.snap),
|
ConstrainX => self.transform_operation.constrain_axis(Axis::X, &mut selected, self.snap),
|
||||||
ConstrainY => self.transform_operation.constrain_axis(Axis::Y, &mut selected, self.snap),
|
ConstrainY => self.transform_operation.constrain_axis(Axis::Y, &mut selected, self.snap),
|
||||||
|
|||||||
@@ -258,7 +258,7 @@ impl<'a> Selected<'a> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.responses.push_back(ToolMessage::DocumentIsDirty.into());
|
self.responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ impl Default for Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub mod message_prelude {
|
pub mod message_prelude {
|
||||||
|
pub use crate::communication::broadcast_message::{BroadcastMessage, BroadcastMessageDiscriminant, BroadcastSignal, BroadcastSignalDiscriminant};
|
||||||
pub use crate::communication::generate_uuid;
|
pub use crate::communication::generate_uuid;
|
||||||
pub use crate::communication::message::{AsMessage, Message, MessageDiscriminant};
|
pub use crate::communication::message::{AsMessage, Message, MessageDiscriminant};
|
||||||
pub use crate::communication::message_handler::{ActionList, MessageHandler};
|
pub use crate::communication::message_handler::{ActionList, MessageHandler};
|
||||||
|
|||||||
@@ -1,90 +1,3 @@
|
|||||||
/// Counts args in the macro invocation by adding `+ 1` for every arg.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// let x = count_args!(("example1"), (10), (25));
|
|
||||||
/// assert_eq!(x, 3);
|
|
||||||
/// ```
|
|
||||||
/// expands to
|
|
||||||
/// ```ignore
|
|
||||||
/// let x = 0 + 1 + 1 + 1;
|
|
||||||
/// assert_eq!(x, 3);
|
|
||||||
/// ```
|
|
||||||
macro_rules! count_args {
|
|
||||||
(@one $($t:tt)*) => { 1 };
|
|
||||||
($(($($x:tt)*)),*$(,)?) => {
|
|
||||||
0 $(+ count_args!(@one $($x)*))*
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates a [`std::collections::HashMap`] for `ToolState`'s `tools` variable.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// let tools = gen_tools_hash_map! {
|
|
||||||
/// Select => select::Select,
|
|
||||||
/// Artboard => artboard::Artboard,
|
|
||||||
/// };
|
|
||||||
/// ```
|
|
||||||
/// expands to
|
|
||||||
/// ```ignore
|
|
||||||
/// let tools = {
|
|
||||||
/// let mut hash_map: std::collections::HashMap<crate::tool::ToolType, Box<dyn crate::tool::Tool>> = std::collections::HashMap::with_capacity(count_args!(/* Macro args */));
|
|
||||||
///
|
|
||||||
/// hash_map.insert(crate::tool::ToolType::Select, Box::new(select::Select::default()));
|
|
||||||
/// hash_map.insert(crate::tool::ToolType::Artboard, Box::new(artboard::Artboard::default()));
|
|
||||||
///
|
|
||||||
/// hash_map
|
|
||||||
/// };
|
|
||||||
/// ```
|
|
||||||
macro_rules! gen_tools_hash_map {
|
|
||||||
($($enum_variant:ident => $struct_path:ty),* $(,)?) => {{
|
|
||||||
let mut hash_map: ::std::collections::HashMap<$crate::viewport_tools::tool::ToolType, ::std::boxed::Box<$crate::viewport_tools::tool::Tool>> = ::std::collections::HashMap::with_capacity(count_args!($(($enum_variant)),*));
|
|
||||||
$(hash_map.insert($crate::viewport_tools::tool::ToolType::$enum_variant, ::std::boxed::Box::new(<$struct_path>::default()));)*
|
|
||||||
|
|
||||||
hash_map
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a string representation of an enum value that exactly matches the given name of each enum variant
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// enum E {
|
|
||||||
/// A(u8),
|
|
||||||
/// B
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// // this line is important
|
|
||||||
/// use E::*;
|
|
||||||
///
|
|
||||||
/// let a = E::A(7);
|
|
||||||
/// let s = match_variant_name!(match (a) { A, B });
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// expands to
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// // ...
|
|
||||||
///
|
|
||||||
/// let s = match a {
|
|
||||||
/// A { .. } => "A",
|
|
||||||
/// B { .. } => "B"
|
|
||||||
/// };
|
|
||||||
/// ```
|
|
||||||
macro_rules! match_variant_name {
|
|
||||||
(match ($e:expr) { $($v:ident),* $(,)? }) => {
|
|
||||||
match $e {
|
|
||||||
$(
|
|
||||||
$v { .. } => stringify!($v)
|
|
||||||
),*
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Syntax sugar for initializing an `ActionList`
|
/// Syntax sugar for initializing an `ActionList`
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
|
|||||||
+130
-249
@@ -31,8 +31,61 @@ pub struct DocumentToolData {
|
|||||||
pub secondary_color: Color,
|
pub secondary_color: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ToolCommon: for<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> + PropertyHolder {}
|
#[derive(Clone, Debug)]
|
||||||
impl<T> ToolCommon for T where T: for<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> + PropertyHolder {}
|
pub struct SignalToMessageMap {
|
||||||
|
pub document_dirty: Option<ToolMessage>,
|
||||||
|
pub selection_changed: Option<ToolMessage>,
|
||||||
|
pub tool_abort: Option<ToolMessage>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ToolTransition {
|
||||||
|
fn signal_to_message_map(&self) -> SignalToMessageMap;
|
||||||
|
fn activate(&self, responses: &mut VecDeque<Message>) {
|
||||||
|
let mut subscribe_message = |broadcast_to_tool_mapping: Option<ToolMessage>, signal: BroadcastSignal| {
|
||||||
|
if let Some(mapping) = broadcast_to_tool_mapping {
|
||||||
|
responses.push_back(
|
||||||
|
BroadcastMessage::SubscribeSignal {
|
||||||
|
on: signal,
|
||||||
|
send: Box::new(mapping.into()),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
let signal_to_tool_map = self.signal_to_message_map();
|
||||||
|
subscribe_message(signal_to_tool_map.document_dirty, BroadcastSignal::DocumentIsDirty);
|
||||||
|
subscribe_message(signal_to_tool_map.tool_abort, BroadcastSignal::ToolAbort);
|
||||||
|
subscribe_message(signal_to_tool_map.selection_changed, BroadcastSignal::SelectionChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deactivate(&self, responses: &mut VecDeque<Message>) {
|
||||||
|
let mut unsubscribe_message = |broadcast_to_tool_mapping: Option<ToolMessage>, signal: BroadcastSignal| {
|
||||||
|
if let Some(mapping) = broadcast_to_tool_mapping {
|
||||||
|
responses.push_back(
|
||||||
|
BroadcastMessage::UnsubscribeSignal {
|
||||||
|
on: signal,
|
||||||
|
message: Box::new(mapping.into()),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
let signal_to_tool_map = self.signal_to_message_map();
|
||||||
|
unsubscribe_message(signal_to_tool_map.document_dirty, BroadcastSignal::DocumentIsDirty);
|
||||||
|
unsubscribe_message(signal_to_tool_map.tool_abort, BroadcastSignal::ToolAbort);
|
||||||
|
unsubscribe_message(signal_to_tool_map.selection_changed, BroadcastSignal::SelectionChanged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub trait ToolMetadata {
|
||||||
|
fn icon_name(&self) -> String;
|
||||||
|
fn tooltip(&self) -> String;
|
||||||
|
fn tool_type(&self) -> ToolType;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ToolCommon: for<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> + PropertyHolder + ToolTransition + ToolMetadata {}
|
||||||
|
impl<T> ToolCommon for T where T: for<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> + PropertyHolder + ToolTransition + ToolMetadata {}
|
||||||
|
|
||||||
type Tool = dyn ToolCommon;
|
type Tool = dyn ToolCommon;
|
||||||
|
|
||||||
@@ -57,24 +110,33 @@ impl ToolData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ToolBarMetadataGroup {
|
||||||
|
pub tooltip: String,
|
||||||
|
pub icon_name: String,
|
||||||
|
pub tool_type: ToolType,
|
||||||
|
}
|
||||||
|
|
||||||
impl PropertyHolder for ToolData {
|
impl PropertyHolder for ToolData {
|
||||||
fn properties(&self) -> Layout {
|
fn properties(&self) -> Layout {
|
||||||
let tool_groups_layout = ToolType::list_tools_in_groups()
|
let tool_groups_layout = list_tools_in_groups()
|
||||||
.iter()
|
.iter()
|
||||||
|
.map(|tool_group| tool_group.iter().map(|tool| ToolBarMetadataGroup {tooltip: tool.tooltip(), icon_name: tool.icon_name(), tool_type: tool.tool_type()}).collect::<Vec<_>>())
|
||||||
|
.chain(coming_soon_tools())
|
||||||
.flat_map(|group| {
|
.flat_map(|group| {
|
||||||
let separator = std::iter::once(WidgetHolder::new(Widget::Separator(Separator {
|
let separator = std::iter::once(WidgetHolder::new(Widget::Separator(Separator {
|
||||||
direction: SeparatorDirection::Vertical,
|
direction: SeparatorDirection::Vertical,
|
||||||
separator_type: SeparatorType::Section,
|
separator_type: SeparatorType::Section,
|
||||||
})));
|
})));
|
||||||
let buttons = group.iter().map(|tool_type| {
|
let buttons = group.into_iter().map(|ToolBarMetadataGroup {tooltip, tool_type, icon_name}| {
|
||||||
WidgetHolder::new(Widget::IconButton(IconButton {
|
WidgetHolder::new(Widget::IconButton(IconButton {
|
||||||
icon: tool_type.icon_name(),
|
icon: icon_name,
|
||||||
size: 32,
|
size: 32,
|
||||||
tooltip: tool_type.tooltip(),
|
tooltip: tooltip.clone(),
|
||||||
active: self.active_tool_type == *tool_type,
|
active: self.active_tool_type == tool_type,
|
||||||
on_update: WidgetCallback::new(|_| {
|
on_update: WidgetCallback::new(move |_| {
|
||||||
if !tool_type.tooltip().contains("Coming Soon") {
|
if !tooltip.contains("Coming Soon") {
|
||||||
ToolMessage::ActivateTool { tool_type: *tool_type }.into()
|
ToolMessage::ActivateTool { tool_type }.into()
|
||||||
} else {
|
} else {
|
||||||
DialogMessage::RequestComingSoonDialog { issue: None }.into()
|
DialogMessage::RequestComingSoonDialog { issue: None }.into()
|
||||||
}
|
}
|
||||||
@@ -105,34 +167,7 @@ impl Default for ToolFsmState {
|
|||||||
ToolFsmState {
|
ToolFsmState {
|
||||||
tool_data: ToolData {
|
tool_data: ToolData {
|
||||||
active_tool_type: ToolType::Select,
|
active_tool_type: ToolType::Select,
|
||||||
tools: gen_tools_hash_map! {
|
tools: list_tools_in_groups().into_iter().flatten().map(|tool| (tool.tool_type(), tool)).collect(),
|
||||||
// General
|
|
||||||
Select => select_tool::SelectTool,
|
|
||||||
Artboard => artboard_tool::ArtboardTool,
|
|
||||||
Navigate => navigate_tool::NavigateTool,
|
|
||||||
Eyedropper => eyedropper_tool::EyedropperTool,
|
|
||||||
Fill => fill_tool::FillTool,
|
|
||||||
Gradient => gradient_tool::GradientTool,
|
|
||||||
|
|
||||||
// Vector
|
|
||||||
Path => path_tool::PathTool,
|
|
||||||
Pen => pen_tool::PenTool,
|
|
||||||
Freehand => freehand_tool::FreehandTool,
|
|
||||||
Spline => spline_tool::SplineTool,
|
|
||||||
Line => line_tool::LineTool,
|
|
||||||
Rectangle => rectangle_tool::RectangleTool,
|
|
||||||
Ellipse => ellipse_tool::EllipseTool,
|
|
||||||
Shape => shape_tool::ShapeTool,
|
|
||||||
Text => text_tool::TextTool,
|
|
||||||
|
|
||||||
// Raster
|
|
||||||
// Brush => brush_tool::BrushTool,
|
|
||||||
// Heal => heal_tool::HealTool,
|
|
||||||
// Clone => clone_tool:::CloneTool,
|
|
||||||
// Patch => patch_tool:::PatchTool,
|
|
||||||
// Relight => relight_tool:::RelightTool,
|
|
||||||
// Detail => detail_tool:::DetailTool,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
document_tool_data: DocumentToolData {
|
document_tool_data: DocumentToolData {
|
||||||
primary_color: Color::BLACK,
|
primary_color: Color::BLACK,
|
||||||
@@ -183,213 +218,66 @@ pub enum ToolType {
|
|||||||
Relight,
|
Relight,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToolType {
|
/// List of all the tools in their conventional ordering and grouping.
|
||||||
/// List of all the tools in their conventional ordering and grouping.
|
pub fn list_tools_in_groups() -> Vec<Vec<Box<Tool>>> {
|
||||||
pub fn list_tools_in_groups() -> [&'static [ToolType]; 3] {
|
vec![
|
||||||
[
|
vec![
|
||||||
&[
|
|
||||||
// General tool group
|
|
||||||
ToolType::Select,
|
|
||||||
ToolType::Artboard,
|
|
||||||
ToolType::Navigate,
|
|
||||||
ToolType::Eyedropper,
|
|
||||||
ToolType::Fill,
|
|
||||||
ToolType::Gradient,
|
|
||||||
],
|
|
||||||
&[
|
|
||||||
// Vector tool group
|
|
||||||
ToolType::Path,
|
|
||||||
ToolType::Pen,
|
|
||||||
ToolType::Freehand,
|
|
||||||
ToolType::Spline,
|
|
||||||
ToolType::Line,
|
|
||||||
ToolType::Rectangle,
|
|
||||||
ToolType::Ellipse,
|
|
||||||
ToolType::Shape,
|
|
||||||
ToolType::Text,
|
|
||||||
],
|
|
||||||
&[
|
|
||||||
// Raster tool group
|
|
||||||
ToolType::Brush,
|
|
||||||
ToolType::Heal,
|
|
||||||
ToolType::Clone,
|
|
||||||
ToolType::Patch,
|
|
||||||
ToolType::Detail,
|
|
||||||
ToolType::Relight,
|
|
||||||
],
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn icon_name(&self) -> String {
|
|
||||||
match self {
|
|
||||||
// General tool group
|
// General tool group
|
||||||
ToolType::Select => "GeneralSelectTool".into(),
|
Box::new(select_tool::SelectTool::default()),
|
||||||
ToolType::Artboard => "GeneralArtboardTool".into(),
|
Box::new(artboard_tool::ArtboardTool::default()),
|
||||||
ToolType::Navigate => "GeneralNavigateTool".into(),
|
Box::new(navigate_tool::NavigateTool::default()),
|
||||||
ToolType::Eyedropper => "GeneralEyedropperTool".into(),
|
Box::new(eyedropper_tool::EyedropperTool::default()),
|
||||||
ToolType::Fill => "GeneralFillTool".into(),
|
Box::new(fill_tool::FillTool::default()),
|
||||||
ToolType::Gradient => "GeneralGradientTool".into(),
|
Box::new(gradient_tool::GradientTool::default()),
|
||||||
|
],
|
||||||
|
vec![
|
||||||
// Vector tool group
|
// Vector tool group
|
||||||
ToolType::Path => "VectorPathTool".into(),
|
Box::new(path_tool::PathTool::default()),
|
||||||
ToolType::Pen => "VectorPenTool".into(),
|
Box::new(pen_tool::PenTool::default()),
|
||||||
ToolType::Freehand => "VectorFreehandTool".into(),
|
Box::new(freehand_tool::FreehandTool::default()),
|
||||||
ToolType::Spline => "VectorSplineTool".into(),
|
Box::new(spline_tool::SplineTool::default()),
|
||||||
ToolType::Line => "VectorLineTool".into(),
|
Box::new(line_tool::LineTool::default()),
|
||||||
ToolType::Rectangle => "VectorRectangleTool".into(),
|
Box::new(rectangle_tool::RectangleTool::default()),
|
||||||
ToolType::Ellipse => "VectorEllipseTool".into(),
|
Box::new(ellipse_tool::EllipseTool::default()),
|
||||||
ToolType::Shape => "VectorShapeTool".into(),
|
Box::new(shape_tool::ShapeTool::default()),
|
||||||
ToolType::Text => "VectorTextTool".into(),
|
Box::new(text_tool::TextTool::default()),
|
||||||
|
],
|
||||||
// Raster tool group
|
]
|
||||||
ToolType::Brush => "RasterBrushTool".into(),
|
|
||||||
ToolType::Heal => "RasterHealTool".into(),
|
|
||||||
ToolType::Clone => "RasterCloneTool".into(),
|
|
||||||
ToolType::Patch => "RasterPatchTool".into(),
|
|
||||||
ToolType::Detail => "RasterDetailTool".into(),
|
|
||||||
ToolType::Relight => "RasterRelightTool".into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tooltip(&self) -> String {
|
|
||||||
match self {
|
|
||||||
// General tool group
|
|
||||||
ToolType::Select => "Select Tool (V)".into(),
|
|
||||||
ToolType::Artboard => "Artboard Tool".into(),
|
|
||||||
ToolType::Navigate => "Navigate Tool (Z)".into(),
|
|
||||||
ToolType::Eyedropper => "Eyedropper Tool (I)".into(),
|
|
||||||
ToolType::Fill => "Fill Tool (F)".into(),
|
|
||||||
ToolType::Gradient => "Gradient Tool (H)".into(),
|
|
||||||
|
|
||||||
// Vector tool group
|
|
||||||
ToolType::Path => "Path Tool (A)".into(),
|
|
||||||
ToolType::Pen => "Pen Tool (P)".into(),
|
|
||||||
ToolType::Freehand => "Freehand Tool (N)".into(),
|
|
||||||
ToolType::Spline => "Spline Tool".into(),
|
|
||||||
ToolType::Line => "Line Tool (L)".into(),
|
|
||||||
ToolType::Rectangle => "Rectangle Tool (M)".into(),
|
|
||||||
ToolType::Ellipse => "Ellipse Tool (E)".into(),
|
|
||||||
ToolType::Shape => "Shape Tool (Y)".into(),
|
|
||||||
ToolType::Text => "Text Tool (T)".into(),
|
|
||||||
|
|
||||||
// Raster tool group
|
|
||||||
ToolType::Brush => "Coming Soon: Brush Tool (B)".into(),
|
|
||||||
ToolType::Heal => "Coming Soon: Heal Tool (J)".into(),
|
|
||||||
ToolType::Clone => "Coming Soon: Clone Tool (C)".into(),
|
|
||||||
ToolType::Patch => "Coming Soon: Patch Tool".into(),
|
|
||||||
ToolType::Detail => "Coming Soon: Detail Tool (D)".into(),
|
|
||||||
ToolType::Relight => "Coming Soon: Relight Tool (O)".into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ToolType {
|
pub fn coming_soon_tools() -> Vec<Vec<ToolBarMetadataGroup>> {
|
||||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
vec![vec![
|
||||||
use ToolType::*;
|
ToolBarMetadataGroup {
|
||||||
|
tool_type: ToolType::Brush,
|
||||||
let name = match_variant_name!(match (self) {
|
icon_name: "RasterBrushTool".into(),
|
||||||
// General tool group
|
tooltip: "Coming Soon: Brush Tool (B)".into(),
|
||||||
Select,
|
|
||||||
Artboard,
|
|
||||||
Navigate,
|
|
||||||
Eyedropper,
|
|
||||||
Fill,
|
|
||||||
Gradient,
|
|
||||||
|
|
||||||
// Vector tool group
|
|
||||||
Path,
|
|
||||||
Pen,
|
|
||||||
Freehand,
|
|
||||||
Spline,
|
|
||||||
Line,
|
|
||||||
Rectangle,
|
|
||||||
Ellipse,
|
|
||||||
Shape,
|
|
||||||
Text,
|
|
||||||
|
|
||||||
// Raster tool group
|
|
||||||
Brush,
|
|
||||||
Heal,
|
|
||||||
Clone,
|
|
||||||
Patch,
|
|
||||||
Detail,
|
|
||||||
Relight,
|
|
||||||
});
|
|
||||||
|
|
||||||
formatter.write_str(name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum StandardToolMessageType {
|
|
||||||
Abort,
|
|
||||||
DocumentIsDirty,
|
|
||||||
SelectionChanged,
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Find a nicer way in Rust to make this generic so we don't have to manually map to enum variants
|
|
||||||
pub fn standard_tool_message(tool: ToolType, message_type: StandardToolMessageType) -> Option<ToolMessage> {
|
|
||||||
match message_type {
|
|
||||||
StandardToolMessageType::DocumentIsDirty => match tool {
|
|
||||||
// General tool group
|
|
||||||
ToolType::Select => Some(SelectToolMessage::DocumentIsDirty.into()),
|
|
||||||
ToolType::Artboard => Some(ArtboardToolMessage::DocumentIsDirty.into()),
|
|
||||||
ToolType::Navigate => None, // Some(NavigateToolMessage::DocumentIsDirty.into()),
|
|
||||||
ToolType::Eyedropper => None, // Some(EyedropperToolMessage::DocumentIsDirty.into()),
|
|
||||||
ToolType::Fill => None, // Some(FillToolMessage::DocumentIsDirty.into()),
|
|
||||||
ToolType::Gradient => Some(GradientToolMessage::DocumentIsDirty.into()),
|
|
||||||
|
|
||||||
// Vector tool group
|
|
||||||
ToolType::Path => Some(PathToolMessage::DocumentIsDirty.into()),
|
|
||||||
ToolType::Pen => Some(PenToolMessage::DocumentIsDirty.into()),
|
|
||||||
ToolType::Freehand => None, // Some(FreehandToolMessage::DocumentIsDirty.into()),
|
|
||||||
ToolType::Spline => None, // Some(SplineToolMessage::DocumentIsDirty.into()),
|
|
||||||
ToolType::Line => None, // Some(LineToolMessage::DocumentIsDirty.into()),
|
|
||||||
ToolType::Rectangle => None, // Some(RectangleToolMessage::DocumentIsDirty.into()),
|
|
||||||
ToolType::Ellipse => None, // Some(EllipseToolMessage::DocumentIsDirty.into()),
|
|
||||||
ToolType::Shape => None, // Some(ShapeToolMessage::DocumentIsDirty.into()),
|
|
||||||
ToolType::Text => Some(TextMessage::DocumentIsDirty.into()),
|
|
||||||
|
|
||||||
// Raster tool group
|
|
||||||
ToolType::Brush => None, // Some(BrushMessage::DocumentIsDirty.into()),
|
|
||||||
ToolType::Heal => None, // Some(HealMessage::DocumentIsDirty.into()),
|
|
||||||
ToolType::Clone => None, // Some(CloneMessage::DocumentIsDirty.into()),
|
|
||||||
ToolType::Patch => None, // Some(PatchMessage::DocumentIsDirty.into()),
|
|
||||||
ToolType::Detail => None, // Some(DetailToolMessage::DocumentIsDirty.into()),
|
|
||||||
ToolType::Relight => None, // Some(RelightMessage::DocumentIsDirty.into()),
|
|
||||||
},
|
},
|
||||||
StandardToolMessageType::Abort => match tool {
|
ToolBarMetadataGroup {
|
||||||
// General tool group
|
tool_type: ToolType::Heal,
|
||||||
ToolType::Select => Some(SelectToolMessage::Abort.into()),
|
icon_name: "RasterHealTool".into(),
|
||||||
ToolType::Artboard => Some(ArtboardToolMessage::Abort.into()),
|
tooltip: "Coming Soon: Heal Tool (J)".into(),
|
||||||
ToolType::Navigate => Some(NavigateToolMessage::Abort.into()),
|
|
||||||
ToolType::Eyedropper => Some(EyedropperToolMessage::Abort.into()),
|
|
||||||
ToolType::Fill => Some(FillToolMessage::Abort.into()),
|
|
||||||
ToolType::Gradient => Some(GradientToolMessage::Abort.into()),
|
|
||||||
|
|
||||||
// Vector tool group
|
|
||||||
ToolType::Path => Some(PathToolMessage::Abort.into()),
|
|
||||||
ToolType::Pen => Some(PenToolMessage::Abort.into()),
|
|
||||||
ToolType::Freehand => Some(FreehandToolMessage::Abort.into()),
|
|
||||||
ToolType::Spline => Some(SplineToolMessage::Abort.into()),
|
|
||||||
ToolType::Line => Some(LineToolMessage::Abort.into()),
|
|
||||||
ToolType::Rectangle => Some(RectangleToolMessage::Abort.into()),
|
|
||||||
ToolType::Ellipse => Some(EllipseToolMessage::Abort.into()),
|
|
||||||
ToolType::Shape => Some(ShapeToolMessage::Abort.into()),
|
|
||||||
ToolType::Text => Some(TextMessage::Abort.into()),
|
|
||||||
|
|
||||||
// Raster tool group
|
|
||||||
ToolType::Brush => None, // Some(BrushMessage::Abort.into()),
|
|
||||||
ToolType::Heal => None, // Some(HealMessage::Abort.into()),
|
|
||||||
ToolType::Clone => None, // Some(CloneMessage::Abort.into()),
|
|
||||||
ToolType::Patch => None, // Some(PatchMessage::Abort.into()),
|
|
||||||
ToolType::Detail => None, // Some(DetailToolMessage::Abort.into()),
|
|
||||||
ToolType::Relight => None, // Some(RelightMessage::Abort.into()),
|
|
||||||
},
|
},
|
||||||
StandardToolMessageType::SelectionChanged => match tool {
|
ToolBarMetadataGroup {
|
||||||
ToolType::Path => Some(PathToolMessage::SelectionChanged.into()),
|
tool_type: ToolType::Clone,
|
||||||
_ => None,
|
icon_name: "RasterCloneTool".into(),
|
||||||
|
tooltip: "Coming Soon: Clone Tool (C))".into(),
|
||||||
},
|
},
|
||||||
}
|
ToolBarMetadataGroup {
|
||||||
|
tool_type: ToolType::Patch,
|
||||||
|
icon_name: "RasterPatchTool".into(),
|
||||||
|
tooltip: "Coming Soon: Patch Tool".into(),
|
||||||
|
},
|
||||||
|
ToolBarMetadataGroup {
|
||||||
|
tool_type: ToolType::Detail,
|
||||||
|
icon_name: "RasterDetailTool".into(),
|
||||||
|
tooltip: "Coming Soon: Detail Tool (D)".into(),
|
||||||
|
},
|
||||||
|
ToolBarMetadataGroup {
|
||||||
|
tool_type: ToolType::Relight,
|
||||||
|
icon_name: "RasterRelightTool".into(),
|
||||||
|
tooltip: "Coming Soon: Relight Tool (O".into(),
|
||||||
|
},
|
||||||
|
]]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn message_to_tool_type(message: &ToolMessage) -> ToolType {
|
pub fn message_to_tool_type(message: &ToolMessage) -> ToolType {
|
||||||
@@ -422,16 +310,9 @@ pub fn message_to_tool_type(message: &ToolMessage) -> ToolType {
|
|||||||
// Patch(_) => ToolType::Patch,
|
// Patch(_) => ToolType::Patch,
|
||||||
// Detail(_) => ToolType::Detail,
|
// Detail(_) => ToolType::Detail,
|
||||||
// Relight(_) => ToolType::Relight,
|
// Relight(_) => ToolType::Relight,
|
||||||
_ => panic!("Conversion from message to tool type impossible because the given ToolMessage does not belong to a tool"),
|
_ => panic!(
|
||||||
|
"Conversion from message to tool type impossible because the given ToolMessage does not belong to a tool. Got: {:?}",
|
||||||
|
message
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_working_colors(document_data: &DocumentToolData, responses: &mut VecDeque<Message>) {
|
|
||||||
responses.push_back(
|
|
||||||
FrontendMessage::UpdateWorkingColors {
|
|
||||||
primary: document_data.primary_color,
|
|
||||||
secondary: document_data.secondary_color,
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -76,15 +76,11 @@ pub enum ToolMessage {
|
|||||||
|
|
||||||
// Messages
|
// Messages
|
||||||
#[remain::unsorted]
|
#[remain::unsorted]
|
||||||
NoOp,
|
|
||||||
AbortCurrentTool,
|
|
||||||
ActivateTool {
|
ActivateTool {
|
||||||
tool_type: ToolType,
|
tool_type: ToolType,
|
||||||
},
|
},
|
||||||
DocumentIsDirty,
|
|
||||||
InitTools,
|
InitTools,
|
||||||
ResetColors,
|
ResetColors,
|
||||||
SelectionChanged,
|
|
||||||
SelectPrimaryColor {
|
SelectPrimaryColor {
|
||||||
color: Color,
|
color: Color,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use super::tool::{message_to_tool_type, standard_tool_message, update_working_colors, StandardToolMessageType, ToolFsmState};
|
use super::tool::{message_to_tool_type, DocumentToolData, ToolFsmState};
|
||||||
use crate::document::DocumentMessageHandler;
|
use crate::document::DocumentMessageHandler;
|
||||||
use crate::input::InputPreprocessorMessageHandler;
|
use crate::input::InputPreprocessorMessageHandler;
|
||||||
use crate::layout::layout_message::LayoutTarget;
|
use crate::layout::layout_message::LayoutTarget;
|
||||||
@@ -24,11 +24,6 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, &InputPreprocessorMes
|
|||||||
#[remain::sorted]
|
#[remain::sorted]
|
||||||
match message {
|
match message {
|
||||||
// Messages
|
// Messages
|
||||||
AbortCurrentTool => {
|
|
||||||
if let Some(tool_message) = standard_tool_message(self.tool_state.tool_data.active_tool_type, StandardToolMessageType::Abort) {
|
|
||||||
responses.push_front(tool_message.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ActivateTool { tool_type } => {
|
ActivateTool { tool_type } => {
|
||||||
let tool_data = &mut self.tool_state.tool_data;
|
let tool_data = &mut self.tool_state.tool_data;
|
||||||
let document_data = &self.tool_state.document_tool_data;
|
let document_data = &self.tool_state.document_tool_data;
|
||||||
@@ -40,9 +35,11 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, &InputPreprocessorMes
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send the Abort state transition to the tool
|
// Send the Abort state transition to the tool
|
||||||
let mut send_abort_to_tool = |tool_type, message: ToolMessage, update_hints_and_cursor: bool| {
|
let mut send_abort_to_tool = |tool_type, update_hints_and_cursor: bool| {
|
||||||
if let Some(tool) = tool_data.tools.get_mut(&tool_type) {
|
if let Some(tool) = tool_data.tools.get_mut(&tool_type) {
|
||||||
tool.process_action(message, (document, document_data, input, font_cache), responses);
|
if let Some(tool_abort_message) = tool.signal_to_message_map().tool_abort {
|
||||||
|
tool.process_action(tool_abort_message, (document, document_data, input, font_cache), responses);
|
||||||
|
}
|
||||||
|
|
||||||
if update_hints_and_cursor {
|
if update_hints_and_cursor {
|
||||||
tool.process_action(ToolMessage::UpdateHints, (document, document_data, input, font_cache), responses);
|
tool.process_action(ToolMessage::UpdateHints, (document, document_data, input, font_cache), responses);
|
||||||
@@ -50,45 +47,40 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, &InputPreprocessorMes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Send the old and new tools a transition to their FSM Abort states
|
// Send the old and new tools a transition to their FSM Abort states
|
||||||
if let Some(tool_message) = standard_tool_message(tool_type, StandardToolMessageType::Abort) {
|
send_abort_to_tool(tool_type, true);
|
||||||
send_abort_to_tool(tool_type, tool_message, true);
|
send_abort_to_tool(old_tool, false);
|
||||||
}
|
|
||||||
if let Some(tool_message) = standard_tool_message(old_tool, StandardToolMessageType::Abort) {
|
|
||||||
send_abort_to_tool(old_tool, tool_message, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the SelectionChanged message to the active tool, this will ensure the selection is updated
|
// Unsubscribe old tool from the broadcaster
|
||||||
if let Some(message) = standard_tool_message(tool_type, StandardToolMessageType::SelectionChanged) {
|
tool_data.tools.get(&tool_type).unwrap().deactivate(responses);
|
||||||
responses.push_back(message.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the DocumentIsDirty message to the active tool's sub-tool message handler
|
|
||||||
if let Some(message) = standard_tool_message(tool_type, StandardToolMessageType::DocumentIsDirty) {
|
|
||||||
responses.push_back(message.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the new active tool
|
// Store the new active tool
|
||||||
tool_data.active_tool_type = tool_type;
|
tool_data.active_tool_type = tool_type;
|
||||||
|
|
||||||
|
// Subscribe new tool
|
||||||
|
tool_data.tools.get(&tool_type).unwrap().activate(responses);
|
||||||
|
|
||||||
|
// Send the SelectionChanged message to the active tool, this will ensure the selection is updated
|
||||||
|
responses.push_back(BroadcastSignal::SelectionChanged.into());
|
||||||
|
|
||||||
|
// Send the DocumentIsDirty message to the active tool's sub-tool message handler
|
||||||
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
|
|
||||||
// Send Properties to the frontend
|
// Send Properties to the frontend
|
||||||
tool_data.tools.get(&tool_type).unwrap().register_properties(responses, LayoutTarget::ToolOptions);
|
tool_data.tools.get(&tool_type).unwrap().register_properties(responses, LayoutTarget::ToolOptions);
|
||||||
|
|
||||||
// Notify the frontend about the new active tool to be displayed
|
// Notify the frontend about the new active tool to be displayed
|
||||||
tool_data.register_properties(responses, LayoutTarget::ToolShelf);
|
tool_data.register_properties(responses, LayoutTarget::ToolShelf);
|
||||||
}
|
}
|
||||||
DocumentIsDirty => {
|
|
||||||
// Send the DocumentIsDirty message to the active tool's sub-tool message handler
|
|
||||||
let active_tool = self.tool_state.tool_data.active_tool_type;
|
|
||||||
if let Some(message) = standard_tool_message(active_tool, StandardToolMessageType::DocumentIsDirty) {
|
|
||||||
responses.push_back(message.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
InitTools => {
|
InitTools => {
|
||||||
let tool_data = &mut self.tool_state.tool_data;
|
let tool_data = &mut self.tool_state.tool_data;
|
||||||
let document_data = &self.tool_state.document_tool_data;
|
let document_data = &self.tool_state.document_tool_data;
|
||||||
let active_tool = &tool_data.active_tool_type;
|
let active_tool = &tool_data.active_tool_type;
|
||||||
|
|
||||||
|
// subscribe tool to broadcast messages
|
||||||
|
tool_data.tools.get(active_tool).unwrap().activate(responses);
|
||||||
|
|
||||||
// Register initial properties
|
// Register initial properties
|
||||||
tool_data.tools.get(active_tool).unwrap().register_properties(responses, LayoutTarget::ToolOptions);
|
tool_data.tools.get(active_tool).unwrap().register_properties(responses, LayoutTarget::ToolOptions);
|
||||||
|
|
||||||
@@ -111,12 +103,6 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, &InputPreprocessorMes
|
|||||||
|
|
||||||
update_working_colors(document_data, responses);
|
update_working_colors(document_data, responses);
|
||||||
}
|
}
|
||||||
SelectionChanged => {
|
|
||||||
let active_tool = self.tool_state.tool_data.active_tool_type;
|
|
||||||
if let Some(message) = standard_tool_message(active_tool, StandardToolMessageType::SelectionChanged) {
|
|
||||||
responses.push_back(message.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SelectPrimaryColor { color } => {
|
SelectPrimaryColor { color } => {
|
||||||
let document_data = &mut self.tool_state.document_tool_data;
|
let document_data = &mut self.tool_state.document_tool_data;
|
||||||
document_data.primary_color = color;
|
document_data.primary_color = color;
|
||||||
@@ -176,3 +162,13 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, &InputPreprocessorMes
|
|||||||
list
|
list
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_working_colors(document_data: &DocumentToolData, responses: &mut VecDeque<Message>) {
|
||||||
|
responses.push_back(
|
||||||
|
FrontendMessage::UpdateWorkingColors {
|
||||||
|
primary: document_data.primary_color,
|
||||||
|
secondary: document_data.secondary_color,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use crate::layout::widgets::PropertyHolder;
|
|||||||
use crate::message_prelude::*;
|
use crate::message_prelude::*;
|
||||||
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
||||||
use crate::viewport_tools::snapping::SnapHandler;
|
use crate::viewport_tools::snapping::SnapHandler;
|
||||||
use crate::viewport_tools::tool::{Fsm, ToolActionHandlerData};
|
use crate::viewport_tools::tool::{Fsm, SignalToMessageMap, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||||
|
|
||||||
use graphene::intersection::Quad;
|
use graphene::intersection::Quad;
|
||||||
|
|
||||||
@@ -41,6 +41,18 @@ pub enum ArtboardToolMessage {
|
|||||||
PointerUp,
|
PointerUp,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToolMetadata for ArtboardTool {
|
||||||
|
fn icon_name(&self) -> String {
|
||||||
|
"GeneralArtboardTool".into()
|
||||||
|
}
|
||||||
|
fn tooltip(&self) -> String {
|
||||||
|
"Artboard Tool".into()
|
||||||
|
}
|
||||||
|
fn tool_type(&self) -> crate::viewport_tools::tool::ToolType {
|
||||||
|
ToolType::Artboard
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for ArtboardTool {
|
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for ArtboardTool {
|
||||||
fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||||
if action == ToolMessage::UpdateHints {
|
if action == ToolMessage::UpdateHints {
|
||||||
@@ -66,6 +78,16 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for ArtboardTool
|
|||||||
|
|
||||||
impl PropertyHolder for ArtboardTool {}
|
impl PropertyHolder for ArtboardTool {}
|
||||||
|
|
||||||
|
impl ToolTransition for ArtboardTool {
|
||||||
|
fn signal_to_message_map(&self) -> SignalToMessageMap {
|
||||||
|
SignalToMessageMap {
|
||||||
|
document_dirty: Some(ArtboardToolMessage::DocumentIsDirty.into()),
|
||||||
|
tool_abort: Some(ArtboardToolMessage::Abort.into()),
|
||||||
|
selection_changed: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
enum ArtboardToolFsmState {
|
enum ArtboardToolFsmState {
|
||||||
Ready,
|
Ready,
|
||||||
@@ -168,7 +190,7 @@ impl Fsm for ArtboardToolFsmState {
|
|||||||
let quad = Quad::from_box([input.mouse.position - tolerance, input.mouse.position + tolerance]);
|
let quad = Quad::from_box([input.mouse.position - tolerance, input.mouse.position + tolerance]);
|
||||||
let intersection = document.artboard_message_handler.artboards_graphene_document.intersects_quad_root(quad, font_cache);
|
let intersection = document.artboard_message_handler.artboards_graphene_document.intersects_quad_root(quad, font_cache);
|
||||||
|
|
||||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
if let Some(intersection) = intersection.last() {
|
if let Some(intersection) = intersection.last() {
|
||||||
tool_data.selected_board = Some(intersection[0]);
|
tool_data.selected_board = Some(intersection[0]);
|
||||||
|
|
||||||
@@ -229,7 +251,7 @@ impl Fsm for ArtboardToolFsmState {
|
|||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ArtboardToolFsmState::ResizingBounds
|
ArtboardToolFsmState::ResizingBounds
|
||||||
@@ -257,7 +279,7 @@ impl Fsm for ArtboardToolFsmState {
|
|||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
|
|
||||||
tool_data.drag_current = mouse_position + closest_move;
|
tool_data.drag_current = mouse_position + closest_move;
|
||||||
}
|
}
|
||||||
@@ -303,7 +325,7 @@ impl Fsm for ArtboardToolFsmState {
|
|||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
|
|
||||||
ArtboardToolFsmState::Drawing
|
ArtboardToolFsmState::Drawing
|
||||||
}
|
}
|
||||||
@@ -333,7 +355,7 @@ impl Fsm for ArtboardToolFsmState {
|
|||||||
bounds.original_transforms.clear();
|
bounds.original_transforms.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
|
|
||||||
ArtboardToolFsmState::Ready
|
ArtboardToolFsmState::Ready
|
||||||
}
|
}
|
||||||
@@ -349,7 +371,7 @@ impl Fsm for ArtboardToolFsmState {
|
|||||||
(_, ArtboardToolMessage::DeleteSelected) => {
|
(_, ArtboardToolMessage::DeleteSelected) => {
|
||||||
if let Some(artboard) = tool_data.selected_board.take() {
|
if let Some(artboard) = tool_data.selected_board.take() {
|
||||||
responses.push_back(ArtboardMessage::DeleteArtboard { artboard }.into());
|
responses.push_back(ArtboardMessage::DeleteArtboard { artboard }.into());
|
||||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
}
|
}
|
||||||
ArtboardToolFsmState::Ready
|
ArtboardToolFsmState::Ready
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use crate::input::keyboard::{Key, MouseMotion};
|
|||||||
use crate::layout::widgets::PropertyHolder;
|
use crate::layout::widgets::PropertyHolder;
|
||||||
use crate::message_prelude::*;
|
use crate::message_prelude::*;
|
||||||
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
||||||
use crate::viewport_tools::tool::{Fsm, ToolActionHandlerData};
|
use crate::viewport_tools::tool::{Fsm, SignalToMessageMap, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||||
|
|
||||||
use graphene::layers::style;
|
use graphene::layers::style;
|
||||||
use graphene::Operation;
|
use graphene::Operation;
|
||||||
@@ -36,6 +36,18 @@ pub enum EllipseToolMessage {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToolMetadata for EllipseTool {
|
||||||
|
fn icon_name(&self) -> String {
|
||||||
|
"VectorEllipseTool".into()
|
||||||
|
}
|
||||||
|
fn tooltip(&self) -> String {
|
||||||
|
"Ellipse Tool (E)".into()
|
||||||
|
}
|
||||||
|
fn tool_type(&self) -> crate::viewport_tools::tool::ToolType {
|
||||||
|
ToolType::Ellipse
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PropertyHolder for EllipseTool {}
|
impl PropertyHolder for EllipseTool {}
|
||||||
|
|
||||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for EllipseTool {
|
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for EllipseTool {
|
||||||
@@ -69,6 +81,16 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for EllipseTool
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToolTransition for EllipseTool {
|
||||||
|
fn signal_to_message_map(&self) -> SignalToMessageMap {
|
||||||
|
SignalToMessageMap {
|
||||||
|
document_dirty: None,
|
||||||
|
tool_abort: Some(EllipseToolMessage::Abort.into()),
|
||||||
|
selection_changed: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
enum EllipseToolFsmState {
|
enum EllipseToolFsmState {
|
||||||
Ready,
|
Ready,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use crate::input::keyboard::MouseMotion;
|
|||||||
use crate::layout::widgets::PropertyHolder;
|
use crate::layout::widgets::PropertyHolder;
|
||||||
use crate::message_prelude::*;
|
use crate::message_prelude::*;
|
||||||
use crate::misc::{HintData, HintGroup, HintInfo};
|
use crate::misc::{HintData, HintGroup, HintInfo};
|
||||||
use crate::viewport_tools::tool::{Fsm, ToolActionHandlerData};
|
use crate::viewport_tools::tool::{Fsm, SignalToMessageMap, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||||
|
|
||||||
use graphene::intersection::Quad;
|
use graphene::intersection::Quad;
|
||||||
use graphene::layers::layer_info::LayerDataType;
|
use graphene::layers::layer_info::LayerDataType;
|
||||||
@@ -31,6 +31,18 @@ pub enum EyedropperToolMessage {
|
|||||||
RightMouseDown,
|
RightMouseDown,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToolMetadata for EyedropperTool {
|
||||||
|
fn icon_name(&self) -> String {
|
||||||
|
"GeneralEyedropperTool".into()
|
||||||
|
}
|
||||||
|
fn tooltip(&self) -> String {
|
||||||
|
"Eyedropper Tool (I)".into()
|
||||||
|
}
|
||||||
|
fn tool_type(&self) -> crate::viewport_tools::tool::ToolType {
|
||||||
|
ToolType::Eyedropper
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PropertyHolder for EyedropperTool {}
|
impl PropertyHolder for EyedropperTool {}
|
||||||
|
|
||||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for EyedropperTool {
|
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for EyedropperTool {
|
||||||
@@ -57,6 +69,16 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for EyedropperTo
|
|||||||
advertise_actions!(EyedropperToolMessageDiscriminant; LeftMouseDown, RightMouseDown);
|
advertise_actions!(EyedropperToolMessageDiscriminant; LeftMouseDown, RightMouseDown);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToolTransition for EyedropperTool {
|
||||||
|
fn signal_to_message_map(&self) -> SignalToMessageMap {
|
||||||
|
SignalToMessageMap {
|
||||||
|
document_dirty: None,
|
||||||
|
tool_abort: Some(EyedropperToolMessage::Abort.into()),
|
||||||
|
selection_changed: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
enum EyedropperToolFsmState {
|
enum EyedropperToolFsmState {
|
||||||
Ready,
|
Ready,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use crate::input::keyboard::MouseMotion;
|
|||||||
use crate::layout::widgets::PropertyHolder;
|
use crate::layout::widgets::PropertyHolder;
|
||||||
use crate::message_prelude::*;
|
use crate::message_prelude::*;
|
||||||
use crate::misc::{HintData, HintGroup, HintInfo};
|
use crate::misc::{HintData, HintGroup, HintInfo};
|
||||||
use crate::viewport_tools::tool::{Fsm, ToolActionHandlerData};
|
use crate::viewport_tools::tool::{Fsm, SignalToMessageMap, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||||
|
|
||||||
use graphene::intersection::Quad;
|
use graphene::intersection::Quad;
|
||||||
use graphene::Operation;
|
use graphene::Operation;
|
||||||
@@ -32,6 +32,18 @@ pub enum FillToolMessage {
|
|||||||
RightMouseDown,
|
RightMouseDown,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToolMetadata for FillTool {
|
||||||
|
fn icon_name(&self) -> String {
|
||||||
|
"GeneralFillTool".into()
|
||||||
|
}
|
||||||
|
fn tooltip(&self) -> String {
|
||||||
|
"Fill Tool (F)".into()
|
||||||
|
}
|
||||||
|
fn tool_type(&self) -> crate::viewport_tools::tool::ToolType {
|
||||||
|
ToolType::Fill
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PropertyHolder for FillTool {}
|
impl PropertyHolder for FillTool {}
|
||||||
|
|
||||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for FillTool {
|
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for FillTool {
|
||||||
@@ -58,6 +70,16 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for FillTool {
|
|||||||
advertise_actions!(FillToolMessageDiscriminant; LeftMouseDown, RightMouseDown);
|
advertise_actions!(FillToolMessageDiscriminant; LeftMouseDown, RightMouseDown);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToolTransition for FillTool {
|
||||||
|
fn signal_to_message_map(&self) -> SignalToMessageMap {
|
||||||
|
SignalToMessageMap {
|
||||||
|
document_dirty: None,
|
||||||
|
tool_abort: Some(FillToolMessage::Abort.into()),
|
||||||
|
selection_changed: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
enum FillToolFsmState {
|
enum FillToolFsmState {
|
||||||
Ready,
|
Ready,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use crate::input::keyboard::MouseMotion;
|
|||||||
use crate::layout::widgets::{Layout, LayoutGroup, NumberInput, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
use crate::layout::widgets::{Layout, LayoutGroup, NumberInput, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||||
use crate::message_prelude::*;
|
use crate::message_prelude::*;
|
||||||
use crate::misc::{HintData, HintGroup, HintInfo};
|
use crate::misc::{HintData, HintGroup, HintInfo};
|
||||||
use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData};
|
use crate::viewport_tools::tool::{DocumentToolData, Fsm, SignalToMessageMap, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||||
|
|
||||||
use graphene::layers::style;
|
use graphene::layers::style;
|
||||||
use graphene::Operation;
|
use graphene::Operation;
|
||||||
@@ -55,6 +55,18 @@ enum FreehandToolFsmState {
|
|||||||
Drawing,
|
Drawing,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToolMetadata for FreehandTool {
|
||||||
|
fn icon_name(&self) -> String {
|
||||||
|
"VectorFreehandTool".into()
|
||||||
|
}
|
||||||
|
fn tooltip(&self) -> String {
|
||||||
|
"Freehand Tool (N)".into()
|
||||||
|
}
|
||||||
|
fn tool_type(&self) -> crate::viewport_tools::tool::ToolType {
|
||||||
|
ToolType::Freehand
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PropertyHolder for FreehandTool {
|
impl PropertyHolder for FreehandTool {
|
||||||
fn properties(&self) -> Layout {
|
fn properties(&self) -> Layout {
|
||||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
||||||
@@ -109,6 +121,16 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for FreehandTool
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToolTransition for FreehandTool {
|
||||||
|
fn signal_to_message_map(&self) -> SignalToMessageMap {
|
||||||
|
SignalToMessageMap {
|
||||||
|
document_dirty: None,
|
||||||
|
tool_abort: Some(FreehandToolMessage::Abort.into()),
|
||||||
|
selection_changed: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for FreehandToolFsmState {
|
impl Default for FreehandToolFsmState {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
FreehandToolFsmState::Ready
|
FreehandToolFsmState::Ready
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use crate::layout::widgets::{Layout, LayoutGroup, PropertyHolder, RadioEntryData
|
|||||||
use crate::message_prelude::*;
|
use crate::message_prelude::*;
|
||||||
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
||||||
use crate::viewport_tools::snapping::SnapHandler;
|
use crate::viewport_tools::snapping::SnapHandler;
|
||||||
use crate::viewport_tools::tool::{Fsm, ToolActionHandlerData};
|
use crate::viewport_tools::tool::{Fsm, SignalToMessageMap, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||||
|
|
||||||
use graphene::color::Color;
|
use graphene::color::Color;
|
||||||
use graphene::intersection::Quad;
|
use graphene::intersection::Quad;
|
||||||
@@ -60,6 +60,18 @@ pub enum GradientOptionsUpdate {
|
|||||||
Type(GradientType),
|
Type(GradientType),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToolMetadata for GradientTool {
|
||||||
|
fn icon_name(&self) -> String {
|
||||||
|
"GeneralGradientTool".into()
|
||||||
|
}
|
||||||
|
fn tooltip(&self) -> String {
|
||||||
|
"Gradient Tool (H))".into()
|
||||||
|
}
|
||||||
|
fn tool_type(&self) -> crate::viewport_tools::tool::ToolType {
|
||||||
|
ToolType::Gradient
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for GradientTool {
|
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for GradientTool {
|
||||||
fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||||
if action == ToolMessage::UpdateHints {
|
if action == ToolMessage::UpdateHints {
|
||||||
@@ -284,6 +296,16 @@ impl SelectedGradient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToolTransition for GradientTool {
|
||||||
|
fn signal_to_message_map(&self) -> SignalToMessageMap {
|
||||||
|
SignalToMessageMap {
|
||||||
|
document_dirty: Some(GradientToolMessage::DocumentIsDirty.into()),
|
||||||
|
tool_abort: Some(GradientToolMessage::Abort.into()),
|
||||||
|
selection_changed: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct GradientToolData {
|
struct GradientToolData {
|
||||||
gradient_overlays: Vec<GradientOverlay>,
|
gradient_overlays: Vec<GradientOverlay>,
|
||||||
@@ -332,7 +354,7 @@ impl Fsm for GradientToolFsmState {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
(GradientToolFsmState::Ready, GradientToolMessage::PointerDown) => {
|
(GradientToolFsmState::Ready, GradientToolMessage::PointerDown) => {
|
||||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
responses.push_back(BroadcastSignal::DocumentIsDirty.into());
|
||||||
|
|
||||||
let mouse = input.mouse.position;
|
let mouse = input.mouse.position;
|
||||||
let tolerance = VECTOR_MANIPULATOR_ANCHOR_MARKER_SIZE.powi(2);
|
let tolerance = VECTOR_MANIPULATOR_ANCHOR_MARKER_SIZE.powi(2);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use crate::layout::widgets::{Layout, LayoutGroup, NumberInput, PropertyHolder, W
|
|||||||
use crate::message_prelude::*;
|
use crate::message_prelude::*;
|
||||||
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
||||||
use crate::viewport_tools::snapping::SnapHandler;
|
use crate::viewport_tools::snapping::SnapHandler;
|
||||||
use crate::viewport_tools::tool::{Fsm, ToolActionHandlerData};
|
use crate::viewport_tools::tool::{Fsm, SignalToMessageMap, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||||
|
|
||||||
use graphene::layers::style;
|
use graphene::layers::style;
|
||||||
use graphene::Operation;
|
use graphene::Operation;
|
||||||
@@ -56,6 +56,18 @@ pub enum LineOptionsUpdate {
|
|||||||
LineWeight(f64),
|
LineWeight(f64),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToolMetadata for LineTool {
|
||||||
|
fn icon_name(&self) -> String {
|
||||||
|
"VectorLineTool".into()
|
||||||
|
}
|
||||||
|
fn tooltip(&self) -> String {
|
||||||
|
"Line Tool (L)".into()
|
||||||
|
}
|
||||||
|
fn tool_type(&self) -> crate::viewport_tools::tool::ToolType {
|
||||||
|
ToolType::Line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PropertyHolder for LineTool {
|
impl PropertyHolder for LineTool {
|
||||||
fn properties(&self) -> Layout {
|
fn properties(&self) -> Layout {
|
||||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
||||||
@@ -110,6 +122,16 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for LineTool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToolTransition for LineTool {
|
||||||
|
fn signal_to_message_map(&self) -> SignalToMessageMap {
|
||||||
|
SignalToMessageMap {
|
||||||
|
document_dirty: None,
|
||||||
|
tool_abort: Some(LineToolMessage::Abort.into()),
|
||||||
|
selection_changed: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
enum LineToolFsmState {
|
enum LineToolFsmState {
|
||||||
Ready,
|
Ready,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use crate::input::keyboard::{Key, MouseMotion};
|
|||||||
use crate::layout::widgets::PropertyHolder;
|
use crate::layout::widgets::PropertyHolder;
|
||||||
use crate::message_prelude::*;
|
use crate::message_prelude::*;
|
||||||
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
||||||
use crate::viewport_tools::tool::{Fsm, ToolActionHandlerData};
|
use crate::viewport_tools::tool::{Fsm, SignalToMessageMap, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||||
|
|
||||||
use glam::DVec2;
|
use glam::DVec2;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -36,6 +36,18 @@ pub enum NavigateToolMessage {
|
|||||||
ZoomCanvasBegin,
|
ZoomCanvasBegin,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToolMetadata for NavigateTool {
|
||||||
|
fn icon_name(&self) -> String {
|
||||||
|
"GeneralNavigateTool".into()
|
||||||
|
}
|
||||||
|
fn tooltip(&self) -> String {
|
||||||
|
"Navigate Tool (Z)".into()
|
||||||
|
}
|
||||||
|
fn tool_type(&self) -> crate::viewport_tools::tool::ToolType {
|
||||||
|
ToolType::Navigate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PropertyHolder for NavigateTool {}
|
impl PropertyHolder for NavigateTool {}
|
||||||
|
|
||||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for NavigateTool {
|
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for NavigateTool {
|
||||||
@@ -69,6 +81,16 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for NavigateTool
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToolTransition for NavigateTool {
|
||||||
|
fn signal_to_message_map(&self) -> SignalToMessageMap {
|
||||||
|
SignalToMessageMap {
|
||||||
|
document_dirty: None,
|
||||||
|
tool_abort: Some(NavigateToolMessage::Abort.into()),
|
||||||
|
selection_changed: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
enum NavigateToolFsmState {
|
enum NavigateToolFsmState {
|
||||||
Ready,
|
Ready,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use crate::layout::widgets::PropertyHolder;
|
|||||||
use crate::message_prelude::*;
|
use crate::message_prelude::*;
|
||||||
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
||||||
use crate::viewport_tools::snapping::SnapHandler;
|
use crate::viewport_tools::snapping::SnapHandler;
|
||||||
use crate::viewport_tools::tool::{Fsm, ToolActionHandlerData};
|
use crate::viewport_tools::tool::{Fsm, SignalToMessageMap, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||||
use crate::viewport_tools::vector_editor::shape_editor::ShapeEditor;
|
use crate::viewport_tools::vector_editor::shape_editor::ShapeEditor;
|
||||||
|
|
||||||
use graphene::intersection::Quad;
|
use graphene::intersection::Quad;
|
||||||
@@ -42,6 +42,18 @@ pub enum PathToolMessage {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToolMetadata for PathTool {
|
||||||
|
fn icon_name(&self) -> String {
|
||||||
|
"VectorPathTool".into()
|
||||||
|
}
|
||||||
|
fn tooltip(&self) -> String {
|
||||||
|
"Path Tool (A)".into()
|
||||||
|
}
|
||||||
|
fn tool_type(&self) -> crate::viewport_tools::tool::ToolType {
|
||||||
|
ToolType::Path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PropertyHolder for PathTool {}
|
impl PropertyHolder for PathTool {}
|
||||||
|
|
||||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for PathTool {
|
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for PathTool {
|
||||||
@@ -76,6 +88,16 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for PathTool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToolTransition for PathTool {
|
||||||
|
fn signal_to_message_map(&self) -> SignalToMessageMap {
|
||||||
|
SignalToMessageMap {
|
||||||
|
document_dirty: Some(PathToolMessage::DocumentIsDirty.into()),
|
||||||
|
tool_abort: Some(PathToolMessage::Abort.into()),
|
||||||
|
selection_changed: Some(PathToolMessage::SelectionChanged.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
enum PathToolFsmState {
|
enum PathToolFsmState {
|
||||||
Ready,
|
Ready,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use crate::layout::widgets::{Layout, LayoutGroup, NumberInput, PropertyHolder, W
|
|||||||
use crate::message_prelude::*;
|
use crate::message_prelude::*;
|
||||||
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
||||||
use crate::viewport_tools::snapping::SnapHandler;
|
use crate::viewport_tools::snapping::SnapHandler;
|
||||||
use crate::viewport_tools::tool::{Fsm, ToolActionHandlerData};
|
use crate::viewport_tools::tool::{Fsm, SignalToMessageMap, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||||
use crate::viewport_tools::vector_editor::constants::ControlPointType;
|
use crate::viewport_tools::vector_editor::constants::ControlPointType;
|
||||||
use crate::viewport_tools::vector_editor::shape_editor::ShapeEditor;
|
use crate::viewport_tools::vector_editor::shape_editor::ShapeEditor;
|
||||||
use crate::viewport_tools::vector_editor::vector_shape::VectorShape;
|
use crate::viewport_tools::vector_editor::vector_shape::VectorShape;
|
||||||
@@ -67,6 +67,18 @@ pub enum PenOptionsUpdate {
|
|||||||
LineWeight(f64),
|
LineWeight(f64),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToolMetadata for PenTool {
|
||||||
|
fn icon_name(&self) -> String {
|
||||||
|
"VectorPenTool".into()
|
||||||
|
}
|
||||||
|
fn tooltip(&self) -> String {
|
||||||
|
"Pen Tool (P)".into()
|
||||||
|
}
|
||||||
|
fn tool_type(&self) -> crate::viewport_tools::tool::ToolType {
|
||||||
|
ToolType::Pen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PropertyHolder for PenTool {
|
impl PropertyHolder for PenTool {
|
||||||
fn properties(&self) -> Layout {
|
fn properties(&self) -> Layout {
|
||||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
||||||
@@ -121,6 +133,16 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for PenTool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToolTransition for PenTool {
|
||||||
|
fn signal_to_message_map(&self) -> SignalToMessageMap {
|
||||||
|
SignalToMessageMap {
|
||||||
|
document_dirty: Some(PenToolMessage::DocumentIsDirty.into()),
|
||||||
|
tool_abort: Some(PenToolMessage::Abort.into()),
|
||||||
|
selection_changed: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for PenToolFsmState {
|
impl Default for PenToolFsmState {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
PenToolFsmState::Ready
|
PenToolFsmState::Ready
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use crate::input::keyboard::{Key, MouseMotion};
|
|||||||
use crate::layout::widgets::PropertyHolder;
|
use crate::layout::widgets::PropertyHolder;
|
||||||
use crate::message_prelude::*;
|
use crate::message_prelude::*;
|
||||||
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
||||||
use crate::viewport_tools::tool::{Fsm, ToolActionHandlerData};
|
use crate::viewport_tools::tool::{Fsm, SignalToMessageMap, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||||
|
|
||||||
use graphene::layers::style;
|
use graphene::layers::style;
|
||||||
use graphene::Operation;
|
use graphene::Operation;
|
||||||
@@ -69,6 +69,28 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for RectangleToo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToolMetadata for RectangleTool {
|
||||||
|
fn icon_name(&self) -> String {
|
||||||
|
"VectorRectangleTool".into()
|
||||||
|
}
|
||||||
|
fn tooltip(&self) -> String {
|
||||||
|
"Rectangle Tool (M)".into()
|
||||||
|
}
|
||||||
|
fn tool_type(&self) -> crate::viewport_tools::tool::ToolType {
|
||||||
|
ToolType::Rectangle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToolTransition for RectangleTool {
|
||||||
|
fn signal_to_message_map(&self) -> SignalToMessageMap {
|
||||||
|
SignalToMessageMap {
|
||||||
|
document_dirty: None,
|
||||||
|
tool_abort: Some(RectangleToolMessage::Abort.into()),
|
||||||
|
selection_changed: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
enum RectangleToolFsmState {
|
enum RectangleToolFsmState {
|
||||||
Ready,
|
Ready,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use crate::layout::widgets::{IconButton, Layout, LayoutGroup, PopoverButton, Pro
|
|||||||
use crate::message_prelude::*;
|
use crate::message_prelude::*;
|
||||||
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
||||||
use crate::viewport_tools::snapping::{self, SnapHandler};
|
use crate::viewport_tools::snapping::{self, SnapHandler};
|
||||||
use crate::viewport_tools::tool::{Fsm, ToolActionHandlerData, ToolType};
|
use crate::viewport_tools::tool::{Fsm, SignalToMessageMap, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||||
use graphene::boolean_ops::BooleanOperation;
|
use graphene::boolean_ops::BooleanOperation;
|
||||||
use graphene::document::Document;
|
use graphene::document::Document;
|
||||||
use graphene::intersection::Quad;
|
use graphene::intersection::Quad;
|
||||||
@@ -56,6 +56,18 @@ pub enum SelectToolMessage {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToolMetadata for SelectTool {
|
||||||
|
fn icon_name(&self) -> String {
|
||||||
|
"GeneralSelectTool".into()
|
||||||
|
}
|
||||||
|
fn tooltip(&self) -> String {
|
||||||
|
"Select Tool (V)".into()
|
||||||
|
}
|
||||||
|
fn tool_type(&self) -> crate::viewport_tools::tool::ToolType {
|
||||||
|
ToolType::Select
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PropertyHolder for SelectTool {
|
impl PropertyHolder for SelectTool {
|
||||||
fn properties(&self) -> Layout {
|
fn properties(&self) -> Layout {
|
||||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
||||||
@@ -258,6 +270,16 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for SelectTool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToolTransition for SelectTool {
|
||||||
|
fn signal_to_message_map(&self) -> SignalToMessageMap {
|
||||||
|
SignalToMessageMap {
|
||||||
|
document_dirty: Some(SelectToolMessage::DocumentIsDirty.into()),
|
||||||
|
tool_abort: Some(SelectToolMessage::Abort.into()),
|
||||||
|
selection_changed: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
enum SelectToolFsmState {
|
enum SelectToolFsmState {
|
||||||
Ready,
|
Ready,
|
||||||
@@ -317,6 +339,7 @@ impl Fsm for SelectToolFsmState {
|
|||||||
use SelectToolMessage::*;
|
use SelectToolMessage::*;
|
||||||
|
|
||||||
if let ToolMessage::Select(event) = event {
|
if let ToolMessage::Select(event) = event {
|
||||||
|
log::debug!("self: {:?}, even: {:?}", self, event);
|
||||||
match (self, event) {
|
match (self, event) {
|
||||||
(_, DocumentIsDirty) => {
|
(_, DocumentIsDirty) => {
|
||||||
let mut buffer = Vec::new();
|
let mut buffer = Vec::new();
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use crate::input::keyboard::{Key, MouseMotion};
|
|||||||
use crate::layout::widgets::{Layout, LayoutGroup, NumberInput, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
use crate::layout::widgets::{Layout, LayoutGroup, NumberInput, PropertyHolder, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||||
use crate::message_prelude::*;
|
use crate::message_prelude::*;
|
||||||
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
||||||
use crate::viewport_tools::tool::{Fsm, ToolActionHandlerData};
|
use crate::viewport_tools::tool::{Fsm, SignalToMessageMap, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||||
|
|
||||||
use graphene::layers::style;
|
use graphene::layers::style;
|
||||||
use graphene::Operation;
|
use graphene::Operation;
|
||||||
@@ -54,6 +54,18 @@ pub enum ShapeOptionsUpdate {
|
|||||||
Vertices(u32),
|
Vertices(u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToolMetadata for ShapeTool {
|
||||||
|
fn icon_name(&self) -> String {
|
||||||
|
"VectorShapeTool".into()
|
||||||
|
}
|
||||||
|
fn tooltip(&self) -> String {
|
||||||
|
"Shape Tool (Y)".into()
|
||||||
|
}
|
||||||
|
fn tool_type(&self) -> crate::viewport_tools::tool::ToolType {
|
||||||
|
ToolType::Shape
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PropertyHolder for ShapeTool {
|
impl PropertyHolder for ShapeTool {
|
||||||
fn properties(&self) -> Layout {
|
fn properties(&self) -> Layout {
|
||||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
||||||
@@ -108,6 +120,16 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for ShapeTool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToolTransition for ShapeTool {
|
||||||
|
fn signal_to_message_map(&self) -> SignalToMessageMap {
|
||||||
|
SignalToMessageMap {
|
||||||
|
document_dirty: None,
|
||||||
|
tool_abort: Some(ShapeToolMessage::Abort.into()),
|
||||||
|
selection_changed: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
enum ShapeToolFsmState {
|
enum ShapeToolFsmState {
|
||||||
Ready,
|
Ready,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use crate::layout::widgets::{Layout, LayoutGroup, NumberInput, PropertyHolder, W
|
|||||||
use crate::message_prelude::*;
|
use crate::message_prelude::*;
|
||||||
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
||||||
use crate::viewport_tools::snapping::SnapHandler;
|
use crate::viewport_tools::snapping::SnapHandler;
|
||||||
use crate::viewport_tools::tool::{DocumentToolData, Fsm, ToolActionHandlerData};
|
use crate::viewport_tools::tool::{DocumentToolData, Fsm, SignalToMessageMap, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||||
|
|
||||||
use graphene::layers::style;
|
use graphene::layers::style;
|
||||||
use graphene::Operation;
|
use graphene::Operation;
|
||||||
@@ -59,6 +59,18 @@ pub enum SplineOptionsUpdate {
|
|||||||
LineWeight(f64),
|
LineWeight(f64),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToolMetadata for SplineTool {
|
||||||
|
fn icon_name(&self) -> String {
|
||||||
|
"VectorSplineTool".into()
|
||||||
|
}
|
||||||
|
fn tooltip(&self) -> String {
|
||||||
|
"Spline Tool".into()
|
||||||
|
}
|
||||||
|
fn tool_type(&self) -> crate::viewport_tools::tool::ToolType {
|
||||||
|
ToolType::Spline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PropertyHolder for SplineTool {
|
impl PropertyHolder for SplineTool {
|
||||||
fn properties(&self) -> Layout {
|
fn properties(&self) -> Layout {
|
||||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
||||||
@@ -113,6 +125,16 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for SplineTool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToolTransition for SplineTool {
|
||||||
|
fn signal_to_message_map(&self) -> SignalToMessageMap {
|
||||||
|
SignalToMessageMap {
|
||||||
|
document_dirty: None,
|
||||||
|
tool_abort: Some(SplineToolMessage::Abort.into()),
|
||||||
|
selection_changed: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for SplineToolFsmState {
|
impl Default for SplineToolFsmState {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
SplineToolFsmState::Ready
|
SplineToolFsmState::Ready
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use crate::layout::layout_message::LayoutTarget;
|
|||||||
use crate::layout::widgets::{FontInput, Layout, LayoutGroup, NumberInput, PropertyHolder, Separator, SeparatorDirection, SeparatorType, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
use crate::layout::widgets::{FontInput, Layout, LayoutGroup, NumberInput, PropertyHolder, Separator, SeparatorDirection, SeparatorType, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||||
use crate::message_prelude::*;
|
use crate::message_prelude::*;
|
||||||
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
|
||||||
use crate::viewport_tools::tool::{Fsm, ToolActionHandlerData};
|
use crate::viewport_tools::tool::{Fsm, SignalToMessageMap, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||||
|
|
||||||
use graphene::intersection::Quad;
|
use graphene::intersection::Quad;
|
||||||
use graphene::layers::style::{self, Fill, Stroke};
|
use graphene::layers::style::{self, Fill, Stroke};
|
||||||
@@ -70,6 +70,18 @@ pub enum TextOptionsUpdate {
|
|||||||
FontSize(u32),
|
FontSize(u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToolMetadata for TextTool {
|
||||||
|
fn icon_name(&self) -> String {
|
||||||
|
"VectorTextTool".into()
|
||||||
|
}
|
||||||
|
fn tooltip(&self) -> String {
|
||||||
|
"Text Tool (T)".into()
|
||||||
|
}
|
||||||
|
fn tool_type(&self) -> crate::viewport_tools::tool::ToolType {
|
||||||
|
ToolType::Text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PropertyHolder for TextTool {
|
impl PropertyHolder for TextTool {
|
||||||
fn properties(&self) -> Layout {
|
fn properties(&self) -> Layout {
|
||||||
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row {
|
||||||
@@ -164,6 +176,16 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for TextTool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToolTransition for TextTool {
|
||||||
|
fn signal_to_message_map(&self) -> SignalToMessageMap {
|
||||||
|
SignalToMessageMap {
|
||||||
|
document_dirty: Some(TextMessage::DocumentIsDirty.into()),
|
||||||
|
tool_abort: Some(TextMessage::Abort.into()),
|
||||||
|
selection_changed: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
enum TextToolFsmState {
|
enum TextToolFsmState {
|
||||||
Ready,
|
Ready,
|
||||||
|
|||||||
@@ -112,6 +112,9 @@ impl JsEditorHandle {
|
|||||||
let message = PortfolioMessage::UpdateDocumentWidgets;
|
let message = PortfolioMessage::UpdateDocumentWidgets;
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
|
|
||||||
|
let message = PropertiesPanelMessage::Init;
|
||||||
|
self.dispatch(message);
|
||||||
|
|
||||||
let message = ToolMessage::InitTools;
|
let message = ToolMessage::InitTools;
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user