Add alignment overlays (#462)
* Add alignment overlays
* Made snapping / overlays not render extra times, made snapping distance larger
* Revert snap tolerance change until user testing can be done
* WIP conversion to messages
* Revert "WIP conversion to messages"
This reverts commit ddcc23865a4a93639a0e4c60db66c664da8ccc09.
* Overlay document always has (0,0) in top left
* Fix AA on overlay lines
* Merge branch 'master' into alignment-overlays
* Squashed commit of the following:
commit dbc19912ca62b85c512985c10c1799cec1014c44
Author: asyncth <97792158+asyncth@users.noreply.github.com>
Date: Sun Jan 16 12:57:03 2022 +0500
Set the mouse cursor in the canvas based on the current tool and its state (#480)
* Add FrontendMouseCursor and DisplayMouseCursor
* Add update_cursor method to the Fsm trait and implement it for all tools
* Rename DisplayMouseCursor to UpdateMouseCursor
* Add 'To CSS Cursor Property' transform decorator and change the mouse cursor in the canvas based on the current tool and its state
* Implement update_cursor for Navigate tool properly
* Keep the cursor when dragging outside of the canvas
* Change the mouse cursor to 'zoom-in' when LMB dragging on canvas with Navigate tool
* Rename FrontendMouseCursor to MouseCursorIcon
* Rename 'event' to 'e' and replace v-on with @
* Change the definition of the MouseCursorIcon type in TS
* Replace switch with dictionary look-up
* Move the definition of MouseCursorIcon closer to where it's used
commit 9b36e6a909ce54bc8bb0db8d11b0caed3eebec85
Author: Keavon Chambers <keavon@keavon.com>
Date: Sat Jan 15 17:24:58 2022 -0800
Fix all remaining Vue/TS errors flagged in Volar
commit 2cc39cdb3782dc3ab42a0cb33f54af577a10b5bd
Author: Keavon Chambers <keavon@keavon.com>
Date: Sat Jan 15 12:55:51 2022 -0800
Tweak whitespace around `use` statements and other lint fixes
commit fa390c3875c9c355dc9c63a1f738575fb780f3b0
Author: Keavon Chambers <keavon@keavon.com>
Date: Sat Jan 15 06:35:06 2022 -0800
Change canvas artboard background color to be 1 shade lighter
commit ea2d00348474976a6021d06978cbead497bc6e2c
Author: Keavon Chambers <keavon@keavon.com>
Date: Fri Jan 14 20:54:38 2022 -0800
Modify all message enum data to use named struct values, not tuples (#479)
* Massively reorganize and clean up the whole Rust codebase
* Modify all message enum data to use named struct values, not tuples
commit 9b6cbb5f50f1c2493a6fbbbd6b2e34e83ce72b38
Author: Keavon Chambers <keavon@keavon.com>
Date: Fri Jan 14 14:58:08 2022 -0800
Massively reorganize and clean up the whole Rust codebase (#478)
* Massively reorganize and clean up the whole Rust codebase
* Additional changes during code review
commit a535f5c1c19c56f53e1d4d8bed7acafb2fac956b
Author: Keavon Chambers <keavon@keavon.com>
Date: Wed Jan 12 16:17:40 2022 -0800
Sort messages and message handlers
commit e70858884dc57cf678cb47bf7f38098e08279c1b
Author: Keavon Chambers <keavon@keavon.com>
Date: Wed Jan 12 14:16:13 2022 -0800
Standardize FrontendMessage message names
commit 0b4934b4f3754ed8732c0a7d50d87b4624874c3e
Author: Keavon Chambers <keavon@keavon.com>
Date: Wed Jan 12 12:45:07 2022 -0800
Rename document_file.rs to document_message_handler.rs
commit ec7bf4a771a3028b07879d1131041bcd9742f666
Author: Keavon Chambers <keavon@keavon.com>
Date: Wed Jan 12 12:19:14 2022 -0800
Rename DocumentsMessage to PortfolioMessage
commit 0991312ba009f6a1452a9892e7d5a248a22939ed
Author: Keavon Chambers <keavon@keavon.com>
Date: Wed Jan 12 11:44:49 2022 -0800
Rename document_message_handler.rs to portfolio_message_handler.rs
commit c76c92e50028feed99fccf509385baea9442bbdf
Author: 0HyperCube <78500760+0HyperCube@users.noreply.github.com>
Date: Wed Jan 12 19:05:55 2022 +0000
Migrate to using MoveSelectedLayersTo (#469)
* migrate to using MoveSelectedLayersTo
* Fix dragging a selected layer with multiple selected layers
* Fix CreatedLayer overriding selection
* Fix MoveSelectedLayersTo behaviour
* Squashed commit of the following:
commit 095d577a49f6b904ba3038327109860e5519f981
Author: Keavon Chambers <keavon@keavon.com>
Date: Mon Jan 10 18:06:12 2022 -0800
Fix NumberInput clamping regression with undefined bounds
commit 9f54a376c4ad20a60731bb4a4eca5a25001fcfd1
Author: mfish33 <32677537+mfish33@users.noreply.github.com>
Date: Sun Jan 9 15:52:55 2022 -0800
Fix bounds with artboards for zoom-to-fit and scrollbar scaling (#473)
* - document load keeps postition
- zoom to fit
- scrollbars use artboard dimensions
* - review comments
- svg export uses all artboard bounds
Co-authored-by: Keavon Chambers <keavon@keavon.com>
commit 61432de4801d63d62faa18aaa624b11a122a97b1
Author: 0HyperCube <78500760+0HyperCube@users.noreply.github.com>
Date: Sat Jan 8 21:06:15 2022 +0000
Fix rotation input (#472)
* Fix insert with no nesting at end of panel
* Deselect other layers on paste
* Resolve logging
commit 574028375b4b0aea6ccd76414dd93272a72add5f
Author: Keavon Chambers <keavon@keavon.com>
Date: Wed Jan 12 06:14:32 2022 -0800
Add `npm start` script alias for `npm run serve`
commit 75e8fc6614d4b498b0beb93ac9313144160e7689
Author: Keavon Chambers <keavon@keavon.com>
Date: Wed Jan 12 05:17:56 2022 -0800
Switch VS Code's Vue extension from Vetur to Volar
commit 389b445ef16065412759c4e4fc1e10835ebb8bdb
Author: Keavon Chambers <keavon@keavon.com>
Date: Wed Jan 12 03:56:28 2022 -0800
Remove Charcoal references from the code for now
commit 095d577a49f6b904ba3038327109860e5519f981
Author: Keavon Chambers <keavon@keavon.com>
Date: Mon Jan 10 18:06:12 2022 -0800
Fix NumberInput clamping regression with undefined bounds
commit 9f54a376c4ad20a60731bb4a4eca5a25001fcfd1
Author: mfish33 <32677537+mfish33@users.noreply.github.com>
Date: Sun Jan 9 15:52:55 2022 -0800
Fix bounds with artboards for zoom-to-fit and scrollbar scaling (#473)
* - document load keeps postition
- zoom to fit
- scrollbars use artboard dimensions
* - review comments
- svg export uses all artboard bounds
Co-authored-by: Keavon Chambers <keavon@keavon.com>
commit 61432de4801d63d62faa18aaa624b11a122a97b1
Author: 0HyperCube <78500760+0HyperCube@users.noreply.github.com>
Date: Sat Jan 8 21:06:15 2022 +0000
Fix rotation input (#472)
commit 3eeac79f5a811c10ab898471f41bd796c5cf16b0
Author: 0HyperCube <78500760+0HyperCube@users.noreply.github.com>
Date: Sat Jan 8 16:30:03 2022 +0000
Enhance the Navigate Tool zoom behavior (#461)
* Snap zoom
* Navigate zoom from centre
* Ctrl to snap zoom in navigate
* Use ctrl for global snap rotate
* Fix the rotation input on snap rotate
* Update hint to use ctrl
* Fix mouse centre on drag
* Click to zoom in
* Clean up centre zoom
* Update user input hints; tweak some variable names for clarity and standardization
Co-authored-by: Keavon Chambers <keavon@keavon.com>
commit a2c2f7fc9da5beb73f677e95d5b4095cd8851318
Author: 0HyperCube <78500760+0HyperCube@users.noreply.github.com>
Date: Sat Jan 8 16:02:02 2022 +0000
Add support for resizing workspace panels (#443)
* Resize panels
* Removing move_selection test pending #444 resolved
* Bind event listners and cursor to the document
* Fix flex grow on document being reset when drawing
* Call onresize when the boundry is dragged
* Add min panel size
* Add explicit function return types
* Dispatch resize event
* Lock pointer instead of setting cursor on document
Co-authored-by: otdavies <oliver@psyfer.io>
Co-authored-by: Keavon Chambers <keavon@keavon.com>
commit 54e91211158c3b6e3f6597fde85f0340a75f1667
Author: mfish33 <32677537+mfish33@users.noreply.github.com>
Date: Sat Jan 8 07:50:08 2022 -0800
Implement artboards and document version enforcement (#466)
* - graphite document artboard implementation
- autosave document load hitch fix
- Autosave will delete saved files when graphite document version changes
* formating
* - top left 0,0
- fixed hitch on first document
- vue calls first render
* Revert
* Merge branch 'master' into artboards
* Small bug fixes and code review tweaks
Co-authored-by: Oliver Davies <oliver@psyfer.io>
Co-authored-by: Keavon Chambers <keavon@keavon.com>
commit 11f15bd6afa7f355b79e2b296f9397c5cb5ad783
Author: Leonard Pauli <leonardpauli@me.com>
Date: Sat Jan 8 14:25:24 2022 +0100
Path Tool: Implement anchor point dragging (#451)
* #82 path-tool: WIP selecting control point working
* Fix bug where duplication with Ctrl+D doesn't properly duplicate (#423)
* bug fix: duplication didn't properly duplicate
* cargo fmt
* changed the formatting slightly for readability
* Small cleanups, changed color of handles upon selection
* Fix changes from merge
* Remove duplicate anchor points on top of one another
* Fix possible issues with thumbnails not being updated from Graphene operations
* path-tool: attempt to move control points on click
* Add dragging for control points
* Editing shape anchors functional. Handles next.
* Comment cleanup & slight cleanup of closest_anchor(..)
* Removing conflict with master
* Tiny code tweaks
Co-authored-by: Keavon Chambers <keavon@keavon.com>
Co-authored-by: caleb <56044292+caleb-ad@users.noreply.github.com>
Co-authored-by: otdavies <oliver@psyfer.io>
Co-authored-by: Dennis <dennis@kobert.dev>
commit 05e8a98f468cb3a8814cf02e482805ed1616e357
Author: Keavon Chambers <keavon@keavon.com>
Date: Fri Jan 7 23:13:33 2022 -0800
Redesign the Layer Tree UI (#468)
commit 8e3d2372289872716ab2567da744a213f0c4d1f3
Author: Oliver Davies <oliver@psyfer.io>
Date: Fri Jan 7 15:53:12 2022 -0800
Ungroup layers (#465)
* WIP handling corner cases, like ungrouping subfolders
* Resolved hanging
* Fix recursive ungrouping
* Functional, corner case free Ungroup. Small Undo issue & warnings
* Update layertree upon undo
* Also update layerdata upon redo
* Add some polish
* Resolved TODOs
* Oops didn't save all after rename, ha.
Co-authored-by: Dennis <dennis@kobert.dev>
commit c1c719294862e049c55c0ede72f2014ba7851dc4
Author: TrueDoctor <dennis@kobert.dev>
Date: Fri Jan 7 04:15:08 2022 +0100
Tidy up path handling in document_file (#464)
* Tidy up path handling in document_file
+ Improve #455
* Cargo Clippy lints
* Rename to_vec to map_to_vec
Co-authored-by: Oliver Davies <oliver@psyfer.io>
commit f4707f9b741973adddf769dee1b64effcc702805
Author: mfish33 <32677537+mfish33@users.noreply.github.com>
Date: Thu Jan 6 18:45:37 2022 -0800
Better decimal rounding in the NumberInput widget (#457)
* better decimal rounding in NumberInput
* - created function to reuse
- used math instead of string manipulation
* updated comment to be correct
* updated comment
* updated comment
commit 0219f060b8e13b7dc9e47335f130d1737b9a8529
Author: Keavon Chambers <keavon@keavon.com>
Date: Wed Jan 5 05:40:32 2022 -0800
Fix build script to use correct branch name
* Fix indentation
* Overlays fade in
* Fix formatting
* Add consts for snap visible and opacity
* Tweak constants for improved UX, rejigger imports
* Fix AA bounding box
* Snap only visible layers
* Add some comments
Co-authored-by: otdavies <oliver@psyfer.io>
Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
72e638c5da
commit
3bf5023ef8
|
|
@ -16,6 +16,8 @@ pub const VIEWPORT_SCROLL_RATE: f64 = 0.6;
|
|||
pub const VIEWPORT_ROTATE_SNAP_INTERVAL: f64 = 15.;
|
||||
|
||||
pub const SNAP_TOLERANCE: f64 = 3.;
|
||||
pub const SNAP_OVERLAY_FADE_DISTANCE: f64 = 20.;
|
||||
pub const SNAP_OVERLAY_UNSNAPPED_OPACITY: f64 = 0.4;
|
||||
|
||||
// Transforming layer
|
||||
pub const ROTATE_SNAP_ANGLE: f64 = 15.;
|
||||
|
|
|
|||
|
|
@ -182,6 +182,10 @@ impl DocumentMessageHandler {
|
|||
self.layer_metadata.iter().filter_map(|(path, data)| data.selected.then(|| path.as_slice()))
|
||||
}
|
||||
|
||||
pub fn non_selected_layers(&self) -> impl Iterator<Item = &[LayerId]> {
|
||||
self.layer_metadata.iter().filter_map(|(path, data)| (!data.selected).then(|| path.as_slice()))
|
||||
}
|
||||
|
||||
pub fn selected_layers_without_children(&self) -> Vec<&[LayerId]> {
|
||||
let unique_layers = GrapheneDocument::shallowest_unique_layers(self.selected_layers());
|
||||
|
||||
|
|
@ -200,6 +204,13 @@ impl DocumentMessageHandler {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn visible_layers(&self) -> impl Iterator<Item = &[LayerId]> {
|
||||
self.all_layers().filter(|path| match self.graphene_document.layer(path) {
|
||||
Ok(layer) => layer.visible,
|
||||
Err(_) => false,
|
||||
})
|
||||
}
|
||||
|
||||
fn serialize_structure(&self, folder: &Folder, structure: &mut Vec<u64>, data: &mut Vec<LayerId>, path: &mut Vec<LayerId>) {
|
||||
let mut space = 0;
|
||||
for (id, layer) in folder.layer_ids.iter().zip(folder.layers()).rev() {
|
||||
|
|
@ -267,7 +278,7 @@ impl DocumentMessageHandler {
|
|||
self.layer_metadata.keys().filter_map(|path| (!path.is_empty()).then(|| path.as_slice()))
|
||||
}
|
||||
|
||||
/// Returns the paths to all layers in order, optionally including only selected or non-selected layers.
|
||||
/// Returns the paths to all layers in order
|
||||
fn sort_layers<'a>(&self, paths: impl Iterator<Item = &'a [LayerId]>) -> Vec<&'a [LayerId]> {
|
||||
// Compute the indices for each layer to be able to sort them
|
||||
let mut layers_with_indices: Vec<(&[LayerId], Vec<usize>)> = paths
|
||||
|
|
@ -302,7 +313,7 @@ impl DocumentMessageHandler {
|
|||
/// Returns the paths to all non_selected layers in order
|
||||
#[allow(dead_code)] // used for test cases
|
||||
pub fn non_selected_layers_sorted(&self) -> Vec<&[LayerId]> {
|
||||
self.sort_layers(self.all_layers().filter(|layer| !self.selected_layers().any(|path| &path == layer)))
|
||||
self.sort_layers(self.non_selected_layers())
|
||||
}
|
||||
|
||||
pub fn layer_metadata(&self, path: &[LayerId]) -> &LayerMetadata {
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ impl Translation {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn increment_amount(self, delta: DVec2) -> Self {
|
||||
Self {
|
||||
dragged_distance: self.dragged_distance + delta,
|
||||
|
|
@ -87,6 +88,7 @@ impl Rotation {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn increment_amount(self, delta: f64) -> Self {
|
||||
Self {
|
||||
dragged_angle: self.dragged_angle + delta,
|
||||
|
|
@ -124,6 +126,7 @@ impl Scale {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn increment_amount(self, delta: f64) -> Self {
|
||||
Self {
|
||||
dragged_factor: self.dragged_factor + delta,
|
||||
|
|
|
|||
|
|
@ -37,16 +37,6 @@ impl MessageHandler<InputPreprocessorMessage, ()> for InputPreprocessorMessageHa
|
|||
}
|
||||
.into(),
|
||||
);
|
||||
responses.push_back(
|
||||
DocumentMessage::Overlays(
|
||||
graphene::Operation::TransformLayer {
|
||||
path: vec![],
|
||||
transform: glam::DAffine2::from_translation(translation).to_cols_array(),
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
responses.push_back(
|
||||
DocumentMessage::Artboard(
|
||||
graphene::Operation::TransformLayer {
|
||||
|
|
|
|||
|
|
@ -1,25 +1,105 @@
|
|||
use crate::consts::SNAP_TOLERANCE;
|
||||
use crate::consts::{COLOR_ACCENT, SNAP_OVERLAY_FADE_DISTANCE, SNAP_OVERLAY_UNSNAPPED_OPACITY, SNAP_TOLERANCE};
|
||||
use crate::document::DocumentMessageHandler;
|
||||
use crate::message_prelude::*;
|
||||
|
||||
use graphene::LayerId;
|
||||
use graphene::layers::style::{self, Stroke};
|
||||
use graphene::{LayerId, Operation};
|
||||
|
||||
use glam::DVec2;
|
||||
use glam::{DAffine2, DVec2};
|
||||
use std::f64::consts::PI;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct SnapHandler {
|
||||
snap_targets: Option<(Vec<f64>, Vec<f64>)>,
|
||||
overlay_paths: Vec<Vec<LayerId>>,
|
||||
}
|
||||
|
||||
impl SnapHandler {
|
||||
/// Updates the snapping overlays with the specified distances.
|
||||
/// `positions_and_distances` is a tuple of `position` and `distance` iterators, respectively, each with `(x, y)` values.
|
||||
fn update_overlays(
|
||||
overlay_paths: &mut Vec<Vec<LayerId>>,
|
||||
responses: &mut VecDeque<Message>,
|
||||
viewport_bounds: DVec2,
|
||||
(positions_and_distances): (impl Iterator<Item = (f64, f64)>, impl Iterator<Item = (f64, f64)>),
|
||||
closest_distance: DVec2,
|
||||
) {
|
||||
/// Draws an alignment line overlay with the correct transform and fade opacity, reusing lines from the pool if available.
|
||||
fn add_overlay_line(responses: &mut VecDeque<Message>, transform: [f64; 6], opacity: f64, index: usize, overlay_paths: &mut Vec<Vec<LayerId>>) {
|
||||
// If there isn't one in the pool to ruse, add a new alignment line to the pool with the intended transform
|
||||
let layer_path = if index >= overlay_paths.len() {
|
||||
let layer_path = vec![generate_uuid()];
|
||||
responses.push_back(
|
||||
DocumentMessage::Overlays(
|
||||
Operation::AddOverlayLine {
|
||||
path: layer_path.clone(),
|
||||
transform,
|
||||
style: style::PathStyle::new(Some(Stroke::new(COLOR_ACCENT, 1.0)), None),
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
overlay_paths.push(layer_path.clone());
|
||||
layer_path
|
||||
}
|
||||
// Otherwise, reuse an overlay line from the pool and update its new transform
|
||||
else {
|
||||
let layer_path = overlay_paths[index].clone();
|
||||
responses.push_back(DocumentMessage::Overlays(Operation::SetLayerTransform { path: layer_path.clone(), transform }.into()).into());
|
||||
layer_path
|
||||
};
|
||||
|
||||
// Then set its opacity to the fade amount
|
||||
responses.push_back(DocumentMessage::Overlays(Operation::SetLayerOpacity { path: layer_path, opacity }.into()).into());
|
||||
}
|
||||
|
||||
let (positions, distances) = positions_and_distances;
|
||||
let mut index = 0;
|
||||
|
||||
// Draw the vertical alignment lines
|
||||
for (x_target, distance) in positions.filter(|(_pos, dist)| dist.abs() < SNAP_OVERLAY_FADE_DISTANCE) {
|
||||
let transform = DAffine2::from_scale_angle_translation(DVec2::new(viewport_bounds.y, 1.), PI / 2., DVec2::new((x_target).round() - 0.5, 0.)).to_cols_array();
|
||||
|
||||
let opacity = if closest_distance.x == distance {
|
||||
1.
|
||||
} else {
|
||||
SNAP_OVERLAY_UNSNAPPED_OPACITY - distance.abs() / (SNAP_OVERLAY_FADE_DISTANCE / SNAP_OVERLAY_UNSNAPPED_OPACITY)
|
||||
};
|
||||
|
||||
add_overlay_line(responses, transform, opacity, index, overlay_paths);
|
||||
index += 1;
|
||||
}
|
||||
// Draw the horizontal alignment lines
|
||||
for (y_target, distance) in distances.filter(|(_pos, dist)| dist.abs() < SNAP_OVERLAY_FADE_DISTANCE) {
|
||||
let transform = DAffine2::from_scale_angle_translation(DVec2::new(viewport_bounds.x, 1.), 0., DVec2::new(0., (y_target).round() - 0.5)).to_cols_array();
|
||||
|
||||
let opacity = if closest_distance.y == distance {
|
||||
1.
|
||||
} else {
|
||||
SNAP_OVERLAY_UNSNAPPED_OPACITY - distance.abs() / (SNAP_OVERLAY_FADE_DISTANCE / SNAP_OVERLAY_UNSNAPPED_OPACITY)
|
||||
};
|
||||
|
||||
add_overlay_line(responses, transform, opacity, index, overlay_paths);
|
||||
index += 1;
|
||||
}
|
||||
Self::remove_unused_overlays(overlay_paths, responses, index);
|
||||
}
|
||||
|
||||
/// Remove overlays from the pool beyond a given index. Pool entries up through that index will be kept.
|
||||
fn remove_unused_overlays(overlay_paths: &mut Vec<Vec<LayerId>>, responses: &mut VecDeque<Message>, remove_after_index: usize) {
|
||||
while overlay_paths.len() > remove_after_index {
|
||||
responses.push_back(DocumentMessage::Overlays(Operation::DeleteLayer { path: overlay_paths.pop().unwrap() }.into()).into());
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets a list of snap targets for the X and Y axes in Viewport coords for the target layers (usually all layers or all non-selected layers.)
|
||||
/// This should be called at the start of a drag.
|
||||
pub fn start_snap(&mut self, document_message_handler: &DocumentMessageHandler, target_layers: Vec<&[LayerId]>, ignore_layers: &[Vec<LayerId>]) {
|
||||
pub fn start_snap<'a>(&mut self, document_message_handler: &DocumentMessageHandler, target_layers: impl Iterator<Item = &'a [LayerId]>) {
|
||||
if document_message_handler.snapping_enabled {
|
||||
// Could be made into sorted Vec or a HashSet for more performant lookups.
|
||||
self.snap_targets = Some(
|
||||
target_layers
|
||||
.iter()
|
||||
.filter(|path| !ignore_layers.iter().any(|layer| layer.as_slice() == **path))
|
||||
.filter_map(|path| document_message_handler.graphene_document.viewport_bounding_box(path).ok()?)
|
||||
.flat_map(|[bound1, bound2]| [bound1, bound2, ((bound1 + bound2) / 2.)])
|
||||
.map(|vec| vec.into())
|
||||
|
|
@ -30,7 +110,14 @@ impl SnapHandler {
|
|||
|
||||
/// Finds the closest snap from an array of layers to the specified snap targets in viewport coords.
|
||||
/// Returns 0 for each axis that there is no snap less than the snap tolerance.
|
||||
pub fn snap_layers(&self, document_message_handler: &DocumentMessageHandler, selected_layers: &[Vec<LayerId>], mouse_delta: DVec2) -> DVec2 {
|
||||
pub fn snap_layers(
|
||||
&mut self,
|
||||
responses: &mut VecDeque<Message>,
|
||||
document_message_handler: &DocumentMessageHandler,
|
||||
selected_layers: &[Vec<LayerId>],
|
||||
viewport_bounds: DVec2,
|
||||
mouse_delta: DVec2,
|
||||
) -> DVec2 {
|
||||
if document_message_handler.snapping_enabled {
|
||||
if let Some((targets_x, targets_y)) = &self.snap_targets {
|
||||
let (snap_x, snap_y): (Vec<f64>, Vec<f64>) = selected_layers
|
||||
|
|
@ -40,24 +127,23 @@ impl SnapHandler {
|
|||
.map(|vec| vec.into())
|
||||
.unzip();
|
||||
|
||||
let closest_move = DVec2::new(
|
||||
targets_x
|
||||
.iter()
|
||||
.flat_map(|target| snap_x.iter().map(move |snap| target - mouse_delta.x - snap))
|
||||
.min_by(|a, b| a.abs().partial_cmp(&b.abs()).expect("Could not compare document bounds."))
|
||||
.unwrap_or(0.),
|
||||
targets_y
|
||||
.iter()
|
||||
.flat_map(|target| snap_y.iter().map(move |snap| target - mouse_delta.y - snap))
|
||||
.min_by(|a, b| a.abs().partial_cmp(&b.abs()).expect("Could not compare document bounds."))
|
||||
.unwrap_or(0.),
|
||||
let positions = targets_x.iter().flat_map(|&target| snap_x.iter().map(move |&snap| (target, target - mouse_delta.x - snap)));
|
||||
let distances = targets_y.iter().flat_map(|&target| snap_y.iter().map(move |&snap| (target, target - mouse_delta.y - snap)));
|
||||
|
||||
let min_positions = positions.clone().min_by(|a, b| a.1.abs().partial_cmp(&b.1.abs()).expect("Could not compare position."));
|
||||
let min_distances = distances.clone().min_by(|a, b| a.1.abs().partial_cmp(&b.1.abs()).expect("Could not compare position."));
|
||||
|
||||
let closest_distance = DVec2::new(min_positions.map_or(0., |(_pos, dist)| dist), min_distances.map_or(0., |(_pos, dist)| dist));
|
||||
|
||||
// Clamp, do not move, if above snap tolerance
|
||||
let clamped_closest_distance = DVec2::new(
|
||||
if closest_distance.x.abs() > SNAP_TOLERANCE { 0. } else { closest_distance.x },
|
||||
if closest_distance.y.abs() > SNAP_TOLERANCE { 0. } else { closest_distance.y },
|
||||
);
|
||||
|
||||
// Clamp, do not move if over snap tolerance
|
||||
DVec2::new(
|
||||
if closest_move.x.abs() > SNAP_TOLERANCE { 0. } else { closest_move.x },
|
||||
if closest_move.y.abs() > SNAP_TOLERANCE { 0. } else { closest_move.y },
|
||||
)
|
||||
Self::update_overlays(&mut self.overlay_paths, responses, viewport_bounds, (positions, distances), clamped_closest_distance);
|
||||
|
||||
clamped_closest_distance
|
||||
} else {
|
||||
DVec2::ZERO
|
||||
}
|
||||
|
|
@ -67,30 +153,26 @@ impl SnapHandler {
|
|||
}
|
||||
|
||||
/// Handles snapping of a viewport position, returning another viewport position.
|
||||
pub fn snap_position(&self, document_message_handler: &DocumentMessageHandler, position_viewport: DVec2) -> DVec2 {
|
||||
pub fn snap_position(&mut self, responses: &mut VecDeque<Message>, viewport_bounds: DVec2, document_message_handler: &DocumentMessageHandler, position_viewport: DVec2) -> DVec2 {
|
||||
if document_message_handler.snapping_enabled {
|
||||
if let Some((targets_x, targets_y)) = &self.snap_targets {
|
||||
// For each list of snap targets, find the shortest distance to move the point to that target.
|
||||
let closest_move = DVec2::new(
|
||||
targets_x
|
||||
.iter()
|
||||
.map(|x| (x - position_viewport.x))
|
||||
.min_by(|a, b| a.abs().partial_cmp(&b.abs()).expect("Could not compare document bounds."))
|
||||
.unwrap_or(0.),
|
||||
targets_y
|
||||
.iter()
|
||||
.map(|y| (y - position_viewport.y))
|
||||
.min_by(|a, b| a.abs().partial_cmp(&b.abs()).expect("Could not compare document bounds."))
|
||||
.unwrap_or(0.),
|
||||
);
|
||||
let positions = targets_x.iter().map(|&x| (x, x - position_viewport.x));
|
||||
let distances = targets_y.iter().map(|&y| (y, y - position_viewport.y));
|
||||
|
||||
let min_positions = positions.clone().min_by(|a, b| a.1.abs().partial_cmp(&b.1.abs()).expect("Could not compare position."));
|
||||
let min_distances = distances.clone().min_by(|a, b| a.1.abs().partial_cmp(&b.1.abs()).expect("Could not compare position."));
|
||||
|
||||
let closest_distance = DVec2::new(min_positions.map_or(0., |(_pos, dist)| dist), min_distances.map_or(0., |(_pos, dist)| dist));
|
||||
|
||||
// Do not move if over snap tolerance
|
||||
let clamped_closest_move = DVec2::new(
|
||||
if closest_move.x.abs() > SNAP_TOLERANCE { 0. } else { closest_move.x },
|
||||
if closest_move.y.abs() > SNAP_TOLERANCE { 0. } else { closest_move.y },
|
||||
let clamped_closest_distance = DVec2::new(
|
||||
if closest_distance.x.abs() > SNAP_TOLERANCE { 0. } else { closest_distance.x },
|
||||
if closest_distance.y.abs() > SNAP_TOLERANCE { 0. } else { closest_distance.y },
|
||||
);
|
||||
|
||||
position_viewport + clamped_closest_move
|
||||
Self::update_overlays(&mut self.overlay_paths, responses, viewport_bounds, (positions, distances), clamped_closest_distance);
|
||||
|
||||
position_viewport + clamped_closest_distance
|
||||
} else {
|
||||
position_viewport
|
||||
}
|
||||
|
|
@ -99,8 +181,9 @@ impl SnapHandler {
|
|||
}
|
||||
}
|
||||
|
||||
/// Removes snap target data. Call this when snapping is done.
|
||||
pub fn cleanup(&mut self) {
|
||||
/// Removes snap target data and overlays. Call this when snapping is done.
|
||||
pub fn cleanup(&mut self, responses: &mut VecDeque<Message>) {
|
||||
Self::remove_unused_overlays(&mut self.overlay_paths, responses, 0);
|
||||
self.snap_targets = None;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ pub type ToolActionHandlerData<'a> = (&'a DocumentMessageHandler, &'a DocumentTo
|
|||
pub trait Fsm {
|
||||
type ToolData;
|
||||
|
||||
#[must_use]
|
||||
fn transition(
|
||||
self,
|
||||
message: ToolMessage,
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ impl Fsm for EllipseToolFsmState {
|
|||
if let ToolMessage::Ellipse(event) = event {
|
||||
match (self, event) {
|
||||
(Ready, DragStart) => {
|
||||
shape_data.start(document, input.mouse.position);
|
||||
shape_data.start(responses, input.viewport_bounds.size(), document, input.mouse.position);
|
||||
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||
shape_data.path = Some(vec![generate_uuid()]);
|
||||
responses.push_back(DocumentMessage::DeselectAllLayers.into());
|
||||
|
|
@ -122,7 +122,7 @@ impl Fsm for EllipseToolFsmState {
|
|||
Drawing
|
||||
}
|
||||
(state, Resize { center, lock_ratio }) => {
|
||||
if let Some(message) = shape_data.calculate_transform(document, center, lock_ratio, input) {
|
||||
if let Some(message) = shape_data.calculate_transform(responses, input.viewport_bounds.size(), document, center, lock_ratio, input) {
|
||||
responses.push_back(message);
|
||||
}
|
||||
|
||||
|
|
@ -135,12 +135,12 @@ impl Fsm for EllipseToolFsmState {
|
|||
false => responses.push_back(DocumentMessage::CommitTransaction.into()),
|
||||
}
|
||||
|
||||
shape_data.cleanup();
|
||||
shape_data.cleanup(responses);
|
||||
Ready
|
||||
}
|
||||
(Drawing, Abort) => {
|
||||
responses.push_back(DocumentMessage::AbortTransaction.into());
|
||||
shape_data.cleanup();
|
||||
shape_data.cleanup(responses);
|
||||
|
||||
Ready
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,8 +111,8 @@ impl Fsm for LineToolFsmState {
|
|||
if let ToolMessage::Line(event) = event {
|
||||
match (self, event) {
|
||||
(Ready, DragStart) => {
|
||||
data.snap_handler.start_snap(document, document.all_layers_sorted(), &[]);
|
||||
data.drag_start = data.snap_handler.snap_position(document, input.mouse.position);
|
||||
data.snap_handler.start_snap(document, document.visible_layers());
|
||||
data.drag_start = data.snap_handler.snap_position(responses, input.viewport_bounds.size(), document, input.mouse.position);
|
||||
|
||||
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||
data.path = Some(vec![generate_uuid()]);
|
||||
|
|
@ -136,7 +136,7 @@ impl Fsm for LineToolFsmState {
|
|||
Drawing
|
||||
}
|
||||
(Drawing, Redraw { center, snap_angle, lock_angle }) => {
|
||||
data.drag_current = data.snap_handler.snap_position(document, input.mouse.position);
|
||||
data.drag_current = data.snap_handler.snap_position(responses, input.viewport_bounds.size(), document, input.mouse.position);
|
||||
|
||||
let values: Vec<_> = [lock_angle, snap_angle, center].iter().map(|k| input.keyboard.get(*k as usize)).collect();
|
||||
responses.push_back(generate_transform(data, values[0], values[1], values[2]));
|
||||
|
|
@ -144,8 +144,8 @@ impl Fsm for LineToolFsmState {
|
|||
Drawing
|
||||
}
|
||||
(Drawing, DragStop) => {
|
||||
data.drag_current = data.snap_handler.snap_position(document, input.mouse.position);
|
||||
data.snap_handler.cleanup();
|
||||
data.drag_current = data.snap_handler.snap_position(responses, input.viewport_bounds.size(), document, input.mouse.position);
|
||||
data.snap_handler.cleanup(responses);
|
||||
|
||||
// TODO: introduce comparison threshold when operating with canvas coordinates (https://github.com/GraphiteEditor/Graphite/issues/100)
|
||||
match data.drag_start == input.mouse.position {
|
||||
|
|
@ -158,7 +158,7 @@ impl Fsm for LineToolFsmState {
|
|||
Ready
|
||||
}
|
||||
(Drawing, Abort) => {
|
||||
data.snap_handler.cleanup();
|
||||
data.snap_handler.cleanup(responses);
|
||||
responses.push_back(DocumentMessage::AbortTransaction.into());
|
||||
data.path = None;
|
||||
Ready
|
||||
|
|
|
|||
|
|
@ -113,8 +113,8 @@ impl Fsm for PenToolFsmState {
|
|||
data.path = Some(vec![generate_uuid()]);
|
||||
data.layer_exists = false;
|
||||
|
||||
data.snap_handler.start_snap(document, document.all_layers_sorted(), &[]);
|
||||
let snapped_position = data.snap_handler.snap_position(document, input.mouse.position);
|
||||
data.snap_handler.start_snap(document, document.visible_layers());
|
||||
let snapped_position = data.snap_handler.snap_position(responses, input.viewport_bounds.size(), document, input.mouse.position);
|
||||
|
||||
let pos = transform.inverse() * DAffine2::from_translation(snapped_position);
|
||||
|
||||
|
|
@ -131,7 +131,7 @@ impl Fsm for PenToolFsmState {
|
|||
Drawing
|
||||
}
|
||||
(Drawing, DragStop) => {
|
||||
let snapped_position = data.snap_handler.snap_position(document, input.mouse.position);
|
||||
let snapped_position = data.snap_handler.snap_position(responses, input.viewport_bounds.size(), document, input.mouse.position);
|
||||
let pos = transform.inverse() * DAffine2::from_translation(snapped_position);
|
||||
|
||||
// TODO: introduce comparison threshold when operating with canvas coordinates (https://github.com/GraphiteEditor/Graphite/issues/100)
|
||||
|
|
@ -146,7 +146,7 @@ impl Fsm for PenToolFsmState {
|
|||
Drawing
|
||||
}
|
||||
(Drawing, PointerMove) => {
|
||||
let snapped_position = data.snap_handler.snap_position(document, input.mouse.position);
|
||||
let snapped_position = data.snap_handler.snap_position(responses, input.viewport_bounds.size(), document, input.mouse.position);
|
||||
let pos = transform.inverse() * DAffine2::from_translation(snapped_position);
|
||||
data.next_point = pos;
|
||||
|
||||
|
|
@ -167,7 +167,7 @@ impl Fsm for PenToolFsmState {
|
|||
|
||||
data.path = None;
|
||||
data.points.clear();
|
||||
data.snap_handler.cleanup();
|
||||
data.snap_handler.cleanup(responses);
|
||||
|
||||
Ready
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ impl Fsm for RectangleToolFsmState {
|
|||
if let ToolMessage::Rectangle(event) = event {
|
||||
match (self, event) {
|
||||
(Ready, DragStart) => {
|
||||
shape_data.start(document, input.mouse.position);
|
||||
shape_data.start(responses, input.viewport_bounds.size(), document, input.mouse.position);
|
||||
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||
shape_data.path = Some(vec![generate_uuid()]);
|
||||
responses.push_back(DocumentMessage::DeselectAllLayers.into());
|
||||
|
|
@ -121,7 +121,7 @@ impl Fsm for RectangleToolFsmState {
|
|||
Drawing
|
||||
}
|
||||
(state, Resize { center, lock_ratio }) => {
|
||||
if let Some(message) = shape_data.calculate_transform(document, center, lock_ratio, input) {
|
||||
if let Some(message) = shape_data.calculate_transform(responses, input.viewport_bounds.size(), document, center, lock_ratio, input) {
|
||||
responses.push_back(message);
|
||||
}
|
||||
|
||||
|
|
@ -134,13 +134,14 @@ impl Fsm for RectangleToolFsmState {
|
|||
false => responses.push_back(DocumentMessage::CommitTransaction.into()),
|
||||
}
|
||||
|
||||
shape_data.cleanup();
|
||||
shape_data.cleanup(responses);
|
||||
|
||||
Ready
|
||||
}
|
||||
(Drawing, Abort) => {
|
||||
responses.push_back(DocumentMessage::AbortTransaction.into());
|
||||
shape_data.cleanup();
|
||||
|
||||
shape_data.cleanup(responses);
|
||||
|
||||
Ready
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@ pub struct Resize {
|
|||
}
|
||||
impl Resize {
|
||||
/// Starts a resize, assigning the snap targets and snapping the starting position.
|
||||
pub fn start(&mut self, document: &DocumentMessageHandler, mouse_position: DVec2) {
|
||||
pub fn start(&mut self, responses: &mut VecDeque<Message>, viewport_bounds: DVec2, document: &DocumentMessageHandler, mouse_position: DVec2) {
|
||||
let layers = document.all_layers_sorted();
|
||||
self.snap_handler.start_snap(document, layers, &[]);
|
||||
self.snap_handler.start_snap(responses, viewport_bounds, document, layers);
|
||||
self.drag_start = self.snap_handler.snap_position(document, mouse_position);
|
||||
}
|
||||
|
||||
|
|
@ -50,8 +50,8 @@ impl Resize {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn cleanup(&mut self) {
|
||||
self.snap_handler.cleanup();
|
||||
pub fn cleanup(&mut self, responses: &mut VecDeque<Message>) {
|
||||
self.snap_handler.cleanup(responses);
|
||||
self.path = None;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ fn add_bounding_box(responses: &mut Vec<Message>) -> Vec<LayerId> {
|
|||
}
|
||||
|
||||
fn transform_from_box(pos1: DVec2, pos2: DVec2) -> [f64; 6] {
|
||||
DAffine2::from_scale_angle_translation(pos2 - pos1, 0., pos1).to_cols_array()
|
||||
DAffine2::from_scale_angle_translation((pos2 - pos1).round(), 0., pos1.round() - DVec2::splat(0.5)).to_cols_array()
|
||||
}
|
||||
|
||||
impl Fsm for SelectToolFsmState {
|
||||
|
|
@ -164,9 +164,6 @@ impl Fsm for SelectToolFsmState {
|
|||
|
||||
data.bounding_box_overlay_layer = Some(path.clone());
|
||||
|
||||
let half_pixel_offset = DVec2::splat(0.5);
|
||||
let pos1 = pos1 + half_pixel_offset;
|
||||
let pos2 = pos2 - half_pixel_offset;
|
||||
let transform = transform_from_box(pos1, pos2);
|
||||
DocumentMessage::Overlays(Operation::SetLayerTransformInViewport { path, transform }.into()).into()
|
||||
}
|
||||
|
|
@ -189,6 +186,10 @@ impl Fsm for SelectToolFsmState {
|
|||
let state = if selected.iter().any(|path| intersection.contains(path)) {
|
||||
buffer.push(DocumentMessage::StartTransaction.into());
|
||||
data.layers_dragging = selected;
|
||||
|
||||
data.snap_handler
|
||||
.start_snap(document, document.visible_layers().filter(|layer| !data.layers_dragging.iter().any(|path| path == layer)));
|
||||
|
||||
Dragging
|
||||
} else {
|
||||
if !input.keyboard.get(add_to_selection as usize) {
|
||||
|
|
@ -201,6 +202,9 @@ impl Fsm for SelectToolFsmState {
|
|||
buffer.push(DocumentMessage::AddSelectedLayers { additional_layers: selected.clone() }.into());
|
||||
buffer.push(DocumentMessage::StartTransaction.into());
|
||||
data.layers_dragging.append(&mut selected);
|
||||
data.snap_handler
|
||||
.start_snap(document, document.visible_layers().filter(|layer| !data.layers_dragging.iter().any(|path| path == layer)));
|
||||
|
||||
Dragging
|
||||
} else {
|
||||
data.drag_box_overlay_layer = Some(add_bounding_box(&mut buffer));
|
||||
|
|
@ -209,13 +213,6 @@ impl Fsm for SelectToolFsmState {
|
|||
};
|
||||
buffer.into_iter().rev().for_each(|message| responses.push_front(message));
|
||||
|
||||
// TODO: Probably delete this now that the overlays system has moved to a separate Graphene document? (@0hypercube)
|
||||
let ignore_layers = if let Some(bounding_box) = &data.bounding_box_overlay_layer {
|
||||
vec![bounding_box.clone()]
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
data.snap_handler.start_snap(document, document.non_selected_layers_sorted(), &ignore_layers);
|
||||
state
|
||||
}
|
||||
(Dragging, MouseMove { snap_angle }) => {
|
||||
|
|
@ -234,7 +231,7 @@ impl Fsm for SelectToolFsmState {
|
|||
|
||||
let mouse_delta = mouse_position - data.drag_current;
|
||||
|
||||
let closest_move = data.snap_handler.snap_layers(document, &data.layers_dragging, mouse_delta);
|
||||
let closest_move = data.snap_handler.snap_layers(responses, document, &data.layers_dragging, input.viewport_bounds.size(), mouse_delta);
|
||||
// TODO: Cache the result of `shallowest_unique_layers` to avoid this heavy computation every frame of movement, see https://github.com/GraphiteEditor/Graphite/pull/481
|
||||
for path in Document::shallowest_unique_layers(data.layers_dragging.iter()) {
|
||||
responses.push_front(
|
||||
|
|
@ -250,15 +247,12 @@ impl Fsm for SelectToolFsmState {
|
|||
}
|
||||
(DrawingBox, MouseMove { .. }) => {
|
||||
data.drag_current = input.mouse.position;
|
||||
let half_pixel_offset = DVec2::splat(0.5);
|
||||
let start = data.drag_start + half_pixel_offset;
|
||||
let size = data.drag_current - start + half_pixel_offset;
|
||||
|
||||
responses.push_front(
|
||||
DocumentMessage::Overlays(
|
||||
Operation::SetLayerTransformInViewport {
|
||||
path: data.drag_box_overlay_layer.clone().unwrap(),
|
||||
transform: DAffine2::from_scale_angle_translation(size, 0., start).to_cols_array(),
|
||||
transform: transform_from_box(data.drag_start, data.drag_current),
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
|
|
@ -271,7 +265,7 @@ impl Fsm for SelectToolFsmState {
|
|||
true => DocumentMessage::Undo,
|
||||
false => DocumentMessage::CommitTransaction,
|
||||
};
|
||||
data.snap_handler.cleanup();
|
||||
data.snap_handler.cleanup(responses);
|
||||
responses.push_front(response.into());
|
||||
Ready
|
||||
}
|
||||
|
|
@ -298,6 +292,7 @@ impl Fsm for SelectToolFsmState {
|
|||
let mut delete = |path: &mut Option<Vec<LayerId>>| path.take().map(|path| responses.push_front(DocumentMessage::Overlays(Operation::DeleteLayer { path }.into()).into()));
|
||||
delete(&mut data.drag_box_overlay_layer);
|
||||
delete(&mut data.bounding_box_overlay_layer);
|
||||
data.snap_handler.cleanup(responses);
|
||||
Ready
|
||||
}
|
||||
(_, Align { axis, aggregate }) => {
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ impl Fsm for ShapeToolFsmState {
|
|||
if let ToolMessage::Shape(event) = event {
|
||||
match (self, event) {
|
||||
(Ready, DragStart) => {
|
||||
shape_data.start(document, input.mouse.position);
|
||||
shape_data.start(responses, input.viewport_bounds.size(), document, input.mouse.position);
|
||||
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||
shape_data.path = Some(vec![generate_uuid()]);
|
||||
responses.push_back(DocumentMessage::DeselectAllLayers.into());
|
||||
|
|
@ -130,7 +130,7 @@ impl Fsm for ShapeToolFsmState {
|
|||
Drawing
|
||||
}
|
||||
(state, Resize { center, lock_ratio }) => {
|
||||
if let Some(message) = shape_data.calculate_transform(document, center, lock_ratio, input) {
|
||||
if let Some(message) = shape_data.calculate_transform(responses, input.viewport_bounds.size(), document, center, lock_ratio, input) {
|
||||
responses.push_back(message);
|
||||
}
|
||||
|
||||
|
|
@ -143,12 +143,14 @@ impl Fsm for ShapeToolFsmState {
|
|||
false => responses.push_back(DocumentMessage::CommitTransaction.into()),
|
||||
}
|
||||
|
||||
shape_data.cleanup();
|
||||
shape_data.cleanup(responses);
|
||||
|
||||
Ready
|
||||
}
|
||||
(Drawing, Abort) => {
|
||||
responses.push_back(DocumentMessage::AbortTransaction.into());
|
||||
shape_data.cleanup();
|
||||
|
||||
shape_data.cleanup(responses);
|
||||
|
||||
Ready
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,17 +17,24 @@ pub struct Resize {
|
|||
}
|
||||
impl Resize {
|
||||
/// Starts a resize, assigning the snap targets and snapping the starting position.
|
||||
pub fn start(&mut self, document: &DocumentMessageHandler, mouse_position: DVec2) {
|
||||
let layers = document.all_layers_sorted();
|
||||
self.snap_handler.start_snap(document, layers, &[]);
|
||||
self.drag_start = self.snap_handler.snap_position(document, mouse_position);
|
||||
pub fn start(&mut self, responses: &mut VecDeque<Message>, viewport_bounds: DVec2, document: &DocumentMessageHandler, mouse_position: DVec2) {
|
||||
self.snap_handler.start_snap(document, document.visible_layers());
|
||||
self.drag_start = self.snap_handler.snap_position(responses, viewport_bounds, document, mouse_position);
|
||||
}
|
||||
|
||||
pub fn calculate_transform(&self, document: &DocumentMessageHandler, center: Key, lock_ratio: Key, ipp: &InputPreprocessorMessageHandler) -> Option<Message> {
|
||||
pub fn calculate_transform(
|
||||
&mut self,
|
||||
responses: &mut VecDeque<Message>,
|
||||
viewport_bounds: DVec2,
|
||||
document: &DocumentMessageHandler,
|
||||
center: Key,
|
||||
lock_ratio: Key,
|
||||
ipp: &InputPreprocessorMessageHandler,
|
||||
) -> Option<Message> {
|
||||
if let Some(path) = &self.path {
|
||||
let mut start = self.drag_start;
|
||||
|
||||
let stop = self.snap_handler.snap_position(document, ipp.mouse.position);
|
||||
let stop = self.snap_handler.snap_position(responses, viewport_bounds, document, ipp.mouse.position);
|
||||
|
||||
let mut size = stop - start;
|
||||
if ipp.keyboard.get(lock_ratio as usize) {
|
||||
|
|
@ -50,8 +57,8 @@ impl Resize {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn cleanup(&mut self) {
|
||||
self.snap_handler.cleanup();
|
||||
pub fn cleanup(&mut self, responses: &mut VecDeque<Message>) {
|
||||
self.snap_handler.cleanup(responses);
|
||||
self.path = None;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue