Add line height and character spacing to the Text node (#2016)

This commit is contained in:
Keavon Chambers
2024-10-01 12:28:27 -07:00
committed by GitHub
parent 904cf09c79
commit 2d86fb24ab
10 changed files with 127 additions and 34 deletions
@@ -127,14 +127,16 @@ 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, f64)> {
pub fn get_text(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<(&String, &Font, f64, f64, f64)> {
let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs("Text")?;
let Some(TaggedValue::String(text)) = &inputs[1].as_value() else { return None };
let Some(TaggedValue::Font(font)) = &inputs[2].as_value() else { return None };
let Some(&TaggedValue::F64(font_size)) = inputs[3].as_value() else { return None };
let Some(&TaggedValue::F64(line_height_ratio)) = inputs[4].as_value() else { return None };
let Some(&TaggedValue::F64(character_spacing)) = inputs[5].as_value() else { return None };
Some((text, font, font_size))
Some((text, font, font_size, line_height_ratio, character_spacing))
}
pub fn get_stroke_width(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<f64> {
@@ -24,7 +24,9 @@ pub struct TextTool {
}
pub struct TextOptions {
font_size: u32,
font_size: f64,
line_height_ratio: f64,
character_spacing: f64,
font_name: String,
font_style: String,
fill: ToolColorOptions,
@@ -33,7 +35,9 @@ pub struct TextOptions {
impl Default for TextOptions {
fn default() -> Self {
Self {
font_size: 24,
font_size: 24.,
line_height_ratio: 1.2,
character_spacing: 1.,
font_name: graphene_core::consts::DEFAULT_FONT_FAMILY.into(),
font_style: graphene_core::consts::DEFAULT_FONT_STYLE.into(),
fill: ToolColorOptions::new_primary(),
@@ -63,7 +67,9 @@ pub enum TextOptionsUpdate {
FillColor(Option<Color>),
FillColorType(ToolColorType),
Font { family: String, style: String },
FontSize(u32),
FontSize(f64),
LineHeightRatio(f64),
CharacterSpacing(f64),
WorkingColors(Option<Color>, Option<Color>),
}
@@ -100,13 +106,29 @@ fn create_text_widgets(tool: &TextTool) -> Vec<WidgetHolder> {
.into()
})
.widget_holder();
let size = NumberInput::new(Some(tool.options.font_size as f64))
let size = NumberInput::new(Some(tool.options.font_size))
.unit(" px")
.label("Size")
.int()
.min(1.)
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
.on_update(|number_input: &NumberInput| TextToolMessage::UpdateOptions(TextOptionsUpdate::FontSize(number_input.value.unwrap() as u32)).into())
.on_update(|number_input: &NumberInput| TextToolMessage::UpdateOptions(TextOptionsUpdate::FontSize(number_input.value.unwrap())).into())
.widget_holder();
let line_height_ratio = NumberInput::new(Some(tool.options.line_height_ratio))
.label("Line Height")
.int()
.min(0.)
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
.step(0.1)
.on_update(|number_input: &NumberInput| TextToolMessage::UpdateOptions(TextOptionsUpdate::LineHeightRatio(number_input.value.unwrap())).into())
.widget_holder();
let character_spacing = NumberInput::new(Some(tool.options.character_spacing))
.label("Character Spacing")
.int()
.min(0.)
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
.step(0.1)
.on_update(|number_input: &NumberInput| TextToolMessage::UpdateOptions(TextOptionsUpdate::CharacterSpacing(number_input.value.unwrap())).into())
.widget_holder();
vec![
font,
@@ -114,6 +136,10 @@ fn create_text_widgets(tool: &TextTool) -> Vec<WidgetHolder> {
style,
Separator::new(SeparatorType::Related).widget_holder(),
size,
Separator::new(SeparatorType::Related).widget_holder(),
line_height_ratio,
Separator::new(SeparatorType::Related).widget_holder(),
character_spacing,
]
}
@@ -149,6 +175,8 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for TextToo
self.send_layout(responses, LayoutTarget::ToolOptions);
}
TextOptionsUpdate::FontSize(font_size) => self.options.font_size = font_size,
TextOptionsUpdate::LineHeightRatio(line_height_ratio) => self.options.line_height_ratio = line_height_ratio,
TextOptionsUpdate::CharacterSpacing(character_spacing) => self.options.character_spacing = character_spacing,
TextOptionsUpdate::FillColor(color) => {
self.options.fill.custom_color = color;
self.options.fill.color_type = ToolColorType::Custom;
@@ -200,6 +228,8 @@ pub struct EditingText {
text: String,
font: Font,
font_size: f64,
line_height_ratio: f64,
character_spacing: f64,
color: Option<Color>,
transform: DAffine2,
}
@@ -233,11 +263,13 @@ 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, font_size) = graph_modification_utils::get_text(self.layer, &document.network_interface)?;
let (text, font, font_size, line_height_ratio, character_spacing) = graph_modification_utils::get_text(self.layer, &document.network_interface)?;
self.editing_text = Some(EditingText {
text: text.clone(),
font: font.clone(),
font_size,
line_height_ratio,
character_spacing,
color: Some(color),
transform,
});
@@ -295,6 +327,8 @@ impl TextToolData {
text: String::new(),
font: editing_text.font.clone(),
size: editing_text.font_size,
line_height_ratio: editing_text.line_height_ratio,
character_spacing: editing_text.character_spacing,
parent: document.new_layer_parent(true),
insert_index: 0,
});
@@ -364,7 +398,14 @@ impl Fsm for TextToolFsmState {
});
if let Some(editing_text) = tool_data.editing_text.as_ref() {
let buzz_face = font_cache.get(&editing_text.font).map(|data| load_face(data));
let far = graphene_core::text::bounding_box(&tool_data.new_text, buzz_face, editing_text.font_size, None);
let far = graphene_core::text::bounding_box(
&tool_data.new_text,
buzz_face,
editing_text.font_size,
editing_text.line_height_ratio,
editing_text.character_spacing,
None,
);
if far.x != 0. && far.y != 0. {
let quad = Quad::from_box([DVec2::ZERO, far]);
let transformed_quad = document.metadata().transform_to_viewport(tool_data.layer) * quad;
@@ -376,11 +417,11 @@ impl Fsm for TextToolFsmState {
}
(_, TextToolMessage::Overlays(mut overlay_context)) => {
for layer in document.network_interface.selected_nodes(&[]).unwrap().selected_layers(document.metadata()) {
let Some((text, font, font_size)) = graph_modification_utils::get_text(layer, &document.network_interface) else {
let Some((text, font, font_size, line_height_ratio, character_spacing)) = graph_modification_utils::get_text(layer, &document.network_interface) else {
continue;
};
let buzz_face = font_cache.get(font).map(|data| load_face(data));
let far = graphene_core::text::bounding_box(text, buzz_face, font_size, None);
let far = graphene_core::text::bounding_box(text, buzz_face, font_size, line_height_ratio, character_spacing, None);
let quad = Quad::from_box([DVec2::ZERO, far]);
let multiplied = document.metadata().transform_to_viewport(layer) * quad;
overlay_context.quad(multiplied, None);
@@ -392,7 +433,9 @@ impl Fsm for TextToolFsmState {
tool_data.editing_text = Some(EditingText {
text: String::new(),
transform: DAffine2::from_translation(input.mouse.position),
font_size: tool_options.font_size as f64,
font_size: tool_options.font_size,
line_height_ratio: tool_options.line_height_ratio,
character_spacing: tool_options.character_spacing,
font: Font::new(tool_options.font_name.clone(), tool_options.font_style.clone()),
color: tool_options.fill.active_color(),
});