mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-30 07:18:11 +08:00
* Fix text tool * Implement buffering to fix freehand tool * Fix tools * Fix clippy lints * Small fixes * Move vector modify back to Monitor nodes * Code review * Fix abort * Fix svg import --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
co-authored by
Keavon Chambers
parent
c738b4a1f9
commit
20470b566b
+2
-1
@@ -30,11 +30,12 @@ impl MessageHandler<NewDocumentDialogMessage, ()> for NewDocumentDialogMessageHa
|
||||
id: NodeId(generate_uuid()),
|
||||
artboard: graphene_core::Artboard::new(IVec2::ZERO, self.dimensions.as_ivec2()),
|
||||
});
|
||||
responses.add(FrontendMessage::TriggerDelayedZoomCanvasToFitAll);
|
||||
}
|
||||
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
responses.add(NodeGraphMessage::UpdateNewNodeGraph);
|
||||
responses.add(Message::StartBuffer);
|
||||
responses.add(FrontendMessage::TriggerDelayedZoomCanvasToFitAll);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ pub enum Message {
|
||||
NoOp,
|
||||
Init,
|
||||
Batched(Box<[Message]>),
|
||||
StartBuffer,
|
||||
EndBuffer(graphene_std::renderer::RenderMetadata),
|
||||
|
||||
#[child]
|
||||
Broadcast(BroadcastMessage),
|
||||
|
||||
@@ -1028,7 +1028,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
||||
return;
|
||||
}
|
||||
|
||||
self.document_undo_history.pop_back();
|
||||
self.undo(ipp, responses);
|
||||
self.network_interface.finish_transaction();
|
||||
responses.add(OverlaysMessage::Draw);
|
||||
}
|
||||
|
||||
+2
-3
@@ -67,10 +67,8 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
|
||||
transform_in,
|
||||
skip_rerender,
|
||||
} => {
|
||||
let parent_transform = network_interface.document_metadata().downstream_transform_to_viewport(layer);
|
||||
let current_transform = Some(network_interface.document_metadata().transform_to_viewport(layer));
|
||||
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, network_interface, responses) {
|
||||
modify_inputs.transform_set(transform, transform_in, parent_transform, current_transform, skip_rerender);
|
||||
modify_inputs.transform_set(transform, transform_in, skip_rerender);
|
||||
}
|
||||
}
|
||||
GraphOperationMessage::TransformSetPivot { layer, pivot } => {
|
||||
@@ -191,6 +189,7 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
|
||||
let layer = modify_inputs.create_layer(id);
|
||||
modify_inputs.insert_text(text, font, size, layer);
|
||||
network_interface.move_layer_to_stack(layer, parent, insert_index, &[]);
|
||||
responses.add(GraphOperationMessage::StrokeSet { layer, stroke: Stroke::default() });
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
}
|
||||
GraphOperationMessage::ResizeArtboard { layer, location, dimensions } => {
|
||||
|
||||
@@ -15,6 +15,7 @@ use graphene_core::vector::{PointId, VectorModificationType};
|
||||
use graphene_core::{Artboard, Color};
|
||||
|
||||
use glam::{DAffine2, DVec2, IVec2};
|
||||
use graphene_std::vector::VectorData;
|
||||
|
||||
#[derive(PartialEq, Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub enum TransformIn {
|
||||
@@ -145,9 +146,12 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
}
|
||||
|
||||
pub fn insert_vector_data(&mut self, subpaths: Vec<Subpath<PointId>>, layer: LayerNodeIdentifier) {
|
||||
let vector_data = VectorData::from_subpaths(subpaths, true);
|
||||
|
||||
let path = resolve_document_node_type("Path")
|
||||
.expect("Path node does not exist")
|
||||
.node_template_input_override([Some(NodeInput::value(TaggedValue::Subpaths(subpaths), false))]);
|
||||
.node_template_input_override([Some(NodeInput::value(TaggedValue::VectorData(vector_data), false))]);
|
||||
|
||||
let transform = resolve_document_node_type("Transform").expect("Transform node does not exist").default_node_template();
|
||||
let fill = resolve_document_node_type("Fill").expect("Fill node does not exist").default_node_template();
|
||||
let stroke = resolve_document_node_type("Stroke").expect("Stroke node does not exist").default_node_template();
|
||||
@@ -195,8 +199,6 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
let stroke_id = NodeId(generate_uuid());
|
||||
self.network_interface.insert_node(stroke_id, stroke, &[]);
|
||||
self.network_interface.move_node_to_chain_start(&stroke_id, layer, &[]);
|
||||
|
||||
self.responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
}
|
||||
|
||||
pub fn insert_image_data(&mut self, image_frame: ImageFrame<Color>, layer: LayerNodeIdentifier) {
|
||||
@@ -334,22 +336,15 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn transform_set(&mut self, mut transform: DAffine2, transform_in: TransformIn, parent_transform: DAffine2, current_transform: Option<DAffine2>, skip_rerender: bool) {
|
||||
let Some(transform_node_id) = self.existing_node_id("Transform") else { return };
|
||||
let upstream_transform = self.network_interface.document_metadata().upstream_transform(transform_node_id);
|
||||
let to = match transform_in {
|
||||
TransformIn::Local => DAffine2::IDENTITY,
|
||||
TransformIn::Scope { scope } => scope * parent_transform,
|
||||
TransformIn::Viewport => parent_transform,
|
||||
pub fn transform_set(&mut self, transform: DAffine2, transform_in: TransformIn, skip_rerender: bool) {
|
||||
let final_transform = match transform_in {
|
||||
TransformIn::Local => DAffine2::IDENTITY * transform,
|
||||
TransformIn::Scope { scope } => scope * transform,
|
||||
TransformIn::Viewport => self.network_interface.document_metadata().downstream_transform_to_viewport(self.layer_node.unwrap()).inverse() * transform,
|
||||
};
|
||||
|
||||
if current_transform
|
||||
.filter(|transform| transform.matrix2.determinant() != 0. && upstream_transform.matrix2.determinant() != 0.)
|
||||
.is_some()
|
||||
{
|
||||
transform *= upstream_transform.inverse();
|
||||
}
|
||||
let final_transform = to.inverse() * transform;
|
||||
let Some(transform_node_id) = self.existing_node_id("Transform") else { return };
|
||||
|
||||
transform_utils::update_transform(self.network_interface, &transform_node_id, final_transform);
|
||||
|
||||
self.responses.add(PropertiesPanelMessage::Refresh);
|
||||
|
||||
@@ -196,6 +196,19 @@ impl ArtboardToolData {
|
||||
location: position.round().as_ivec2(),
|
||||
dimensions: size.round().as_ivec2(),
|
||||
});
|
||||
|
||||
// TODO: Resize artboard children when resizing left/top edges so that they stay in the same viewport space
|
||||
// let old_top_left = bounds.bounds[0].round().as_ivec2();
|
||||
// let new_top_left = position.round().as_ivec2();
|
||||
// let top_left_delta = new_top_left - old_top_left;
|
||||
// if top_left_delta != IVec2::ZERO {
|
||||
// responses.add(GraphOperationMessage::TransformChange {
|
||||
// layer: self.selected_artboard.unwrap(),
|
||||
// transform: DAffine2::from_translation((-top_left_delta).into()),
|
||||
// transform_in: TransformIn::Local,
|
||||
// skip_rerender: false,
|
||||
// });
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -206,6 +206,7 @@ impl Fsm for EllipseToolFsmState {
|
||||
let nodes = vec![(NodeId(0), node)];
|
||||
|
||||
let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, document.new_layer_parent(true), responses);
|
||||
responses.add(Message::StartBuffer);
|
||||
responses.add(GraphOperationMessage::TransformSet {
|
||||
layer,
|
||||
transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., input.mouse.position),
|
||||
|
||||
@@ -229,15 +229,11 @@ impl Fsm for FreehandToolFsmState {
|
||||
let nodes = vec![(NodeId(0), node)];
|
||||
|
||||
let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, parent, responses);
|
||||
responses.add(Message::StartBuffer);
|
||||
tool_options.fill.apply_fill(layer, responses);
|
||||
tool_options.stroke.apply_stroke(tool_data.weight, layer, responses);
|
||||
tool_data.layer = Some(layer);
|
||||
|
||||
let transform = document.metadata().transform_to_viewport(parent);
|
||||
let position = transform.inverse().transform_point2(input.mouse.position);
|
||||
|
||||
extend_path_with_next_segment(tool_data, position, responses);
|
||||
|
||||
FreehandToolFsmState::Drawing
|
||||
}
|
||||
(FreehandToolFsmState::Drawing, FreehandToolMessage::PointerMove) => {
|
||||
|
||||
@@ -188,6 +188,7 @@ impl Fsm for LineToolFsmState {
|
||||
let nodes = vec![(NodeId(0), node)];
|
||||
|
||||
let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, document.new_layer_parent(false), responses);
|
||||
responses.add(Message::StartBuffer);
|
||||
responses.add(GraphOperationMessage::TransformSet {
|
||||
layer,
|
||||
transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., input.mouse.position),
|
||||
|
||||
@@ -525,21 +525,11 @@ impl Fsm for PenToolFsmState {
|
||||
tool_data.layer = Some(layer);
|
||||
tool_data.next_point = position;
|
||||
tool_data.next_handle_start = position;
|
||||
} else {
|
||||
// New path layer
|
||||
let node_type = resolve_document_node_type("Path").expect("Path node does not exist");
|
||||
let nodes = vec![(NodeId(0), node_type.default_node_template())];
|
||||
|
||||
let parent = document.new_layer_parent(true);
|
||||
let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, parent, responses);
|
||||
tool_options.fill.apply_fill(layer, responses);
|
||||
tool_options.stroke.apply_stroke(tool_options.line_weight, layer, responses);
|
||||
tool_data.layer = Some(layer);
|
||||
|
||||
} else if let Some(layer) = tool_data.layer {
|
||||
// Add the first point to a new layer
|
||||
// Generate first point
|
||||
let id = PointId::generate();
|
||||
let transform = document.metadata().transform_to_document(parent);
|
||||
let pos = transform.inverse().transform_point2(snapped.snapped_point_document);
|
||||
let pos = document.metadata().transform_to_viewport(layer).inverse().transform_point2(viewport);
|
||||
let modification_type = VectorModificationType::InsertPoint { id, position: pos };
|
||||
responses.add(GraphOperationMessage::Vector { layer, modification_type });
|
||||
tool_data.add_point(LastPoint {
|
||||
@@ -550,9 +540,21 @@ impl Fsm for PenToolFsmState {
|
||||
});
|
||||
tool_data.next_point = pos;
|
||||
tool_data.next_handle_start = pos;
|
||||
} else {
|
||||
// New path layer
|
||||
let node_type = resolve_document_node_type("Path").expect("Path node does not exist");
|
||||
let nodes = vec![(NodeId(0), node_type.default_node_template())];
|
||||
|
||||
let parent = document.new_layer_parent(true);
|
||||
let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, parent, responses);
|
||||
tool_options.fill.apply_fill(layer, responses);
|
||||
tool_options.stroke.apply_stroke(tool_options.line_weight, layer, responses);
|
||||
tool_data.layer = Some(layer);
|
||||
responses.add(Message::StartBuffer);
|
||||
responses.add(PenToolMessage::DragStart);
|
||||
return PenToolFsmState::Ready;
|
||||
}
|
||||
tool_data.handle_end = None;
|
||||
|
||||
// Enter the dragging handle state while the mouse is held down, allowing the user to move the mouse and position the handle
|
||||
PenToolFsmState::DraggingHandle
|
||||
}
|
||||
|
||||
@@ -265,6 +265,7 @@ impl Fsm for PolygonToolFsmState {
|
||||
let nodes = vec![(NodeId(0), node)];
|
||||
|
||||
let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, document.new_layer_parent(false), responses);
|
||||
responses.add(Message::StartBuffer);
|
||||
responses.add(GraphOperationMessage::TransformSet {
|
||||
layer,
|
||||
transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., input.mouse.position),
|
||||
|
||||
@@ -212,6 +212,7 @@ impl Fsm for RectangleToolFsmState {
|
||||
let nodes = vec![(NodeId(0), node)];
|
||||
|
||||
let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, document.new_layer_parent(true), responses);
|
||||
responses.add(Message::StartBuffer);
|
||||
responses.add(GraphOperationMessage::TransformSet {
|
||||
layer,
|
||||
transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., input.mouse.position),
|
||||
|
||||
@@ -205,14 +205,6 @@ impl Fsm for SplineToolFsmState {
|
||||
responses.add(DocumentMessage::DeselectAllLayers);
|
||||
|
||||
let parent = document.new_layer_parent(true);
|
||||
let transform = document.metadata().transform_to_viewport(parent);
|
||||
|
||||
let snapped_position = input.mouse.position;
|
||||
|
||||
let pos = transform.inverse().transform_point2(snapped_position);
|
||||
|
||||
tool_data.points.push(pos);
|
||||
tool_data.next_point = pos;
|
||||
|
||||
tool_data.weight = tool_options.line_weight;
|
||||
|
||||
@@ -225,6 +217,8 @@ impl Fsm for SplineToolFsmState {
|
||||
tool_options.stroke.apply_stroke(tool_data.weight, layer, responses);
|
||||
tool_data.layer = Some(layer);
|
||||
|
||||
responses.add(Message::StartBuffer);
|
||||
|
||||
SplineToolFsmState::Drawing
|
||||
}
|
||||
(SplineToolFsmState::Drawing, SplineToolMessage::DragStop) => {
|
||||
@@ -237,11 +231,9 @@ impl Fsm for SplineToolFsmState {
|
||||
let transform = document.metadata().transform_to_viewport(layer);
|
||||
let pos = transform.inverse().transform_point2(snapped_position);
|
||||
|
||||
if let Some(last_pos) = tool_data.points.last() {
|
||||
if last_pos.distance(pos) > DRAG_THRESHOLD {
|
||||
tool_data.points.push(pos);
|
||||
tool_data.next_point = pos;
|
||||
}
|
||||
if tool_data.points.last().map_or(true, |last_pos| last_pos.distance(pos) > DRAG_THRESHOLD) {
|
||||
tool_data.points.push(pos);
|
||||
tool_data.next_point = pos;
|
||||
}
|
||||
|
||||
update_spline(document, tool_data, true, responses);
|
||||
|
||||
@@ -213,11 +213,7 @@ struct TextToolData {
|
||||
|
||||
impl TextToolData {
|
||||
/// Set the editing state of the currently modifying layer
|
||||
fn set_editing(&self, editable: bool, font_cache: &FontCache, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
|
||||
if let Some(node_id) = graph_modification_utils::get_fill_id(self.layer, &document.network_interface) {
|
||||
responses.add(NodeGraphMessage::SetVisibility { node_id, visible: !editable });
|
||||
}
|
||||
|
||||
fn set_editing(&self, editable: bool, font_cache: &FontCache, responses: &mut VecDeque<Message>) {
|
||||
if let Some(editing_text) = self.editing_text.as_ref().filter(|_| editable) {
|
||||
responses.add(FrontendMessage::DisplayEditableTextbox {
|
||||
text: editing_text.text.clone(),
|
||||
@@ -229,6 +225,8 @@ impl TextToolData {
|
||||
});
|
||||
} else {
|
||||
responses.add(FrontendMessage::DisplayRemoveEditableTextbox);
|
||||
// Clear all selected nodes when no longer editing
|
||||
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: Vec::new() });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,17 +251,23 @@ impl TextToolData {
|
||||
}
|
||||
|
||||
if tool_state == TextToolFsmState::Editing {
|
||||
self.set_editing(false, font_cache, document, responses);
|
||||
self.set_editing(false, font_cache, responses);
|
||||
}
|
||||
|
||||
self.layer = layer;
|
||||
self.load_layer_text_node(document);
|
||||
if self.load_layer_text_node(document).is_some() {
|
||||
responses.add(DocumentMessage::AddTransaction);
|
||||
|
||||
responses.add(DocumentMessage::AddTransaction);
|
||||
self.set_editing(true, font_cache, responses);
|
||||
|
||||
self.set_editing(true, font_cache, document, responses);
|
||||
|
||||
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![self.layer.to_node()] });
|
||||
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![self.layer.to_node()] });
|
||||
// Make the rendered text invisible while editing
|
||||
responses.add(NodeGraphMessage::SetInput {
|
||||
input_connector: InputConnector::node(graph_modification_utils::get_text_id(self.layer, &document.network_interface).unwrap(), 1),
|
||||
input: NodeInput::value(TaggedValue::String("".to_string()), false),
|
||||
});
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
};
|
||||
}
|
||||
|
||||
fn interact(
|
||||
@@ -294,6 +298,7 @@ impl TextToolData {
|
||||
parent: document.new_layer_parent(true),
|
||||
insert_index: 0,
|
||||
});
|
||||
responses.add(Message::StartBuffer);
|
||||
responses.add(GraphOperationMessage::FillSet {
|
||||
layer: self.layer,
|
||||
fill: if editing_text.color.is_some() { Fill::Solid(editing_text.color.unwrap()) } else { Fill::None },
|
||||
@@ -305,14 +310,15 @@ impl TextToolData {
|
||||
skip_rerender: true,
|
||||
});
|
||||
|
||||
self.set_editing(true, font_cache, document, responses);
|
||||
self.set_editing(true, font_cache, responses);
|
||||
|
||||
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![self.layer.to_node()] });
|
||||
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
TextToolFsmState::Editing
|
||||
} else {
|
||||
// Removing old text as editable
|
||||
self.set_editing(false, font_cache, document, responses);
|
||||
self.set_editing(false, font_cache, responses);
|
||||
|
||||
TextToolFsmState::Ready
|
||||
}
|
||||
@@ -404,7 +410,7 @@ impl Fsm for TextToolFsmState {
|
||||
}
|
||||
(state, TextToolMessage::Abort) => {
|
||||
if state == TextToolFsmState::Editing {
|
||||
tool_data.set_editing(false, font_cache, document, responses);
|
||||
tool_data.set_editing(false, font_cache, responses);
|
||||
}
|
||||
|
||||
TextToolFsmState::Ready
|
||||
@@ -415,12 +421,13 @@ impl Fsm for TextToolFsmState {
|
||||
TextToolFsmState::Editing
|
||||
}
|
||||
(TextToolFsmState::Editing, TextToolMessage::TextChange { new_text }) => {
|
||||
tool_data.set_editing(false, font_cache, responses);
|
||||
|
||||
responses.add(NodeGraphMessage::SetInput {
|
||||
input_connector: InputConnector::node(graph_modification_utils::get_text_id(tool_data.layer, &document.network_interface).unwrap(), 1),
|
||||
input: NodeInput::value(TaggedValue::String(new_text), false),
|
||||
});
|
||||
|
||||
tool_data.set_editing(false, font_cache, document, responses);
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
|
||||
TextToolFsmState::Ready
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user