From 99966d848d9c10982ea4f478d8b8b085c93fb879 Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Mon, 14 Jul 2025 14:36:39 -0700 Subject: [PATCH] Fix text per-glyph instance overlays and click target issues --- editor/src/messages/portfolio/document_migration.rs | 7 +++++-- .../common_functionality/graph_modification_utils.rs | 7 +++++-- .../tool/common_functionality/utility_functions.rs | 12 ++++++++++-- editor/src/messages/tool/tool_messages/text_tool.rs | 4 ++-- node-graph/gcore/src/text/to_path.rs | 2 +- node-graph/gsvg-renderer/src/renderer.rs | 2 +- 6 files changed, 24 insertions(+), 10 deletions(-) diff --git a/editor/src/messages/portfolio/document_migration.rs b/editor/src/messages/portfolio/document_migration.rs index a68547d7..96213b4e 100644 --- a/editor/src/messages/portfolio/document_migration.rs +++ b/editor/src/messages/portfolio/document_migration.rs @@ -974,8 +974,11 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId], let mut node_template = resolve_document_node_type(reference)?.default_node_template(); document.network_interface.replace_implementation(node_id, network_path, &mut node_template); - document.network_interface.add_import(TaggedValue::None, false, 0, "Primary", "", &[*node_id]); - document.network_interface.add_import(TaggedValue::U32(0), false, 1, "Loop Level", "TODO", &[*node_id]); + let mut node_path = network_path.to_vec(); + node_path.push(*node_id); + + document.network_interface.add_import(TaggedValue::None, false, 0, "Primary", "", &node_path); + document.network_interface.add_import(TaggedValue::U32(0), false, 1, "Loop Level", "TODO", &node_path); } // ================================== diff --git a/editor/src/messages/tool/common_functionality/graph_modification_utils.rs b/editor/src/messages/tool/common_functionality/graph_modification_utils.rs index 33c19d0c..126e5b16 100644 --- a/editor/src/messages/tool/common_functionality/graph_modification_utils.rs +++ b/editor/src/messages/tool/common_functionality/graph_modification_utils.rs @@ -357,7 +357,7 @@ pub fn get_text_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkIn } /// Gets properties from the Text node -pub fn get_text(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<(&String, &Font, TypesettingConfig)> { +pub fn get_text(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<(&String, &Font, TypesettingConfig, bool)> { let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs("Text")?; let Some(TaggedValue::String(text)) = &inputs[1].as_value() else { return None }; @@ -368,6 +368,9 @@ pub fn get_text(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInter let Some(&TaggedValue::OptionalF64(max_width)) = inputs[6].as_value() else { return None }; let Some(&TaggedValue::OptionalF64(max_height)) = inputs[7].as_value() else { return None }; let Some(&TaggedValue::F64(tilt)) = inputs[8].as_value() else { return None }; + let Some(TaggedValue::Bool(per_glyph_instances)) = &inputs[9].as_value() else { + return None; + }; let typesetting = TypesettingConfig { font_size, @@ -377,7 +380,7 @@ pub fn get_text(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInter max_height, tilt, }; - Some((text, font, typesetting)) + Some((text, font, typesetting, *per_glyph_instances)) } pub fn get_stroke_width(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option { diff --git a/editor/src/messages/tool/common_functionality/utility_functions.rs b/editor/src/messages/tool/common_functionality/utility_functions.rs index 6ae3f130..c6707ab3 100644 --- a/editor/src/messages/tool/common_functionality/utility_functions.rs +++ b/editor/src/messages/tool/common_functionality/utility_functions.rs @@ -66,14 +66,22 @@ where /// Calculates the bounding box of the layer's text, based on the settings for max width and height specified in the typesetting config. pub fn text_bounding_box(layer: LayerNodeIdentifier, document: &DocumentMessageHandler, font_cache: &FontCache) -> Quad { - let Some((text, font, typesetting)) = get_text(layer, &document.network_interface) else { + let Some((text, font, typesetting, per_glyph_instances)) = get_text(layer, &document.network_interface) else { return Quad::from_box([DVec2::ZERO, DVec2::ZERO]); }; let font_data = font_cache.get(font).map(|data| load_font(data)); let far = graphene_std::text::bounding_box(text, font_data, typesetting, false); - Quad::from_box([DVec2::ZERO, far]) + // TODO: Once the instances refactor is complete and per_glyph_instances can be removed (since it'll be the default), + // TODO: remove this because the top of the dashed bounding overlay should no longer be based on the first line's baseline. + let vertical_offset = if per_glyph_instances { + DVec2::NEG_Y * typesetting.font_size * (1. + (typesetting.line_height_ratio - 1.) / 2.) + } else { + DVec2::ZERO + }; + + Quad::from_box([DVec2::ZERO + vertical_offset, far + vertical_offset]) } pub fn calculate_segment_angle(anchor: PointId, segment: SegmentId, vector_data: &VectorData, prefer_handle_direction: bool) -> Option { diff --git a/editor/src/messages/tool/tool_messages/text_tool.rs b/editor/src/messages/tool/tool_messages/text_tool.rs index d719c787..0653bb83 100644 --- a/editor/src/messages/tool/tool_messages/text_tool.rs +++ b/editor/src/messages/tool/tool_messages/text_tool.rs @@ -329,7 +329,7 @@ impl TextToolData { fn load_layer_text_node(&mut self, document: &DocumentMessageHandler) -> Option<()> { let transform = document.metadata().transform_to_viewport(self.layer); let color = graph_modification_utils::get_fill_color(self.layer, &document.network_interface).unwrap_or(Color::BLACK); - let (text, font, typesetting) = graph_modification_utils::get_text(self.layer, &document.network_interface)?; + let (text, font, typesetting, _) = graph_modification_utils::get_text(self.layer, &document.network_interface)?; self.editing_text = Some(EditingText { text: text.clone(), font: font.clone(), @@ -524,7 +524,7 @@ impl Fsm for TextToolFsmState { bounding_box_manager.render_quad(&mut overlay_context); // Draw red overlay if text is clipped let transformed_quad = layer_transform * bounds; - if let Some((text, font, typesetting)) = graph_modification_utils::get_text(layer.unwrap(), &document.network_interface) { + if let Some((text, font, typesetting, _)) = graph_modification_utils::get_text(layer.unwrap(), &document.network_interface) { let font_data = font_cache.get(font).map(|data| load_font(data)); if lines_clipping(text.as_str(), font_data, typesetting) { overlay_context.line(transformed_quad.0[2], transformed_quad.0[3], Some(COLOR_OVERLAY_RED), Some(3.)); diff --git a/node-graph/gcore/src/text/to_path.rs b/node-graph/gcore/src/text/to_path.rs index ec649ce3..b96e03ff 100644 --- a/node-graph/gcore/src/text/to_path.rs +++ b/node-graph/gcore/src/text/to_path.rs @@ -181,7 +181,7 @@ fn layout_text(str: &str, font_data: Option>, typesetting: TypesettingC })?; const DISPLAY_SCALE: f32 = 1.; - let mut builder = layout_cx.ranged_builder(&mut font_cx, str, DISPLAY_SCALE, true); + let mut builder = layout_cx.ranged_builder(&mut font_cx, str, DISPLAY_SCALE, false); builder.push_default(StyleProperty::FontSize(typesetting.font_size as f32)); builder.push_default(StyleProperty::LetterSpacing(typesetting.character_spacing as f32)); diff --git a/node-graph/gsvg-renderer/src/renderer.rs b/node-graph/gsvg-renderer/src/renderer.rs index a71cdf1b..216a9b66 100644 --- a/node-graph/gsvg-renderer/src/renderer.rs +++ b/node-graph/gsvg-renderer/src/renderer.rs @@ -756,7 +756,7 @@ impl GraphicElementRendered for VectorDataTable { .chain(single_anchors_targets.into_iter()) .collect::>(); - metadata.click_targets.insert(element_id, click_targets); + metadata.click_targets.entry(element_id).or_insert(click_targets); } if let Some(upstream_graphic_group) = &instance.upstream_graphic_group {