Skip to content

Commit 6635754

Browse files
Nitish-bot0HyperCubeKeavon
authored
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 <[email protected]> Co-authored-by: Keavon Chambers <[email protected]>
1 parent f225756 commit 6635754

34 files changed

+519
-261
lines changed

.vscode/settings.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,5 @@
5151
"files.insertFinalNewline": true,
5252
"files.associations": {
5353
"*.graphite": "json"
54-
},
55-
"rust-analyzer.checkOnSave": false
54+
}
5655
}

editor/src/messages/frontend/frontend_message.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,17 @@ pub enum FrontendMessage {
2626
},
2727
DisplayEditableTextbox {
2828
text: String,
29-
#[serde(rename = "lineWidth")]
30-
line_width: Option<f64>,
29+
#[serde(rename = "lineHeightRatio")]
30+
line_height_ratio: f64,
3131
#[serde(rename = "fontSize")]
3232
font_size: f64,
3333
color: Color,
3434
url: String,
3535
transform: [f64; 6],
36+
#[serde(rename = "maxWidth")]
37+
max_width: Option<f64>,
38+
#[serde(rename = "maxHeight")]
39+
max_height: Option<f64>,
3640
},
3741
DisplayEditableTextboxTransform {
3842
transform: [f64; 6],

editor/src/messages/input_mapper/input_mappings.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,10 @@ pub fn input_mappings() -> Mapping {
154154
entry!(KeyDown(Escape); action_dispatch=EyedropperToolMessage::Abort),
155155
//
156156
// TextToolMessage
157-
entry!(KeyUp(MouseLeft); action_dispatch=TextToolMessage::Interact),
157+
entry!(PointerMove; refresh_keys=[Alt, Shift], action_dispatch=TextToolMessage::PointerMove { center: Alt, lock_ratio: Shift }),
158+
entry!(KeyDown(MouseLeft); action_dispatch=TextToolMessage::DragStart),
159+
entry!(KeyUp(MouseLeft); action_dispatch=TextToolMessage::DragStop),
160+
entry!(KeyDown(MouseRight); action_dispatch=TextToolMessage::CommitText),
158161
entry!(KeyDown(Escape); action_dispatch=TextToolMessage::CommitText),
159162
entry!(KeyDown(Enter); modifiers=[Accel], action_dispatch=TextToolMessage::CommitText),
160163
//

editor/src/messages/portfolio/document/graph_operation/graph_operation_message.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::messages::prelude::*;
66
use bezier_rs::Subpath;
77
use graph_craft::document::NodeId;
88
use graphene_core::raster::{BlendMode, ImageFrame};
9-
use graphene_core::text::Font;
9+
use graphene_core::text::{Font, TypesettingConfig};
1010
use graphene_core::vector::brush_stroke::BrushStroke;
1111
use graphene_core::vector::style::{Fill, Stroke};
1212
use graphene_core::vector::PointId;
@@ -93,9 +93,7 @@ pub enum GraphOperationMessage {
9393
id: NodeId,
9494
text: String,
9595
font: Font,
96-
size: f64,
97-
line_height_ratio: f64,
98-
character_spacing: f64,
96+
typesetting: TypesettingConfig,
9997
parent: LayerNodeIdentifier,
10098
insert_index: usize,
10199
},

editor/src/messages/portfolio/document/graph_operation/graph_operation_message_handler.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::messages::prelude::*;
77

88
use graph_craft::document::{NodeId, NodeInput};
99
use graphene_core::renderer::Quad;
10-
use graphene_core::text::Font;
10+
use graphene_core::text::{Font, TypesettingConfig};
1111
use graphene_core::vector::style::{Fill, Gradient, GradientStops, GradientType, LineCap, LineJoin, Stroke};
1212
use graphene_core::Color;
1313
use graphene_std::vector::convert_usvg_path;
@@ -174,15 +174,13 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
174174
id,
175175
text,
176176
font,
177-
size,
178-
line_height_ratio,
179-
character_spacing,
177+
typesetting,
180178
parent,
181179
insert_index,
182180
} => {
183181
let mut modify_inputs = ModifyInputsContext::new(network_interface, responses);
184182
let layer = modify_inputs.create_layer(id);
185-
modify_inputs.insert_text(text, font, size, line_height_ratio, character_spacing, layer);
183+
modify_inputs.insert_text(text, font, typesetting, layer);
186184
network_interface.move_layer_to_stack(layer, parent, insert_index, &[]);
187185
responses.add(GraphOperationMessage::StrokeSet { layer, stroke: Stroke::default() });
188186
responses.add(NodeGraphMessage::RunDocumentGraph);
@@ -279,7 +277,7 @@ fn import_usvg_node(modify_inputs: &mut ModifyInputsContext, node: &usvg::Node,
279277
}
280278
usvg::Node::Text(text) => {
281279
let font = Font::new(graphene_core::consts::DEFAULT_FONT_FAMILY.to_string(), graphene_core::consts::DEFAULT_FONT_STYLE.to_string());
282-
modify_inputs.insert_text(text.chunks().iter().map(|chunk| chunk.text()).collect(), font, 24., 1.2, 1., layer);
280+
modify_inputs.insert_text(text.chunks().iter().map(|chunk| chunk.text()).collect(), font, TypesettingConfig::default(), layer);
283281
modify_inputs.fill_set(Fill::Solid(Color::BLACK));
284282
}
285283
}

editor/src/messages/portfolio/document/graph_operation/utility_types.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use graph_craft::concrete;
99
use graph_craft::document::value::TaggedValue;
1010
use graph_craft::document::{NodeId, NodeInput};
1111
use graphene_core::raster::{BlendMode, ImageFrame};
12-
use graphene_core::text::Font;
12+
use graphene_core::text::{Font, TypesettingConfig};
1313
use graphene_core::vector::brush_stroke::BrushStroke;
1414
use graphene_core::vector::style::{Fill, Stroke};
1515
use graphene_core::vector::{PointId, VectorModificationType};
@@ -179,17 +179,19 @@ impl<'a> ModifyInputsContext<'a> {
179179
}
180180
}
181181

