Migrate attribute call sites from string keys to typed keys

This commit is contained in:
Timon
2026-07-18 13:02:52 +00:00
committed by Keavon Chambers
parent 58178fd48e
commit 475c67facc
31 changed files with 529 additions and 511 deletions

View File

@@ -1,5 +1,5 @@
use core_types::list::{Item, List};
use core_types::{ATTR_TYPE, Ctx};
use core_types::{Ctx, attr};
use serde_json::Value;
use crate::unescape_string;
@@ -265,7 +265,7 @@ fn query_json_all(
let mut results = Vec::new();
resolve_all(&value, &segments, !*unquote_strings.element(), &mut results);
results.into_iter().map(|(text, ty)| Item::new_from_element(text).with_attribute(ATTR_TYPE, ty.to_string())).collect()
results.into_iter().map(|(text, ty)| Item::new_from_element(text).with_attr::<attr::Type>(ty.to_string())).collect()
}
/// A parsed segment of a JSON access path.

View File

@@ -1,5 +1,5 @@
use core_types::attr;
use core_types::list::{Item, List};
use core_types::{ATTR_EDITOR_CLICK_TARGET, ATTR_EDITOR_TEXT_FRAME, ATTR_TRANSFORM};
use glam::{DAffine2, DVec2};
use parley::GlyphRun;
use skrifa::GlyphId;
@@ -15,13 +15,13 @@ pub struct PathBuilder {
origin: DVec2,
glyph_subpaths: Vec<Subpath<PointId>>,
pub vector_list: List<Vector>,
/// Per-glyph AABBs collected in single-item mode, published as `ATTR_EDITOR_CLICK_TARGET` in `finalize()`.
/// Per-glyph AABBs collected in single-item mode, published as `vector_types::attr::editor::ClickTarget` in `finalize()`.
merged_click_target_bboxes: Vec<[DVec2; 2]>,
/// Per-glyph baselines, parallel to `merged_click_target_bboxes`. Groups glyphs by line for the widening pass.
merged_click_target_baselines: Vec<f64>,
/// Per-glyph AABBs in glyph-local space (multi-item mode), widened in `finalize()` to fill gaps.
per_glyph_bboxes: Vec<Option<[DVec2; 2]>>,
/// Text frame size, stamped per item as `ATTR_EDITOR_TEXT_FRAME` relative to each item's origin.
/// Text frame size, stamped per item as `attr::editor::TextFrame` relative to each item's origin.
text_frame_size: DVec2,
/// First glyph's baseline offset (pre-height-filter). Used for the empty placeholder item so
/// `local_transforms` stays stable when all glyphs are clipped during a resize drag.
@@ -85,8 +85,8 @@ impl PathBuilder {
let frame_in_item_local = DAffine2::from_scale_angle_translation(self.text_frame_size, 0., -glyph_offset);
let item = Item::new_from_element(Vector::from_subpaths(core::mem::take(&mut self.glyph_subpaths), false))
.with_attribute(ATTR_TRANSFORM, DAffine2::from_translation(glyph_offset))
.with_attribute(ATTR_EDITOR_TEXT_FRAME, frame_in_item_local);
.with_attr::<attr::Transform>(DAffine2::from_translation(glyph_offset))
.with_attr::<attr::editor::TextFrame>(frame_in_item_local);
self.vector_list.push(item);
// Defer click target creation to `finalize()` where adjacent AABBs get widened
@@ -170,8 +170,8 @@ impl PathBuilder {
if self.vector_list.is_empty() {
let frame_in_item_local = DAffine2::from_scale_angle_translation(self.text_frame_size, 0., -self.first_glyph_offset);
let item = Item::new_from_element(Vector::default())
.with_attribute(ATTR_TRANSFORM, DAffine2::from_translation(self.first_glyph_offset))
.with_attribute(ATTR_EDITOR_TEXT_FRAME, frame_in_item_local);
.with_attr::<attr::Transform>(DAffine2::from_translation(self.first_glyph_offset))
.with_attr::<attr::editor::TextFrame>(frame_in_item_local);
self.vector_list.push(item);
}
@@ -184,7 +184,7 @@ impl PathBuilder {
.enumerate()
.filter_map(|(index, bbox)| {
let bbox = (*bbox)?;
let offset = self.vector_list.attribute_cloned_or_default::<DAffine2>(ATTR_TRANSFORM, index).translation;
let offset = self.vector_list.attr_cloned_or_default::<attr::Transform>(index).translation;
Some((index, offset, [bbox[0] + offset, bbox[1] + offset]))
})
.collect();
@@ -197,7 +197,7 @@ impl PathBuilder {
for (entry, widened) in entries.iter().zip(layer_bboxes.iter()) {
let glyph_local = [widened[0] - entry.1, widened[1] - entry.1];
let rect = Subpath::new_rectangle(glyph_local[0], glyph_local[1]);
self.vector_list.set_attribute(ATTR_EDITOR_CLICK_TARGET, entry.0, Vector::from_subpaths([rect], false));
self.vector_list.set_attr::<vector_types::attr::editor::ClickTarget>(entry.0, Vector::from_subpaths([rect], false));
}
}
@@ -207,14 +207,14 @@ impl PathBuilder {
widen_horizontal_gaps(&mut bboxes, &self.merged_click_target_baselines);
let widened_subpaths: Vec<_> = bboxes.iter().map(|[min, max]| Subpath::new_rectangle(*min, *max)).collect();
self.vector_list.set_attribute(ATTR_EDITOR_CLICK_TARGET, 0, Vector::from_subpaths(widened_subpaths, false));
self.vector_list.set_attr::<vector_types::attr::editor::ClickTarget>(0, Vector::from_subpaths(widened_subpaths, false));
}
// Fill in text frame for items that don't have one yet (single-item mode, where item 0 = identity)
let frame = DAffine2::from_scale(self.text_frame_size);
for index in 0..self.vector_list.len() {
if self.vector_list.attribute::<DAffine2>(ATTR_EDITOR_TEXT_FRAME, index).is_none() {
self.vector_list.set_attribute(ATTR_EDITOR_TEXT_FRAME, index, frame);
if self.vector_list.attr::<attr::editor::TextFrame>(index).is_none() {
self.vector_list.set_attr::<attr::editor::TextFrame>(index, frame);
}
}

View File

@@ -1,6 +1,6 @@
use core_types::list::{Item, List};
use core_types::registry::types::SignedInteger;
use core_types::{ATTR_END, ATTR_NAME, ATTR_START, Ctx};
use core_types::{Ctx, attr};
/// Checks whether the string contains a match for the given regular expression pattern. Optionally restricts the match to only the start and/or end of the string.
#[node_macro::node(category("Text: Regex"))]
@@ -159,10 +159,7 @@ fn regex_find(
let start = captured.map_or(0_u64, |m| m.start() as u64);
let end = captured.map_or(0_u64, |m| m.end() as u64);
let name = capture_names.get(i).cloned().flatten().unwrap_or_default();
Item::new_from_element(text)
.with_attribute(ATTR_START, start)
.with_attribute(ATTR_END, end)
.with_attribute(ATTR_NAME, name)
Item::new_from_element(text).with_attr::<attr::Start>(start).with_attr::<attr::End>(end).with_attr::<attr::Name>(name)
})
.collect()
}
@@ -208,8 +205,8 @@ fn regex_find_all(
.filter_map(|m| m.ok())
.map(|m| {
Item::new_from_element(m.as_str().to_string())
.with_attribute(ATTR_START, m.start() as u64)
.with_attribute(ATTR_END, m.end() as u64)
.with_attr::<attr::Start>(m.start() as u64)
.with_attr::<attr::End>(m.end() as u64)
})
.collect()
}

View File

@@ -1,11 +1,7 @@
use super::TypesettingConfig;
use super::text_context::TextContext;
use core_types::blending::BlendMode;
use core_types::list::{Item, List, NodeIdPath};
use core_types::{
ATTR_BLEND_MODE, ATTR_EDITOR_LAYER_PATH, ATTR_FONT, ATTR_FONT_SIZE, ATTR_LETTER_SPACING, ATTR_LETTER_TILT, ATTR_LINE_HEIGHT, ATTR_MAX_HEIGHT, ATTR_MAX_WIDTH, ATTR_OPACITY, ATTR_OPACITY_FILL,
ATTR_TEXT_ALIGN, ATTR_TRANSFORM,
};
use core_types::attr;
use core_types::list::{Item, List};
use glam::{DAffine2, DVec2};
use graphene_resource::Resource;
use vector_types::Vector;
@@ -33,45 +29,45 @@ pub fn shape_text_item(item: &Item<String>, separate_glyphs: bool) -> List<Vecto
// Use fallback font when none is explicitly attached.
let font: Resource = {
let font: Resource = item.attribute_cloned_or_default(ATTR_FONT);
let font: Resource = item.attr_cloned_or_default::<crate::attr::Font>();
if font.is_empty() { super::FALLBACK_FONT_RESOURCE.clone() } else { font }
};
let defaults = TypesettingConfig::default();
let typesetting = TypesettingConfig {
font_size: item.attribute_cloned_or(ATTR_FONT_SIZE, defaults.font_size),
line_height_ratio: item.attribute_cloned_or(ATTR_LINE_HEIGHT, defaults.line_height_ratio),
letter_spacing: item.attribute_cloned_or(ATTR_LETTER_SPACING, defaults.letter_spacing),
letter_tilt: item.attribute_cloned_or(ATTR_LETTER_TILT, defaults.letter_tilt),
max_width: item.attribute_cloned_or::<Option<f64>>(ATTR_MAX_WIDTH, defaults.max_width),
max_height: item.attribute_cloned_or::<Option<f64>>(ATTR_MAX_HEIGHT, defaults.max_height),
align: item.attribute_cloned_or(ATTR_TEXT_ALIGN, defaults.align),
font_size: item.attr_cloned_or::<attr::FontSize>(defaults.font_size),
line_height_ratio: item.attr_cloned_or::<attr::LineHeight>(defaults.line_height_ratio),
letter_spacing: item.attr_cloned_or::<attr::LetterSpacing>(defaults.letter_spacing),
letter_tilt: item.attr_cloned_or::<attr::LetterTilt>(defaults.letter_tilt),
max_width: item.attr_cloned_or::<attr::MaxWidth>(defaults.max_width),
max_height: item.attr_cloned_or::<attr::MaxHeight>(defaults.max_height),
align: item.attr_cloned_or::<crate::attr::TextAlign>(defaults.align),
};
let vectors = to_path(text, &font, typesetting, separate_glyphs);
let transform = item.attribute_cloned_or_default::<DAffine2>(ATTR_TRANSFORM);
let layer_path = item.attribute::<NodeIdPath>(ATTR_EDITOR_LAYER_PATH).cloned();
let blend_mode = item.attribute::<BlendMode>(ATTR_BLEND_MODE).copied();
let opacity = item.attribute::<f64>(ATTR_OPACITY).copied();
let opacity_fill = item.attribute::<f64>(ATTR_OPACITY_FILL).copied();
let transform = item.attr_cloned_or_default::<attr::Transform>();
let layer_path = item.attr::<attr::editor::LayerPath>().cloned();
let blend_mode = item.attr::<attr::BlendMode>().copied();
let opacity = item.attr::<attr::Opacity>().copied();
let opacity_fill = item.attr::<attr::OpacityFill>().copied();
let mut result = List::new();
for mut produced in vectors.into_iter() {
if transform != DAffine2::IDENTITY {
let local = produced.attribute_cloned_or_default::<DAffine2>(ATTR_TRANSFORM);
produced.set_attribute(ATTR_TRANSFORM, transform * local);
let local = produced.attr_cloned_or_default::<attr::Transform>();
produced.set_attr::<attr::Transform>(transform * local);
}
if let Some(layer_path) = &layer_path {
produced.set_attribute(ATTR_EDITOR_LAYER_PATH, layer_path.clone());
produced.set_attr::<attr::editor::LayerPath>(layer_path.clone());
}
if let Some(blend_mode) = blend_mode {
produced.set_attribute(ATTR_BLEND_MODE, blend_mode);
produced.set_attr::<attr::BlendMode>(blend_mode);
}
if let Some(opacity) = opacity {
produced.set_attribute(ATTR_OPACITY, opacity);
produced.set_attr::<attr::Opacity>(opacity);
}
if let Some(opacity_fill) = opacity_fill {
produced.set_attribute(ATTR_OPACITY_FILL, opacity_fill);
produced.set_attr::<attr::OpacityFill>(opacity_fill);
}
result.push(produced);
}