mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-03 03:28:11 +08:00
Add "Grid" to the Shape tool along with row/column gizmos (#2921)
* integrated grid shape in shape-tool * add overlays,detection,transform for gizmo * fix compile issues * handle negative correctly,fix undo redo and abort * fix missed merge conflicts * fixed mouse cursor,correctly translatiing * cleanup * fix click-target area inside rect and spacing * add 10px closer to gizmo line * resolved conflicts * Code review --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
co-authored by
Keavon Chambers
parent
65171a5b8e
commit
3e50d177b7
@@ -3,15 +3,14 @@ use crate::consts::{DEFAULT_STROKE_WIDTH, SNAP_POINT_TOLERANCE};
|
||||
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
|
||||
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::network_interface::InputConnector;
|
||||
use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
|
||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
||||
use crate::messages::tool::common_functionality::gizmos::gizmo_manager::GizmoManager;
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils::NodeGraphLayer;
|
||||
use crate::messages::tool::common_functionality::resize::Resize;
|
||||
use crate::messages::tool::common_functionality::shapes::arc_shape::Arc;
|
||||
use crate::messages::tool::common_functionality::shapes::circle_shape::Circle;
|
||||
use crate::messages::tool::common_functionality::shapes::grid_shape::Grid;
|
||||
use crate::messages::tool::common_functionality::shapes::line_shape::{LineToolData, clicked_on_line_endpoints};
|
||||
use crate::messages::tool::common_functionality::shapes::polygon_shape::Polygon;
|
||||
use crate::messages::tool::common_functionality::shapes::shape_utility::{ShapeToolModifierKey, ShapeType, anchor_overlays, transform_cage_overlays};
|
||||
@@ -20,11 +19,10 @@ use crate::messages::tool::common_functionality::shapes::{Ellipse, Line, Rectang
|
||||
use crate::messages::tool::common_functionality::snapping::{self, SnapCandidatePoint, SnapData, SnapTypeConfiguration};
|
||||
use crate::messages::tool::common_functionality::transformation_cage::{BoundingBoxManager, EdgeBool};
|
||||
use crate::messages::tool::common_functionality::utility_functions::{closest_point, resize_bounds, rotate_bounds, skew_bounds, transforming_transform_cage};
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::{NodeId, NodeInput};
|
||||
use graph_craft::document::NodeId;
|
||||
use graphene_std::Color;
|
||||
use graphene_std::renderer::Quad;
|
||||
use graphene_std::vector::misc::ArcType;
|
||||
use graphene_std::vector::misc::{ArcType, GridType};
|
||||
use std::vec;
|
||||
|
||||
#[derive(Default, ExtractField)]
|
||||
@@ -41,6 +39,7 @@ pub struct ShapeToolOptions {
|
||||
vertices: u32,
|
||||
shape_type: ShapeType,
|
||||
arc_type: ArcType,
|
||||
grid_type: GridType,
|
||||
}
|
||||
|
||||
impl Default for ShapeToolOptions {
|
||||
@@ -52,6 +51,7 @@ impl Default for ShapeToolOptions {
|
||||
vertices: 5,
|
||||
shape_type: ShapeType::Polygon,
|
||||
arc_type: ArcType::Open,
|
||||
grid_type: GridType::Rectangular,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,7 @@ pub enum ShapeOptionsUpdate {
|
||||
Vertices(u32),
|
||||
ShapeType(ShapeType),
|
||||
ArcType(ArcType),
|
||||
GridType(GridType),
|
||||
}
|
||||
|
||||
#[impl_message(Message, ToolMessage, Shape)]
|
||||
@@ -134,6 +135,12 @@ fn create_shape_option_widget(shape_type: ShapeType) -> WidgetHolder {
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
MenuListEntry::new("Grid").label("Grid").on_commit(move |_| {
|
||||
ShapeToolMessage::UpdateOptions {
|
||||
options: ShapeOptionsUpdate::ShapeType(ShapeType::Grid),
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
]];
|
||||
DropdownInput::new(entries).selected_index(Some(shape_type as u32)).widget_holder()
|
||||
}
|
||||
@@ -177,6 +184,24 @@ fn create_weight_widget(line_weight: f64) -> WidgetHolder {
|
||||
.widget_holder()
|
||||
}
|
||||
|
||||
fn create_grid_type_widget(grid_type: GridType) -> WidgetHolder {
|
||||
let entries = vec![
|
||||
RadioEntryData::new("Rectangular").label("Rectangular").on_update(move |_| {
|
||||
ShapeToolMessage::UpdateOptions {
|
||||
options: ShapeOptionsUpdate::GridType(GridType::Rectangular),
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
RadioEntryData::new("Isometric").label("Isometric").on_update(move |_| {
|
||||
ShapeToolMessage::UpdateOptions {
|
||||
options: ShapeOptionsUpdate::GridType(GridType::Isometric),
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
];
|
||||
RadioInput::new(entries).selected_index(Some(grid_type as u32)).widget_holder()
|
||||
}
|
||||
|
||||
impl LayoutHolder for ShapeTool {
|
||||
fn layout(&self) -> Layout {
|
||||
let mut widgets = vec![];
|
||||
@@ -196,6 +221,11 @@ impl LayoutHolder for ShapeTool {
|
||||
}
|
||||
}
|
||||
|
||||
if self.options.shape_type == ShapeType::Grid {
|
||||
widgets.push(create_grid_type_widget(self.options.grid_type));
|
||||
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||
}
|
||||
|
||||
if self.options.shape_type != ShapeType::Line {
|
||||
widgets.append(&mut self.options.fill.create_widgets(
|
||||
"Fill",
|
||||
@@ -297,6 +327,9 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Shap
|
||||
ShapeOptionsUpdate::ArcType(arc_type) => {
|
||||
self.options.arc_type = arc_type;
|
||||
}
|
||||
ShapeOptionsUpdate::GridType(grid_type) => {
|
||||
self.options.grid_type = grid_type;
|
||||
}
|
||||
}
|
||||
|
||||
update_dynamic_hints(&self.fsm_state, responses, &self.tool_data);
|
||||
@@ -527,10 +560,11 @@ impl Fsm for ShapeToolFsmState {
|
||||
if is_skewing || (dragging_bounds && is_near_square && !hovering_over_gizmo) {
|
||||
bounds.render_skew_gizmos(&mut overlay_context, tool_data.skew_edge);
|
||||
}
|
||||
if !is_skewing && dragging_bounds && !hovering_over_gizmo {
|
||||
if let Some(edges) = edges {
|
||||
tool_data.skew_edge = bounds.get_closest_edge(edges, input.mouse.position);
|
||||
}
|
||||
if dragging_bounds
|
||||
&& !is_skewing && !hovering_over_gizmo
|
||||
&& let Some(edges) = edges
|
||||
{
|
||||
tool_data.skew_edge = bounds.get_closest_edge(edges, input.mouse.position);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -595,64 +629,12 @@ impl Fsm for ShapeToolFsmState {
|
||||
self
|
||||
}
|
||||
(ShapeToolFsmState::Drawing(_), ShapeToolMessage::IncreaseSides) => {
|
||||
if let Some(layer) = tool_data.data.layer {
|
||||
let Some(node_id) = graph_modification_utils::get_polygon_id(layer, &document.network_interface).or(graph_modification_utils::get_star_id(layer, &document.network_interface))
|
||||
else {
|
||||
return self;
|
||||
};
|
||||
|
||||
let Some(node_inputs) = NodeGraphLayer::new(layer, &document.network_interface)
|
||||
.find_node_inputs("Regular Polygon")
|
||||
.or(NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs("Star"))
|
||||
else {
|
||||
return self;
|
||||
};
|
||||
|
||||
let Some(&TaggedValue::U32(n)) = node_inputs.get(1).unwrap().as_value() else {
|
||||
return self;
|
||||
};
|
||||
|
||||
responses.add(ShapeToolMessage::UpdateOptions {
|
||||
options: ShapeOptionsUpdate::Vertices(n + 1),
|
||||
});
|
||||
|
||||
responses.add(NodeGraphMessage::SetInput {
|
||||
input_connector: InputConnector::node(node_id, 1),
|
||||
input: NodeInput::value(TaggedValue::U32(n + 1), false),
|
||||
});
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
}
|
||||
Polygon::increase_decrease_sides(true, document, tool_data, responses);
|
||||
|
||||
self
|
||||
}
|
||||
(ShapeToolFsmState::Drawing(_), ShapeToolMessage::DecreaseSides) => {
|
||||
if let Some(layer) = tool_data.data.layer {
|
||||
let Some(node_id) = graph_modification_utils::get_polygon_id(layer, &document.network_interface).or(graph_modification_utils::get_star_id(layer, &document.network_interface))
|
||||
else {
|
||||
return self;
|
||||
};
|
||||
|
||||
let Some(node_inputs) = NodeGraphLayer::new(layer, &document.network_interface)
|
||||
.find_node_inputs("Regular Polygon")
|
||||
.or(NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs("Star"))
|
||||
else {
|
||||
return self;
|
||||
};
|
||||
|
||||
let Some(&TaggedValue::U32(n)) = node_inputs.get(1).unwrap().as_value() else {
|
||||
return self;
|
||||
};
|
||||
|
||||
responses.add(ShapeToolMessage::UpdateOptions {
|
||||
options: ShapeOptionsUpdate::Vertices((n - 1).max(3)),
|
||||
});
|
||||
|
||||
responses.add(NodeGraphMessage::SetInput {
|
||||
input_connector: InputConnector::node(node_id, 1),
|
||||
input: NodeInput::value(TaggedValue::U32((n - 1).max(3)), false),
|
||||
});
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
}
|
||||
Polygon::increase_decrease_sides(false, document, tool_data, responses);
|
||||
|
||||
self
|
||||
}
|
||||
@@ -681,6 +663,8 @@ impl Fsm for ShapeToolFsmState {
|
||||
modifier: ShapeToolData::shape_tool_modifier_keys(),
|
||||
});
|
||||
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
|
||||
return ShapeToolFsmState::ModifyingGizmo;
|
||||
}
|
||||
|
||||
@@ -692,10 +676,10 @@ impl Fsm for ShapeToolFsmState {
|
||||
document.network_interface.selected_nodes().selected_visible_and_unlocked_layers(&document.network_interface),
|
||||
|_| false,
|
||||
preferences,
|
||||
) {
|
||||
if clicked_on_line_endpoints(layer, document, input, tool_data) && !input.keyboard.key(Key::Control) {
|
||||
return ShapeToolFsmState::DraggingLineEndpoints;
|
||||
}
|
||||
) && clicked_on_line_endpoints(layer, document, input, tool_data)
|
||||
&& !input.keyboard.key(Key::Control)
|
||||
{
|
||||
return ShapeToolFsmState::DraggingLineEndpoints;
|
||||
}
|
||||
|
||||
let (resize, rotate, skew) = transforming_transform_cage(document, &mut tool_data.bounding_box_manager, input, responses, &mut tool_data.layers_dragging, None);
|
||||
@@ -735,7 +719,7 @@ impl Fsm for ShapeToolFsmState {
|
||||
};
|
||||
|
||||
match tool_data.current_shape {
|
||||
ShapeType::Polygon | ShapeType::Star | ShapeType::Circle | ShapeType::Arc | ShapeType::Rectangle | ShapeType::Ellipse => tool_data.data.start(document, input),
|
||||
ShapeType::Polygon | ShapeType::Star | ShapeType::Circle | ShapeType::Arc | ShapeType::Grid | ShapeType::Rectangle | ShapeType::Ellipse => tool_data.data.start(document, input),
|
||||
ShapeType::Line => {
|
||||
let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
|
||||
let snapped = tool_data.data.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default());
|
||||
@@ -750,6 +734,7 @@ impl Fsm for ShapeToolFsmState {
|
||||
ShapeType::Star => Star::create_node(tool_options.vertices),
|
||||
ShapeType::Circle => Circle::create_node(),
|
||||
ShapeType::Arc => Arc::create_node(tool_options.arc_type),
|
||||
ShapeType::Grid => Grid::create_node(tool_options.grid_type),
|
||||
ShapeType::Rectangle => Rectangle::create_node(),
|
||||
ShapeType::Ellipse => Ellipse::create_node(),
|
||||
ShapeType::Line => Line::create_node(document, tool_data.data.drag_start),
|
||||
@@ -761,7 +746,7 @@ impl Fsm for ShapeToolFsmState {
|
||||
let defered_responses = &mut VecDeque::new();
|
||||
|
||||
match tool_data.current_shape {
|
||||
ShapeType::Polygon | ShapeType::Star | ShapeType::Circle | ShapeType::Arc | ShapeType::Rectangle | ShapeType::Ellipse => {
|
||||
ShapeType::Polygon | ShapeType::Star | ShapeType::Circle | ShapeType::Arc | ShapeType::Grid | ShapeType::Rectangle | ShapeType::Ellipse => {
|
||||
defered_responses.add(GraphOperationMessage::TransformSet {
|
||||
layer,
|
||||
transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., input.mouse.position),
|
||||
@@ -798,6 +783,7 @@ impl Fsm for ShapeToolFsmState {
|
||||
ShapeType::Star => Star::update_shape(document, input, layer, tool_data, modifier, responses),
|
||||
ShapeType::Circle => Circle::update_shape(document, input, layer, tool_data, modifier, responses),
|
||||
ShapeType::Arc => Arc::update_shape(document, input, layer, tool_data, modifier, responses),
|
||||
ShapeType::Grid => Grid::update_shape(document, input, layer, tool_options.grid_type, tool_data, modifier, responses),
|
||||
ShapeType::Rectangle => Rectangle::update_shape(document, input, layer, tool_data, modifier, responses),
|
||||
ShapeType::Ellipse => Ellipse::update_shape(document, input, layer, tool_data, modifier, responses),
|
||||
ShapeType::Line => Line::update_shape(document, input, layer, tool_data, modifier, responses),
|
||||
@@ -905,11 +891,11 @@ impl Fsm for ShapeToolFsmState {
|
||||
}
|
||||
(ShapeToolFsmState::ResizingBounds | ShapeToolFsmState::SkewingBounds { .. }, ShapeToolMessage::PointerOutsideViewport { .. }) => {
|
||||
// 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;
|
||||
bounds.original_bound_transform.translation += shift;
|
||||
}
|
||||
if let Some(shift) = tool_data.auto_panning.shift_viewport(input, responses)
|
||||
&& let Some(bounds) = &mut tool_data.bounding_box_manager
|
||||
{
|
||||
bounds.center_of_transformation += shift;
|
||||
bounds.original_bound_transform.translation += shift;
|
||||
}
|
||||
|
||||
self
|
||||
@@ -1039,6 +1025,11 @@ fn update_dynamic_hints(state: &ShapeToolFsmState, responses: &mut VecDeque<Mess
|
||||
HintInfo::keys([Key::Shift], "Constrain Arc").prepend_plus(),
|
||||
HintInfo::keys([Key::Alt], "From Center").prepend_plus(),
|
||||
])],
|
||||
ShapeType::Grid => vec![HintGroup(vec![
|
||||
HintInfo::mouse(MouseMotion::LmbDrag, "Draw Grid"),
|
||||
HintInfo::keys([Key::Shift], "Constrain Regular").prepend_plus(),
|
||||
HintInfo::keys([Key::Alt], "From Center").prepend_plus(),
|
||||
])],
|
||||
};
|
||||
HintData(hint_groups)
|
||||
}
|
||||
@@ -1048,6 +1039,7 @@ fn update_dynamic_hints(state: &ShapeToolFsmState, responses: &mut VecDeque<Mess
|
||||
ShapeType::Polygon | ShapeType::Star | ShapeType::Arc => HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain Regular"), HintInfo::keys([Key::Alt], "From Center")]),
|
||||
ShapeType::Rectangle => HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain Square"), HintInfo::keys([Key::Alt], "From Center")]),
|
||||
ShapeType::Ellipse => HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain Circular"), HintInfo::keys([Key::Alt], "From Center")]),
|
||||
ShapeType::Grid => HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain Regular"), HintInfo::keys([Key::Alt], "From Center")]),
|
||||
ShapeType::Line => HintGroup(vec![
|
||||
HintInfo::keys([Key::Shift], "15° Increments"),
|
||||
HintInfo::keys([Key::Alt], "From Center"),
|
||||
|
||||
Reference in New Issue
Block a user