182-
pub fn insert_text(&mut self, text: String, font: Font, size: f64, line_height_ratio: f64, character_spacing: f64, layer: LayerNodeIdentifier) {
182+
pub fn insert_text(&mut self, text: String, font: Font, typesetting: TypesettingConfig, layer: LayerNodeIdentifier) {
183183
let stroke = resolve_document_node_type("Stroke").expect("Stroke node does not exist").default_node_template();
184184
let fill = resolve_document_node_type("Fill").expect("Fill node does not exist").default_node_template();
185185
let transform = resolve_document_node_type("Transform").expect("Transform node does not exist").default_node_template();
186186
let text = resolve_document_node_type("Text").expect("Text node does not exist").node_template_input_override([
187187
Some(NodeInput::scope("editor-api")),
188188
Some(NodeInput::value(TaggedValue::String(text), false)),
189189
Some(NodeInput::value(TaggedValue::Font(font), false)),
190-
Some(NodeInput::value(TaggedValue::F64(size), false)),
191-
Some(NodeInput::value(TaggedValue::F64(line_height_ratio), false)),
192-
Some(NodeInput::value(TaggedValue::F64(character_spacing), false)),
190+
Some(NodeInput::value(TaggedValue::F64(typesetting.font_size), false)),
191+
Some(NodeInput::value(TaggedValue::F64(typesetting.line_height_ratio), false)),
192+
Some(NodeInput::value(TaggedValue::F64(typesetting.character_spacing), false)),
193+
Some(NodeInput::value(TaggedValue::OptionalF64(typesetting.max_width), false)),
194+
Some(NodeInput::value(TaggedValue::OptionalF64(typesetting.max_height), false)),
193195
]);
194196

195197
let text_id = NodeId::new();

editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use graph_craft::imaginate_input::ImaginateSamplingMethod;
1616
use graph_craft::ProtoNodeIdentifier;
1717
use graphene_core::raster::brush_cache::BrushCache;
1818
use graphene_core::raster::{CellularDistanceFunction, CellularReturnType, Color, DomainWarpType, FractalType, Image, ImageFrame, NoiseType, RedGreenBlue, RedGreenBlueAlpha};
19-
use graphene_core::text::Font;
19+
use graphene_core::text::{Font, TypesettingConfig};
2020
use graphene_core::transform::Footprint;
2121
use graphene_core::vector::VectorData;
2222
use graphene_core::*;
@@ -2112,9 +2112,11 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
21122112
TaggedValue::Font(Font::new(graphene_core::consts::DEFAULT_FONT_FAMILY.into(), graphene_core::consts::DEFAULT_FONT_STYLE.into())),
21132113
false,
21142114
),
2115-
NodeInput::value(TaggedValue::F64(24.), false),
2116-
NodeInput::value(TaggedValue::F64(1.2), false),
2117-
NodeInput::value(TaggedValue::F64(1.), false),
2115+
NodeInput::value(TaggedValue::F64(TypesettingConfig::default().font_size), false),
2116+
NodeInput::value(TaggedValue::F64(TypesettingConfig::default().line_height_ratio), false),
2117+
NodeInput::value(TaggedValue::F64(TypesettingConfig::default().character_spacing), false),
2118+
NodeInput::value(TaggedValue::OptionalF64(TypesettingConfig::default().max_width), false),
2119+
NodeInput::value(TaggedValue::OptionalF64(TypesettingConfig::default().max_height), false),
21182120
],
21192121
..Default::default()
21202122
},
@@ -2126,6 +2128,8 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
21262128
"Size".to_string(),
21272129
"Line Height".to_string(),
21282130
"Character Spacing".to_string(),
2131+
"Max Width".to_string(),
2132+
"Max Height".to_string(),
21292133
],
21302134
output_names: vec!["Vector".to_string()],
21312135
..Default::default()

editor/src/messages/portfolio/document/node_graph/node_properties.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,27 @@ fn number_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, na
661661
.on_commit(commit_value)
662662
.widget_holder(),
663663
]),
664+
Some(&TaggedValue::OptionalF64(x)) => {
665+
// 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
666+
let toggle_enabled = move |checkbox_input: &CheckboxInput| TaggedValue::OptionalF64(if checkbox_input.checked { Some(100.) } else { None });
667+
widgets.extend_from_slice(&[
668+
Separator::new(SeparatorType::Unrelated).widget_holder(),
669+
Separator::new(SeparatorType::Related).widget_holder(),
670+
// The checkbox toggles if the value is Some or None
671+
CheckboxInput::new(x.is_some())
672+
.on_update(update_value(toggle_enabled, node_id, index))
673+
.on_commit(commit_value)
674+
.widget_holder(),
675+
Separator::new(SeparatorType::Related).widget_holder(),
676+
Separator::new(SeparatorType::Unrelated).widget_holder(),
677+
number_props
678+
.value(x)
679+
.on_update(update_value(move |x: &NumberInput| TaggedValue::OptionalF64(x.value), node_id, index))
680+
.disabled(x.is_none())
681+
.on_commit(commit_value)
682+
.widget_holder(),
683+
]);
684+
}
664685
_ => {}
665686
}
666687

