Implement viewport selection (#178)

* Begin implementing viewport selection

* Implement viewport click and drag selection for ellipse and rectangle

* Begin implementing line selection

* Remove debug prints

* Run cargo format

* Use DVec2 instead of kurbo::Point

* Line and polyline intersection

* Run cargo format

* Add fix for missing layer panel update

* Replace point selection with box selection

* Formatting

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Paul Kupper
2021-07-05 01:05:12 +02:00
committed by GitHub
parent 20420c1286
commit ad064602a5
13 changed files with 293 additions and 25 deletions

View File

@@ -281,6 +281,8 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
for path in paths {
responses.extend(self.select_layer(&path));
}
// TODO: Correctly update layer panel in clear_selection instead of here
responses.extend(self.handle_folder_changed(Vec::new()));
}
Undo => {
// this is a temporary fix and will be addressed by #123

View File

@@ -104,6 +104,12 @@ impl Default for Mapping {
fn default() -> Self {
let (up, down, pointer_move) = mapping![
entry! {action=DocumentMessage::PasteLayers, key_down=KeyV, modifiers=[KeyControl]},
// Select
entry! {action=SelectMessage::MouseMove, message=InputMapperMessage::PointerMove},
entry! {action=SelectMessage::DragStart, key_down=Lmb},
entry! {action=SelectMessage::DragStop, key_up=Lmb},
entry! {action=SelectMessage::Abort, key_down=Rmb},
entry! {action=SelectMessage::Abort, key_down=KeyEscape},
// Rectangle
entry! {action=RectangleMessage::Center, key_down=KeyAlt},
entry! {action=RectangleMessage::UnCenter, key_up=KeyAlt},

View File

@@ -1,4 +1,11 @@
use crate::input::InputPreprocessor;
use document_core::color::Color;
use document_core::layers::style::Fill;
use document_core::layers::style::Stroke;
use document_core::layers::{style, SELECTION_TOLERANCE};
use document_core::Operation;
use glam::{DAffine2, DVec2};
use crate::input::{mouse::ViewportPosition, InputPreprocessor};
use crate::tool::{DocumentToolData, Fsm, ToolActionHandlerData};
use crate::{message_prelude::*, SvgDocument};
@@ -11,19 +18,29 @@ pub struct Select {
#[impl_message(Message, ToolMessage, Select)]
#[derive(PartialEq, Clone, Debug)]
pub enum SelectMessage {
DragStart,
DragStop,
MouseMove,
Abort,
}
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Select {
fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
self.fsm_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, data.2, responses);
}
advertise_actions!();
fn actions(&self) -> ActionList {
use SelectToolFsmState::*;
match self.fsm_state {
Ready => actions!(SelectMessageDiscriminant; DragStart),
Dragging => actions!(SelectMessageDiscriminant; DragStop, MouseMove, Abort),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum SelectToolFsmState {
Ready,
Dragging,
}
impl Default for SelectToolFsmState {
@@ -32,29 +49,96 @@ impl Default for SelectToolFsmState {
}
}
#[derive(Default)]
struct SelectToolData;
#[derive(Clone, Debug, Default)]
struct SelectToolData {
drag_start: ViewportPosition,
drag_current: ViewportPosition,
}
impl Fsm for SelectToolFsmState {
type ToolData = SelectToolData;
fn transition(
self,
event: ToolMessage,
_document: &SvgDocument,
_tool_data: &DocumentToolData,
_data: &mut Self::ToolData,
_input: &InputPreprocessor,
_responses: &mut VecDeque<Message>,
) -> Self {
fn transition(self, event: ToolMessage, document: &SvgDocument, tool_data: &DocumentToolData, data: &mut Self::ToolData, input: &InputPreprocessor, responses: &mut VecDeque<Message>) -> Self {
let transform = document.root.transform;
use SelectMessage::*;
use SelectToolFsmState::*;
if let ToolMessage::Select(event) = event {
match (self, event) {
(Ready, MouseMove) => self,
(Ready, DragStart) => {
data.drag_start = input.mouse.position;
data.drag_current = input.mouse.position;
responses.push_back(Operation::MountWorkingFolder { path: vec![] }.into());
Dragging
}
(Dragging, MouseMove) => {
data.drag_current = input.mouse.position;
responses.push_back(Operation::ClearWorkingFolder.into());
responses.push_back(make_operation(data, tool_data, transform));
Dragging
}
(Dragging, DragStop) => {
data.drag_current = input.mouse.position;
responses.push_back(Operation::ClearWorkingFolder.into());
let (point_1, point_2) = if data.drag_start == data.drag_current {
let (x, y) = (data.drag_current.x as f64, data.drag_current.y as f64);
(
DVec2::new(x - SELECTION_TOLERANCE, y - SELECTION_TOLERANCE),
DVec2::new(x + SELECTION_TOLERANCE, y + SELECTION_TOLERANCE),
)
} else {
(
DVec2::new(data.drag_start.x as f64, data.drag_start.y as f64),
DVec2::new(data.drag_current.x as f64, data.drag_current.y as f64),
)
};
let quad = [
DVec2::new(point_1.x, point_1.y),
DVec2::new(point_2.x, point_1.y),
DVec2::new(point_2.x, point_2.y),
DVec2::new(point_1.x, point_2.y),
];
if data.drag_start == data.drag_current {
if let Some(intersection) = document.intersects_quad_root(quad).last() {
responses.push_back(DocumentMessage::SelectLayers(vec![intersection.clone()]).into());
} else {
responses.push_back(DocumentMessage::SelectLayers(vec![]).into());
}
} else {
responses.push_back(DocumentMessage::SelectLayers(document.intersects_quad_root(quad)).into());
}
Ready
}
(Dragging, Abort) => {
responses.push_back(Operation::DiscardWorkingFolder.into());
Ready
}
_ => self,
}
} else {
self
}
}
}
fn make_operation(data: &SelectToolData, _tool_data: &DocumentToolData, transform: DAffine2) -> Message {
let x0 = data.drag_start.x as f64;
let y0 = data.drag_start.y as f64;
let x1 = data.drag_current.x as f64;
let y1 = data.drag_current.y as f64;
Operation::AddRect {
path: vec![],
insert_index: -1,
transform: (transform.inverse() * glam::DAffine2::from_scale_angle_translation(DVec2::new(x1 - x0, y1 - y0), 0., DVec2::new(x0, y0))).to_cols_array(),
style: style::PathStyle::new(Some(Stroke::new(Color::from_rgb8(0x31, 0x94, 0xD6), 2.0)), Some(Fill::none())),
}
.into()
}