Make the drawing tools' Weight control sync with the selected layer (#4120)
This commit is contained in:
parent
9512f7df41
commit
1596469e92
|
|
@ -355,7 +355,20 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||
responses.add(NodeGraphMessage::DeleteSelectedNodes { delete_children: true });
|
||||
}
|
||||
NodeGraphMessage::DeleteNodes { node_ids, delete_children } => {
|
||||
// Detect stroke proto nodes among the doomed nodes before they're gone, so the stroke-using tools'
|
||||
// Weight widgets can re-read the layer (they'll now read 0 px since the stroke node is missing).
|
||||
let any_stroke_deleted = node_ids.iter().any(|node_id| {
|
||||
network_interface
|
||||
.reference(node_id, selection_network_path)
|
||||
.is_some_and(|reference| reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::stroke::IDENTIFIER))
|
||||
});
|
||||
network_interface.delete_nodes(node_ids, delete_children, selection_network_path);
|
||||
if any_stroke_deleted {
|
||||
responses.add(PenToolMessage::SelectionChanged);
|
||||
responses.add(FreehandToolMessage::SelectionChanged);
|
||||
responses.add(SplineToolMessage::SelectionChanged);
|
||||
responses.add(ShapeToolMessage::SelectionChanged);
|
||||
}
|
||||
}
|
||||
// Deletes selected_nodes. If `reconnect` is true, then all children nodes (secondary input) of the selected nodes are deleted and the siblings (primary input/output) are reconnected.
|
||||
// If `reconnect` is false, then only the selected nodes are deleted and not reconnected.
|
||||
|
|
@ -1728,9 +1741,9 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||
}
|
||||
NodeGraphMessage::SetInputValue { node_id, input_index, value } => {
|
||||
let is_fill = matches!(value, TaggedValue::Fill(_));
|
||||
let is_text_node = network_interface
|
||||
.reference(&node_id, selection_network_path)
|
||||
.is_some_and(|reference| reference == DefinitionIdentifier::ProtoNode(graphene_std::text::text::IDENTIFIER));
|
||||
let reference = network_interface.reference(&node_id, selection_network_path);
|
||||
let is_text_node = reference.as_ref().is_some_and(|r| *r == DefinitionIdentifier::ProtoNode(graphene_std::text::text::IDENTIFIER));
|
||||
let is_stroke_node = reference.as_ref().is_some_and(|r| *r == DefinitionIdentifier::ProtoNode(graphene_std::vector::stroke::IDENTIFIER));
|
||||
let input = NodeInput::value(value, false);
|
||||
responses.add(NodeGraphMessage::SetInput {
|
||||
input_connector: InputConnector::node(node_id, input_index),
|
||||
|
|
@ -1743,6 +1756,13 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||
if is_text_node {
|
||||
responses.add(TextToolMessage::SelectionChanged);
|
||||
}
|
||||
if is_stroke_node {
|
||||
// The dispatcher delivers each only to its tool when active, so this just covers all four stroke-using tools.
|
||||
responses.add(PenToolMessage::SelectionChanged);
|
||||
responses.add(FreehandToolMessage::SelectionChanged);
|
||||
responses.add(SplineToolMessage::SelectionChanged);
|
||||
responses.add(ShapeToolMessage::SelectionChanged);
|
||||
}
|
||||
if network_interface.connected_to_output(&node_id, selection_network_path) {
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -493,6 +493,39 @@ pub fn get_stroke_width(layer: LayerNodeIdentifier, network_interface: &NodeNetw
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the node ID of a layer's upstream Stroke proto node, if one exists.
|
||||
pub fn get_stroke_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
|
||||
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name(&DefinitionIdentifier::ProtoNode(graphene_std::vector::stroke::IDENTIFIER))
|
||||
}
|
||||
|
||||
/// Stroke weight of the first selected non-artboard layer, used by tool control bars to mirror the selection's weight.
|
||||
/// Returns `Some(0.)` if the layer has no Stroke node so the widget reads "0 px", and `None` only when no layer is selected.
|
||||
pub fn first_selected_stroke_weight(document: &DocumentMessageHandler) -> Option<f64> {
|
||||
document
|
||||
.network_interface
|
||||
.selected_nodes()
|
||||
.selected_layers_except_artboards(&document.network_interface)
|
||||
.next()
|
||||
.map(|layer| get_stroke_width(layer, &document.network_interface).unwrap_or(0.))
|
||||
}
|
||||
|
||||
/// Writes the weight back to every selected non-artboard layer's stroke. Layers with an existing stroke just have their
|
||||
/// `WeightInput` updated; layers without one get a fresh stroke node added (defaulting to a black stroke with the new
|
||||
/// weight) only when the new weight is nonzero, so changing back to 0 doesn't keep adding empty strokes.
|
||||
pub fn set_stroke_weight_for_selected_layers(weight: f64, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
|
||||
let layers: Vec<_> = document.network_interface.selected_nodes().selected_layers_except_artboards(&document.network_interface).collect();
|
||||
for layer in layers {
|
||||
if let Some(node_id) = get_stroke_id(layer, &document.network_interface) {
|
||||
let input_index = graphene_std::vector::stroke::WeightInput::INDEX;
|
||||
let value = TaggedValue::F64(weight);
|
||||
responses.add(NodeGraphMessage::SetInputValue { node_id, input_index, value });
|
||||
} else if weight > 0. {
|
||||
let stroke = graphene_std::vector::style::Stroke::default().with_weight(weight);
|
||||
responses.add(GraphOperationMessage::StrokeSet { layer, stroke });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if a specified layer uses an upstream node matching the given name.
|
||||
pub fn is_layer_fed_by_node_of_name(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface, identifier: &DefinitionIdentifier) -> bool {
|
||||
NodeGraphLayer::new(layer, network_interface).find_node_inputs(identifier).is_some()
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ pub enum FreehandToolMessage {
|
|||
// Standard messages
|
||||
Overlays { context: OverlayContext },
|
||||
Abort,
|
||||
SelectionChanged,
|
||||
WorkingColorChanged,
|
||||
|
||||
// Tool-specific messages
|
||||
|
|
@ -161,6 +162,16 @@ impl LayoutHolder for FreehandTool {
|
|||
#[message_handler_data]
|
||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for FreehandTool {
|
||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, context: &mut ToolActionMessageContext<'a>) {
|
||||
if matches!(&message, ToolMessage::Freehand(FreehandToolMessage::SelectionChanged)) {
|
||||
if let Some(weight) = graph_modification_utils::first_selected_stroke_weight(context.document)
|
||||
&& self.options.line_weight != weight
|
||||
{
|
||||
self.options.line_weight = weight;
|
||||
self.send_layout(responses, LayoutTarget::ToolOptions);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let ToolMessage::Freehand(FreehandToolMessage::UpdateOptions { options }) = message else {
|
||||
self.fsm_state.process_event(message, &mut self.data, context, &self.options, responses, true);
|
||||
return;
|
||||
|
|
@ -171,7 +182,10 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Free
|
|||
self.options.fill.color_type = ToolColorType::Custom;
|
||||
}
|
||||
FreehandOptionsUpdate::FillColorType(color_type) => self.options.fill.color_type = color_type,
|
||||
FreehandOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
||||
FreehandOptionsUpdate::LineWeight(line_weight) => {
|
||||
self.options.line_weight = line_weight;
|
||||
graph_modification_utils::set_stroke_weight_for_selected_layers(line_weight, context.document, responses);
|
||||
}
|
||||
FreehandOptionsUpdate::StrokeColor(color) => {
|
||||
self.options.stroke.custom_color = color;
|
||||
self.options.stroke.color_type = ToolColorType::Custom;
|
||||
|
|
@ -208,6 +222,7 @@ impl ToolTransition for FreehandTool {
|
|||
EventToMessageMap {
|
||||
overlay_provider: Some(|context: OverlayContext| FreehandToolMessage::Overlays { context }.into()),
|
||||
tool_abort: Some(FreehandToolMessage::Abort.into()),
|
||||
selection_changed: Some(FreehandToolMessage::SelectionChanged.into()),
|
||||
working_color_changed: Some(FreehandToolMessage::WorkingColorChanged.into()),
|
||||
..Default::default()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -249,6 +249,14 @@ impl LayoutHolder for PenTool {
|
|||
#[message_handler_data]
|
||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for PenTool {
|
||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, context: &mut ToolActionMessageContext<'a>) {
|
||||
if matches!(&message, ToolMessage::Pen(PenToolMessage::SelectionChanged))
|
||||
&& let Some(weight) = graph_modification_utils::first_selected_stroke_weight(context.document)
|
||||
&& self.options.line_weight != weight
|
||||
{
|
||||
self.options.line_weight = weight;
|
||||
self.send_layout(responses, LayoutTarget::ToolOptions);
|
||||
}
|
||||
|
||||
let ToolMessage::Pen(PenToolMessage::UpdateOptions { options }) = message else {
|
||||
self.fsm_state.process_event(message, &mut self.tool_data, context, &self.options, responses, true);
|
||||
return;
|
||||
|
|
@ -259,7 +267,10 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for PenT
|
|||
self.options.pen_overlay_mode = overlay_mode_type;
|
||||
responses.add(OverlaysMessage::Draw);
|
||||
}
|
||||
PenOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
||||
PenOptionsUpdate::LineWeight(line_weight) => {
|
||||
self.options.line_weight = line_weight;
|
||||
graph_modification_utils::set_stroke_weight_for_selected_layers(line_weight, context.document, responses);
|
||||
}
|
||||
PenOptionsUpdate::FillColor(color) => {
|
||||
self.options.fill.custom_color = color;
|
||||
self.options.fill.color_type = ToolColorType::Custom;
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ pub enum ShapeToolMessage {
|
|||
// Standard messages
|
||||
Overlays { context: OverlayContext },
|
||||
Abort,
|
||||
SelectionChanged,
|
||||
WorkingColorChanged,
|
||||
|
||||
// Tool-specific messages
|
||||
|
|
@ -415,6 +416,16 @@ impl LayoutHolder for ShapeTool {
|
|||
#[message_handler_data]
|
||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for ShapeTool {
|
||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, context: &mut ToolActionMessageContext<'a>) {
|
||||
if matches!(&message, ToolMessage::Shape(ShapeToolMessage::SelectionChanged)) {
|
||||
if let Some(weight) = graph_modification_utils::first_selected_stroke_weight(context.document)
|
||||
&& self.options.line_weight != weight
|
||||
{
|
||||
self.options.line_weight = weight;
|
||||
self.send_layout(responses, LayoutTarget::ToolOptions);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let ToolMessage::Shape(ShapeToolMessage::UpdateOptions { options }) = message else {
|
||||
self.fsm_state.process_event(message, &mut self.tool_data, context, &self.options, responses, true);
|
||||
return;
|
||||
|
|
@ -429,6 +440,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Shap
|
|||
}
|
||||
ShapeOptionsUpdate::LineWeight(line_weight) => {
|
||||
self.options.line_weight = line_weight;
|
||||
graph_modification_utils::set_stroke_weight_for_selected_layers(line_weight, context.document, responses);
|
||||
}
|
||||
ShapeOptionsUpdate::StrokeColor(color) => {
|
||||
self.options.stroke.custom_color = color;
|
||||
|
|
@ -527,6 +539,7 @@ impl ToolTransition for ShapeTool {
|
|||
EventToMessageMap {
|
||||
overlay_provider: Some(|context| ShapeToolMessage::Overlays { context }.into()),
|
||||
tool_abort: Some(ShapeToolMessage::Abort.into()),
|
||||
selection_changed: Some(ShapeToolMessage::SelectionChanged.into()),
|
||||
working_color_changed: Some(ShapeToolMessage::WorkingColorChanged.into()),
|
||||
..Default::default()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ pub enum SplineToolMessage {
|
|||
Overlays { context: OverlayContext },
|
||||
CanvasTransformed,
|
||||
Abort,
|
||||
SelectionChanged,
|
||||
WorkingColorChanged,
|
||||
|
||||
// Tool-specific messages
|
||||
|
|
@ -168,12 +169,25 @@ impl LayoutHolder for SplineTool {
|
|||
#[message_handler_data]
|
||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for SplineTool {
|
||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, context: &mut ToolActionMessageContext<'a>) {
|
||||
if matches!(&message, ToolMessage::Spline(SplineToolMessage::SelectionChanged)) {
|
||||
if let Some(weight) = graph_modification_utils::first_selected_stroke_weight(context.document)
|
||||
&& self.options.line_weight != weight
|
||||
{
|
||||
self.options.line_weight = weight;
|
||||
self.send_layout(responses, LayoutTarget::ToolOptions);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let ToolMessage::Spline(SplineToolMessage::UpdateOptions { options }) = message else {
|
||||
self.fsm_state.process_event(message, &mut self.tool_data, context, &self.options, responses, true);
|
||||
return;
|
||||
};
|
||||
match options {
|
||||
SplineOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight,
|
||||
SplineOptionsUpdate::LineWeight(line_weight) => {
|
||||
self.options.line_weight = line_weight;
|
||||
graph_modification_utils::set_stroke_weight_for_selected_layers(line_weight, context.document, responses);
|
||||
}
|
||||
SplineOptionsUpdate::FillColor(color) => {
|
||||
self.options.fill.custom_color = color;
|
||||
self.options.fill.color_type = ToolColorType::Custom;
|
||||
|
|
@ -224,8 +238,8 @@ impl ToolTransition for SplineTool {
|
|||
overlay_provider: Some(|context: OverlayContext| SplineToolMessage::Overlays { context }.into()),
|
||||
canvas_transformed: Some(SplineToolMessage::CanvasTransformed.into()),
|
||||
tool_abort: Some(SplineToolMessage::Abort.into()),
|
||||
selection_changed: Some(SplineToolMessage::SelectionChanged.into()),
|
||||
working_color_changed: Some(SplineToolMessage::WorkingColorChanged.into()),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,9 +99,9 @@ impl ToolMetadata for TextTool {
|
|||
}
|
||||
|
||||
fn create_text_widgets(tool: &TextTool, font_catalog: &FontCatalog, document: &DocumentMessageHandler) -> Vec<WidgetInstance> {
|
||||
// If a single text layer is selected, the toolbar's font/style menus drive that layer's text node directly, going through the
|
||||
// If a single text layer is selected, the control bar's font/style menus drive that layer's text node directly, going through the
|
||||
// same code path as the Properties panel (LoadFontData + SetInputValue, with closest_style and font_style_to_restore bookkeeping).
|
||||
// Otherwise the menus only update the toolbar option for the next created text.
|
||||
// Otherwise the menus only update the control bar option for the next created text.
|
||||
let text_node_id = can_edit_selected(document).and_then(|layer| graph_modification_utils::get_text_id(layer, &document.network_interface));
|
||||
|
||||
let font_input_index = graphene_std::text::text::FontInput::INDEX;
|
||||
|
|
@ -324,8 +324,8 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Text
|
|||
};
|
||||
match options {
|
||||
TextOptionsUpdate::Font { font } => {
|
||||
// The toolbar font/style menus go through `SetInputValue` directly when a text layer is selected, so this
|
||||
// arm only fires when no layer is selected (toolbar font is just the default for the next-created text).
|
||||
// The control bar font/style menus go through `SetInputValue` directly when a text layer is selected, so this
|
||||
// arm only fires when no layer is selected (control bar font is just the default for the next-created text).
|
||||
self.options.font = font.clone();
|
||||
if let Some(editing_text) = self.tool_data.editing_text.as_mut() {
|
||||
editing_text.font = font;
|
||||
|
|
|
|||
Loading…
Reference in New Issue