mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-01 06:48:11 +08:00
WIP conversion to messages
This commit is contained in:
+47
-32
@@ -1,5 +1,6 @@
|
|||||||
use std::f64::consts::PI;
|
use std::{collections::HashMap, f64::consts::PI};
|
||||||
|
|
||||||
|
use super::DocumentMessageHandler;
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use graphene::{
|
use graphene::{
|
||||||
layers::style::{self, Stroke},
|
layers::style::{self, Stroke},
|
||||||
@@ -11,41 +12,40 @@ use crate::{
|
|||||||
message_prelude::*,
|
message_prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::DocumentMessageHandler;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct SnapHandler {
|
pub struct SnapHandler {
|
||||||
|
enabled: bool,
|
||||||
|
document: DocumentMessageHandler,
|
||||||
snap_targets: Option<(Vec<f64>, Vec<f64>)>,
|
snap_targets: Option<(Vec<f64>, Vec<f64>)>,
|
||||||
|
snappables: HashMap<Vec<LayerId>, Vec<Vec<LayerId>>>,
|
||||||
|
overlay_messages: Vec<Message>,
|
||||||
|
|
||||||
overlay_paths: Vec<Vec<LayerId>>,
|
overlay_paths: Vec<Vec<LayerId>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SnapHandler {
|
impl SnapHandler {
|
||||||
fn add_overlays(&mut self, responses: &mut VecDeque<Message>, viewport_bounds: DVec2) {
|
fn add_overlay_line(&mut self, transform: DAffine2) -> Vec<LayerId> {
|
||||||
fn add_overlay_line(responses: &mut VecDeque<Message>, transform: DAffine2) -> Vec<LayerId> {
|
let layer_path = vec![generate_uuid()];
|
||||||
let layer_path = vec![generate_uuid()];
|
|
||||||
|
|
||||||
let operation = Operation::AddOverlayLine {
|
let operation = Operation::AddOverlayLine {
|
||||||
path: layer_path.clone(),
|
path: layer_path.clone(),
|
||||||
transform: transform.to_cols_array(),
|
transform: transform.to_cols_array(),
|
||||||
style: style::PathStyle::new(Some(Stroke::new(COLOR_ACCENT, 1.0)), None),
|
style: style::PathStyle::new(Some(Stroke::new(COLOR_ACCENT, 1.0)), None),
|
||||||
};
|
};
|
||||||
responses.push_back(DocumentMessage::Overlay(operation.into()).into());
|
self.overlay_messages.push(DocumentMessage::Overlay(operation.into()).into());
|
||||||
|
|
||||||
layer_path
|
layer_path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add_overlays(&mut self, viewport_bounds: DVec2) {
|
||||||
if let Some((x_targets, y_targets)) = &self.snap_targets {
|
if let Some((x_targets, y_targets)) = &self.snap_targets {
|
||||||
for x_target in x_targets {
|
for x_target in x_targets {
|
||||||
self.overlay_paths.push(add_overlay_line(
|
self.overlay_paths
|
||||||
responses,
|
.push(self.add_overlay_line(DAffine2::from_scale_angle_translation(DVec2::new(viewport_bounds.y, 1.), PI / 2., DVec2::new(x_target.round(), 0.))));
|
||||||
DAffine2::from_scale_angle_translation(DVec2::new(viewport_bounds.y, 1.), PI / 2., DVec2::new(x_target.round(), 0.)),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
for y_target in y_targets {
|
for y_target in y_targets {
|
||||||
self.overlay_paths.push(add_overlay_line(
|
self.overlay_paths
|
||||||
responses,
|
.push(self.add_overlay_line(DAffine2::from_scale_angle_translation(DVec2::new(viewport_bounds.x, 1.), 0., DVec2::new(0., y_target.round()))));
|
||||||
DAffine2::from_scale_angle_translation(DVec2::new(viewport_bounds.x, 1.), 0., DVec2::new(0., y_target.round())),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,29 +58,30 @@ impl SnapHandler {
|
|||||||
|
|
||||||
/// 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.)
|
/// 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.
|
/// This should be called at the start of a drag.
|
||||||
pub fn start_snap(&mut self, responses: &mut VecDeque<Message>, viewport_bounds: DVec2, document_message_handler: &DocumentMessageHandler, target_layers: Vec<Vec<LayerId>>) {
|
pub fn start_snap(&mut self, viewport_bounds: DVec2, target_layers: Vec<Vec<LayerId>>) {
|
||||||
if document_message_handler.snapping_enabled {
|
if self.document.snapping_enabled {
|
||||||
// Could be made into sorted Vec or a HashSet for more performant lookups.
|
// Could be made into sorted Vec or a HashSet for more performant lookups.
|
||||||
self.snap_targets = Some(
|
self.snap_targets = Some(
|
||||||
target_layers
|
target_layers
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|path| document_message_handler.graphene_document.viewport_bounding_box(path).ok()?)
|
.filter_map(|path| self.document.graphene_document.viewport_bounding_box(path).ok()?)
|
||||||
.flat_map(|[bound1, bound2]| [bound1, bound2, ((bound1 + bound2) / 2.)])
|
.flat_map(|[bound1, bound2]| [bound1, bound2, ((bound1 + bound2) / 2.)])
|
||||||
.map(|vec| vec.into())
|
.map(|vec| vec.into())
|
||||||
.unzip(),
|
.unzip(),
|
||||||
);
|
);
|
||||||
self.add_overlays(responses, viewport_bounds);
|
self.add_overlays(viewport_bounds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finds the closest snap from an array of layers to the specified snap targets in viewport coords.
|
/// 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.
|
/// 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(&self, mouse_delta: DVec2) -> DVec2 {
|
||||||
if document_message_handler.snapping_enabled {
|
if self.enabled {
|
||||||
if let Some((targets_x, targets_y)) = &self.snap_targets {
|
if let Some((targets_x, targets_y)) = &self.snap_targets {
|
||||||
let (snap_x, snap_y): (Vec<f64>, Vec<f64>) = selected_layers
|
let (snap_x, snap_y): (Vec<f64>, Vec<f64>) = self
|
||||||
|
.snappables
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|path| document_message_handler.graphene_document.viewport_bounding_box(path).ok()?)
|
.filter_map(|(path, _)| self.document.graphene_document.viewport_bounding_box(path).ok()?)
|
||||||
.flat_map(|[bound1, bound2]| [bound1, bound2, (bound1 + bound2) / 2.])
|
.flat_map(|[bound1, bound2]| [bound1, bound2, (bound1 + bound2) / 2.])
|
||||||
.map(|vec| vec.into())
|
.map(|vec| vec.into())
|
||||||
.unzip();
|
.unzip();
|
||||||
@@ -112,8 +113,8 @@ impl SnapHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Handles snapping of a viewport position, returning another viewport position.
|
/// 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(&self, position_viewport: DVec2) -> DVec2 {
|
||||||
if document_message_handler.snapping_enabled {
|
if self.document.snapping_enabled {
|
||||||
if let Some((targets_x, targets_y)) = &self.snap_targets {
|
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.
|
// For each list of snap targets, find the shortest distance to move the point to that target.
|
||||||
let closest_move = DVec2::new(
|
let closest_move = DVec2::new(
|
||||||
@@ -148,4 +149,18 @@ impl SnapHandler {
|
|||||||
self.remove_overlays(responses);
|
self.remove_overlays(responses);
|
||||||
self.snap_targets = None;
|
self.snap_targets = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---
|
||||||
|
pub fn add_snappable(&mut self, layer: Vec<LayerId>) {
|
||||||
|
self.snappables.insert(layer, vec![]);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_snappable(&mut self, layer: Vec<LayerId>) {
|
||||||
|
self.snappables.remove(&layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_overlays(&mut self, responses: &mut VecDeque<Message>) {
|
||||||
|
responses.extend(self.overlay_messages.into_iter());
|
||||||
|
self.overlay_messages.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::message_prelude::*;
|
use crate::message_prelude::*;
|
||||||
|
use glam::DVec2;
|
||||||
use graphene::color::Color;
|
use graphene::color::Color;
|
||||||
|
|
||||||
use crate::input::InputPreprocessor;
|
use crate::input::InputPreprocessor;
|
||||||
@@ -21,6 +22,10 @@ pub enum ToolMessage {
|
|||||||
DocumentIsDirty,
|
DocumentIsDirty,
|
||||||
UpdateHints,
|
UpdateHints,
|
||||||
SetToolOptions(ToolType, ToolOptions),
|
SetToolOptions(ToolType, ToolOptions),
|
||||||
|
SetSnapping(bool),
|
||||||
|
AddSnappable(Vec<LayerId>),
|
||||||
|
RemoveSnappable(Vec<LayerId>),
|
||||||
|
SnapLayer(Vec<LayerId>),
|
||||||
#[child]
|
#[child]
|
||||||
Fill(FillMessage),
|
Fill(FillMessage),
|
||||||
#[child]
|
#[child]
|
||||||
|
|||||||
Reference in New Issue
Block a user