@@ -1734,6 +1755,8 @@ pub(crate) fn text_properties(document_node: &DocumentNode, node_id: NodeId, _co
17341755
let size = number_widget(document_node, node_id, 3, "Size", NumberInput::default().unit(" px").min(1.), true);
17351756
let line_height_ratio = number_widget(document_node, node_id, 4, "Line Height", NumberInput::default().min(0.).step(0.1), true);
17361757
let character_spacing = number_widget(document_node, node_id, 5, "Character Spacing", NumberInput::default().min(0.).step(0.1), true);
1758+
let max_width = number_widget(document_node, node_id, 6, "Max Width", NumberInput::default().unit(" px").min(1.), false);
1759+
let max_height = number_widget(document_node, node_id, 7, "Max Height", NumberInput::default().unit(" px").min(1.), false);
17371760

17381761
let mut result = vec![LayoutGroup::Row { widgets: text }, LayoutGroup::Row { widgets: font }];
17391762
if let Some(style) = style {
@@ -1742,6 +1765,8 @@ pub(crate) fn text_properties(document_node: &DocumentNode, node_id: NodeId, _co
17421765
result.push(LayoutGroup::Row { widgets: size });
17431766
result.push(LayoutGroup::Row { widgets: line_height_ratio });
17441767
result.push(LayoutGroup::Row { widgets: character_spacing });
1768+
result.push(LayoutGroup::Row { widgets: max_width });
1769+
result.push(LayoutGroup::Row { widgets: max_height });
17451770
result
17461771
}
17471772

editor/src/messages/portfolio/document/overlays/utility_types.rs

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,26 +32,52 @@ impl core::hash::Hash for OverlayContext {
3232

3333
impl OverlayContext {
3434
pub fn quad(&mut self, quad: Quad, color_fill: Option<&str>) {
35+
self.dashed_quad(quad, color_fill, None, None);
36+
}
37+
38+
pub fn dashed_quad(&mut self, quad: Quad, color_fill: Option<&str>, dash_width: Option<f64>, gap_width: Option<f64>) {
39+
// Set the dash pattern
40+
if let Some(dash_width) = dash_width {
41+
let gap_width = gap_width.unwrap_or(1.);
42+
let array = js_sys::Array::new();
43+
array.push(&JsValue::from(dash_width - 1.));
44+
array.push(&JsValue::from(gap_width));
45+
self.render_context
46+
.set_line_dash(&JsValue::from(array))
47+
.map_err(|error| log::warn!("Error drawing dashed line: {:?}", error))
48+
.ok();
49+
}
50+
3551
self.render_context.begin_path();
3652
self.render_context.move_to(quad.0[3].x.round() - 0.5, quad.0[3].y.round() - 0.5);
53+
3754
for i in 0..4 {
3855
self.render_context.line_to(quad.0[i].x.round() - 0.5, quad.0[i].y.round() - 0.5);
3956
}
57+
4058
if let Some(color_fill) = color_fill {
4159
self.render_context.set_fill_style_str(color_fill);
4260
self.render_context.fill();
4361
}
62+
4463
self.render_context.set_stroke_style_str(COLOR_OVERLAY_BLUE);
4564
self.render_context.stroke();
65+
66+
// Reset the dash pattern back to solid
67+
if dash_width.is_some() {
68+
self.render_context
69+
.set_line_dash(&JsValue::from(js_sys::Array::new()))
70+
.map_err(|error| log::warn!("Error drawing dashed line: {:?}", error))
71+
.ok();
72+
}
4673
}
4774

4875
pub fn line(&mut self, start: DVec2, end: DVec2, color: Option<&str>) {
4976
self.dashed_line(start, end, color, None, None)
5077
}
5178

5279
pub fn dashed_line(&mut self, start: DVec2, end: DVec2, color: Option<&str>, dash_width: Option<f64>, gap_width: Option<f64>) {
53-
let start = start.round() - DVec2::splat(0.5);
54-
let end = end.round() - DVec2::splat(0.5);
80+
// Set the dash pattern
5581
if let Some(dash_width) = dash_width {
5682
let gap_width = gap_width.unwrap_or(1.);
5783
let array = js_sys::Array::new();
@@ -61,24 +87,24 @@ impl OverlayContext {
6187
.set_line_dash(&JsValue::from(array))
6288
.map_err(|error| log::warn!("Error drawing dashed line: {:?}", error))
6389
.ok();
64-
} else {
65-
let array = js_sys::Array::new();
66-
self.render_context
67-
.set_line_dash(&JsValue::from(array))
68-
.map_err(|error| log::warn!("Error drawing dashed line: {:?}", error))
69-
.ok();
7090
}
91+
92+
let start = start.round() - DVec2::splat(0.5);
93+
let end = end.round() - DVec2::splat(0.5);
94+
7195
self.render_context.begin_path();
7296
self.render_context.move_to(start.x, start.y);
7397
self.render_context.line_to(end.x, end.y);
7498
self.render_context.set_stroke_style_str(color.unwrap_or(COLOR_OVERLAY_BLUE));
7599
self.render_context.stroke();
76100

77-
// Reset the dash pattern to solid after drawing
78-
self.render_context
79-
.set_line_dash(&JsValue::from(js_sys::Array::new()))
80-
.map_err(|error| log::warn!("Error drawing dashed line: {:?}", error))
81-
.ok();
101+
// Reset the dash pattern back to solid
102+
if dash_width.is_some() {
103+
self.render_context
104+
.set_line_dash(&JsValue::from(js_sys::Array::new()))
105+
.map_err(|error| log::warn!("Error drawing dashed line: {:?}", error))
106+
.ok();
107+
}
82108
}
83109

84110
pub fn manipulator_handle(&mut self, position: DVec2, selected: bool) {

editor/src/messages/portfolio/portfolio_message_handler.rs

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::node_graph_executor::{ExportConfig, NodeGraphExecutor};
1515

1616
use graph_craft::document::value::TaggedValue;
1717
use graph_craft::document::{NodeId, NodeInput};
18-
use graphene_core::text::Font;
18+
use graphene_core::text::{Font, TypesettingConfig};
1919
use graphene_std::vector::style::{Fill, FillType, Gradient};
2020
use interpreted_executor::dynamic_executor::IntrospectError;
2121

@@ -530,7 +530,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
530530
}
531531

532532
// Upgrade Text node to include line height and character spacing, which were previously hardcoded to 1, from https://github.com/GraphiteEditor/Graphite/pull/2016
533-
if reference == "Text" && inputs_count == 4 {
533+
if reference == "Text" && inputs_count != 8 {
534534
let node_definition = resolve_document_node_type(reference).unwrap();
535535
let document_node = node_definition.default_node_template().document_node;
536536
document.network_interface.replace_implementation(node_id, &[], document_node.implementation.clone());
@@ -541,12 +541,34 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
541541
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), &[]);
542542
document.network_interface.set_input(&InputConnector::node(*node_id, 2), old_inputs[2].clone(), &[]);
543543
document.network_interface.set_input(&InputConnector::node(*node_id, 3), old_inputs[3].clone(), &[]);
544-
document
545-
.network_interface
546-
.set_input(&InputConnector::node(*node_id, 4), NodeInput::value(TaggedValue::F64(1.), false), &[]);
547-
document
548-
.network_interface
549-
.set_input(&InputConnector::node(*node_id, 5), NodeInput::value(TaggedValue::F64(1.), false), &[]);
544+
document.network_interface.set_input(
545+
&InputConnector::node(*node_id, 4),
546+
if inputs_count == 6 {
547+
old_inputs[4].clone()
548+
} else {
549+
NodeInput::value(TaggedValue::F64(TypesettingConfig::default().line_height_ratio), false)
550+
},
551+
&[],
552+
);
553+
document.network_interface.set_input(
554+
&InputConnector::node(*node_id, 5),
555+
if inputs_count == 6 {
556+
old_inputs[5].clone()
557+
} else {
558+
NodeInput::value(TaggedValue::F64(TypesettingConfig::default().character_spacing), false)
559+
},
560+
&[],
561+
);
562+
document.network_interface.set_input(
563+
&InputConnector::node(*node_id, 6),
564+
NodeInput::value(TaggedValue::OptionalF64(TypesettingConfig::default().max_width), false),
565+
&[],
566+
);
567+
document.network_interface.set_input(
568+
&InputConnector::node(*node_id, 7),
569+
NodeInput::value(TaggedValue::OptionalF64(TypesettingConfig::default().max_height), false),
570+
&[],
571+
);
550572
}
551573

552574
// Upgrade Sine, Cosine, and Tangent nodes to include a boolean input for whether the output should be in radians, which was previously the only option but is now not the default

0 commit comments

Comments
 (0)