diff --git a/editor/src/messages/input_mapper/input_mappings.rs b/editor/src/messages/input_mapper/input_mappings.rs index 9e774b6c25..381f6f282b 100644 --- a/editor/src/messages/input_mapper/input_mappings.rs +++ b/editor/src/messages/input_mapper/input_mappings.rs @@ -102,7 +102,7 @@ pub fn input_mappings() -> Mapping { entry!(PointerMove; refresh_keys=[Control, Shift], action_dispatch=TransformLayerMessage::PointerMove { slow_key: Shift, increments_key: Control }), // // SelectToolMessage - entry!(PointerMove; refresh_keys=[Control, Alt, Shift], action_dispatch=SelectToolMessage::PointerMove { modifier_keys: SelectToolPointerKeys { axis_align: Shift, snap_angle: Shift, center: Alt, duplicate: Alt } }), + entry!(PointerMove; refresh_keys=[Control, Alt, Shift], action_dispatch=SelectToolMessage::PointerMove { modifier_keys: SelectToolPointerKeys { axis_align: Shift, snap_angle: Shift, center: Alt, duplicate: Alt, space_drag: Space } }), entry!(KeyDown(MouseLeft); action_dispatch=SelectToolMessage::DragStart { extend_selection: Shift, remove_from_selection: Alt, select_deepest: Accel, lasso_select: Control, skew: Control }), entry!(KeyUp(MouseLeft); action_dispatch=SelectToolMessage::DragStop { remove_from_selection: Alt }), entry!(KeyDown(Enter); action_dispatch=SelectToolMessage::Enter), diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 6402f609fd..cfa531f63b 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -1503,6 +1503,7 @@ impl MessageHandler> for DocumentMes snap_angle: Key::Shift, center: Key::Alt, duplicate: Key::Alt, + space_drag: Key::Space, }, }); responses.add(NodeGraphMessage::RunDocumentGraph); diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs index c491b3cba0..e667f9bb52 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs @@ -97,6 +97,8 @@ pub struct NodeGraphMessageHandler { frontend_nodes: Vec, /// Used to keep track of what wires are sent to the front end so the old ones can be removed frontend_wires: HashSet<(NodeId, usize)>, + /// The last mouse position in viewport coordinates when space was pressed + last_mouse_viewport_for_space: Option, } /// NodeGraphMessageHandler always modifies the network which the selected nodes are in. No GraphOperationMessages should be added here, since those messages will always affect the document network. @@ -1017,6 +1019,21 @@ impl<'a> MessageHandler> for NodeG let messages = [NodeGraphMessage::PointerOutsideViewport { shift }.into(), NodeGraphMessage::PointerMove { shift }.into()]; self.auto_panning.setup_by_mouse_position(ipp, viewport, &messages, responses); + if ipp.keyboard.get(Key::Space as usize) { + if let Some(last_mouse) = self.last_mouse_viewport_for_space { + let delta = ipp.mouse.position - last_mouse; + if let Some((box_start, _)) = &mut self.box_selection_start { + let graph_delta = network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.inverse().transform_vector2(delta); + *box_start += graph_delta; + } else { + responses.add(NavigationMessage::CanvasPan { delta }); + } + } + self.last_mouse_viewport_for_space = Some(ipp.mouse.position); + } else { + self.last_mouse_viewport_for_space = None; + } + let viewport_location = ipp.mouse.position; let point = network_metadata .persistent_metadata @@ -2811,6 +2828,7 @@ impl Default for NodeGraphMessageHandler { end_index: None, frontend_nodes: Vec::new(), frontend_wires: HashSet::new(), + last_mouse_viewport_for_space: None, } } } @@ -2829,5 +2847,6 @@ impl PartialEq for NodeGraphMessageHandler { && self.wire_in_progress_from_connector == other.wire_in_progress_from_connector && self.wire_in_progress_to_connector == other.wire_in_progress_to_connector && self.context_menu == other.context_menu + && self.last_mouse_viewport_for_space == other.last_mouse_viewport_for_space } } diff --git a/editor/src/messages/portfolio/document/utility_types/transformation.rs b/editor/src/messages/portfolio/document/utility_types/transformation.rs index 63e1b8baa8..4c7e581537 100644 --- a/editor/src/messages/portfolio/document/utility_types/transformation.rs +++ b/editor/src/messages/portfolio/document/utility_types/transformation.rs @@ -59,6 +59,35 @@ impl OriginalTransforms { Some(transform_utils::get_current_transform(&document_node.inputs)) } + pub fn shift(&mut self, delta: DVec2, metadata: &DocumentMetadata) { + match self { + OriginalTransforms::Layer(layer_map) => { + for (&layer, transform) in layer_map { + let to = metadata.downstream_transform_to_viewport(layer); + let layer_delta = to.inverse() * DAffine2::from_translation(delta) * to; + *transform = layer_delta * *transform; + } + } + OriginalTransforms::Path(path_map) => { + for (&layer, points) in path_map { + let to = metadata.transform_to_viewport(layer); + let layer_delta = to.inverse() * DAffine2::from_translation(delta) * to; + for anchor in points.anchors.values_mut() { + anchor.initial = layer_delta.transform_point2(anchor.initial); + anchor.current = layer_delta.transform_point2(anchor.current); + } + for handle in points.handles.values_mut() { + handle.initial = layer_delta.transform_point2(handle.initial); + handle.relative = layer_delta.transform_point2(handle.relative); + if let Some((_, pos)) = &mut handle.mirror { + *pos = layer_delta.transform_point2(*pos); + } + } + } + } + } + } + pub fn update<'a>(&mut self, selected: &'a [LayerNodeIdentifier], network_interface: &NodeNetworkInterface, shape_editor: Option<&'a ShapeState>) { match self { OriginalTransforms::Layer(layer_map) => { diff --git a/editor/src/messages/tool/common_functionality/resize.rs b/editor/src/messages/tool/common_functionality/resize.rs index 032666ca27..5878a1f239 100644 --- a/editor/src/messages/tool/common_functionality/resize.rs +++ b/editor/src/messages/tool/common_functionality/resize.rs @@ -11,6 +11,9 @@ pub struct Resize { pub drag_start: DVec2, pub layer: Option, pub snap_manager: SnapManager, + + pub last_mouse_viewport_for_space: Option, + pub base_drag_start: DVec2, } impl Resize { @@ -20,6 +23,8 @@ impl Resize { let point = SnapCandidatePoint::handle(root_transform.inverse().transform_point2(input.mouse.position)); let snapped = self.snap_manager.free_snap(&SnapData::new(document, input, viewport), &point, SnapTypeConfiguration::default()); self.drag_start = snapped.snapped_point_document; + self.base_drag_start = self.drag_start; + self.last_mouse_viewport_for_space = None; } /// Calculate the drag start position in viewport space. @@ -119,6 +124,24 @@ impl Resize { let ignore = if let Some(layer) = self.layer { vec![layer] } else { vec![] }; let snap_data = &SnapData::ignore(document, input, viewport, &ignore); + let space_down = input.keyboard.get(Key::Space as usize); + if space_down { + if self.last_mouse_viewport_for_space.is_none() { + self.last_mouse_viewport_for_space = Some(mouse); + self.base_drag_start = self.drag_start; + } + let total_offset_viewport = mouse - self.last_mouse_viewport_for_space.unwrap(); + let total_offset_document = document_to_viewport.inverse().transform_vector2(total_offset_viewport); + self.drag_start = self.base_drag_start + total_offset_document; + } else { + if let Some(initial_mouse) = self.last_mouse_viewport_for_space { + let total_offset_viewport = mouse - initial_mouse; + let total_offset_document = document_to_viewport.inverse().transform_vector2(total_offset_viewport); + self.drag_start = self.base_drag_start + total_offset_document; + } + self.last_mouse_viewport_for_space = None; + } + if lock_ratio { let viewport_size = points_viewport[1] - points_viewport[0]; let raw_size = if in_document { diff --git a/editor/src/messages/tool/common_functionality/shapes/line_shape.rs b/editor/src/messages/tool/common_functionality/shapes/line_shape.rs index 16b64a2315..7fa3438f7e 100644 --- a/editor/src/messages/tool/common_functionality/shapes/line_shape.rs +++ b/editor/src/messages/tool/common_functionality/shapes/line_shape.rs @@ -59,6 +59,24 @@ impl Line { shape_tool_data.line_data.drag_current = ipp.mouse.position; let keyboard = &ipp.keyboard; + + let current_mouse = ipp.mouse.position; + let space_down = keyboard.key(Key::Space); + + if space_down { + if let Some(previous_mouse) = shape_tool_data.data.last_mouse_viewport_for_space { + let delta_viewport = current_mouse - previous_mouse; + if delta_viewport.length_squared() > 0. { + let document_to_viewport = document.metadata().document_to_viewport; + let delta_document = document_to_viewport.inverse().transform_vector2(delta_viewport); + shape_tool_data.data.drag_start += delta_document; + } + } + shape_tool_data.data.last_mouse_viewport_for_space = Some(current_mouse); + } else { + shape_tool_data.data.last_mouse_viewport_for_space = None; + } + let ignore = [layer]; let snap_data = SnapData::ignore(document, ipp, viewport, &ignore); let mut document_points = generate_line(shape_tool_data, snap_data, keyboard.key(lock_angle), keyboard.key(snap_angle), keyboard.key(center)); diff --git a/editor/src/messages/tool/tool_messages/artboard_tool.rs b/editor/src/messages/tool/tool_messages/artboard_tool.rs index e257dc359c..f9ba6617d9 100644 --- a/editor/src/messages/tool/tool_messages/artboard_tool.rs +++ b/editor/src/messages/tool/tool_messages/artboard_tool.rs @@ -114,6 +114,7 @@ struct ArtboardToolData { snap_candidates: Vec, dragging_current_artboard_location: glam::IVec2, draw: Resize, + last_mouse_viewport_for_space: Option, } impl ArtboardToolData { @@ -315,6 +316,32 @@ impl Fsm for ArtboardToolFsmState { state } (ArtboardToolFsmState::ResizingBounds, ArtboardToolMessage::PointerMove { constrain_axis_or_aspect, center }) => { + let current_mouse = input.mouse.position; + let space_down = input.keyboard.get(Key::Space as usize); + if space_down { + if let Some(previous_mouse) = tool_data.last_mouse_viewport_for_space { + let delta_viewport = current_mouse - previous_mouse; + if delta_viewport.length_squared() > 0. { + let document_to_viewport = document.metadata().document_to_viewport; + let delta_document = document_to_viewport.inverse().transform_vector2(delta_viewport); + + if let Some(bounds) = &mut tool_data.bounding_box_manager { + bounds.center_of_transformation += delta_viewport; + if let Some(movement) = &mut bounds.selected_edges { + movement.bounds[0] += delta_document; + movement.bounds[1] += delta_document; + } + tool_data.drag_start += delta_document; + } + } + } + tool_data.last_mouse_viewport_for_space = Some(current_mouse); + } else { + tool_data.last_mouse_viewport_for_space = None; + } + + // responses.add(OverlaysMessage::Draw); + let from_center = input.keyboard.get(center as usize); let constrain_square = input.keyboard.get(constrain_axis_or_aspect as usize); tool_data.resize_artboard(responses, document, input, viewport, from_center, constrain_square); @@ -329,6 +356,22 @@ impl Fsm for ArtboardToolFsmState { ArtboardToolFsmState::ResizingBounds } (ArtboardToolFsmState::Dragging, ArtboardToolMessage::PointerMove { constrain_axis_or_aspect, center }) => { + let current_mouse = input.mouse.position; + if input.keyboard.get(Key::Space as usize) { + if let Some(previous_mouse) = tool_data.last_mouse_viewport_for_space { + let delta_viewport = current_mouse - previous_mouse; + if delta_viewport.length_squared() > 0. { + let document_to_viewport = document.metadata().document_to_viewport; + let delta_document = document_to_viewport.inverse().transform_vector2(delta_viewport); + tool_data.drag_start += delta_document; + tool_data.drag_current += delta_document; + } + } + tool_data.last_mouse_viewport_for_space = Some(current_mouse); + } else { + tool_data.last_mouse_viewport_for_space = None; + } + if let Some(bounds) = &mut tool_data.bounding_box_manager { let axis_align = input.keyboard.get(constrain_axis_or_aspect as usize); @@ -368,6 +411,23 @@ impl Fsm for ArtboardToolFsmState { ArtboardToolFsmState::Dragging } (ArtboardToolFsmState::Drawing, ArtboardToolMessage::PointerMove { constrain_axis_or_aspect, center }) => { + let current_mouse = input.mouse.position; + + let space_down = input.keyboard.get(Key::Space as usize); + if space_down { + if let Some(previous_mouse) = tool_data.last_mouse_viewport_for_space { + let delta_viewport = current_mouse - previous_mouse; + if delta_viewport.length_squared() > 0. { + let document_to_viewport = document.metadata().document_to_viewport; + let delta_document = document_to_viewport.inverse().transform_vector2(delta_viewport); + tool_data.drag_start += delta_document; + } + } + tool_data.last_mouse_viewport_for_space = Some(current_mouse); + } else { + tool_data.last_mouse_viewport_for_space = None; + } + // The draw.calculate_points_ignore_layer uses this value to avoid snapping to itself. tool_data.draw.layer = tool_data.selected_artboard; let [start, end] = tool_data.draw.calculate_points_ignore_layer(document, input, viewport, center, constrain_axis_or_aspect, true); diff --git a/editor/src/messages/tool/tool_messages/freehand_tool.rs b/editor/src/messages/tool/tool_messages/freehand_tool.rs index 9412577b48..08c8849527 100644 --- a/editor/src/messages/tool/tool_messages/freehand_tool.rs +++ b/editor/src/messages/tool/tool_messages/freehand_tool.rs @@ -217,6 +217,8 @@ struct FreehandToolData { dragged: bool, weight: f64, layer: Option, + last_mouse_for_space: Option, + points: Vec<(PointId, DVec2)>, } impl Fsm for FreehandToolFsmState { @@ -253,6 +255,7 @@ impl Fsm for FreehandToolFsmState { tool_data.dragged = false; tool_data.end_point = None; tool_data.weight = tool_options.line_weight; + tool_data.points.clear(); // Extend an endpoint of the selected path let selected_nodes = document.network_interface.selected_nodes(); @@ -297,6 +300,31 @@ impl Fsm for FreehandToolFsmState { FreehandToolFsmState::Drawing } (FreehandToolFsmState::Drawing, FreehandToolMessage::PointerMove) => { + let space_down = input.keyboard.get(Key::Space as usize); + if space_down { + if let Some(previous_mouse) = tool_data.last_mouse_for_space { + let delta_viewport = input.mouse.position - previous_mouse; + if delta_viewport.length_squared() > 0. { + let document_to_viewport = document.metadata().document_to_viewport; + let delta_document = document_to_viewport.inverse().transform_vector2(delta_viewport); + + if let Some(layer) = tool_data.layer { + if let Some((pos, _)) = &mut tool_data.end_point { + *pos += delta_document; + } + for (id, point) in &mut tool_data.points { + *point += delta_document; + let modification_type = VectorModificationType::ApplyPointDelta { point: *id, delta: delta_document }; + responses.add(GraphOperationMessage::Vector { layer, modification_type }); + } + } + } + } + tool_data.last_mouse_for_space = Some(input.mouse.position); + } else { + tool_data.last_mouse_for_space = None; + } + if let Some(layer) = tool_data.layer { let transform = document.metadata().transform_to_viewport(layer); let position = transform.inverse().transform_point2(input.mouse.position); @@ -382,6 +410,7 @@ fn extend_path_with_next_segment(tool_data: &mut FreehandToolData, position: DVe tool_data.dragged = true; tool_data.end_point = Some((position, id)); + tool_data.points.push((id, position)); } #[cfg(test)] diff --git a/editor/src/messages/tool/tool_messages/gradient_tool.rs b/editor/src/messages/tool/tool_messages/gradient_tool.rs index fa5b3f38a4..d0ae06c2bc 100644 --- a/editor/src/messages/tool/tool_messages/gradient_tool.rs +++ b/editor/src/messages/tool/tool_messages/gradient_tool.rs @@ -143,6 +143,8 @@ struct SelectedGradient { transform: DAffine2, gradient: Gradient, dragging: GradientDragTarget, + base_start: DVec2, + base_end: DVec2, } impl SelectedGradient { @@ -151,6 +153,8 @@ impl SelectedGradient { Self { layer: Some(layer), transform, + base_start: gradient.start, + base_end: gradient.end, gradient, dragging: GradientDragTarget::End, } @@ -161,9 +165,16 @@ impl SelectedGradient { self } - pub fn update_gradient(&mut self, mut mouse: DVec2, responses: &mut VecDeque, snap_rotate: bool, gradient_type: GradientType) { + pub fn capture_base(&mut self) { + self.base_start = self.gradient.start; + self.base_end = self.gradient.end; + } + + pub fn update_gradient(&mut self, mouse: DVec2, responses: &mut VecDeque, snap_rotate: bool, gradient_type: GradientType, mouse_offset: DVec2) { self.gradient.gradient_type = gradient_type; + let mut adjusted_mouse = mouse - mouse_offset; + if snap_rotate && matches!(self.dragging, GradientDragTarget::End | GradientDragTarget::Start) { let point = if self.dragging == GradientDragTarget::Start { self.transform.transform_point2(self.gradient.end) @@ -171,7 +182,7 @@ impl SelectedGradient { self.transform.transform_point2(self.gradient.start) }; - let delta = point - mouse; + let delta = point - adjusted_mouse; let length = delta.length(); let mut angle = -delta.angle_to(DVec2::X); @@ -180,10 +191,10 @@ impl SelectedGradient { angle = (angle / snap_resolution).round() * snap_resolution; let rotated = DVec2::new(length * angle.cos(), length * angle.sin()); - mouse = point - rotated; + adjusted_mouse = point - rotated; } - let transformed_mouse = self.transform.inverse().transform_point2(mouse); + let transformed_mouse = self.transform.inverse().transform_point2(adjusted_mouse); match self.dragging { GradientDragTarget::Start => self.gradient.start = transformed_mouse, @@ -192,7 +203,7 @@ impl SelectedGradient { let (start, end) = (self.transform.transform_point2(self.gradient.start), self.transform.transform_point2(self.gradient.end)); // Calculate the new position by finding the closest point on the line - let new_pos = ((end - start).angle_to(mouse - start)).cos() * start.distance(mouse) / start.distance(end); + let new_pos = ((end - start).angle_to(adjusted_mouse - start)).cos() * start.distance(adjusted_mouse) / start.distance(end); // Should not go off end but can swap let clamped = new_pos.clamp(0., 1.); @@ -203,6 +214,17 @@ impl SelectedGradient { self.dragging = GradientDragTarget::Step(self.gradient.stops.iter().position(|x| *x == new_pos).unwrap()); } } + + if mouse_offset != DVec2::ZERO { + let delta_gradient = self.transform.inverse().transform_vector2(mouse_offset); + self.gradient.start = self.base_start + delta_gradient; + self.gradient.end = self.base_end + delta_gradient; + } else { + // Update base coordinates while not panning so they are ready for the next Space press + self.base_start = self.gradient.start; + self.base_end = self.gradient.end; + } + self.render_gradient(responses); } @@ -239,6 +261,7 @@ struct GradientToolData { selected_gradient: Option, snap_manager: SnapManager, drag_start: DVec2, + last_mouse_viewport_for_space: Option, auto_panning: AutoPanning, } @@ -400,6 +423,9 @@ impl Fsm for GradientToolFsmState { tool_data.selected_gradient = Some(SelectedGradient { layer: Some(layer), transform, + + base_start: gradient.start, + base_end: gradient.end, gradient: gradient.clone(), dragging: GradientDragTarget::Step(index), }) @@ -414,6 +440,8 @@ impl Fsm for GradientToolFsmState { tool_data.selected_gradient = Some(SelectedGradient { layer: Some(layer), transform, + base_start: gradient.start, + base_end: gradient.end, gradient: gradient.clone(), dragging: dragging_target, }) @@ -460,7 +488,25 @@ impl Fsm for GradientToolFsmState { (GradientToolFsmState::Drawing, GradientToolMessage::PointerMove { constrain_axis }) => { if let Some(selected_gradient) = &mut tool_data.selected_gradient { let mouse = input.mouse.position; // tool_data.snap_manager.snap_position(responses, document, input.mouse.position); - selected_gradient.update_gradient(mouse, responses, input.keyboard.get(constrain_axis as usize), selected_gradient.gradient.gradient_type); + + let mut mouse_offset = DVec2::ZERO; + + if input.keyboard.key(Key::Space) { + if tool_data.last_mouse_viewport_for_space.is_none() { + tool_data.last_mouse_viewport_for_space = Some(mouse); + selected_gradient.capture_base(); + } + mouse_offset = mouse - tool_data.last_mouse_viewport_for_space.unwrap(); + } else if let Some(initial_mouse) = tool_data.last_mouse_viewport_for_space { + let total_offset = mouse - initial_mouse; + let delta_gradient = selected_gradient.transform.inverse().transform_vector2(total_offset); + selected_gradient.gradient.start = selected_gradient.base_start + delta_gradient; + selected_gradient.gradient.end = selected_gradient.base_end + delta_gradient; + tool_data.last_mouse_viewport_for_space = None; + responses.add(OverlaysMessage::Draw); + } + + selected_gradient.update_gradient(mouse, responses, input.keyboard.get(constrain_axis as usize), selected_gradient.gradient.gradient_type, mouse_offset); } // Auto-panning @@ -509,6 +555,7 @@ impl Fsm for GradientToolFsmState { (GradientToolFsmState::Drawing, GradientToolMessage::Abort) => { responses.add(DocumentMessage::AbortTransaction); tool_data.snap_manager.cleanup(responses); + tool_data.last_mouse_viewport_for_space = None; responses.add(OverlaysMessage::Draw); GradientToolFsmState::Ready diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 35fd18e940..719469986e 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -604,6 +604,7 @@ struct PathToolData { hovered_layers: Vec, ghost_outline: Vec<(Vec, LayerNodeIdentifier)>, make_path_editable_is_allowed: bool, + last_mouse_for_space: Option, } impl PathToolData { @@ -2051,6 +2052,24 @@ impl Fsm for PathToolFsmState { tool_data.started_drawing_from_inside = false; tool_data.stored_selection = None; + let current_mouse = input.mouse.position; + let space_down = input.keyboard.get(move_anchor_with_handles as usize); + + if space_down { + if let Some(previous_mouse) = tool_data.last_mouse_for_space { + let delta_viewport = current_mouse - previous_mouse; + tool_data.drag_start_pos += delta_viewport; + for point in &mut tool_data.lasso_polygon { + *point += delta_viewport; + } + } + tool_data.last_mouse_for_space = Some(current_mouse); + } + + tool_data.previous_mouse_position = document.metadata().document_to_viewport.inverse().transform_point2(current_mouse); + tool_data.started_drawing_from_inside = false; + tool_data.stored_selection = None; + if selection_shape == SelectionShapeType::Lasso { extend_lasso(&mut tool_data.lasso_polygon, input.mouse.position); } @@ -2099,6 +2118,57 @@ impl Fsm for PathToolFsmState { segment_editing_modifier, }, ) => { + let current_mouse = input.mouse.position; + let space_down = input.keyboard.get(move_anchor_with_handles as usize); + if space_down { + if let Some(previous_mouse) = tool_data.last_mouse_for_space { + let delta_viewport = current_mouse - previous_mouse; + if delta_viewport.length_squared() > 0. { + let document_to_viewport = document.metadata().document_to_viewport; + let delta_document = document_to_viewport.inverse().transform_vector2(delta_viewport); + + tool_data.drag_start_pos += delta_viewport; + tool_data.previous_mouse_position += delta_document; + + shape_editor.move_selected_points_and_segments(None, document, delta_document, false, true, false, None, false, responses); + } + } + tool_data.last_mouse_for_space = Some(current_mouse); + + // Auto-panning + let messages = [ + PathToolMessage::PointerOutsideViewport { + toggle_colinear, + equidistant, + move_anchor_with_handles, + snap_angle, + lock_angle, + delete_segment, + break_colinear_molding, + segment_editing_modifier, + } + .into(), + PathToolMessage::PointerMove { + toggle_colinear, + equidistant, + move_anchor_with_handles, + snap_angle, + lock_angle, + delete_segment, + break_colinear_molding, + segment_editing_modifier, + } + .into(), + ]; + tool_data.auto_panning.setup_by_mouse_position(input, viewport, &messages, responses); + + responses.add(OverlaysMessage::Draw); + + return PathToolFsmState::Dragging(tool_data.dragging_state); + } else { + tool_data.last_mouse_for_space = None; + } + let selected_only_handles = !shape_editor.selected_points().any(|point| matches!(point, ManipulatorPointId::Anchor(_))); tool_data.stored_selection = None; diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index 717903ab10..84039e8686 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -71,6 +71,7 @@ pub struct SelectToolPointerKeys { pub snap_angle: Key, pub center: Key, pub duplicate: Key, + pub space_drag: Key, } #[impl_message(Message, ToolMessage, Select)] @@ -401,6 +402,7 @@ struct SelectToolData { snap_candidates: Vec, auto_panning: AutoPanning, drag_start_center: ViewportPosition, + last_mouse_viewport_for_space: Option, } impl SelectToolData { @@ -1170,6 +1172,23 @@ impl Fsm for SelectToolFsmState { tool_data.axis_align = input.keyboard.key(modifier_keys.axis_align); + let current_mouse = input.mouse.position; + let space_down = input.keyboard.get(Key::Space as usize); + if space_down { + if tool_data.last_mouse_viewport_for_space.is_none() { + tool_data.last_mouse_viewport_for_space = Some(current_mouse); + } + let previous = tool_data.last_mouse_viewport_for_space.unwrap(); + let delta_viewport = current_mouse - previous; + if delta_viewport.length_squared() > 0. { + tool_data.drag_start += delta_viewport; + tool_data.drag_current += delta_viewport; + tool_data.last_mouse_viewport_for_space = Some(current_mouse); + } + } else { + tool_data.last_mouse_viewport_for_space = None; + } + // Ignore the non duplicated layers if the current layers have not spawned yet. let layers_exist = tool_data.layers_dragging.iter().all(|&layer| document.metadata().click_targets(layer).is_some()); let ignore = tool_data.non_duplicated_layers.as_ref().filter(|_| !layers_exist).unwrap_or(&tool_data.layers_dragging); @@ -1217,6 +1236,26 @@ impl Fsm for SelectToolFsmState { } (SelectToolFsmState::ResizingBounds, SelectToolMessage::PointerMove { modifier_keys }) => { if let Some(bounds) = &mut tool_data.bounding_box_manager { + let space_down = input.keyboard.get(Key::Space as usize); + let current_mouse = input.mouse.position; + if space_down { + if tool_data.last_mouse_viewport_for_space.is_none() { + tool_data.last_mouse_viewport_for_space = Some(current_mouse); + } + let previous = tool_data.last_mouse_viewport_for_space.unwrap(); + let delta = current_mouse - previous; + if delta.length_squared() > 0. { + bounds.center_of_transformation += delta; + bounds.original_bound_transform.translation += delta; + bounds.original_transforms.shift(delta, document.metadata()); + tool_data.drag_start += delta; + tool_data.drag_current += delta; + tool_data.last_mouse_viewport_for_space = Some(current_mouse); + } + } else { + tool_data.last_mouse_viewport_for_space = None; + } + resize_bounds( document, responses, @@ -1240,6 +1279,27 @@ impl Fsm for SelectToolFsmState { } (SelectToolFsmState::SkewingBounds { skew }, SelectToolMessage::PointerMove { .. }) => { if let Some(bounds) = &mut tool_data.bounding_box_manager { + let space_down = input.keyboard.get(Key::Space as usize); + let current_mouse = input.mouse.position; + + if space_down { + if tool_data.last_mouse_viewport_for_space.is_none() { + tool_data.last_mouse_viewport_for_space = Some(current_mouse); + } + let previous = tool_data.last_mouse_viewport_for_space.unwrap(); + let delta = current_mouse - previous; + if delta.length_squared() > 0. { + bounds.center_of_transformation += delta; + bounds.original_bound_transform.translation += delta; + bounds.original_transforms.shift(delta, document.metadata()); + tool_data.drag_start += delta; + tool_data.drag_current += delta; + tool_data.last_mouse_viewport_for_space = Some(current_mouse); + } + } else { + tool_data.last_mouse_viewport_for_space = None; + } + skew_bounds( document, responses, @@ -1254,6 +1314,27 @@ impl Fsm for SelectToolFsmState { } (SelectToolFsmState::RotatingBounds, SelectToolMessage::PointerMove { .. }) => { if let Some(bounds) = &mut tool_data.bounding_box_manager { + let space_down = input.keyboard.get(Key::Space as usize); + let current_mouse = input.mouse.position; + + if space_down { + if tool_data.last_mouse_viewport_for_space.is_none() { + tool_data.last_mouse_viewport_for_space = Some(current_mouse); + } + let previous = tool_data.last_mouse_viewport_for_space.unwrap(); + let delta = current_mouse - previous; + if delta.length_squared() > 0. { + bounds.center_of_transformation += delta; + bounds.original_bound_transform.translation += delta; + bounds.original_transforms.shift(delta, document.metadata()); + tool_data.drag_start += delta; + tool_data.drag_current += delta; + tool_data.last_mouse_viewport_for_space = Some(current_mouse); + } + } else { + tool_data.last_mouse_viewport_for_space = None; + } + rotate_bounds( document, responses, @@ -1290,7 +1371,29 @@ impl Fsm for SelectToolFsmState { responses.add(ToolMessage::UpdateHints); } - tool_data.drag_current = input.mouse.position; + let current_mouse = input.mouse.position; + let space_down = input.keyboard.get(Key::Space as usize); + if space_down { + if tool_data.last_mouse_viewport_for_space.is_none() { + tool_data.last_mouse_viewport_for_space = Some(current_mouse); + } + let previous = tool_data.last_mouse_viewport_for_space.unwrap(); + let delta = current_mouse - previous; + if delta.length_squared() > 0. { + tool_data.drag_start += delta; + + if selection_shape == SelectionShapeType::Lasso { + for point in &mut tool_data.lasso_polygon { + *point += delta; + } + } + + tool_data.last_mouse_viewport_for_space = Some(current_mouse); + } + } else { + tool_data.last_mouse_viewport_for_space = None; + } + tool_data.drag_current = current_mouse; responses.add(OverlaysMessage::Draw); if selection_shape == SelectionShapeType::Lasso { @@ -1375,10 +1478,16 @@ impl Fsm for SelectToolFsmState { self } - (SelectToolFsmState::Drawing { .. }, SelectToolMessage::PointerOutsideViewport { .. }) => { + (SelectToolFsmState::Drawing { selection_shape, .. }, SelectToolMessage::PointerOutsideViewport { .. }) => { // Auto-panning if let Some(shift) = tool_data.auto_panning.shift_viewport(input, viewport, responses) { tool_data.drag_start += shift; + + if selection_shape == SelectionShapeType::Lasso { + for point in &mut tool_data.lasso_polygon { + *point += shift; + } + } } self diff --git a/editor/src/messages/tool/tool_messages/shape_tool.rs b/editor/src/messages/tool/tool_messages/shape_tool.rs index ead0e1e1ce..147e34260a 100644 --- a/editor/src/messages/tool/tool_messages/shape_tool.rs +++ b/editor/src/messages/tool/tool_messages/shape_tool.rs @@ -498,6 +498,8 @@ pub struct ShapeToolData { // Gizmos gizmo_manager: GizmoManager, + + last_mouse_viewport_for_space: Option, } impl ShapeToolData { @@ -908,6 +910,25 @@ impl Fsm for ShapeToolFsmState { } (ShapeToolFsmState::ResizingBounds, ShapeToolMessage::PointerMove { modifier }) => { if let Some(bounds) = &mut tool_data.bounding_box_manager { + let space_down = input.keyboard.get(Key::Space as usize); + let current_mouse = input.mouse.position; + + if space_down { + if tool_data.last_mouse_viewport_for_space.is_none() { + tool_data.last_mouse_viewport_for_space = Some(current_mouse); + } + let previous = tool_data.last_mouse_viewport_for_space.unwrap(); + let delta = current_mouse - previous; + if delta.length_squared() > 0. { + bounds.center_of_transformation += delta; + bounds.original_bound_transform.translation += delta; + bounds.original_transforms.shift(delta, document.metadata()); + tool_data.last_mouse_viewport_for_space = Some(current_mouse); + } + } else { + tool_data.last_mouse_viewport_for_space = None; + } + let messages = [ShapeToolMessage::PointerOutsideViewport { modifier }.into(), ShapeToolMessage::PointerMove { modifier }.into()]; resize_bounds( document, @@ -930,6 +951,26 @@ impl Fsm for ShapeToolFsmState { } (ShapeToolFsmState::RotatingBounds, ShapeToolMessage::PointerMove { modifier }) => { if let Some(bounds) = &mut tool_data.bounding_box_manager { + let space_down = input.keyboard.get(Key::Space as usize); + let current_mouse = input.mouse.position; + + if space_down { + if tool_data.last_mouse_viewport_for_space.is_none() { + tool_data.last_mouse_viewport_for_space = Some(current_mouse); + } + let previous = tool_data.last_mouse_viewport_for_space.unwrap(); + let delta = current_mouse - previous; + if delta.length_squared() > 0. { + bounds.center_of_transformation += delta; + bounds.original_bound_transform.translation += delta; + bounds.original_transforms.shift(delta, document.metadata()); + tool_data.data.drag_start += delta; + tool_data.last_mouse_viewport_for_space = Some(current_mouse); + } + } else { + tool_data.last_mouse_viewport_for_space = None; + } + rotate_bounds( document, responses, @@ -946,6 +987,25 @@ impl Fsm for ShapeToolFsmState { } (ShapeToolFsmState::SkewingBounds { skew }, ShapeToolMessage::PointerMove { .. }) => { if let Some(bounds) = &mut tool_data.bounding_box_manager { + let space_down = input.keyboard.get(Key::Space as usize); + let current_mouse = input.mouse.position; + + if space_down { + if tool_data.last_mouse_viewport_for_space.is_none() { + tool_data.last_mouse_viewport_for_space = Some(current_mouse); + } + let previous = tool_data.last_mouse_viewport_for_space.unwrap(); + let delta = current_mouse - previous; + if delta.length_squared() > 0. { + bounds.center_of_transformation += delta; + bounds.original_bound_transform.translation += delta; + bounds.original_transforms.shift(delta, document.metadata()); + tool_data.last_mouse_viewport_for_space = Some(current_mouse); + } + } else { + tool_data.last_mouse_viewport_for_space = None; + } + skew_bounds( document, responses, diff --git a/editor/src/messages/tool/tool_messages/spline_tool.rs b/editor/src/messages/tool/tool_messages/spline_tool.rs index 9aa7fcb5e0..78f6e8e741 100644 --- a/editor/src/messages/tool/tool_messages/spline_tool.rs +++ b/editor/src/messages/tool/tool_messages/spline_tool.rs @@ -253,6 +253,8 @@ struct SplineToolData { merge_endpoints: Vec<(EndpointPosition, PointId)>, snap_manager: SnapManager, auto_panning: AutoPanning, + last_mouse_for_space: Option, + hovered_point: Option, } impl SplineToolData { @@ -264,6 +266,8 @@ impl SplineToolData { self.preview_segment = None; self.extend = false; self.points = Vec::new(); + self.last_mouse_for_space = None; + self.hovered_point = None; } /// Get the snapped point while ignoring current layer @@ -425,6 +429,28 @@ impl Fsm for SplineToolFsmState { } (SplineToolFsmState::Drawing, SplineToolMessage::PointerMove) => { let Some(layer) = tool_data.current_layer else { return SplineToolFsmState::Ready }; + + let space_down = input.keyboard.get(Key::Space as usize); + if space_down { + if let Some(previous_mouse) = tool_data.last_mouse_for_space { + let delta_viewport = input.mouse.position - previous_mouse; + if delta_viewport.length_squared() > 0. { + let document_to_viewport = document.metadata().document_to_viewport; + let delta_document = document_to_viewport.inverse().transform_vector2(delta_viewport); + + tool_data.next_point += delta_document; + for (id, point) in &mut tool_data.points { + *point += delta_document; + let modification_type = VectorModificationType::ApplyPointDelta { point: *id, delta: delta_document }; + responses.add(GraphOperationMessage::Vector { layer, modification_type }); + } + } + } + tool_data.last_mouse_for_space = Some(input.mouse.position); + } else { + tool_data.last_mouse_for_space = None; + } + let ignore = |cp: PointId| tool_data.preview_point.is_some_and(|pp| pp == cp) || tool_data.points.last().is_some_and(|(ep, _)| *ep == cp); let join_point = closest_point(document, input.mouse.position, PATH_JOIN_THRESHOLD, vec![layer].into_iter(), ignore); diff --git a/editor/src/messages/tool/tool_messages/text_tool.rs b/editor/src/messages/tool/tool_messages/text_tool.rs index fdd2678aef..9610c8ff1a 100644 --- a/editor/src/messages/tool/tool_messages/text_tool.rs +++ b/editor/src/messages/tool/tool_messages/text_tool.rs @@ -387,6 +387,7 @@ struct TextToolData { snap_candidates: Vec, // TODO: Handle multiple layers in the future layer_dragging: Option, + last_mouse_viewport_for_space: Option, } impl TextToolData { @@ -768,6 +769,26 @@ impl Fsm for TextToolFsmState { && let Some(movement) = &mut bounds.selected_edges { let (centered, constrain) = (input.keyboard.key(center), input.keyboard.key(lock_ratio)); + + let space_down = input.keyboard.get(Key::Space as usize); + let current_mouse = input.mouse.position; + if space_down { + if tool_data.last_mouse_viewport_for_space.is_none() { + tool_data.last_mouse_viewport_for_space = Some(current_mouse); + } + let previous = tool_data.last_mouse_viewport_for_space.unwrap(); + let delta = current_mouse - previous; + if delta.length_squared() > 0. { + bounds.center_of_transformation += delta; + bounds.original_bound_transform.translation += delta; + tool_data.drag_start += delta; + tool_data.drag_current += delta; + tool_data.last_mouse_viewport_for_space = Some(current_mouse); + } + } else { + tool_data.last_mouse_viewport_for_space = None; + } + let center_position = centered.then_some(bounds.center_of_transformation); let Some(dragging_layer) = tool_data.layer_dragging else { return TextToolFsmState::Ready }; diff --git a/frontend/wasm/Cargo.toml b/frontend/wasm/Cargo.toml index a18b3facdf..08c12856d0 100644 --- a/frontend/wasm/Cargo.toml +++ b/frontend/wasm/Cargo.toml @@ -49,7 +49,7 @@ demangle-name-section = true dwarf-debug-info = false [package.metadata.wasm-pack.profile.release] -wasm-opt = ["-Os", "-g"] +wasm-opt = false [package.metadata.wasm-pack.profile.release.wasm-bindgen] debug-js-glue = false @@ -57,7 +57,7 @@ demangle-name-section = false dwarf-debug-info = false [package.metadata.wasm-pack.profile.profiling] -wasm-opt = ["-Os", "-g"] +wasm-opt = false [package.metadata.wasm-pack.profile.profiling.wasm-bindgen] debug-js-glue = true