Additional clean up and bug fixes after migrating document-legacy
This commit is contained in:
parent
4733134b22
commit
5c7e04a725
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -18,6 +18,8 @@ pub const VIEWPORT_SCROLL_RATE: f64 = 0.6;
|
||||||
|
|
||||||
pub const VIEWPORT_ROTATE_SNAP_INTERVAL: f64 = 15.;
|
pub const VIEWPORT_ROTATE_SNAP_INTERVAL: f64 = 15.;
|
||||||
|
|
||||||
|
pub const VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR: f64 = 0.95;
|
||||||
|
|
||||||
// Snapping axis
|
// Snapping axis
|
||||||
pub const SNAP_AXIS_TOLERANCE: f64 = 3.;
|
pub const SNAP_AXIS_TOLERANCE: f64 = 3.;
|
||||||
pub const SNAP_AXIS_OVERLAY_FADE_DISTANCE: f64 = 15.;
|
pub const SNAP_AXIS_OVERLAY_FADE_DISTANCE: f64 = 15.;
|
||||||
|
|
@ -79,9 +81,7 @@ pub const DEFAULT_FONT_FAMILY: &str = "Merriweather";
|
||||||
pub const DEFAULT_FONT_STYLE: &str = "Normal (400)";
|
pub const DEFAULT_FONT_STYLE: &str = "Normal (400)";
|
||||||
|
|
||||||
// Document
|
// Document
|
||||||
pub const GRAPHITE_DOCUMENT_VERSION: &str = "0.1.1"; // Remember to update the demo artwork in /demos with both this version number and the contents so it remains editable
|
pub const GRAPHITE_DOCUMENT_VERSION: &str = "0.1.2"; // Remember to update the demo artwork in /demos with both this version number and the contents so it remains editable
|
||||||
pub const DEFAULT_DOCUMENT_NAME: &str = "Untitled Document";
|
pub const DEFAULT_DOCUMENT_NAME: &str = "Untitled Document";
|
||||||
pub const FILE_SAVE_SUFFIX: &str = ".graphite";
|
pub const FILE_SAVE_SUFFIX: &str = ".graphite";
|
||||||
pub const MAX_UNDO_HISTORY_LEN: usize = 100; // TODO: Add this to user preferences
|
pub const MAX_UNDO_HISTORY_LEN: usize = 100; // TODO: Add this to user preferences
|
||||||
|
|
||||||
pub const VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR: f32 = 1.05;
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
use crate::consts::VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR;
|
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
|
|
@ -34,7 +33,6 @@ impl MessageHandler<NewDocumentDialogMessage, ()> for NewDocumentDialogMessageHa
|
||||||
});
|
});
|
||||||
responses.add(NavigationMessage::FitViewportToBounds {
|
responses.add(NavigationMessage::FitViewportToBounds {
|
||||||
bounds: [DVec2::ZERO, self.dimensions.as_dvec2()],
|
bounds: [DVec2::ZERO, self.dimensions.as_dvec2()],
|
||||||
padding_scale_factor: Some(VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR),
|
|
||||||
prevent_zoom_past_100: true,
|
prevent_zoom_past_100: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,9 @@ pub fn default_mapping() -> Mapping {
|
||||||
// it as an available action in the respective message handler file (such as the bottom of `document_message_handler.rs`).
|
// it as an available action in the respective message handler file (such as the bottom of `document_message_handler.rs`).
|
||||||
|
|
||||||
let mappings = mapping![
|
let mappings = mapping![
|
||||||
// HIGHER PRIORITY:
|
// ===============
|
||||||
|
// HIGHER PRIORITY
|
||||||
|
// ===============
|
||||||
//
|
//
|
||||||
// NavigationMessage
|
// NavigationMessage
|
||||||
entry!(
|
entry!(
|
||||||
|
|
@ -41,7 +43,9 @@ pub fn default_mapping() -> Mapping {
|
||||||
entry!(KeyDown(Lmb); action_dispatch=NavigationMessage::TransformFromMenuEnd { commit_key: Key::Lmb }),
|
entry!(KeyDown(Lmb); action_dispatch=NavigationMessage::TransformFromMenuEnd { commit_key: Key::Lmb }),
|
||||||
entry!(KeyDown(Mmb); action_dispatch=NavigationMessage::TransformFromMenuEnd { commit_key: Key::Mmb }),
|
entry!(KeyDown(Mmb); action_dispatch=NavigationMessage::TransformFromMenuEnd { commit_key: Key::Mmb }),
|
||||||
entry!(KeyDown(Rmb); action_dispatch=NavigationMessage::TransformFromMenuEnd { commit_key: Key::Rmb }),
|
entry!(KeyDown(Rmb); action_dispatch=NavigationMessage::TransformFromMenuEnd { commit_key: Key::Rmb }),
|
||||||
// NORMAL PRIORITY:
|
// ===============
|
||||||
|
// NORMAL PRIORITY
|
||||||
|
// ===============
|
||||||
//
|
//
|
||||||
// NodeGraphMessage
|
// NodeGraphMessage
|
||||||
entry!(KeyDown(Delete); modifiers=[Accel], action_dispatch=NodeGraphMessage::DeleteSelectedNodes { reconnect: false }),
|
entry!(KeyDown(Delete); modifiers=[Accel], action_dispatch=NodeGraphMessage::DeleteSelectedNodes { reconnect: false }),
|
||||||
|
|
@ -312,7 +316,6 @@ pub fn default_mapping() -> Mapping {
|
||||||
entry!(KeyDown(KeyS); action_dispatch=TransformLayerMessage::BeginScale),
|
entry!(KeyDown(KeyS); action_dispatch=TransformLayerMessage::BeginScale),
|
||||||
//
|
//
|
||||||
// NavigationMessage
|
// NavigationMessage
|
||||||
entry!(KeyDown(Lmb); modifiers=[Alt], action_dispatch=NavigationMessage::RotateCanvasBegin { was_dispatched_from_menu: false }),
|
|
||||||
entry!(KeyDown(Mmb); modifiers=[Alt], action_dispatch=NavigationMessage::RotateCanvasBegin { was_dispatched_from_menu: false }),
|
entry!(KeyDown(Mmb); modifiers=[Alt], action_dispatch=NavigationMessage::RotateCanvasBegin { was_dispatched_from_menu: false }),
|
||||||
entry!(KeyDown(Mmb); modifiers=[Shift], action_dispatch=NavigationMessage::ZoomCanvasBegin),
|
entry!(KeyDown(Mmb); modifiers=[Shift], action_dispatch=NavigationMessage::ZoomCanvasBegin),
|
||||||
entry!(KeyDown(Lmb); modifiers=[Shift, Space], action_dispatch=NavigationMessage::ZoomCanvasBegin),
|
entry!(KeyDown(Lmb); modifiers=[Shift, Space], action_dispatch=NavigationMessage::ZoomCanvasBegin),
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate,
|
||||||
use crate::messages::portfolio::document::utility_types::LayerId;
|
use crate::messages::portfolio::document::utility_types::LayerId;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
use graph_craft::document::NodeId;
|
use graph_craft::document::{NodeId, NodeNetwork};
|
||||||
use graphene_core::raster::BlendMode;
|
use graphene_core::raster::BlendMode;
|
||||||
use graphene_core::raster::Image;
|
use graphene_core::raster::Image;
|
||||||
use graphene_core::vector::style::ViewMode;
|
use graphene_core::vector::style::ViewMode;
|
||||||
|
|
@ -40,7 +40,7 @@ pub enum DocumentMessage {
|
||||||
aggregate: AlignAggregate,
|
aggregate: AlignAggregate,
|
||||||
},
|
},
|
||||||
BackupDocument {
|
BackupDocument {
|
||||||
document: DocumentMessageHandler,
|
network: NodeNetwork,
|
||||||
},
|
},
|
||||||
ClearLayerTree,
|
ClearLayerTree,
|
||||||
CommitTransaction,
|
CommitTransaction,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use super::utility_types::error::EditorError;
|
use super::utility_types::error::EditorError;
|
||||||
use super::utility_types::misc::{SnappingOptions, SnappingState};
|
use super::utility_types::misc::{SnappingOptions, SnappingState};
|
||||||
use crate::application::generate_uuid;
|
use crate::application::{generate_uuid, GRAPHITE_GIT_COMMIT_HASH};
|
||||||
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};
|
||||||
use crate::messages::input_mapper::utility_types::macros::action_keys;
|
use crate::messages::input_mapper::utility_types::macros::action_keys;
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::portfolio::document::node_graph::NodeGraphHandlerData;
|
use crate::messages::portfolio::document::node_graph::NodeGraphHandlerData;
|
||||||
|
|
@ -9,7 +9,7 @@ use crate::messages::portfolio::document::properties_panel::utility_types::Prope
|
||||||
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
|
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
|
||||||
use crate::messages::portfolio::document::utility_types::document_metadata::{is_artboard, DocumentMetadata, LayerNodeIdentifier};
|
use crate::messages::portfolio::document::utility_types::document_metadata::{is_artboard, DocumentMetadata, LayerNodeIdentifier};
|
||||||
use crate::messages::portfolio::document::utility_types::layer_panel::RawBuffer;
|
use crate::messages::portfolio::document::utility_types::layer_panel::RawBuffer;
|
||||||
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, DocumentMode, FlipAxis};
|
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, DocumentMode, FlipAxis, PTZ};
|
||||||
use crate::messages::portfolio::document::utility_types::LayerId;
|
use crate::messages::portfolio::document::utility_types::LayerId;
|
||||||
use crate::messages::portfolio::utility_types::PersistentData;
|
use crate::messages::portfolio::utility_types::PersistentData;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
@ -29,56 +29,55 @@ use graphene_std::wasm_application_io::WasmEditorApi;
|
||||||
|
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::hash_map::DefaultHasher;
|
|
||||||
use std::hash::Hasher;
|
|
||||||
use std::vec;
|
use std::vec;
|
||||||
|
|
||||||
/// Utility function for providing a default boolean value to serde.
|
|
||||||
#[inline(always)]
|
|
||||||
fn return_true() -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct DocumentMessageHandler {
|
pub struct DocumentMessageHandler {
|
||||||
|
// ======================
|
||||||
// Child message handlers
|
// Child message handlers
|
||||||
navigation_handler: NavigationMessageHandler,
|
// ======================
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
node_graph_handler: NodeGraphMessageHandler,
|
node_graph_handler: NodeGraphMessageHandler,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
|
navigation_handler: NavigationMessageHandler,
|
||||||
|
#[serde(skip)]
|
||||||
overlays_message_handler: OverlaysMessageHandler,
|
overlays_message_handler: OverlaysMessageHandler,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
properties_panel_message_handler: PropertiesPanelMessageHandler,
|
properties_panel_message_handler: PropertiesPanelMessageHandler,
|
||||||
|
// ============================================
|
||||||
// Fields that are saved in the document format
|
// Fields that are saved in the document format
|
||||||
//
|
// ============================================
|
||||||
pub name: String,
|
#[serde(default = "default_network")]
|
||||||
pub version: String,
|
|
||||||
pub network: NodeNetwork,
|
pub network: NodeNetwork,
|
||||||
pub saved_document_identifier: u64,
|
#[serde(default = "default_name")]
|
||||||
pub auto_saved_document_identifier: u64,
|
pub name: String,
|
||||||
|
#[serde(default = "default_version")]
|
||||||
// Fields that can be non-fatally missing from the saved document format
|
version: String,
|
||||||
//
|
#[serde(default = "default_commit_hash")]
|
||||||
#[serde(default)]
|
commit_hash: String,
|
||||||
pub document_mode: DocumentMode,
|
#[serde(default = "default_pan_tilt_zoom")]
|
||||||
#[serde(default)]
|
navigation: PTZ,
|
||||||
|
#[serde(default = "default_document_mode")]
|
||||||
|
document_mode: DocumentMode,
|
||||||
|
#[serde(default = "default_view_mode")]
|
||||||
pub view_mode: ViewMode,
|
pub view_mode: ViewMode,
|
||||||
#[serde(default = "return_true")]
|
#[serde(default = "default_overlays_visible")]
|
||||||
pub overlays_visible: bool,
|
overlays_visible: bool,
|
||||||
#[serde(default = "return_true")]
|
#[serde(default = "default_rulers_visible")]
|
||||||
pub rulers_visible: bool,
|
pub rulers_visible: bool,
|
||||||
#[serde(default)]
|
#[serde(default = "default_collapsed")]
|
||||||
pub commit_hash: String,
|
pub collapsed: Vec<LayerNodeIdentifier>, // TODO: Is this actually used? Maybe or maybe not. Investigate and potentially remove.
|
||||||
#[serde(default)]
|
// =============================================
|
||||||
pub collapsed: Vec<LayerNodeIdentifier>,
|
|
||||||
|
|
||||||
// Fields omitted from the saved document format
|
// Fields omitted from the saved document format
|
||||||
//
|
// =============================================
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub document_undo_history: VecDeque<DocumentMessageHandler>,
|
document_undo_history: VecDeque<NodeNetwork>,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub document_redo_history: VecDeque<DocumentMessageHandler>,
|
document_redo_history: VecDeque<NodeNetwork>,
|
||||||
|
#[serde(skip)]
|
||||||
|
saved_hash: Option<u64>,
|
||||||
|
#[serde(skip)]
|
||||||
|
auto_saved_hash: Option<u64>,
|
||||||
/// Don't allow aborting transactions whilst undoing to avoid #559
|
/// Don't allow aborting transactions whilst undoing to avoid #559
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
undo_in_progress: bool,
|
undo_in_progress: bool,
|
||||||
|
|
@ -88,41 +87,87 @@ pub struct DocumentMessageHandler {
|
||||||
layer_range_selection_reference: Option<LayerNodeIdentifier>,
|
layer_range_selection_reference: Option<LayerNodeIdentifier>,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub metadata: DocumentMetadata,
|
pub metadata: DocumentMetadata,
|
||||||
/// The state_identifier serves to provide a way to uniquely identify a particular state that the document is in.
|
|
||||||
/// This identifier is not a hash and is not guaranteed to be equal for equivalent documents.
|
|
||||||
#[serde(skip)]
|
|
||||||
pub state_identifier: DefaultHasher,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for DocumentMessageHandler {
|
impl Default for DocumentMessageHandler {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
network: root_network(),
|
// ======================
|
||||||
saved_document_identifier: 0,
|
// Child message handlers
|
||||||
auto_saved_document_identifier: 0,
|
// ======================
|
||||||
name: DEFAULT_DOCUMENT_NAME.to_string(),
|
node_graph_handler: Default::default(),
|
||||||
version: GRAPHITE_DOCUMENT_VERSION.to_string(),
|
|
||||||
commit_hash: crate::application::GRAPHITE_GIT_COMMIT_HASH.to_string(),
|
|
||||||
collapsed: Vec::new(),
|
|
||||||
document_mode: DocumentMode::DesignMode,
|
|
||||||
view_mode: ViewMode::default(),
|
|
||||||
snapping_state: SnappingState::default(),
|
|
||||||
overlays_visible: true,
|
|
||||||
rulers_visible: true,
|
|
||||||
document_undo_history: VecDeque::new(),
|
|
||||||
document_redo_history: VecDeque::new(),
|
|
||||||
undo_in_progress: false,
|
|
||||||
layer_range_selection_reference: None,
|
|
||||||
navigation_handler: NavigationMessageHandler::default(),
|
navigation_handler: NavigationMessageHandler::default(),
|
||||||
overlays_message_handler: OverlaysMessageHandler::default(),
|
overlays_message_handler: OverlaysMessageHandler::default(),
|
||||||
properties_panel_message_handler: PropertiesPanelMessageHandler::default(),
|
properties_panel_message_handler: PropertiesPanelMessageHandler::default(),
|
||||||
node_graph_handler: Default::default(),
|
// ============================================
|
||||||
state_identifier: DefaultHasher::new(),
|
// Fields that are saved in the document format
|
||||||
|
// ============================================
|
||||||
|
network: root_network(),
|
||||||
|
name: DEFAULT_DOCUMENT_NAME.to_string(),
|
||||||
|
version: GRAPHITE_DOCUMENT_VERSION.to_string(),
|
||||||
|
commit_hash: GRAPHITE_GIT_COMMIT_HASH.to_string(),
|
||||||
|
navigation: PTZ::default(),
|
||||||
|
document_mode: DocumentMode::DesignMode,
|
||||||
|
view_mode: ViewMode::default(),
|
||||||
|
overlays_visible: true,
|
||||||
|
rulers_visible: true,
|
||||||
|
collapsed: Vec::new(),
|
||||||
|
// =============================================
|
||||||
|
// Fields omitted from the saved document format
|
||||||
|
// =============================================
|
||||||
|
document_undo_history: VecDeque::new(),
|
||||||
|
document_redo_history: VecDeque::new(),
|
||||||
|
saved_hash: None,
|
||||||
|
auto_saved_hash: None,
|
||||||
|
undo_in_progress: false,
|
||||||
|
snapping_state: SnappingState::default(),
|
||||||
|
layer_range_selection_reference: None,
|
||||||
metadata: Default::default(),
|
metadata: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn default_network() -> NodeNetwork {
|
||||||
|
DocumentMessageHandler::default().network
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn default_name() -> String {
|
||||||
|
DocumentMessageHandler::default().name
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn default_version() -> String {
|
||||||
|
DocumentMessageHandler::default().version
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn default_commit_hash() -> String {
|
||||||
|
DocumentMessageHandler::default().commit_hash
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn default_pan_tilt_zoom() -> PTZ {
|
||||||
|
DocumentMessageHandler::default().navigation
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn default_document_mode() -> DocumentMode {
|
||||||
|
DocumentMessageHandler::default().document_mode
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn default_view_mode() -> ViewMode {
|
||||||
|
DocumentMessageHandler::default().view_mode
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn default_overlays_visible() -> bool {
|
||||||
|
DocumentMessageHandler::default().overlays_visible
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn default_rulers_visible() -> bool {
|
||||||
|
DocumentMessageHandler::default().rulers_visible
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn default_collapsed() -> Vec<LayerNodeIdentifier> {
|
||||||
|
DocumentMessageHandler::default().collapsed
|
||||||
|
}
|
||||||
|
|
||||||
fn root_network() -> NodeNetwork {
|
fn root_network() -> NodeNetwork {
|
||||||
{
|
{
|
||||||
let mut network = NodeNetwork::default();
|
let mut network = NodeNetwork::default();
|
||||||
|
|
@ -178,12 +223,6 @@ fn root_network() -> NodeNetwork {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for DocumentMessageHandler {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.state_identifier.finish() == other.state_identifier.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DocumentInputs<'a> {
|
pub struct DocumentInputs<'a> {
|
||||||
pub document_id: u64,
|
pub document_id: u64,
|
||||||
pub ipp: &'a InputPreprocessorMessageHandler,
|
pub ipp: &'a InputPreprocessorMessageHandler,
|
||||||
|
|
@ -210,8 +249,11 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
|
||||||
#[remain::unsorted]
|
#[remain::unsorted]
|
||||||
Navigation(message) => {
|
Navigation(message) => {
|
||||||
let document_bounds = self.metadata().document_bounds_viewport_space();
|
let document_bounds = self.metadata().document_bounds_viewport_space();
|
||||||
self.navigation_handler
|
self.navigation_handler.process_message(
|
||||||
.process_message(message, responses, (&self.metadata, document_bounds, ipp, self.selected_visible_layers_bounding_box_viewport()));
|
message,
|
||||||
|
responses,
|
||||||
|
(&self.metadata, document_bounds, ipp, self.selected_visible_layers_bounding_box_viewport(), &mut self.navigation),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
#[remain::unsorted]
|
#[remain::unsorted]
|
||||||
Overlays(message) => {
|
Overlays(message) => {
|
||||||
|
|
@ -290,7 +332,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
|
||||||
}
|
}
|
||||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||||
}
|
}
|
||||||
BackupDocument { document } => self.backup_with_document(document, responses),
|
BackupDocument { network } => self.backup_with_document(network, responses),
|
||||||
ClearLayerTree => {
|
ClearLayerTree => {
|
||||||
// Send an empty layer tree
|
// Send an empty layer tree
|
||||||
let data_buffer: RawBuffer = Self::default().serialize_root().as_slice().into();
|
let data_buffer: RawBuffer = Self::default().serialize_root().as_slice().into();
|
||||||
|
|
@ -544,7 +586,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
|
||||||
responses.add(OverlaysMessage::Draw);
|
responses.add(OverlaysMessage::Draw);
|
||||||
}
|
}
|
||||||
RenderRulers => {
|
RenderRulers => {
|
||||||
let document_transform_scale = self.navigation_handler.snapped_scale();
|
let document_transform_scale = self.navigation_handler.snapped_scale(self.navigation.zoom);
|
||||||
|
|
||||||
let ruler_origin = self.metadata().document_to_viewport.transform_point2(DVec2::ZERO);
|
let ruler_origin = self.metadata().document_to_viewport.transform_point2(DVec2::ZERO);
|
||||||
let log = document_transform_scale.log2();
|
let log = document_transform_scale.log2();
|
||||||
|
|
@ -559,7 +601,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
RenderScrollbars => {
|
RenderScrollbars => {
|
||||||
let document_transform_scale = self.navigation_handler.snapped_scale();
|
let document_transform_scale = self.navigation_handler.snapped_scale(self.navigation.zoom);
|
||||||
|
|
||||||
let scale = 0.5 + ASYMPTOTIC_EFFECT + document_transform_scale * SCALE_EFFECT;
|
let scale = 0.5 + ASYMPTOTIC_EFFECT + document_transform_scale * SCALE_EFFECT;
|
||||||
|
|
||||||
|
|
@ -758,11 +800,8 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
|
||||||
}
|
}
|
||||||
ZoomCanvasToFitAll => {
|
ZoomCanvasToFitAll => {
|
||||||
if let Some(bounds) = self.metadata().document_bounds_document_space(true) {
|
if let Some(bounds) = self.metadata().document_bounds_document_space(true) {
|
||||||
responses.add(NavigationMessage::FitViewportToBounds {
|
responses.add(NavigationMessage::SetCanvasRotation { angle_radians: 0. });
|
||||||
bounds,
|
responses.add(NavigationMessage::FitViewportToBounds { bounds, prevent_zoom_past_100: true });
|
||||||
padding_scale_factor: Some(VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR),
|
|
||||||
prevent_zoom_past_100: true,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -819,10 +858,6 @@ impl DocumentMessageHandler {
|
||||||
.reduce(graphene_core::renderer::Quad::combine_bounds)
|
.reduce(graphene_core::renderer::Quad::combine_bounds)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_state_identifier(&self) -> u64 {
|
|
||||||
self.state_identifier.finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn network(&self) -> &NodeNetwork {
|
pub fn network(&self) -> &NodeNetwork {
|
||||||
&self.network
|
&self.network
|
||||||
}
|
}
|
||||||
|
|
@ -853,7 +888,7 @@ impl DocumentMessageHandler {
|
||||||
|
|
||||||
pub fn with_name(name: String, ipp: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) -> Self {
|
pub fn with_name(name: String, ipp: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) -> Self {
|
||||||
let mut document = Self { name, ..Self::default() };
|
let mut document = Self { name, ..Self::default() };
|
||||||
let transform = document.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.size() / 2.);
|
let transform = document.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.size() / 2., DVec2::ZERO, 0., 1.);
|
||||||
document.metadata.document_to_viewport = transform;
|
document.metadata.document_to_viewport = transform;
|
||||||
responses.add(DocumentMessage::UpdateDocumentTransform { transform });
|
responses.add(DocumentMessage::UpdateDocumentTransform { transform });
|
||||||
|
|
||||||
|
|
@ -934,9 +969,9 @@ impl DocumentMessageHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Places a document into the history system
|
/// Places a document into the history system
|
||||||
fn backup_with_document(&mut self, document: DocumentMessageHandler, responses: &mut VecDeque<Message>) {
|
fn backup_with_document(&mut self, network: NodeNetwork, responses: &mut VecDeque<Message>) {
|
||||||
self.document_redo_history.clear();
|
self.document_redo_history.clear();
|
||||||
self.document_undo_history.push_back(document);
|
self.document_undo_history.push_back(network);
|
||||||
if self.document_undo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN {
|
if self.document_undo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN {
|
||||||
self.document_undo_history.pop_front();
|
self.document_undo_history.pop_front();
|
||||||
}
|
}
|
||||||
|
|
@ -947,38 +982,30 @@ impl DocumentMessageHandler {
|
||||||
|
|
||||||
/// Copies the entire document into the history system
|
/// Copies the entire document into the history system
|
||||||
pub fn backup(&mut self, responses: &mut VecDeque<Message>) {
|
pub fn backup(&mut self, responses: &mut VecDeque<Message>) {
|
||||||
self.backup_with_document(self.clone(), responses);
|
self.backup_with_document(self.network.clone(), responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Is this now redundant?
|
// TODO: Is this now redundant?
|
||||||
/// Push a message backing up the document in its current state
|
/// Push a message backing up the document in its current state
|
||||||
pub fn backup_nonmut(&self, responses: &mut VecDeque<Message>) {
|
pub fn backup_nonmut(&self, responses: &mut VecDeque<Message>) {
|
||||||
responses.add(DocumentMessage::BackupDocument { document: self.clone() });
|
responses.add(DocumentMessage::BackupDocument { network: self.network.clone() });
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Replace the document with a new document save, returning the document save.
|
/// Replace the document with a new document save, returning the document save.
|
||||||
pub fn replace_document(&mut self, document: DocumentMessageHandler) -> DocumentMessageHandler {
|
pub fn replace_document(&mut self, network: NodeNetwork) -> NodeNetwork {
|
||||||
// Replace the network. (Keeping the root is required if the bounds of the viewport have changed during the operation.)
|
std::mem::replace(&mut self.network, network)
|
||||||
let old_root = self.metadata().document_to_viewport;
|
|
||||||
let document = std::mem::replace(self, document);
|
|
||||||
self.metadata.document_to_viewport = old_root;
|
|
||||||
|
|
||||||
document
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn undo(&mut self, responses: &mut VecDeque<Message>) {
|
pub fn undo(&mut self, responses: &mut VecDeque<Message>) {
|
||||||
// Push the UpdateOpenDocumentsList message to the bus in order to update the save status of the open documents
|
// Push the UpdateOpenDocumentsList message to the bus in order to update the save status of the open documents
|
||||||
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
||||||
|
|
||||||
let Some(document) = self.document_undo_history.pop_back() else {
|
let Some(network) = self.document_undo_history.pop_back() else { return };
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
responses.add(BroadcastEvent::SelectionChanged);
|
responses.add(BroadcastEvent::SelectionChanged);
|
||||||
|
|
||||||
let document_save = self.replace_document(document);
|
let previous_network = std::mem::replace(&mut self.network, network);
|
||||||
|
self.document_redo_history.push_back(previous_network);
|
||||||
self.document_redo_history.push_back(document_save);
|
|
||||||
if self.document_redo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN {
|
if self.document_redo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN {
|
||||||
self.document_redo_history.pop_front();
|
self.document_redo_history.pop_front();
|
||||||
}
|
}
|
||||||
|
|
@ -991,12 +1018,12 @@ impl DocumentMessageHandler {
|
||||||
// Push the UpdateOpenDocumentsList message to the bus in order to update the save status of the open documents
|
// Push the UpdateOpenDocumentsList message to the bus in order to update the save status of the open documents
|
||||||
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
||||||
|
|
||||||
let Some(document) = self.document_redo_history.pop_back() else { return };
|
let Some(network) = self.document_redo_history.pop_back() else { return };
|
||||||
|
|
||||||
responses.add(BroadcastEvent::SelectionChanged);
|
responses.add(BroadcastEvent::SelectionChanged);
|
||||||
|
|
||||||
let document_save = self.replace_document(document);
|
let previous_network = std::mem::replace(&mut self.network, network);
|
||||||
self.document_undo_history.push_back(document_save);
|
self.document_undo_history.push_back(previous_network);
|
||||||
if self.document_undo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN {
|
if self.document_undo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN {
|
||||||
self.document_undo_history.pop_front();
|
self.document_undo_history.pop_front();
|
||||||
}
|
}
|
||||||
|
|
@ -1005,33 +1032,31 @@ impl DocumentMessageHandler {
|
||||||
responses.add(NodeGraphMessage::SendGraph { should_rerender: true });
|
responses.add(NodeGraphMessage::SendGraph { should_rerender: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_identifier(&self) -> u64 {
|
pub fn current_hash(&self) -> Option<u64> {
|
||||||
// We can use the last state of the document to serve as the identifier to compare against
|
self.document_undo_history.iter().last().map(|network| network.current_hash())
|
||||||
// This is useful since when the document is empty the identifier will be 0
|
|
||||||
self.document_undo_history.iter().last().map(|document| document.state_identifier.finish()).unwrap_or(0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_auto_saved(&self) -> bool {
|
pub fn is_auto_saved(&self) -> bool {
|
||||||
self.current_identifier() == self.auto_saved_document_identifier
|
self.current_hash() == self.auto_saved_hash
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_saved(&self) -> bool {
|
pub fn is_saved(&self) -> bool {
|
||||||
self.current_identifier() == self.saved_document_identifier
|
self.current_hash() == self.saved_hash
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_auto_save_state(&mut self, is_saved: bool) {
|
pub fn set_auto_save_state(&mut self, is_saved: bool) {
|
||||||
if is_saved {
|
if is_saved {
|
||||||
self.auto_saved_document_identifier = self.current_identifier();
|
self.auto_saved_hash = self.current_hash();
|
||||||
} else {
|
} else {
|
||||||
self.auto_saved_document_identifier = generate_uuid();
|
self.auto_saved_hash = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_save_state(&mut self, is_saved: bool) {
|
pub fn set_save_state(&mut self, is_saved: bool) {
|
||||||
if is_saved {
|
if is_saved {
|
||||||
self.saved_document_identifier = self.current_identifier();
|
self.saved_hash = self.current_hash();
|
||||||
} else {
|
} else {
|
||||||
self.saved_document_identifier = generate_uuid();
|
self.saved_hash = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1177,7 +1202,7 @@ impl DocumentMessageHandler {
|
||||||
.on_update(|_| NavigationMessage::SetCanvasZoom { zoom_factor: 1. }.into())
|
.on_update(|_| NavigationMessage::SetCanvasZoom { zoom_factor: 1. }.into())
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
Separator::new(SeparatorType::Related).widget_holder(),
|
Separator::new(SeparatorType::Related).widget_holder(),
|
||||||
NumberInput::new(Some(self.navigation_handler.snapped_scale() * 100.))
|
NumberInput::new(Some(self.navigation_handler.snapped_scale(self.navigation.zoom) * 100.))
|
||||||
.unit("%")
|
.unit("%")
|
||||||
.min(0.000001)
|
.min(0.000001)
|
||||||
.max(1000000.)
|
.max(1000000.)
|
||||||
|
|
@ -1193,7 +1218,7 @@ impl DocumentMessageHandler {
|
||||||
.increment_callback_increase(|_| NavigationMessage::IncreaseCanvasZoom { center_on_mouse: false }.into())
|
.increment_callback_increase(|_| NavigationMessage::IncreaseCanvasZoom { center_on_mouse: false }.into())
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
];
|
];
|
||||||
let rotation_value = self.navigation_handler.snapped_angle() / (std::f64::consts::PI / 180.);
|
let rotation_value = self.navigation_handler.snapped_angle(self.navigation.tilt) / (std::f64::consts::PI / 180.);
|
||||||
if rotation_value.abs() > 0.00001 {
|
if rotation_value.abs() > 0.00001 {
|
||||||
widgets.extend([
|
widgets.extend([
|
||||||
Separator::new(SeparatorType::Related).widget_holder(),
|
Separator::new(SeparatorType::Related).widget_holder(),
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ pub enum NavigationMessage {
|
||||||
},
|
},
|
||||||
FitViewportToBounds {
|
FitViewportToBounds {
|
||||||
bounds: [DVec2; 2],
|
bounds: [DVec2; 2],
|
||||||
padding_scale_factor: Option<f32>,
|
|
||||||
prevent_zoom_past_100: bool,
|
prevent_zoom_past_100: bool,
|
||||||
},
|
},
|
||||||
FitViewportToSelection,
|
FitViewportToSelection,
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, MouseMotion};
|
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, MouseMotion};
|
||||||
use crate::messages::input_mapper::utility_types::input_mouse::ViewportPosition;
|
use crate::messages::input_mapper::utility_types::input_mouse::ViewportPosition;
|
||||||
use crate::messages::portfolio::document::utility_types::document_metadata::DocumentMetadata;
|
use crate::messages::portfolio::document::utility_types::document_metadata::DocumentMetadata;
|
||||||
|
use crate::messages::portfolio::document::utility_types::misc::PTZ;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||||
|
|
||||||
|
|
@ -30,27 +31,16 @@ enum TransformOperation {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct NavigationMessageHandler {
|
pub struct NavigationMessageHandler {
|
||||||
pub pan: DVec2,
|
|
||||||
pub tilt: f64,
|
|
||||||
pub zoom: f64,
|
|
||||||
|
|
||||||
#[serde(skip)]
|
|
||||||
transform_operation: TransformOperation,
|
transform_operation: TransformOperation,
|
||||||
#[serde(skip)]
|
|
||||||
mouse_position: ViewportPosition,
|
mouse_position: ViewportPosition,
|
||||||
#[serde(skip)]
|
|
||||||
finish_operation_with_click: bool,
|
finish_operation_with_click: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for NavigationMessageHandler {
|
impl Default for NavigationMessageHandler {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
pan: DVec2::ZERO,
|
|
||||||
tilt: 0.,
|
|
||||||
zoom: 1.,
|
|
||||||
|
|
||||||
mouse_position: ViewportPosition::default(),
|
mouse_position: ViewportPosition::default(),
|
||||||
finish_operation_with_click: false,
|
finish_operation_with_click: false,
|
||||||
transform_operation: TransformOperation::None,
|
transform_operation: TransformOperation::None,
|
||||||
|
|
@ -58,30 +48,29 @@ impl Default for NavigationMessageHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MessageHandler<NavigationMessage, (&DocumentMetadata, Option<[DVec2; 2]>, &InputPreprocessorMessageHandler, Option<[DVec2; 2]>)> for NavigationMessageHandler {
|
impl MessageHandler<NavigationMessage, (&DocumentMetadata, Option<[DVec2; 2]>, &InputPreprocessorMessageHandler, Option<[DVec2; 2]>, &mut PTZ)> for NavigationMessageHandler {
|
||||||
#[remain::check]
|
#[remain::check]
|
||||||
fn process_message(
|
fn process_message(
|
||||||
&mut self,
|
&mut self,
|
||||||
message: NavigationMessage,
|
message: NavigationMessage,
|
||||||
responses: &mut VecDeque<Message>,
|
responses: &mut VecDeque<Message>,
|
||||||
(document_metadata, document_bounds, ipp, selection_bounds): (&DocumentMetadata, Option<[DVec2; 2]>, &InputPreprocessorMessageHandler, Option<[DVec2; 2]>),
|
(document_metadata, document_bounds, ipp, selection_bounds, ptz): (&DocumentMetadata, Option<[DVec2; 2]>, &InputPreprocessorMessageHandler, Option<[DVec2; 2]>, &mut PTZ),
|
||||||
) {
|
) {
|
||||||
use NavigationMessage::*;
|
use NavigationMessage::*;
|
||||||
|
|
||||||
let old_zoom = self.zoom;
|
let old_zoom = ptz.zoom;
|
||||||
|
|
||||||
#[remain::sorted]
|
#[remain::sorted]
|
||||||
match message {
|
match message {
|
||||||
DecreaseCanvasZoom { center_on_mouse } => {
|
DecreaseCanvasZoom { center_on_mouse } => {
|
||||||
let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().rev().find(|scale| **scale < self.zoom).unwrap_or(&self.zoom);
|
let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().rev().find(|scale| **scale < ptz.zoom).unwrap_or(&ptz.zoom);
|
||||||
if center_on_mouse {
|
if center_on_mouse {
|
||||||
responses.add(self.center_zoom(ipp.viewport_bounds.size(), new_scale / self.zoom, ipp.mouse.position));
|
responses.add(self.center_zoom(ipp.viewport_bounds.size(), new_scale / ptz.zoom, ipp.mouse.position));
|
||||||
}
|
}
|
||||||
responses.add(SetCanvasZoom { zoom_factor: new_scale });
|
responses.add(SetCanvasZoom { zoom_factor: new_scale });
|
||||||
}
|
}
|
||||||
FitViewportToBounds {
|
FitViewportToBounds {
|
||||||
bounds: [pos1, pos2],
|
bounds: [pos1, pos2],
|
||||||
padding_scale_factor,
|
|
||||||
prevent_zoom_past_100,
|
prevent_zoom_past_100,
|
||||||
} => {
|
} => {
|
||||||
let v1 = document_metadata.document_to_viewport.inverse().transform_point2(DVec2::ZERO);
|
let v1 = document_metadata.document_to_viewport.inverse().transform_point2(DVec2::ZERO);
|
||||||
|
|
@ -92,33 +81,32 @@ impl MessageHandler<NavigationMessage, (&DocumentMetadata, Option<[DVec2; 2]>, &
|
||||||
let size = 1. / size;
|
let size = 1. / size;
|
||||||
let new_scale = size.min_element();
|
let new_scale = size.min_element();
|
||||||
|
|
||||||
self.pan += center;
|
ptz.pan += center;
|
||||||
self.zoom *= new_scale;
|
ptz.zoom *= new_scale * VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR;
|
||||||
|
|
||||||
self.zoom /= padding_scale_factor.unwrap_or(1.) as f64;
|
// Keep the canvas filling less than the full available viewport bounds if requested.
|
||||||
|
// And if the zoom is close to the full viewport bounds, we ignore the padding because 100% is preferrable if it still fits.
|
||||||
if self.zoom > 1. && prevent_zoom_past_100 {
|
if prevent_zoom_past_100 && ptz.zoom > VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR {
|
||||||
self.zoom = 1.
|
ptz.zoom = 1.;
|
||||||
}
|
}
|
||||||
|
|
||||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||||
self.create_document_transform(ipp.viewport_bounds.center(), responses);
|
self.create_document_transform(ipp.viewport_bounds.center(), &ptz, responses);
|
||||||
}
|
}
|
||||||
FitViewportToSelection => {
|
FitViewportToSelection => {
|
||||||
if let Some(bounds) = selection_bounds {
|
if let Some(bounds) = selection_bounds {
|
||||||
let transform = document_metadata.document_to_viewport.inverse();
|
let transform = document_metadata.document_to_viewport.inverse();
|
||||||
responses.add(FitViewportToBounds {
|
responses.add(FitViewportToBounds {
|
||||||
bounds: [transform.transform_point2(bounds[0]), transform.transform_point2(bounds[1])],
|
bounds: [transform.transform_point2(bounds[0]), transform.transform_point2(bounds[1])],
|
||||||
padding_scale_factor: Some(VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR),
|
|
||||||
prevent_zoom_past_100: false,
|
prevent_zoom_past_100: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IncreaseCanvasZoom { center_on_mouse } => {
|
IncreaseCanvasZoom { center_on_mouse } => {
|
||||||
let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().find(|scale| **scale > self.zoom).unwrap_or(&self.zoom);
|
let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().find(|scale| **scale > ptz.zoom).unwrap_or(&ptz.zoom);
|
||||||
if center_on_mouse {
|
if center_on_mouse {
|
||||||
responses.add(self.center_zoom(ipp.viewport_bounds.size(), new_scale / self.zoom, ipp.mouse.position));
|
responses.add(self.center_zoom(ipp.viewport_bounds.size(), new_scale / ptz.zoom, ipp.mouse.position));
|
||||||
}
|
}
|
||||||
responses.add(SetCanvasZoom { zoom_factor: new_scale });
|
responses.add(SetCanvasZoom { zoom_factor: new_scale });
|
||||||
}
|
}
|
||||||
|
|
@ -144,7 +132,7 @@ impl MessageHandler<NavigationMessage, (&DocumentMetadata, Option<[DVec2; 2]>, &
|
||||||
if !(wait_for_snap_angle_release && new_snap && !snap_tilt_released) {
|
if !(wait_for_snap_angle_release && new_snap && !snap_tilt_released) {
|
||||||
// When disabling snap, keep the viewed rotation as it was previously.
|
// When disabling snap, keep the viewed rotation as it was previously.
|
||||||
if !new_snap && snap_tilt {
|
if !new_snap && snap_tilt {
|
||||||
self.tilt = self.snapped_angle();
|
ptz.tilt = self.snapped_angle(ptz.tilt);
|
||||||
}
|
}
|
||||||
self.transform_operation = TransformOperation::Rotate {
|
self.transform_operation = TransformOperation::Rotate {
|
||||||
pre_commit_tilt,
|
pre_commit_tilt,
|
||||||
|
|
@ -160,15 +148,15 @@ impl MessageHandler<NavigationMessage, (&DocumentMetadata, Option<[DVec2; 2]>, &
|
||||||
start_offset.angle_between(end_offset)
|
start_offset.angle_between(end_offset)
|
||||||
};
|
};
|
||||||
|
|
||||||
responses.add(SetCanvasRotation { angle_radians: self.tilt + rotation });
|
responses.add(SetCanvasRotation { angle_radians: ptz.tilt + rotation });
|
||||||
}
|
}
|
||||||
TransformOperation::Zoom { snap_zoom_enabled, pre_commit_zoom } => {
|
TransformOperation::Zoom { snap_zoom_enabled, pre_commit_zoom } => {
|
||||||
let zoom_start = self.snapped_scale();
|
let zoom_start = self.snapped_scale(ptz.zoom);
|
||||||
|
|
||||||
let new_snap = ipp.keyboard.get(snap_zoom as usize);
|
let new_snap = ipp.keyboard.get(snap_zoom as usize);
|
||||||
// When disabling snap, keep the viewed zoom as it was previously
|
// When disabling snap, keep the viewed zoom as it was previously
|
||||||
if !new_snap && snap_zoom_enabled {
|
if !new_snap && snap_zoom_enabled {
|
||||||
self.zoom = self.snapped_scale();
|
ptz.zoom = self.snapped_scale(ptz.zoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
if snap_zoom_enabled != new_snap {
|
if snap_zoom_enabled != new_snap {
|
||||||
|
|
@ -181,16 +169,16 @@ impl MessageHandler<NavigationMessage, (&DocumentMetadata, Option<[DVec2; 2]>, &
|
||||||
let difference = self.mouse_position.y - ipp.mouse.position.y;
|
let difference = self.mouse_position.y - ipp.mouse.position.y;
|
||||||
let amount = 1. + difference * VIEWPORT_ZOOM_MOUSE_RATE;
|
let amount = 1. + difference * VIEWPORT_ZOOM_MOUSE_RATE;
|
||||||
|
|
||||||
self.zoom *= amount;
|
ptz.zoom *= amount;
|
||||||
self.zoom *= Self::clamp_zoom(self.zoom, document_bounds, old_zoom, ipp);
|
ptz.zoom *= Self::clamp_zoom(ptz.zoom, document_bounds, old_zoom, ipp);
|
||||||
|
|
||||||
if let Some(mouse) = zoom_from_viewport {
|
if let Some(mouse) = zoom_from_viewport {
|
||||||
let zoom_factor = self.snapped_scale() / zoom_start;
|
let zoom_factor = self.snapped_scale(ptz.zoom) / zoom_start;
|
||||||
|
|
||||||
responses.add(SetCanvasZoom { zoom_factor: self.zoom });
|
responses.add(SetCanvasZoom { zoom_factor: ptz.zoom });
|
||||||
responses.add(self.center_zoom(ipp.viewport_bounds.size(), zoom_factor, mouse));
|
responses.add(self.center_zoom(ipp.viewport_bounds.size(), zoom_factor, mouse));
|
||||||
} else {
|
} else {
|
||||||
responses.add(SetCanvasZoom { zoom_factor: self.zoom });
|
responses.add(SetCanvasZoom { zoom_factor: ptz.zoom });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -215,7 +203,7 @@ impl MessageHandler<NavigationMessage, (&DocumentMetadata, Option<[DVec2; 2]>, &
|
||||||
});
|
});
|
||||||
|
|
||||||
self.transform_operation = TransformOperation::Rotate {
|
self.transform_operation = TransformOperation::Rotate {
|
||||||
pre_commit_tilt: self.tilt,
|
pre_commit_tilt: ptz.tilt,
|
||||||
snap_tilt_released: false,
|
snap_tilt_released: false,
|
||||||
snap_tilt: false,
|
snap_tilt: false,
|
||||||
};
|
};
|
||||||
|
|
@ -224,17 +212,17 @@ impl MessageHandler<NavigationMessage, (&DocumentMetadata, Option<[DVec2; 2]>, &
|
||||||
self.finish_operation_with_click = was_dispatched_from_menu;
|
self.finish_operation_with_click = was_dispatched_from_menu;
|
||||||
}
|
}
|
||||||
SetCanvasRotation { angle_radians } => {
|
SetCanvasRotation { angle_radians } => {
|
||||||
self.tilt = angle_radians;
|
ptz.tilt = angle_radians;
|
||||||
self.create_document_transform(ipp.viewport_bounds.center(), responses);
|
self.create_document_transform(ipp.viewport_bounds.center(), &ptz, responses);
|
||||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||||
}
|
}
|
||||||
SetCanvasZoom { zoom_factor } => {
|
SetCanvasZoom { zoom_factor } => {
|
||||||
self.zoom = zoom_factor.clamp(VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_SCALE_MAX);
|
ptz.zoom = zoom_factor.clamp(VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_SCALE_MAX);
|
||||||
self.zoom *= Self::clamp_zoom(self.zoom, document_bounds, old_zoom, ipp);
|
ptz.zoom *= Self::clamp_zoom(ptz.zoom, document_bounds, old_zoom, ipp);
|
||||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||||
self.create_document_transform(ipp.viewport_bounds.center(), responses);
|
self.create_document_transform(ipp.viewport_bounds.center(), &ptz, responses);
|
||||||
}
|
}
|
||||||
TransformCanvasEnd { abort_transform } => {
|
TransformCanvasEnd { abort_transform } => {
|
||||||
if abort_transform {
|
if abort_transform {
|
||||||
|
|
@ -244,19 +232,19 @@ impl MessageHandler<NavigationMessage, (&DocumentMetadata, Option<[DVec2; 2]>, &
|
||||||
responses.add(SetCanvasRotation { angle_radians: pre_commit_tilt });
|
responses.add(SetCanvasRotation { angle_radians: pre_commit_tilt });
|
||||||
}
|
}
|
||||||
TransformOperation::Pan { pre_commit_pan, .. } => {
|
TransformOperation::Pan { pre_commit_pan, .. } => {
|
||||||
self.pan = pre_commit_pan;
|
ptz.pan = pre_commit_pan;
|
||||||
self.create_document_transform(ipp.viewport_bounds.center(), responses);
|
self.create_document_transform(ipp.viewport_bounds.center(), &ptz, responses);
|
||||||
}
|
}
|
||||||
TransformOperation::Zoom { pre_commit_zoom, .. } => {
|
TransformOperation::Zoom { pre_commit_zoom, .. } => {
|
||||||
self.zoom = pre_commit_zoom;
|
ptz.zoom = pre_commit_zoom;
|
||||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||||
self.create_document_transform(ipp.viewport_bounds.center(), responses);
|
self.create_document_transform(ipp.viewport_bounds.center(), &ptz, responses);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.tilt = self.snapped_angle();
|
ptz.tilt = self.snapped_angle(ptz.tilt);
|
||||||
self.zoom = self.snapped_scale();
|
ptz.zoom = self.snapped_scale(ptz.zoom);
|
||||||
responses.add(BroadcastEvent::CanvasTransformed);
|
responses.add(BroadcastEvent::CanvasTransformed);
|
||||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||||
responses.add(ToolMessage::UpdateCursor);
|
responses.add(ToolMessage::UpdateCursor);
|
||||||
|
|
@ -271,10 +259,10 @@ impl MessageHandler<NavigationMessage, (&DocumentMetadata, Option<[DVec2; 2]>, &
|
||||||
TranslateCanvas { delta } => {
|
TranslateCanvas { delta } => {
|
||||||
let transformed_delta = document_metadata.document_to_viewport.inverse().transform_vector2(delta);
|
let transformed_delta = document_metadata.document_to_viewport.inverse().transform_vector2(delta);
|
||||||
|
|
||||||
self.pan += transformed_delta;
|
ptz.pan += transformed_delta;
|
||||||
responses.add(BroadcastEvent::CanvasTransformed);
|
responses.add(BroadcastEvent::CanvasTransformed);
|
||||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||||
self.create_document_transform(ipp.viewport_bounds.center(), responses);
|
self.create_document_transform(ipp.viewport_bounds.center(), &ptz, responses);
|
||||||
}
|
}
|
||||||
TranslateCanvasBegin => {
|
TranslateCanvasBegin => {
|
||||||
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Grabbing });
|
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Grabbing });
|
||||||
|
|
@ -284,14 +272,14 @@ impl MessageHandler<NavigationMessage, (&DocumentMetadata, Option<[DVec2; 2]>, &
|
||||||
});
|
});
|
||||||
|
|
||||||
self.mouse_position = ipp.mouse.position;
|
self.mouse_position = ipp.mouse.position;
|
||||||
self.transform_operation = TransformOperation::Pan { pre_commit_pan: self.pan };
|
self.transform_operation = TransformOperation::Pan { pre_commit_pan: ptz.pan };
|
||||||
}
|
}
|
||||||
TranslateCanvasByViewportFraction { delta } => {
|
TranslateCanvasByViewportFraction { delta } => {
|
||||||
let transformed_delta = document_metadata.document_to_viewport.inverse().transform_vector2(delta * ipp.viewport_bounds.size());
|
let transformed_delta = document_metadata.document_to_viewport.inverse().transform_vector2(delta * ipp.viewport_bounds.size());
|
||||||
|
|
||||||
self.pan += transformed_delta;
|
ptz.pan += transformed_delta;
|
||||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||||
self.create_document_transform(ipp.viewport_bounds.center(), responses);
|
self.create_document_transform(ipp.viewport_bounds.center(), &ptz, responses);
|
||||||
}
|
}
|
||||||
WheelCanvasTranslate { use_y_as_x } => {
|
WheelCanvasTranslate { use_y_as_x } => {
|
||||||
let delta = match use_y_as_x {
|
let delta = match use_y_as_x {
|
||||||
|
|
@ -306,10 +294,10 @@ impl MessageHandler<NavigationMessage, (&DocumentMetadata, Option<[DVec2; 2]>, &
|
||||||
if ipp.mouse.scroll_delta.y > 0 {
|
if ipp.mouse.scroll_delta.y > 0 {
|
||||||
zoom_factor = 1. / zoom_factor
|
zoom_factor = 1. / zoom_factor
|
||||||
}
|
}
|
||||||
zoom_factor *= Self::clamp_zoom(self.zoom * zoom_factor, document_bounds, old_zoom, ipp);
|
zoom_factor *= Self::clamp_zoom(ptz.zoom * zoom_factor, document_bounds, old_zoom, ipp);
|
||||||
|
|
||||||
responses.add(self.center_zoom(ipp.viewport_bounds.size(), zoom_factor, ipp.mouse.position));
|
responses.add(self.center_zoom(ipp.viewport_bounds.size(), zoom_factor, ipp.mouse.position));
|
||||||
responses.add(SetCanvasZoom { zoom_factor: self.zoom * zoom_factor });
|
responses.add(SetCanvasZoom { zoom_factor: ptz.zoom * zoom_factor });
|
||||||
}
|
}
|
||||||
ZoomCanvasBegin => {
|
ZoomCanvasBegin => {
|
||||||
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::ZoomIn });
|
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::ZoomIn });
|
||||||
|
|
@ -328,7 +316,7 @@ impl MessageHandler<NavigationMessage, (&DocumentMetadata, Option<[DVec2; 2]>, &
|
||||||
});
|
});
|
||||||
|
|
||||||
self.transform_operation = TransformOperation::Zoom {
|
self.transform_operation = TransformOperation::Zoom {
|
||||||
pre_commit_zoom: self.zoom,
|
pre_commit_zoom: ptz.zoom,
|
||||||
snap_zoom_enabled: false,
|
snap_zoom_enabled: false,
|
||||||
};
|
};
|
||||||
self.mouse_position = ipp.mouse.position;
|
self.mouse_position = ipp.mouse.position;
|
||||||
|
|
@ -372,43 +360,40 @@ impl MessageHandler<NavigationMessage, (&DocumentMetadata, Option<[DVec2; 2]>, &
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NavigationMessageHandler {
|
impl NavigationMessageHandler {
|
||||||
pub fn snapped_angle(&self) -> f64 {
|
pub fn snapped_angle(&self, tilt: f64) -> f64 {
|
||||||
let increment_radians: f64 = VIEWPORT_ROTATE_SNAP_INTERVAL.to_radians();
|
let increment_radians: f64 = VIEWPORT_ROTATE_SNAP_INTERVAL.to_radians();
|
||||||
if let TransformOperation::Rotate { snap_tilt: true, .. } = self.transform_operation {
|
if let TransformOperation::Rotate { snap_tilt: true, .. } = self.transform_operation {
|
||||||
(self.tilt / increment_radians).round() * increment_radians
|
(tilt / increment_radians).round() * increment_radians
|
||||||
} else {
|
} else {
|
||||||
self.tilt
|
tilt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn snapped_scale(&self) -> f64 {
|
pub fn snapped_scale(&self, zoom: f64) -> f64 {
|
||||||
if let TransformOperation::Zoom { snap_zoom_enabled: true, .. } = self.transform_operation {
|
if let TransformOperation::Zoom { snap_zoom_enabled: true, .. } = self.transform_operation {
|
||||||
*VIEWPORT_ZOOM_LEVELS
|
*VIEWPORT_ZOOM_LEVELS.iter().min_by(|a, b| (**a - zoom).abs().partial_cmp(&(**b - zoom).abs()).unwrap()).unwrap_or(&zoom)
|
||||||
.iter()
|
|
||||||
.min_by(|a, b| (**a - self.zoom).abs().partial_cmp(&(**b - self.zoom).abs()).unwrap())
|
|
||||||
.unwrap_or(&self.zoom)
|
|
||||||
} else {
|
} else {
|
||||||
self.zoom
|
zoom
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn calculate_offset_transform(&self, viewport_center: DVec2) -> DAffine2 {
|
pub fn calculate_offset_transform(&self, viewport_center: DVec2, pan: DVec2, tilt: f64, zoom: f64) -> DAffine2 {
|
||||||
let scaled_centre = viewport_center / self.snapped_scale();
|
let scaled_centre = viewport_center / self.snapped_scale(zoom);
|
||||||
|
|
||||||
// Try to avoid fractional coordinates to reduce anti aliasing.
|
// Try to avoid fractional coordinates to reduce anti aliasing.
|
||||||
let scale = self.snapped_scale();
|
let scale = self.snapped_scale(zoom);
|
||||||
let rounded_pan = ((self.pan + scaled_centre) * scale).round() / scale - scaled_centre;
|
let rounded_pan = ((pan + scaled_centre) * scale).round() / scale - scaled_centre;
|
||||||
|
|
||||||
// TODO: replace with DAffine2::from_scale_angle_translation and fix the errors
|
// TODO: replace with DAffine2::from_scale_angle_translation and fix the errors
|
||||||
let offset_transform = DAffine2::from_translation(scaled_centre);
|
let offset_transform = DAffine2::from_translation(scaled_centre);
|
||||||
let scale_transform = DAffine2::from_scale(DVec2::splat(scale));
|
let scale_transform = DAffine2::from_scale(DVec2::splat(scale));
|
||||||
let angle_transform = DAffine2::from_angle(self.snapped_angle());
|
let angle_transform = DAffine2::from_angle(self.snapped_angle(tilt));
|
||||||
let translation_transform = DAffine2::from_translation(rounded_pan);
|
let translation_transform = DAffine2::from_translation(rounded_pan);
|
||||||
scale_transform * offset_transform * angle_transform * translation_transform
|
scale_transform * offset_transform * angle_transform * translation_transform
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_document_transform(&self, viewport_center: DVec2, responses: &mut VecDeque<Message>) {
|
fn create_document_transform(&self, viewport_center: DVec2, ptz: &PTZ, responses: &mut VecDeque<Message>) {
|
||||||
let transform = self.calculate_offset_transform(viewport_center);
|
let transform = self.calculate_offset_transform(viewport_center, ptz.pan, ptz.tilt, ptz.zoom);
|
||||||
responses.add(DocumentMessage::UpdateDocumentTransform { transform });
|
responses.add(DocumentMessage::UpdateDocumentTransform { transform });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -122,12 +122,11 @@ impl FrontendNodeType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct NodeGraphMessageHandler {
|
pub struct NodeGraphMessageHandler {
|
||||||
pub layer_path: Option<Vec<LayerId>>,
|
pub layer_path: Option<Vec<LayerId>>,
|
||||||
pub network: Vec<NodeId>,
|
pub network: Vec<NodeId>,
|
||||||
has_selection: bool,
|
has_selection: bool,
|
||||||
#[serde(skip)]
|
|
||||||
pub widgets: [LayoutGroup; 2],
|
pub widgets: [LayoutGroup; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,7 @@ use crate::messages::portfolio::document::node_graph::NodePropertiesContext;
|
||||||
use crate::messages::portfolio::utility_types::PersistentData;
|
use crate::messages::portfolio::utility_types::PersistentData;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
#[derive(Debug, Clone, Default)]
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
|
||||||
pub struct PropertiesPanelMessageHandler;
|
pub struct PropertiesPanelMessageHandler;
|
||||||
|
|
||||||
impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPanelMessageHandlerData<'a>)> for PropertiesPanelMessageHandler {
|
impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPanelMessageHandlerData<'a>)> for PropertiesPanelMessageHandler {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ use crate::messages::portfolio::document::utility_types::LayerId;
|
||||||
|
|
||||||
use graphene_core::raster::color::Color;
|
use graphene_core::raster::color::Color;
|
||||||
|
|
||||||
|
use glam::DVec2;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
|
@ -59,7 +60,7 @@ pub enum DocumentRenderMode<'a> {
|
||||||
LayerCutout(&'a [LayerId], Color),
|
LayerCutout(&'a [LayerId], Color),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug)]
|
||||||
/// SnappingState determines the current individual snapping states
|
/// SnappingState determines the current individual snapping states
|
||||||
pub struct SnappingState {
|
pub struct SnappingState {
|
||||||
pub snapping_enabled: bool,
|
pub snapping_enabled: bool,
|
||||||
|
|
@ -91,3 +92,16 @@ impl fmt::Display for SnappingOptions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct PTZ {
|
||||||
|
pub pan: DVec2,
|
||||||
|
pub tilt: f64,
|
||||||
|
pub zoom: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PTZ {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { pan: DVec2::ZERO, tilt: 0., zoom: 1. }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -614,10 +614,13 @@ impl Fsm for SelectToolFsmState {
|
||||||
}
|
}
|
||||||
tool_data.drag_current = mouse_position + closest_move;
|
tool_data.drag_current = mouse_position + closest_move;
|
||||||
|
|
||||||
if input.keyboard.key(duplicate) && tool_data.not_duplicated_layers.is_none() {
|
// TODO: Reenable this feature after fixing it
|
||||||
tool_data.start_duplicates(document, responses);
|
if false {
|
||||||
} else if !input.keyboard.key(duplicate) && tool_data.not_duplicated_layers.is_some() {
|
if input.keyboard.key(duplicate) && tool_data.not_duplicated_layers.is_none() {
|
||||||
tool_data.stop_duplicates(document, responses);
|
tool_data.start_duplicates(document, responses);
|
||||||
|
} else if !input.keyboard.key(duplicate) && tool_data.not_duplicated_layers.is_some() {
|
||||||
|
tool_data.stop_duplicates(document, responses);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectToolFsmState::Dragging
|
SelectToolFsmState::Dragging
|
||||||
|
|
@ -625,7 +628,8 @@ impl Fsm for SelectToolFsmState {
|
||||||
(SelectToolFsmState::ResizingBounds, SelectToolMessage::PointerMove { axis_align, center, .. }) => {
|
(SelectToolFsmState::ResizingBounds, SelectToolMessage::PointerMove { axis_align, center, .. }) => {
|
||||||
if let Some(bounds) = &mut tool_data.bounding_box_manager {
|
if let Some(bounds) = &mut tool_data.bounding_box_manager {
|
||||||
if let Some(movement) = &mut bounds.selected_edges {
|
if let Some(movement) = &mut bounds.selected_edges {
|
||||||
let (center, axis_align) = (input.keyboard.key(center), input.keyboard.key(axis_align));
|
let (_center, axis_align) = (input.keyboard.key(center), input.keyboard.key(axis_align));
|
||||||
|
let center = false; // TODO: Reenable this feature after fixing it
|
||||||
|
|
||||||
let mouse_position = input.mouse.position;
|
let mouse_position = input.mouse.position;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
use crate::document::value::TaggedValue;
|
use crate::document::value::TaggedValue;
|
||||||
use crate::proto::{ConstructionArgs, ProtoNetwork, ProtoNode, ProtoNodeInput};
|
use crate::proto::{ConstructionArgs, ProtoNetwork, ProtoNode, ProtoNodeInput};
|
||||||
use graphene_core::{GraphicGroup, ProtoNodeIdentifier, Type};
|
|
||||||
|
|
||||||
use dyn_any::{DynAny, StaticType};
|
use dyn_any::{DynAny, StaticType};
|
||||||
use glam::IVec2;
|
|
||||||
pub use graphene_core::uuid::generate_uuid;
|
pub use graphene_core::uuid::generate_uuid;
|
||||||
|
use graphene_core::{GraphicGroup, ProtoNodeIdentifier, Type};
|
||||||
|
|
||||||
|
use glam::IVec2;
|
||||||
|
use std::collections::hash_map::DefaultHasher;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
pub mod value;
|
pub mod value;
|
||||||
|
|
||||||
|
|
@ -14,8 +17,7 @@ pub type NodeId = u64;
|
||||||
/// Hash two IDs together, returning a new ID that is always consistant for two input IDs in a specific order.
|
/// Hash two IDs together, returning a new ID that is always consistant for two input IDs in a specific order.
|
||||||
/// This is used during [`NodeNetwork::flatten`] in order to ensure consistant yet non-conflicting IDs for inner networks.
|
/// This is used during [`NodeNetwork::flatten`] in order to ensure consistant yet non-conflicting IDs for inner networks.
|
||||||
fn merge_ids(a: u64, b: u64) -> u64 {
|
fn merge_ids(a: u64, b: u64) -> u64 {
|
||||||
use std::hash::{Hash, Hasher};
|
let mut hasher = DefaultHasher::new();
|
||||||
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
|
||||||
a.hash(&mut hasher);
|
a.hash(&mut hasher);
|
||||||
b.hash(&mut hasher);
|
b.hash(&mut hasher);
|
||||||
hasher.finish()
|
hasher.finish()
|
||||||
|
|
@ -448,6 +450,12 @@ impl std::hash::Hash for NodeNetwork {
|
||||||
|
|
||||||
/// Graph modification functions
|
/// Graph modification functions
|
||||||
impl NodeNetwork {
|
impl NodeNetwork {
|
||||||
|
pub fn current_hash(&self) -> u64 {
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
self.hash(&mut hasher);
|
||||||
|
hasher.finish()
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the original output nodes of this network, ignoring any preview node
|
/// Get the original output nodes of this network, ignoring any preview node
|
||||||
pub fn original_outputs(&self) -> &Vec<NodeOutput> {
|
pub fn original_outputs(&self) -> &Vec<NodeOutput> {
|
||||||
self.previous_outputs.as_ref().unwrap_or(&self.outputs)
|
self.previous_outputs.as_ref().unwrap_or(&self.outputs)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue