mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-03 01:38:11 +08:00
Add max width/height to text layers and draggable text boxes to the Text tool (#2118)
* Make progress in text tool * Add line_width to gcore and gstd * minor fix * Dragging sets line_width correctly * Get draw overlay to work * Typo fix * Make progress in text tool * Add line_width to gcore and gstd * minor fix * Dragging sets line_width correctly * Get draw overlay to work * Typo fix * Improve text bounding box * Add toggle for editing line width * Take absolute value of drag * Fix optional properties * Code review * Attempt to add box height and abort with keys * Attempt to add key modifiers and snap manager * Use resize for improved dragging * Refactor typesetting configuration into a struct * Fix missing px unit in frontend * Remove lines on rendered text * Fix backwards compatibility * Refactor lenient slection as an associate function in tool data * Add dashed quad to text nodes * Use correct names for max height and width * Additional renames and reorder * ReResolve conflict * Code review and improvements --------- Co-authored-by: hypercube <0hypercube@gmail.com> Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
co-authored by
hypercube
Keavon Chambers
parent
f225756655
commit
66357540bb
@@ -6,7 +6,7 @@ use crate::messages::prelude::*;
|
||||
use bezier_rs::Subpath;
|
||||
use graph_craft::document::NodeId;
|
||||
use graphene_core::raster::{BlendMode, ImageFrame};
|
||||
use graphene_core::text::Font;
|
||||
use graphene_core::text::{Font, TypesettingConfig};
|
||||
use graphene_core::vector::brush_stroke::BrushStroke;
|
||||
use graphene_core::vector::style::{Fill, Stroke};
|
||||
use graphene_core::vector::PointId;
|
||||
@@ -93,9 +93,7 @@ pub enum GraphOperationMessage {
|
||||
id: NodeId,
|
||||
text: String,
|
||||
font: Font,
|
||||
size: f64,
|
||||
line_height_ratio: f64,
|
||||
character_spacing: f64,
|
||||
typesetting: TypesettingConfig,
|
||||
parent: LayerNodeIdentifier,
|
||||
insert_index: usize,
|
||||
},
|
||||
|
||||
+4
-6
@@ -7,7 +7,7 @@ use crate::messages::prelude::*;
|
||||
|
||||
use graph_craft::document::{NodeId, NodeInput};
|
||||
use graphene_core::renderer::Quad;
|
||||
use graphene_core::text::Font;
|
||||
use graphene_core::text::{Font, TypesettingConfig};
|
||||
use graphene_core::vector::style::{Fill, Gradient, GradientStops, GradientType, LineCap, LineJoin, Stroke};
|
||||
use graphene_core::Color;
|
||||
use graphene_std::vector::convert_usvg_path;
|
||||
@@ -174,15 +174,13 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
|
||||
id,
|
||||
text,
|
||||
font,
|
||||
size,
|
||||
line_height_ratio,
|
||||
character_spacing,
|
||||
typesetting,
|
||||
parent,
|
||||
insert_index,
|
||||
} => {
|
||||
let mut modify_inputs = ModifyInputsContext::new(network_interface, responses);
|
||||
let layer = modify_inputs.create_layer(id);
|
||||
modify_inputs.insert_text(text, font, size, line_height_ratio, character_spacing, layer);
|
||||
modify_inputs.insert_text(text, font, typesetting, layer);
|
||||
network_interface.move_layer_to_stack(layer, parent, insert_index, &[]);
|
||||
responses.add(GraphOperationMessage::StrokeSet { layer, stroke: Stroke::default() });
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
@@ -279,7 +277,7 @@ fn import_usvg_node(modify_inputs: &mut ModifyInputsContext, node: &usvg::Node,
|
||||
}
|
||||
usvg::Node::Text(text) => {
|
||||
let font = Font::new(graphene_core::consts::DEFAULT_FONT_FAMILY.to_string(), graphene_core::consts::DEFAULT_FONT_STYLE.to_string());
|
||||
modify_inputs.insert_text(text.chunks().iter().map(|chunk| chunk.text()).collect(), font, 24., 1.2, 1., layer);
|
||||
modify_inputs.insert_text(text.chunks().iter().map(|chunk| chunk.text()).collect(), font, TypesettingConfig::default(), layer);
|
||||
modify_inputs.fill_set(Fill::Solid(Color::BLACK));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use graph_craft::concrete;
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::{NodeId, NodeInput};
|
||||
use graphene_core::raster::{BlendMode, ImageFrame};
|
||||
use graphene_core::text::Font;
|
||||
use graphene_core::text::{Font, TypesettingConfig};
|
||||
use graphene_core::vector::brush_stroke::BrushStroke;
|
||||
use graphene_core::vector::style::{Fill, Stroke};
|
||||
use graphene_core::vector::{PointId, VectorModificationType};
|
||||
@@ -179,7 +179,7 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_text(&mut self, text: String, font: Font, size: f64, line_height_ratio: f64, character_spacing: f64, layer: LayerNodeIdentifier) {
|
||||
pub fn insert_text(&mut self, text: String, font: Font, typesetting: TypesettingConfig, layer: LayerNodeIdentifier) {
|
||||
let stroke = resolve_document_node_type("Stroke").expect("Stroke node does not exist").default_node_template();
|
||||
let fill = resolve_document_node_type("Fill").expect("Fill node does not exist").default_node_template();
|
||||
let transform = resolve_document_node_type("Transform").expect("Transform node does not exist").default_node_template();
|
||||
@@ -187,9 +187,11 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
Some(NodeInput::scope("editor-api")),
|
||||
Some(NodeInput::value(TaggedValue::String(text), false)),
|
||||
Some(NodeInput::value(TaggedValue::Font(font), false)),
|
||||
Some(NodeInput::value(TaggedValue::F64(size), false)),
|
||||
Some(NodeInput::value(TaggedValue::F64(line_height_ratio), false)),
|
||||
Some(NodeInput::value(TaggedValue::F64(character_spacing), false)),
|
||||
Some(NodeInput::value(TaggedValue::F64(typesetting.font_size), false)),
|
||||
Some(NodeInput::value(TaggedValue::F64(typesetting.line_height_ratio), false)),
|
||||
Some(NodeInput::value(TaggedValue::F64(typesetting.character_spacing), false)),
|
||||
Some(NodeInput::value(TaggedValue::OptionalF64(typesetting.max_width), false)),
|
||||
Some(NodeInput::value(TaggedValue::OptionalF64(typesetting.max_height), false)),
|
||||
]);
|
||||
|
||||
let text_id = NodeId::new();
|
||||
|
||||
@@ -16,7 +16,7 @@ use graph_craft::imaginate_input::ImaginateSamplingMethod;
|
||||
use graph_craft::ProtoNodeIdentifier;
|
||||
use graphene_core::raster::brush_cache::BrushCache;
|
||||
use graphene_core::raster::{CellularDistanceFunction, CellularReturnType, Color, DomainWarpType, FractalType, Image, ImageFrame, NoiseType, RedGreenBlue, RedGreenBlueAlpha};
|
||||
use graphene_core::text::Font;
|
||||
use graphene_core::text::{Font, TypesettingConfig};
|
||||
use graphene_core::transform::Footprint;
|
||||
use graphene_core::vector::VectorData;
|
||||
use graphene_core::*;
|
||||
@@ -2112,9 +2112,11 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||
TaggedValue::Font(Font::new(graphene_core::consts::DEFAULT_FONT_FAMILY.into(), graphene_core::consts::DEFAULT_FONT_STYLE.into())),
|
||||
false,
|
||||
),
|
||||
NodeInput::value(TaggedValue::F64(24.), false),
|
||||
NodeInput::value(TaggedValue::F64(1.2), false),
|
||||
NodeInput::value(TaggedValue::F64(1.), false),
|
||||
NodeInput::value(TaggedValue::F64(TypesettingConfig::default().font_size), false),
|
||||
NodeInput::value(TaggedValue::F64(TypesettingConfig::default().line_height_ratio), false),
|
||||
NodeInput::value(TaggedValue::F64(TypesettingConfig::default().character_spacing), false),
|
||||
NodeInput::value(TaggedValue::OptionalF64(TypesettingConfig::default().max_width), false),
|
||||
NodeInput::value(TaggedValue::OptionalF64(TypesettingConfig::default().max_height), false),
|
||||
],
|
||||
..Default::default()
|
||||
},
|
||||
@@ -2126,6 +2128,8 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||
"Size".to_string(),
|
||||
"Line Height".to_string(),
|
||||
"Character Spacing".to_string(),
|
||||
"Max Width".to_string(),
|
||||
"Max Height".to_string(),
|
||||
],
|
||||
output_names: vec!["Vector".to_string()],
|
||||
..Default::default()
|
||||
|
||||
@@ -661,6 +661,27 @@ fn number_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, na
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
]),
|
||||
Some(&TaggedValue::OptionalF64(x)) => {
|
||||
// TODO: Don't wipe out the previously set value (setting it back to the default of 100) when reenabling this checkbox back to Some from None
|
||||
let toggle_enabled = move |checkbox_input: &CheckboxInput| TaggedValue::OptionalF64(if checkbox_input.checked { Some(100.) } else { None });
|
||||
widgets.extend_from_slice(&[
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
Separator::new(SeparatorType::Related).widget_holder(),
|
||||
// The checkbox toggles if the value is Some or None
|
||||
CheckboxInput::new(x.is_some())
|
||||
.on_update(update_value(toggle_enabled, node_id, index))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
Separator::new(SeparatorType::Related).widget_holder(),
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
number_props
|
||||
.value(x)
|
||||
.on_update(update_value(move |x: &NumberInput| TaggedValue::OptionalF64(x.value), node_id, index))
|
||||
.disabled(x.is_none())
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
]);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -1734,6 +1755,8 @@ pub(crate) fn text_properties(document_node: &DocumentNode, node_id: NodeId, _co
|
||||
let size = number_widget(document_node, node_id, 3, "Size", NumberInput::default().unit(" px").min(1.), true);
|
||||
let line_height_ratio = number_widget(document_node, node_id, 4, "Line Height", NumberInput::default().min(0.).step(0.1), true);
|
||||
let character_spacing = number_widget(document_node, node_id, 5, "Character Spacing", NumberInput::default().min(0.).step(0.1), true);
|
||||
let max_width = number_widget(document_node, node_id, 6, "Max Width", NumberInput::default().unit(" px").min(1.), false);
|
||||
let max_height = number_widget(document_node, node_id, 7, "Max Height", NumberInput::default().unit(" px").min(1.), false);
|
||||
|
||||
let mut result = vec![LayoutGroup::Row { widgets: text }, LayoutGroup::Row { widgets: font }];
|
||||
if let Some(style) = style {
|
||||
@@ -1742,6 +1765,8 @@ pub(crate) fn text_properties(document_node: &DocumentNode, node_id: NodeId, _co
|
||||
result.push(LayoutGroup::Row { widgets: size });
|
||||
result.push(LayoutGroup::Row { widgets: line_height_ratio });
|
||||
result.push(LayoutGroup::Row { widgets: character_spacing });
|
||||
result.push(LayoutGroup::Row { widgets: max_width });
|
||||
result.push(LayoutGroup::Row { widgets: max_height });
|
||||
result
|
||||
}
|
||||
|
||||
|
||||
@@ -32,26 +32,11 @@ impl core::hash::Hash for OverlayContext {
|
||||
|
||||
impl OverlayContext {
|
||||
pub fn quad(&mut self, quad: Quad, color_fill: Option<&str>) {
|
||||
self.render_context.begin_path();
|
||||
self.render_context.move_to(quad.0[3].x.round() - 0.5, quad.0[3].y.round() - 0.5);
|
||||
for i in 0..4 {
|
||||
self.render_context.line_to(quad.0[i].x.round() - 0.5, quad.0[i].y.round() - 0.5);
|
||||
}
|
||||
if let Some(color_fill) = color_fill {
|
||||
self.render_context.set_fill_style_str(color_fill);
|
||||
self.render_context.fill();
|
||||
}
|
||||
self.render_context.set_stroke_style_str(COLOR_OVERLAY_BLUE);
|
||||
self.render_context.stroke();
|
||||
self.dashed_quad(quad, color_fill, None, None);
|
||||
}
|
||||
|
||||
pub fn line(&mut self, start: DVec2, end: DVec2, color: Option<&str>) {
|
||||
self.dashed_line(start, end, color, None, None)
|
||||
}
|
||||
|
||||
pub fn dashed_line(&mut self, start: DVec2, end: DVec2, color: Option<&str>, dash_width: Option<f64>, gap_width: Option<f64>) {
|
||||
let start = start.round() - DVec2::splat(0.5);
|
||||
let end = end.round() - DVec2::splat(0.5);
|
||||
pub fn dashed_quad(&mut self, quad: Quad, color_fill: Option<&str>, dash_width: Option<f64>, gap_width: Option<f64>) {
|
||||
// Set the dash pattern
|
||||
if let Some(dash_width) = dash_width {
|
||||
let gap_width = gap_width.unwrap_or(1.);
|
||||
let array = js_sys::Array::new();
|
||||
@@ -61,24 +46,65 @@ impl OverlayContext {
|
||||
.set_line_dash(&JsValue::from(array))
|
||||
.map_err(|error| log::warn!("Error drawing dashed line: {:?}", error))
|
||||
.ok();
|
||||
} else {
|
||||
}
|
||||
|
||||
self.render_context.begin_path();
|
||||
self.render_context.move_to(quad.0[3].x.round() - 0.5, quad.0[3].y.round() - 0.5);
|
||||
|
||||
for i in 0..4 {
|
||||
self.render_context.line_to(quad.0[i].x.round() - 0.5, quad.0[i].y.round() - 0.5);
|
||||
}
|
||||
|
||||
if let Some(color_fill) = color_fill {
|
||||
self.render_context.set_fill_style_str(color_fill);
|
||||
self.render_context.fill();
|
||||
}
|
||||
|
||||
self.render_context.set_stroke_style_str(COLOR_OVERLAY_BLUE);
|
||||
self.render_context.stroke();
|
||||
|
||||
// Reset the dash pattern back to solid
|
||||
if dash_width.is_some() {
|
||||
self.render_context
|
||||
.set_line_dash(&JsValue::from(js_sys::Array::new()))
|
||||
.map_err(|error| log::warn!("Error drawing dashed line: {:?}", error))
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn line(&mut self, start: DVec2, end: DVec2, color: Option<&str>) {
|
||||
self.dashed_line(start, end, color, None, None)
|
||||
}
|
||||
|
||||
pub fn dashed_line(&mut self, start: DVec2, end: DVec2, color: Option<&str>, dash_width: Option<f64>, gap_width: Option<f64>) {
|
||||
// Set the dash pattern
|
||||
if let Some(dash_width) = dash_width {
|
||||
let gap_width = gap_width.unwrap_or(1.);
|
||||
let array = js_sys::Array::new();
|
||||
array.push(&JsValue::from(dash_width - 1.));
|
||||
array.push(&JsValue::from(gap_width));
|
||||
self.render_context
|
||||
.set_line_dash(&JsValue::from(array))
|
||||
.map_err(|error| log::warn!("Error drawing dashed line: {:?}", error))
|
||||
.ok();
|
||||
}
|
||||
|
||||
let start = start.round() - DVec2::splat(0.5);
|
||||
let end = end.round() - DVec2::splat(0.5);
|
||||
|
||||
self.render_context.begin_path();
|
||||
self.render_context.move_to(start.x, start.y);
|
||||
self.render_context.line_to(end.x, end.y);
|
||||
self.render_context.set_stroke_style_str(color.unwrap_or(COLOR_OVERLAY_BLUE));
|
||||
self.render_context.stroke();
|
||||
|
||||
// Reset the dash pattern to solid after drawing
|
||||
self.render_context
|
||||
.set_line_dash(&JsValue::from(js_sys::Array::new()))
|
||||
.map_err(|error| log::warn!("Error drawing dashed line: {:?}", error))
|
||||
.ok();
|
||||
// Reset the dash pattern back to solid
|
||||
if dash_width.is_some() {
|
||||
self.render_context
|
||||
.set_line_dash(&JsValue::from(js_sys::Array::new()))
|
||||
.map_err(|error| log::warn!("Error drawing dashed line: {:?}", error))
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn manipulator_handle(&mut self, position: DVec2, selected: bool) {
|
||||
|
||||
Reference in New Issue
Block a user