Restore select tool functionality and shallow select mode (#1583)

* Restore select tool functionality

* UI tweak

* Change all of the things

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
0HyperCube 2024-01-18 22:28:45 +00:00 committed by GitHub
parent 5c9d3c5d75
commit 208e907fa4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 150 additions and 133 deletions

View File

@ -398,12 +398,29 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
responses.add(FrontendMessage::UpdateDocumentLayerStructure { data_buffer });
}
DuplicateSelectedLayers => {
// TODO: Reimplement selected layer duplication
// self.backup(responses);
// self.layer_range_selection_reference = None;
// for path in self.selected_layers_sorted() {
// responses.add(DocumentOperation::DuplicateLayer { path: path.to_vec() });
// }
self.backup(responses);
for layer_ancestors in self.metadata.shallowest_unique_layers(self.selected_nodes.selected_layers(&self.metadata)) {
let Some(layer) = layer_ancestors.last().copied() else { continue };
let Some(parent) = layer.parent(&self.metadata) else { continue };
let Some(node) = self.network().nodes.get(&layer.to_node()).and_then(|node| node.inputs.first()).and_then(|input| input.as_node()) else {
continue;
};
let nodes = NodeGraphMessageHandler::copy_nodes(
self.network(),
&self
.network()
.upstream_flow_back_from_nodes(vec![node], false)
.enumerate()
.map(|(index, (_, node_id))| (node_id, NodeId(index as u64)))
.collect(),
)
.collect();
let id = NodeId(generate_uuid());
let insert_index = -1;
responses.add(GraphOperationMessage::NewCustomLayer { id, nodes, parent, insert_index });
}
}
FlipSelectedLayers { flip_axis } => {
self.backup(responses);

View File

@ -86,33 +86,20 @@ impl SelectedEdges {
let mut pivot = self.pivot_from_bounds(min, max);
if let Some(center_around) = center_around {
// The below ratio is: `dragging edge / being centered`.
// The `is_finite()` checks are in case the user is dragging the edge where the pivot is located (in which case the centering mode is ignored).
let center_around = transform.inverse().transform_point2(center_around);
if self.top {
let ratio = (center_around.y - min.y) / (center_around.y - self.bounds[0].y);
if ratio.is_finite() {
max.y = center_around.y + ratio * (self.bounds[1].y - center_around.y);
pivot.y = center_around.y;
}
pivot.y = center_around.y;
max.y = center_around.y * 2. - min.y;
} else if self.bottom {
let ratio = (max.y - center_around.y) / (self.bounds[1].y - center_around.y);
if ratio.is_finite() {
min.y = center_around.y - ratio * (center_around.y - self.bounds[0].y);
pivot.y = center_around.y;
}
pivot.y = center_around.y;
min.y = center_around.y * 2. - max.y;
}
if self.left {
let ratio = (center_around.x - min.x) / (center_around.x - self.bounds[0].x);
if ratio.is_finite() {
max.x = center_around.x + ratio * (self.bounds[1].x - center_around.x);
pivot.x = center_around.x;
}
pivot.x = center_around.x;
max.x = center_around.x * 2. - min.x;
} else if self.right {
let ratio = (max.x - center_around.x) / (self.bounds[1].x - center_around.x);
if ratio.is_finite() {
min.x = center_around.x - ratio * (center_around.x - self.bounds[0].x);
pivot.x = center_around.x;
}
pivot.x = center_around.x;
min.x = center_around.x * 2. - max.x;
}
}
@ -128,7 +115,9 @@ impl SelectedEdges {
let delta_size = new_size - size;
min -= delta_size * min_pivot;
max = min + new_size;
} else if let Some(SizeSnapData { manager, points, snap_data }) = snap {
}
if let Some(SizeSnapData { manager, points, snap_data }) = snap {
let view_to_doc = snap_data.document.metadata.document_to_viewport.inverse();
let bounds_to_doc = view_to_doc * transform;
let mut best_snap = SnappedPoint::infinite_snap(pivot);
@ -140,7 +129,13 @@ impl SelectedEdges {
let normalised = (bounds_space - self.bounds[0]) / (self.bounds[1] - self.bounds[0]);
let updated = normalised * (max - min) + min;
point.document_point = bounds_to_doc.transform_point2(updated);
let mut snapped = if !(self.top || self.bottom) || !(self.left || self.right) {
let mut snapped = if constrain {
let constraint = SnapConstraint::Line {
origin: point.document_point,
direction: (point.document_point - bounds_to_doc.transform_point2(pivot)).normalize_or_zero(),
};
manager.constrained_snap(&snap_data, point, constraint, None)
} else if !(self.top || self.bottom) || !(self.left || self.right) {
let axis = if !(self.top || self.bottom) { DVec2::X } else { DVec2::Y };
let constraint = SnapConstraint::Line {
origin: point.document_point,
@ -158,10 +153,10 @@ impl SelectedEdges {
let snapped_bounds = bounds_to_doc.inverse().transform_point2(snapped.snapped_point_document);
let mut scale_factor = (snapped_bounds - pivot) / (updated - pivot);
if !(self.left || self.right) {
if !(self.left || self.right) && !constrain {
scale_factor.x = 1.
}
if !(self.top || self.bottom) {
if !(self.top || self.bottom) && !constrain {
scale_factor.y = 1.
}

View File

@ -115,7 +115,7 @@ impl ArtboardToolData {
fn start_resizing(&mut self, _selected_edges: (bool, bool, bool, bool), _document: &DocumentMessageHandler, _input: &InputPreprocessorMessageHandler) {
if let Some(bounds) = &mut self.bounding_box_manager {
bounds.center_of_transformation = (bounds.bounds[0] + bounds.bounds[1]) / 2.;
bounds.center_of_transformation = bounds.transform.transform_point2((bounds.bounds[0] + bounds.bounds[1]) / 2.);
}
}

View File

@ -1,18 +1,20 @@
#![allow(clippy::too_many_arguments)]
use super::tool_prelude::*;
use crate::application::generate_uuid;
use crate::consts::{ROTATE_SNAP_ANGLE, SELECTION_TOLERANCE};
use crate::messages::input_mapper::utility_types::input_mouse::ViewportPosition;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, FlipAxis};
use crate::messages::portfolio::document::utility_types::transformation::Selected;
use crate::messages::tool;
use crate::messages::tool::common_functionality::graph_modification_utils::is_layer_fed_by_node_of_name;
use crate::messages::tool::common_functionality::pivot::Pivot;
use crate::messages::tool::common_functionality::snapping::{self, SnapCandidatePoint, SnapConstraint, SnapData, SnapManager, SnappedPoint};
use crate::messages::tool::common_functionality::transformation_cage::*;
use graph_craft::document::NodeNetwork;
use graph_craft::document::{NodeId, NodeNetwork};
use graphene_core::renderer::Quad;
use std::fmt;
@ -96,21 +98,21 @@ impl ToolMetadata for SelectTool {
}
impl SelectTool {
// fn deep_selection_widget(&self) -> WidgetHolder {
// let layer_selection_behavior_entries = [NestedSelectionBehavior::Deepest, NestedSelectionBehavior::Shallowest]
// .iter()
// .map(|mode| {
// MenuListEntry::new(mode.to_string())
// .value(mode.to_string())
// .on_update(move |_| SelectToolMessage::SelectOptions(SelectOptionsUpdate::NestedSelectionBehavior(*mode)).into())
// })
// .collect();
fn deep_selection_widget(&self) -> WidgetHolder {
let layer_selection_behavior_entries = [NestedSelectionBehavior::Deepest, NestedSelectionBehavior::Shallowest]
.iter()
.map(|mode| {
MenuListEntry::new(mode.to_string())
.value(mode.to_string())
.on_update(move |_| SelectToolMessage::SelectOptions(SelectOptionsUpdate::NestedSelectionBehavior(*mode)).into())
})
.collect();
// DropdownInput::new(vec![layer_selection_behavior_entries])
// .selected_index(Some((self.tool_data.nested_selection_behavior == NestedSelectionBehavior::Shallowest) as u32))
// .tooltip("Choose if clicking nested layers directly selects the deepest, or selects the shallowest and deepens by double clicking")
// .widget_holder()
// }
DropdownInput::new(vec![layer_selection_behavior_entries])
.selected_index(Some((self.tool_data.nested_selection_behavior == NestedSelectionBehavior::Shallowest) as u32))
.tooltip("Choose if clicking nested layers directly selects the deepest, or selects the shallowest and deepens by double clicking")
.widget_holder()
}
fn pivot_widget(&self, disabled: bool) -> WidgetHolder {
PivotInput::new(self.tool_data.pivot.to_pivot_position())
@ -163,10 +165,12 @@ impl SelectTool {
impl LayoutHolder for SelectTool {
fn layout(&self) -> Layout {
let mut widgets = Vec::new();
// widgets.push(self.deep_selection_widget()); // TODO: Reenable once Deep/Shallow Selection is implemented again
// Select mode (Deep/Shallow)
widgets.push(self.deep_selection_widget());
// Pivot
// widgets.push(Separator::new(SeparatorType::Related).widget_holder()); // TODO: Reenable once Deep/Shallow Selection is implemented again
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
widgets.push(self.pivot_widget(self.tool_data.selected_layers_count == 0));
// Align
@ -179,13 +183,11 @@ impl LayoutHolder for SelectTool {
let disabled = self.tool_data.selected_layers_count == 0;
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
widgets.extend(self.flip_widgets(disabled));
widgets.push(PopoverButton::new("Flip", "Coming soon").disabled(disabled).widget_holder());
// Boolean
if self.tool_data.selected_layers_count >= 2 {
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
widgets.extend(self.boolean_widgets());
widgets.push(PopoverButton::new("Boolean", "Coming soon").widget_holder());
}
Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets }]))
@ -259,7 +261,7 @@ struct SelectToolData {
layer_selected_on_start: Option<LayerNodeIdentifier>,
select_single_layer: Option<LayerNodeIdentifier>,
has_dragged: bool,
not_duplicated_layers: Option<Vec<LayerNodeIdentifier>>,
non_duplicated_layers: Option<Vec<LayerNodeIdentifier>>,
bounding_box_manager: Option<BoundingBoxManager>,
snap_manager: SnapManager,
cursor: MouseCursorIcon,
@ -300,57 +302,54 @@ impl SelectToolData {
/// Duplicates the currently dragging layers. Called when Alt is pressed and the layers have not yet been duplicated.
fn start_duplicates(&mut self, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
responses.add(DocumentMessage::DeselectAllLayers);
self.non_duplicated_layers = Some(self.layers_dragging.clone());
let mut new_dragging = Vec::new();
for layer_ancestors in document.metadata().shallowest_unique_layers(self.layers_dragging.iter().copied().rev()) {
let Some(layer) = layer_ancestors.last().copied() else { continue };
let Some(parent) = layer.parent(&document.metadata) else { continue };
// Take the selected layers and store them in a separate list.
self.not_duplicated_layers = Some(self.layers_dragging.clone());
// Copy the layer
let node = layer.to_node();
let Some(node) = document.network().nodes.get(&node).and_then(|node| node.inputs.first()).and_then(|input| input.as_node()) else {
continue;
};
// Duplicate each previously selected layer and select the new ones.
for layer_ancestors in document.metadata().shallowest_unique_layers(self.layers_dragging.iter().copied()) {
let layer = *layer_ancestors.last().unwrap();
// Moves the original back to its starting position.
responses.add_front(GraphOperationMessage::TransformChange {
let nodes = NodeGraphMessageHandler::copy_nodes(
document.network(),
&document
.network()
.upstream_flow_back_from_nodes(vec![node], false)
.enumerate()
.map(|(index, (_, node_id))| (node_id, NodeId(index as u64)))
.collect(),
)
.collect();
// Moves the layer back to its starting position.
responses.add(GraphOperationMessage::TransformChange {
layer,
transform: DAffine2::from_translation(self.drag_start - self.drag_current),
transform_in: TransformIn::Viewport,
skip_rerender: true,
});
// Copy the layers.
// Not using the Copy message allows us to retrieve the ids of the new layers to initialize the drag.
todo!();
// let layer = match document.document_legacy.layer(layer_path) {
// Ok(layer) => layer.clone(),
// Err(e) => {
// warn!("Could not access selected layer {layer_path:?}: {e:?}");
// continue;
// }
// };
// let layer_metadata = *document.layer_metadata(layer_path);
// *layer_path.last_mut().unwrap() = generate_uuid();
// responses.add(Operation::InsertLayer {
// layer: Box::new(layer),
// destination_path: layer_path.clone(),
// insert_index: -1,
// duplicating: false,
// });
// responses.add(DocumentMessage::UpdateLayerMetadata {
// layer_path: layer_path.clone(),
// layer_metadata,
// });
let id = NodeId(generate_uuid());
let insert_index = -1;
let layer = LayerNodeIdentifier::new_unchecked(id);
responses.add(GraphOperationMessage::NewCustomLayer { id, nodes, parent, insert_index });
new_dragging.push(layer);
}
let nodes = new_dragging.iter().map(|layer| layer.to_node()).collect();
responses.add(NodeGraphMessage::SelectedNodesSet { nodes });
self.layers_dragging = new_dragging;
}
/// Removes the duplicated layers. Called when Alt is released and the layers have previously been duplicated.
fn stop_duplicates(&mut self, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
let Some(originals) = self.not_duplicated_layers.take() else {
let Some(original) = self.non_duplicated_layers.take() else {
return;
};
responses.add(DocumentMessage::DeselectAllLayers);
// Delete the duplicated layers
for layer_ancestors in document.metadata().shallowest_unique_layers(self.layers_dragging.iter().copied()) {
responses.add(GraphOperationMessage::DeleteLayer {
@ -358,22 +357,17 @@ impl SelectToolData {
});
}
// Move the original to under the mouse
for layer_ancestors in document.metadata().shallowest_unique_layers(originals.iter().copied()) {
responses.add_front(GraphOperationMessage::TransformChange {
layer: *layer_ancestors.last().unwrap(),
for &layer in &original {
responses.add(GraphOperationMessage::TransformChange {
layer,
transform: DAffine2::from_translation(self.drag_current - self.drag_start),
transform_in: TransformIn::Viewport,
skip_rerender: true,
});
}
// Select the originals
responses.add(NodeGraphMessage::SelectedNodesSet {
nodes: originals.iter().map(|layer| layer.to_node()).collect::<Vec<_>>(),
});
self.layers_dragging = originals;
let nodes = original.iter().map(|layer| layer.to_node()).collect();
responses.add(NodeGraphMessage::SelectedNodesSet { nodes });
self.layers_dragging = original;
}
}
@ -455,7 +449,7 @@ impl Fsm for SelectToolFsmState {
self
}
(SelectToolFsmState::Ready, SelectToolMessage::DragStart { add_to_selection, select_deepest: _ }) => {
(SelectToolFsmState::Ready, SelectToolMessage::DragStart { add_to_selection, select_deepest }) => {
tool_data.drag_start = input.mouse.position;
tool_data.drag_current = input.mouse.position;
@ -538,11 +532,14 @@ impl Fsm for SelectToolFsmState {
tool_data.layers_dragging = selected;
SelectToolFsmState::RotatingBounds
} else if intersection.is_some_and(|intersection| selected.iter().any(|selected_layer| intersection.starts_with(*selected_layer, document.metadata())))
&& tool_data.nested_selection_behavior == NestedSelectionBehavior::Deepest
{
} else if intersection.is_some_and(|intersection| selected.iter().any(|selected_layer| intersection.starts_with(*selected_layer, document.metadata()))) {
responses.add(DocumentMessage::StartTransaction);
tool_data.select_single_layer = intersection;
if tool_data.nested_selection_behavior == NestedSelectionBehavior::Deepest {
tool_data.select_single_layer = intersection;
} else {
tool_data.select_single_layer = intersection.and_then(|intersection| intersection.ancestors(&document.metadata).find(|ancestor| selected.contains(ancestor)));
}
tool_data.layers_dragging = selected;
@ -552,7 +549,7 @@ impl Fsm for SelectToolFsmState {
} else {
tool_data.layers_dragging = selected;
if !input.keyboard.key(add_to_selection) && tool_data.nested_selection_behavior == NestedSelectionBehavior::Deepest {
if !input.keyboard.key(add_to_selection) {
responses.add(DocumentMessage::DeselectAllLayers);
tool_data.layers_dragging.clear();
}
@ -564,8 +561,8 @@ impl Fsm for SelectToolFsmState {
selected = vec![intersection];
match tool_data.nested_selection_behavior {
NestedSelectionBehavior::Shallowest => drag_shallowest_manipulation(responses, selected, tool_data, document),
NestedSelectionBehavior::Deepest => drag_deepest_manipulation(responses, selected, tool_data),
NestedSelectionBehavior::Shallowest if !input.keyboard.key(select_deepest) => drag_shallowest_manipulation(responses, selected, tool_data, document),
_ => drag_deepest_manipulation(responses, selected, tool_data),
}
tool_data.get_snap_candidates(document, input);
SelectToolFsmState::Dragging
@ -579,18 +576,28 @@ impl Fsm for SelectToolFsmState {
SelectToolFsmState::DrawingBox
}
};
tool_data.not_duplicated_layers = None;
tool_data.non_duplicated_layers = None;
state
}
(SelectToolFsmState::Dragging, SelectToolMessage::PointerMove { axis_align, duplicate, .. }) => {
tool_data.has_dragged = true;
if input.keyboard.key(duplicate) && tool_data.non_duplicated_layers.is_none() {
tool_data.start_duplicates(document, responses);
} else if !input.keyboard.key(duplicate) && tool_data.non_duplicated_layers.is_some() {
tool_data.stop_duplicates(document, responses);
}
let axis_align = input.keyboard.key(axis_align);
let mouse_position = axis_align_drag(axis_align, input.mouse.position, tool_data.drag_start);
let total_mouse_delta_document = document.metadata.document_to_viewport.inverse().transform_vector2(mouse_position - tool_data.drag_start);
let snap_data = SnapData::ignore(document, input, &tool_data.layers_dragging);
// 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_target(layer).is_some());
let ignore = tool_data.non_duplicated_layers.as_ref().filter(|_| !layers_exist).unwrap_or(&tool_data.layers_dragging);
let snap_data = SnapData::ignore(document, input, ignore);
let mouse_delta_document = document.metadata.document_to_viewport.inverse().transform_vector2(mouse_position - tool_data.drag_current);
let mut offset = mouse_delta_document;
let mut best_snap = SnappedPoint::infinite_snap(document.metadata.document_to_viewport.inverse().transform_point2(mouse_position));
@ -627,22 +634,12 @@ impl Fsm for SelectToolFsmState {
}
tool_data.drag_current += mouse_delta;
// TODO: Reenable this feature after fixing it
if false {
if input.keyboard.key(duplicate) && tool_data.not_duplicated_layers.is_none() {
tool_data.start_duplicates(document, responses);
} else if !input.keyboard.key(duplicate) && tool_data.not_duplicated_layers.is_some() {
tool_data.stop_duplicates(document, responses);
}
}
SelectToolFsmState::Dragging
}
(SelectToolFsmState::ResizingBounds, SelectToolMessage::PointerMove { axis_align, center, .. }) => {
if let Some(bounds) = &mut tool_data.bounding_box_manager {
if let Some(movement) = &mut bounds.selected_edges {
let (_center, constrain) = (input.keyboard.key(center), input.keyboard.key(axis_align));
let center = false; // TODO: Reenable this feature after fixing it
let (center, constrain) = (input.keyboard.key(center), input.keyboard.key(axis_align));
let center = center.then_some(bounds.center_of_transformation);
let snap = Some(SizeSnapData {
@ -944,21 +941,32 @@ impl Fsm for SelectToolFsmState {
}
}
fn not_artboard<'a>(document: &'a DocumentMessageHandler) -> impl Fn(&LayerNodeIdentifier) -> bool + 'a {
|&layer| !document.metadata.is_artboard(layer)
}
fn drag_shallowest_manipulation(responses: &mut VecDeque<Message>, selected: Vec<LayerNodeIdentifier>, tool_data: &mut SelectToolData, document: &DocumentMessageHandler) {
let layer = selected[0];
let ancestor = layer
.ancestors(document.metadata())
.find(|&ancestor| document.selected_nodes.selected_layers_contains(ancestor, document.metadata()));
for layer in selected {
let ancestor = layer
.ancestors(document.metadata())
.filter(not_artboard(document))
.find(|&ancestor| document.selected_nodes.selected_layers_contains(ancestor, document.metadata()));
let new_selected = ancestor.unwrap_or_else(|| layer.child_of_root(document.metadata()));
let new_selected = ancestor.unwrap_or_else(|| {
layer
.ancestors(document.metadata())
.take_while(|&layer| layer != LayerNodeIdentifier::ROOT)
.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);
}
tool_data.layers_dragging = vec![new_selected];
responses.add(NodeGraphMessage::SelectedNodesSet {
nodes: tool_data.layers_dragging.iter().map(|layer| layer.to_node()).collect(),
});
// tool_data
// .snap_manager
// .start_snap(document, input, document.bounding_boxes(Some(&tool_data.layers_dragging), None, font_cache), true, true);
}
fn drag_deepest_manipulation(responses: &mut VecDeque<Message>, mut selected: Vec<LayerNodeIdentifier>, tool_data: &mut SelectToolData) {
@ -966,9 +974,6 @@ fn drag_deepest_manipulation(responses: &mut VecDeque<Message>, mut selected: Ve
responses.add(NodeGraphMessage::SelectedNodesSet {
nodes: tool_data.layers_dragging.iter().map(|layer| layer.to_node()).collect(),
});
// tool_data
// .snap_manager
// .start_snap(document, input, document.bounding_boxes(Some(&tool_data.layers_dragging), None, font_cache), true, true);
}
fn edit_layer_shallowest_manipulation(document: &DocumentMessageHandler, layer: LayerNodeIdentifier, responses: &mut VecDeque<Message>) {
@ -977,7 +982,7 @@ fn edit_layer_shallowest_manipulation(document: &DocumentMessageHandler, layer:
return;
}
let Some(new_selected) = layer.ancestors(document.metadata()).find(|ancestor| {
let Some(new_selected) = layer.ancestors(document.metadata()).filter(not_artboard(document)).find(|ancestor| {
ancestor
.parent(document.metadata())
.is_some_and(|parent| document.selected_nodes.selected_layers_contains(parent, document.metadata()))