mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-07-08 00:05:00 +00:00
Fix 'Shallow Select' mode behavior for selection context transference (#2604)
* Fix ancestor always returning None during shallow select * Fixes * fix shift remove on both * cleanup * one more cleanup * final(?) fix * some cleanup * more stuff * make shallow the default * fixes * fix * fix * fix * Code review --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
23b2c5bdf2
commit
90be1f42c6
7 changed files with 179 additions and 71 deletions
|
@ -310,13 +310,10 @@ impl OverlayContext {
|
|||
}
|
||||
|
||||
pub fn draw_angle(&mut self, pivot: DVec2, radius: f64, arc_radius: f64, offset_angle: f64, angle: f64) {
|
||||
let color_line = COLOR_OVERLAY_BLUE;
|
||||
|
||||
let end_point1 = pivot + radius * DVec2::from_angle(angle + offset_angle);
|
||||
let end_point2 = pivot + radius * DVec2::from_angle(offset_angle);
|
||||
self.line(pivot, end_point1, Some(color_line), None);
|
||||
self.line(pivot, end_point2, Some(color_line), None);
|
||||
|
||||
self.line(pivot, end_point1, None, None);
|
||||
self.dashed_line(pivot, end_point2, None, None, Some(2.), Some(2.), Some(0.5));
|
||||
self.draw_arc(pivot, arc_radius, offset_angle, (angle) % TAU + offset_angle);
|
||||
}
|
||||
|
||||
|
|
|
@ -261,11 +261,21 @@ impl LayerNodeIdentifier {
|
|||
metadata.get_relations(self).and_then(|relations| relations.last_child)
|
||||
}
|
||||
|
||||
/// Does the layer have children? If so, then it is a folder
|
||||
/// Does the layer have children? If so, then it is a folder.
|
||||
pub fn has_children(self, metadata: &DocumentMetadata) -> bool {
|
||||
self.first_child(metadata).is_some()
|
||||
}
|
||||
|
||||
/// Is the layer a child of the given layer?
|
||||
pub fn is_child_of(self, metadata: &DocumentMetadata, parent: &LayerNodeIdentifier) -> bool {
|
||||
parent.children(metadata).any(|child| child == self)
|
||||
}
|
||||
|
||||
/// Is the layer an ancestor of the given layer?
|
||||
pub fn is_ancestor_of(self, metadata: &DocumentMetadata, child: &LayerNodeIdentifier) -> bool {
|
||||
child.ancestors(metadata).any(|ancestor| ancestor == self)
|
||||
}
|
||||
|
||||
/// Iterator over all direct children (excluding self and recursive children)
|
||||
pub fn children(self, metadata: &DocumentMetadata) -> AxisIter {
|
||||
AxisIter {
|
||||
|
|
|
@ -10,6 +10,7 @@ fn draw_dashed_line(line_start: DVec2, line_end: DVec2, transform: DAffine2, ove
|
|||
|
||||
overlay_context.dashed_line(min_viewport, max_viewport, None, None, Some(2.), Some(2.), Some(0.5));
|
||||
}
|
||||
|
||||
/// Draws a solid line with a length annotation between two points transformed by the given affine transformations.
|
||||
fn draw_line_with_length(line_start: DVec2, line_end: DVec2, transform: DAffine2, document_to_viewport: DAffine2, overlay_context: &mut OverlayContext, label_alignment: LabelAlignment) {
|
||||
let transform_to_document = document_to_viewport.inverse() * transform;
|
||||
|
|
|
@ -267,7 +267,7 @@ impl Fsm for ArtboardToolFsmState {
|
|||
let constrain_square = input.keyboard.get(constrain_axis_or_aspect as usize);
|
||||
tool_data.resize_artboard(responses, document, input, from_center, constrain_square);
|
||||
|
||||
// AutoPanning
|
||||
// Auto-panning
|
||||
let messages = [
|
||||
ArtboardToolMessage::PointerOutsideViewport { constrain_axis_or_aspect, center }.into(),
|
||||
ArtboardToolMessage::PointerMove { constrain_axis_or_aspect, center }.into(),
|
||||
|
@ -306,7 +306,7 @@ impl Fsm for ArtboardToolFsmState {
|
|||
bounds.bounds[0] = position.round();
|
||||
bounds.bounds[1] = position.round() + size.round();
|
||||
|
||||
// AutoPanning
|
||||
// Auto-panning
|
||||
let messages = [
|
||||
ArtboardToolMessage::PointerOutsideViewport { constrain_axis_or_aspect, center }.into(),
|
||||
ArtboardToolMessage::PointerMove { constrain_axis_or_aspect, center }.into(),
|
||||
|
@ -345,7 +345,7 @@ impl Fsm for ArtboardToolFsmState {
|
|||
})
|
||||
}
|
||||
|
||||
// AutoPanning
|
||||
// Auto-panning
|
||||
let messages = [
|
||||
ArtboardToolMessage::PointerOutsideViewport { constrain_axis_or_aspect, center }.into(),
|
||||
ArtboardToolMessage::PointerMove { constrain_axis_or_aspect, center }.into(),
|
||||
|
@ -377,25 +377,25 @@ impl Fsm for ArtboardToolFsmState {
|
|||
ArtboardToolFsmState::Ready { hovered }
|
||||
}
|
||||
(ArtboardToolFsmState::ResizingBounds, ArtboardToolMessage::PointerOutsideViewport { .. }) => {
|
||||
// AutoPanning
|
||||
// Auto-panning
|
||||
let _ = tool_data.auto_panning.shift_viewport(input, responses);
|
||||
|
||||
ArtboardToolFsmState::ResizingBounds
|
||||
}
|
||||
(ArtboardToolFsmState::Dragging, ArtboardToolMessage::PointerOutsideViewport { .. }) => {
|
||||
// AutoPanning
|
||||
// Auto-panning
|
||||
tool_data.auto_panning.shift_viewport(input, responses);
|
||||
|
||||
ArtboardToolFsmState::Dragging
|
||||
}
|
||||
(ArtboardToolFsmState::Drawing, ArtboardToolMessage::PointerOutsideViewport { .. }) => {
|
||||
// AutoPanning
|
||||
// Auto-panning
|
||||
tool_data.auto_panning.shift_viewport(input, responses);
|
||||
|
||||
ArtboardToolFsmState::Drawing
|
||||
}
|
||||
(state, ArtboardToolMessage::PointerOutsideViewport { constrain_axis_or_aspect, center }) => {
|
||||
// AutoPanning
|
||||
// Auto-panning
|
||||
let messages = [
|
||||
ArtboardToolMessage::PointerOutsideViewport { constrain_axis_or_aspect, center }.into(),
|
||||
ArtboardToolMessage::PointerMove { constrain_axis_or_aspect, center }.into(),
|
||||
|
|
|
@ -52,8 +52,8 @@ pub enum SelectOptionsUpdate {
|
|||
#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
pub enum NestedSelectionBehavior {
|
||||
#[default]
|
||||
Deepest,
|
||||
Shallowest,
|
||||
Deepest,
|
||||
}
|
||||
|
||||
impl fmt::Display for NestedSelectionBehavior {
|
||||
|
@ -115,7 +115,7 @@ impl ToolMetadata for SelectTool {
|
|||
|
||||
impl SelectTool {
|
||||
fn deep_selection_widget(&self) -> WidgetHolder {
|
||||
let layer_selection_behavior_entries = [NestedSelectionBehavior::Deepest, NestedSelectionBehavior::Shallowest]
|
||||
let layer_selection_behavior_entries = [NestedSelectionBehavior::Shallowest, NestedSelectionBehavior::Deepest]
|
||||
.iter()
|
||||
.map(|mode| {
|
||||
MenuListEntry::new(format!("{mode:?}"))
|
||||
|
@ -125,7 +125,7 @@ impl SelectTool {
|
|||
.collect();
|
||||
|
||||
DropdownInput::new(vec![layer_selection_behavior_entries])
|
||||
.selected_index(Some((self.tool_data.nested_selection_behavior == NestedSelectionBehavior::Shallowest) as u32))
|
||||
.selected_index(Some((self.tool_data.nested_selection_behavior == NestedSelectionBehavior::Deepest) as u32))
|
||||
.tooltip("Choose if clicking nested layers directly selects the deepest, or selects the shallowest and deepens by double clicking")
|
||||
.widget_holder()
|
||||
}
|
||||
|
@ -288,11 +288,24 @@ impl ToolTransition for SelectTool {
|
|||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
enum SelectToolFsmState {
|
||||
Ready { selection: NestedSelectionBehavior },
|
||||
Drawing { selection_shape: SelectionShapeType, has_drawn: bool },
|
||||
Dragging { axis: Axis, using_compass: bool, has_dragged: bool },
|
||||
Ready {
|
||||
selection: NestedSelectionBehavior,
|
||||
},
|
||||
Drawing {
|
||||
selection_shape: SelectionShapeType,
|
||||
has_drawn: bool,
|
||||
},
|
||||
Dragging {
|
||||
axis: Axis,
|
||||
using_compass: bool,
|
||||
has_dragged: bool,
|
||||
deepest: bool,
|
||||
remove: bool,
|
||||
},
|
||||
ResizingBounds,
|
||||
SkewingBounds { skew: Key },
|
||||
SkewingBounds {
|
||||
skew: Key,
|
||||
},
|
||||
RotatingBounds,
|
||||
DraggingPivot,
|
||||
}
|
||||
|
@ -918,7 +931,7 @@ impl Fsm for SelectToolFsmState {
|
|||
let axis_state = compass_rose_state.axis_type().filter(|_| can_grab_compass_rose);
|
||||
(axis_state.unwrap_or_default(), axis_state.is_some())
|
||||
};
|
||||
SelectToolFsmState::Dragging { axis, using_compass, has_dragged: false }
|
||||
SelectToolFsmState::Dragging { axis, using_compass, has_dragged: false, deepest: input.keyboard.key(select_deepest), remove: input.keyboard.key(extend_selection) }
|
||||
}
|
||||
// Dragging near the transform cage bounding box to rotate it
|
||||
else if rotating_bounds {
|
||||
|
@ -954,7 +967,8 @@ impl Fsm for SelectToolFsmState {
|
|||
// Dragging a selection box
|
||||
else {
|
||||
tool_data.layers_dragging = selected;
|
||||
if !input.keyboard.key(extend_selection) && !input.keyboard.key(remove_from_selection) {
|
||||
let extend = input.keyboard.key(extend_selection);
|
||||
if !extend && !input.keyboard.key(remove_from_selection) {
|
||||
responses.add(DocumentMessage::DeselectAllLayers);
|
||||
tool_data.layers_dragging.clear();
|
||||
}
|
||||
|
@ -964,13 +978,13 @@ impl Fsm for SelectToolFsmState {
|
|||
selected = intersection_list;
|
||||
|
||||
match tool_data.nested_selection_behavior {
|
||||
NestedSelectionBehavior::Shallowest if !input.keyboard.key(select_deepest) => drag_shallowest_manipulation(responses, selected, tool_data, document),
|
||||
_ => drag_deepest_manipulation(responses, selected, tool_data, document),
|
||||
NestedSelectionBehavior::Shallowest if !input.keyboard.key(select_deepest) => drag_shallowest_manipulation(responses, selected, tool_data, document, false, extend),
|
||||
_ => drag_deepest_manipulation(responses, selected, tool_data, document, false),
|
||||
}
|
||||
tool_data.get_snap_candidates(document, input);
|
||||
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
SelectToolFsmState::Dragging { axis: Axis::None, using_compass: false, has_dragged: false }
|
||||
SelectToolFsmState::Dragging { axis: Axis::None, using_compass: false, has_dragged: false, deepest: input.keyboard.key(select_deepest), remove: input.keyboard.key(extend_selection) }
|
||||
} else {
|
||||
let selection_shape = if input.keyboard.key(lasso_select) { SelectionShapeType::Lasso } else { SelectionShapeType::Box };
|
||||
SelectToolFsmState::Drawing { selection_shape, has_drawn: false }
|
||||
|
@ -986,7 +1000,16 @@ impl Fsm for SelectToolFsmState {
|
|||
let selection = tool_data.nested_selection_behavior;
|
||||
SelectToolFsmState::Ready { selection }
|
||||
}
|
||||
(SelectToolFsmState::Dragging { axis, using_compass, has_dragged }, SelectToolMessage::PointerMove(modifier_keys)) => {
|
||||
(
|
||||
SelectToolFsmState::Dragging {
|
||||
axis,
|
||||
using_compass,
|
||||
has_dragged,
|
||||
deepest,
|
||||
remove,
|
||||
},
|
||||
SelectToolMessage::PointerMove(modifier_keys),
|
||||
) => {
|
||||
if !has_dragged {
|
||||
responses.add(ToolMessage::UpdateHints);
|
||||
}
|
||||
|
@ -1028,7 +1051,7 @@ impl Fsm for SelectToolFsmState {
|
|||
}
|
||||
tool_data.drag_current += mouse_delta;
|
||||
|
||||
// AutoPanning
|
||||
// Auto-panning
|
||||
let messages = [
|
||||
SelectToolMessage::PointerOutsideViewport(modifier_keys.clone()).into(),
|
||||
SelectToolMessage::PointerMove(modifier_keys).into(),
|
||||
|
@ -1039,6 +1062,8 @@ impl Fsm for SelectToolFsmState {
|
|||
axis,
|
||||
using_compass,
|
||||
has_dragged: true,
|
||||
deepest,
|
||||
remove,
|
||||
}
|
||||
}
|
||||
(SelectToolFsmState::ResizingBounds, SelectToolMessage::PointerMove(modifier_keys)) => {
|
||||
|
@ -1080,7 +1105,7 @@ impl Fsm for SelectToolFsmState {
|
|||
|
||||
selected.apply_transformation(bounds.original_bound_transform * transformation * bounds.original_bound_transform.inverse(), None);
|
||||
|
||||
// AutoPanning
|
||||
// Auto-panning
|
||||
let messages = [
|
||||
SelectToolMessage::PointerOutsideViewport(modifier_keys.clone()).into(),
|
||||
SelectToolMessage::PointerMove(modifier_keys).into(),
|
||||
|
@ -1169,7 +1194,7 @@ impl Fsm for SelectToolFsmState {
|
|||
let snapped_mouse_position = mouse_position;
|
||||
tool_data.pivot.set_viewport_position(snapped_mouse_position, document, responses);
|
||||
|
||||
// AutoPanning
|
||||
// Auto-panning
|
||||
let messages = [
|
||||
SelectToolMessage::PointerOutsideViewport(modifier_keys.clone()).into(),
|
||||
SelectToolMessage::PointerMove(modifier_keys).into(),
|
||||
|
@ -1190,7 +1215,7 @@ impl Fsm for SelectToolFsmState {
|
|||
extend_lasso(&mut tool_data.lasso_polygon, tool_data.drag_current);
|
||||
}
|
||||
|
||||
// AutoPanning
|
||||
// Auto-panning
|
||||
let messages = [
|
||||
SelectToolMessage::PointerOutsideViewport(modifier_keys.clone()).into(),
|
||||
SelectToolMessage::PointerMove(modifier_keys).into(),
|
||||
|
@ -1227,17 +1252,32 @@ impl Fsm for SelectToolFsmState {
|
|||
let selection = tool_data.nested_selection_behavior;
|
||||
SelectToolFsmState::Ready { selection }
|
||||
}
|
||||
(SelectToolFsmState::Dragging { axis, using_compass, has_dragged }, SelectToolMessage::PointerOutsideViewport(_)) => {
|
||||
// AutoPanning
|
||||
(
|
||||
SelectToolFsmState::Dragging {
|
||||
axis,
|
||||
using_compass,
|
||||
has_dragged,
|
||||
deepest,
|
||||
remove,
|
||||
},
|
||||
SelectToolMessage::PointerOutsideViewport(_),
|
||||
) => {
|
||||
// Auto-panning
|
||||
if let Some(shift) = tool_data.auto_panning.shift_viewport(input, responses) {
|
||||
tool_data.drag_current += shift;
|
||||
tool_data.drag_start += shift;
|
||||
}
|
||||
|
||||
SelectToolFsmState::Dragging { axis, using_compass, has_dragged }
|
||||
SelectToolFsmState::Dragging {
|
||||
axis,
|
||||
using_compass,
|
||||
has_dragged,
|
||||
deepest,
|
||||
remove,
|
||||
}
|
||||
}
|
||||
(SelectToolFsmState::ResizingBounds | SelectToolFsmState::SkewingBounds { .. }, SelectToolMessage::PointerOutsideViewport(_)) => {
|
||||
// AutoPanning
|
||||
// Auto-panning
|
||||
if let Some(shift) = tool_data.auto_panning.shift_viewport(input, responses) {
|
||||
if let Some(bounds) = &mut tool_data.bounding_box_manager {
|
||||
bounds.center_of_transformation += shift;
|
||||
|
@ -1248,13 +1288,13 @@ impl Fsm for SelectToolFsmState {
|
|||
self
|
||||
}
|
||||
(SelectToolFsmState::DraggingPivot, SelectToolMessage::PointerOutsideViewport(_)) => {
|
||||
// AutoPanning
|
||||
// Auto-panning
|
||||
let _ = tool_data.auto_panning.shift_viewport(input, responses);
|
||||
|
||||
self
|
||||
}
|
||||
(SelectToolFsmState::Drawing { .. }, SelectToolMessage::PointerOutsideViewport(_)) => {
|
||||
// AutoPanning
|
||||
// Auto-panning
|
||||
if let Some(shift) = tool_data.auto_panning.shift_viewport(input, responses) {
|
||||
tool_data.drag_start += shift;
|
||||
}
|
||||
|
@ -1262,7 +1302,7 @@ impl Fsm for SelectToolFsmState {
|
|||
self
|
||||
}
|
||||
(state, SelectToolMessage::PointerOutsideViewport(modifier_keys)) => {
|
||||
// AutoPanning
|
||||
// Auto-panning
|
||||
let messages = [
|
||||
SelectToolMessage::PointerOutsideViewport(modifier_keys.clone()).into(),
|
||||
SelectToolMessage::PointerMove(modifier_keys).into(),
|
||||
|
@ -1271,7 +1311,7 @@ impl Fsm for SelectToolFsmState {
|
|||
|
||||
state
|
||||
}
|
||||
(SelectToolFsmState::Dragging { has_dragged, .. }, SelectToolMessage::DragStop { remove_from_selection }) => {
|
||||
(SelectToolFsmState::Dragging { has_dragged, remove, deepest, .. }, SelectToolMessage::DragStop { remove_from_selection }) => {
|
||||
// Deselect layer if not snap dragging
|
||||
responses.add(DocumentMessage::EndTransaction);
|
||||
tool_data.axis_align = false;
|
||||
|
@ -1306,15 +1346,28 @@ impl Fsm for SelectToolFsmState {
|
|||
.collect(),
|
||||
});
|
||||
}
|
||||
} else if let Some(selecting_layer) = tool_data.select_single_layer.take() {
|
||||
} else if tool_data.select_single_layer.take().is_some() {
|
||||
// Previously, we may have had many layers selected. If the user clicks without dragging, we should just select the one layer that has been clicked.
|
||||
if !has_dragged {
|
||||
if selecting_layer == LayerNodeIdentifier::ROOT_PARENT {
|
||||
log::error!("selecting_layer should not be ROOT_PARENT");
|
||||
} else {
|
||||
responses.add(NodeGraphMessage::SelectedNodesSet {
|
||||
nodes: vec![selecting_layer.to_node()],
|
||||
});
|
||||
let selected = document.click_list(input).collect::<Vec<_>>();
|
||||
let intersection = document.find_deepest(&selected);
|
||||
if let Some(intersection) = intersection {
|
||||
tool_data.layer_selected_on_start = Some(intersection);
|
||||
|
||||
match tool_data.nested_selection_behavior {
|
||||
NestedSelectionBehavior::Shallowest if remove && !deepest => drag_shallowest_manipulation(responses, selected, tool_data, document, true, true),
|
||||
NestedSelectionBehavior::Deepest if remove => drag_deepest_manipulation(responses, selected, tool_data, document, true),
|
||||
NestedSelectionBehavior::Shallowest if !deepest => drag_shallowest_manipulation(responses, selected, tool_data, document, false, true),
|
||||
_ => {
|
||||
responses.add(DocumentMessage::DeselectAllLayers);
|
||||
tool_data.layers_dragging.clear();
|
||||
drag_deepest_manipulation(responses, selected, tool_data, document, false)
|
||||
}
|
||||
}
|
||||
|
||||
tool_data.get_snap_candidates(document, input);
|
||||
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1528,7 +1581,7 @@ impl Fsm for SelectToolFsmState {
|
|||
]);
|
||||
responses.add(FrontendMessage::UpdateInputHints { hint_data });
|
||||
}
|
||||
SelectToolFsmState::Dragging { axis, using_compass, has_dragged } if *has_dragged => {
|
||||
SelectToolFsmState::Dragging { axis, using_compass, has_dragged, .. } if *has_dragged => {
|
||||
let mut hint_data = vec![
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
||||
HintGroup(vec![
|
||||
|
@ -1591,16 +1644,59 @@ fn not_artboard(document: &DocumentMessageHandler) -> impl Fn(&LayerNodeIdentifi
|
|||
|&layer| layer != LayerNodeIdentifier::ROOT_PARENT && !document.network_interface.is_artboard(&layer.to_node(), &[])
|
||||
}
|
||||
|
||||
fn drag_shallowest_manipulation(responses: &mut VecDeque<Message>, selected: Vec<LayerNodeIdentifier>, tool_data: &mut SelectToolData, document: &DocumentMessageHandler) {
|
||||
for layer in selected {
|
||||
let ancestor = layer
|
||||
.ancestors(document.metadata())
|
||||
.filter(not_artboard(document))
|
||||
.find(|&ancestor| document.network_interface.selected_nodes().selected_layers_contains(ancestor, document.metadata()));
|
||||
fn drag_shallowest_manipulation(responses: &mut VecDeque<Message>, selected: Vec<LayerNodeIdentifier>, tool_data: &mut SelectToolData, document: &DocumentMessageHandler, remove: bool, exists: bool) {
|
||||
if selected.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let new_selected = ancestor.unwrap_or_else(|| layer.ancestors(document.metadata()).filter(not_artboard(document)).last().unwrap_or(layer));
|
||||
tool_data.layers_dragging.retain(|layer| !layer.ancestors(document.metadata()).any(|ancestor| ancestor == new_selected));
|
||||
tool_data.layers_dragging.push(new_selected);
|
||||
let clicked_layer = document.find_deepest(&selected).unwrap_or_else(|| {
|
||||
LayerNodeIdentifier::ROOT_PARENT
|
||||
.children(document.metadata())
|
||||
.next()
|
||||
.expect("ROOT_PARENT should have at least one layer when clicking")
|
||||
});
|
||||
|
||||
let metadata = document.metadata();
|
||||
|
||||
let selected_layers = document.network_interface.selected_nodes().selected_layers(document.metadata()).collect::<Vec<_>>();
|
||||
let final_selection: Option<LayerNodeIdentifier> = (!selected_layers.is_empty() && selected_layers != vec![LayerNodeIdentifier::ROOT_PARENT]).then_some(()).and_then(|_| {
|
||||
let mut relevant_layers = document.network_interface.selected_nodes().selected_layers(document.metadata()).collect::<Vec<_>>();
|
||||
if !relevant_layers.contains(&clicked_layer) {
|
||||
relevant_layers.push(clicked_layer);
|
||||
}
|
||||
clicked_layer
|
||||
.ancestors(metadata)
|
||||
.filter(not_artboard(document))
|
||||
.find(|&ancestor| relevant_layers.iter().all(|layer| *layer == ancestor || ancestor.is_ancestor_of(metadata, layer)))
|
||||
.and_then(|least_common_ancestor| {
|
||||
let common_siblings: Vec<_> = least_common_ancestor.children(metadata).collect();
|
||||
(clicked_layer == least_common_ancestor)
|
||||
.then_some(least_common_ancestor)
|
||||
.or_else(|| common_siblings.iter().find(|&&child| clicked_layer == child || child.is_ancestor_of(metadata, &clicked_layer)).copied())
|
||||
})
|
||||
});
|
||||
|
||||
if final_selection.is_some_and(|layer| selected_layers.iter().any(|selected| layer.is_child_of(metadata, selected))) {
|
||||
if exists && remove && selected_layers.len() == 1 {
|
||||
responses.add(DocumentMessage::DeselectAllLayers);
|
||||
tool_data.layers_dragging.clear();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if !exists && !remove {
|
||||
responses.add(DocumentMessage::DeselectAllLayers);
|
||||
tool_data.layers_dragging.clear();
|
||||
}
|
||||
|
||||
let new_selected = final_selection.unwrap_or_else(|| clicked_layer.ancestors(document.metadata()).filter(not_artboard(document)).last().unwrap_or(clicked_layer));
|
||||
tool_data.layers_dragging.extend(vec![new_selected]);
|
||||
tool_data.layers_dragging.retain(|&selected_layer| !selected_layer.is_child_of(metadata, &new_selected));
|
||||
if remove {
|
||||
tool_data.layers_dragging.retain(|&selected_layer| clicked_layer != selected_layer);
|
||||
if selected_layers.contains(&new_selected) {
|
||||
tool_data.layers_dragging.retain(|&selected_layer| new_selected != selected_layer);
|
||||
}
|
||||
}
|
||||
|
||||
responses.add(NodeGraphMessage::SelectedNodesSet {
|
||||
|
@ -1619,15 +1715,18 @@ fn drag_shallowest_manipulation(responses: &mut VecDeque<Message>, selected: Vec
|
|||
});
|
||||
}
|
||||
|
||||
fn drag_deepest_manipulation(responses: &mut VecDeque<Message>, selected: Vec<LayerNodeIdentifier>, tool_data: &mut SelectToolData, document: &DocumentMessageHandler) {
|
||||
tool_data.layers_dragging.append(&mut vec![
|
||||
document.find_deepest(&selected).unwrap_or(
|
||||
LayerNodeIdentifier::ROOT_PARENT
|
||||
.children(document.metadata())
|
||||
.next()
|
||||
.expect("ROOT_PARENT should have a layer child when clicking"),
|
||||
),
|
||||
]);
|
||||
fn drag_deepest_manipulation(responses: &mut VecDeque<Message>, selected: Vec<LayerNodeIdentifier>, tool_data: &mut SelectToolData, document: &DocumentMessageHandler, remove: bool) {
|
||||
let layer = document.find_deepest(&selected).unwrap_or(
|
||||
LayerNodeIdentifier::ROOT_PARENT
|
||||
.children(document.metadata())
|
||||
.next()
|
||||
.expect("ROOT_PARENT should have a layer child when clicking"),
|
||||
);
|
||||
if !remove {
|
||||
tool_data.layers_dragging.extend(vec![layer]);
|
||||
} else {
|
||||
tool_data.layers_dragging.retain(|&selected_layer| layer != selected_layer);
|
||||
}
|
||||
responses.add(NodeGraphMessage::SelectedNodesSet {
|
||||
nodes: tool_data
|
||||
.layers_dragging
|
||||
|
|
|
@ -702,7 +702,7 @@ impl Fsm for TextToolFsmState {
|
|||
});
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
|
||||
// AutoPanning
|
||||
// Auto-panning
|
||||
let messages = [
|
||||
TextToolMessage::PointerOutsideViewport { center, lock_ratio }.into(),
|
||||
TextToolMessage::PointerMove { center, lock_ratio }.into(),
|
||||
|
@ -725,7 +725,7 @@ impl Fsm for TextToolFsmState {
|
|||
TextToolFsmState::Placing
|
||||
}
|
||||
(TextToolFsmState::ResizingBounds | TextToolFsmState::Dragging, TextToolMessage::PointerOutsideViewport { .. }) => {
|
||||
// AutoPanning
|
||||
// Auto-panning
|
||||
if let Some(shift) = tool_data.auto_panning.shift_viewport(input, responses) {
|
||||
if let Some(bounds) = &mut tool_data.bounding_box_manager {
|
||||
bounds.center_of_transformation += shift;
|
||||
|
|
|
@ -242,7 +242,7 @@ impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayer
|
|||
|
||||
if matches!(axis_constraint, Axis::Both | Axis::X) && translation.x != 0. {
|
||||
let end = if self.local { (quad[1] - quad[0]).rotate(e1) + quad[0] } else { quad[1] };
|
||||
overlay_context.line(quad[0], end, None, None);
|
||||
overlay_context.dashed_line(quad[0], end, None, None, Some(2.), Some(2.), Some(0.5));
|
||||
|
||||
let x_transform = DAffine2::from_translation((quad[0] + end) / 2.);
|
||||
overlay_context.text(&format_rounded(translation.x, 3), COLOR_OVERLAY_BLUE, None, x_transform, 4., [Pivot::Middle, Pivot::End]);
|
||||
|
@ -250,7 +250,7 @@ impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayer
|
|||
|
||||
if matches!(axis_constraint, Axis::Both | Axis::Y) && translation.y != 0. {
|
||||
let end = if self.local { (quad[3] - quad[0]).rotate(e1) + quad[0] } else { quad[3] };
|
||||
overlay_context.line(quad[0], end, None, None);
|
||||
overlay_context.dashed_line(quad[0], end, None, None, Some(2.), Some(2.), Some(0.5));
|
||||
let x_parameter = viewport_translate.x.clamp(-1., 1.);
|
||||
let y_transform = DAffine2::from_translation((quad[0] + end) / 2. + x_parameter * DVec2::X * 0.);
|
||||
let pivot_selection = if x_parameter >= -1e-3 { Pivot::Start } else { Pivot::End };
|
||||
|
@ -258,9 +258,10 @@ impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayer
|
|||
overlay_context.text(&format_rounded(translation.y, 2), COLOR_OVERLAY_BLUE, None, y_transform, 3., [pivot_selection, Pivot::Middle]);
|
||||
}
|
||||
}
|
||||
|
||||
if matches!(axis_constraint, Axis::Both) && translation.x != 0. && translation.y != 0. {
|
||||
overlay_context.dashed_line(quad[1], quad[2], None, None, Some(2.), Some(2.), Some(0.5));
|
||||
overlay_context.dashed_line(quad[3], quad[2], None, None, Some(2.), Some(2.), Some(0.5));
|
||||
overlay_context.line(quad[1], quad[2], None, None);
|
||||
overlay_context.line(quad[3], quad[2], None, None);
|
||||
}
|
||||
}
|
||||
TransformOperation::Scaling(scale) => {
|
||||
|
@ -274,7 +275,7 @@ impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayer
|
|||
let end_point = pivot + local_edge * scale.max(1.);
|
||||
|
||||
if scale > 0. {
|
||||
overlay_context.dashed_line(pivot, boundary_point, None, None, Some(4.), Some(4.), Some(0.5));
|
||||
overlay_context.dashed_line(pivot, boundary_point, None, None, Some(2.), Some(2.), Some(0.5));
|
||||
}
|
||||
overlay_context.line(boundary_point, end_point, None, None);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue