Skip to content

Commit 104ee79

Browse files
committed
text: Render the caret outside of text mask
The caret in its rightmost position can be rendered outside of the mask.
1 parent 71d972d commit 104ee79

File tree

1 file changed

+43
-10
lines changed

1 file changed

+43
-10
lines changed

core/src/display_object/edit_text.rs

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ use core::fmt;
3535
use gc_arena::barrier::unlock;
3636
use gc_arena::{Collect, Gc, Lock, Mutation, RefLock};
3737
use ruffle_macros::istr;
38+
use ruffle_render::commands::Command as RenderCommand;
3839
use ruffle_render::commands::CommandHandler;
3940
use ruffle_render::quality::StageQuality;
4041
use ruffle_render::transform::Transform;
@@ -1100,9 +1101,9 @@ impl<'gc> EditText<'gc> {
11001101
/// Render lines according to the given procedure.
11011102
///
11021103
/// This skips invisible lines.
1103-
fn render_lines<F>(self, context: &mut RenderContext<'_, 'gc>, f: F)
1104+
fn render_lines<F>(self, context: &mut RenderContext<'_, 'gc>, mut f: F)
11041105
where
1105-
F: Fn(&mut RenderContext<'_, 'gc>, &LayoutLine<'gc>),
1106+
F: FnMut(&mut RenderContext<'_, 'gc>, &LayoutLine<'gc>),
11061107
{
11071108
// Skip lines that are off-screen.
11081109
let lines_to_skip = self.scroll().saturating_sub(1);
@@ -1112,10 +1113,14 @@ impl<'gc> EditText<'gc> {
11121113
}
11131114

11141115
/// Render the visible text along with selection and the caret.
1115-
fn render_text(self, context: &mut RenderContext<'_, 'gc>) {
1116+
fn render_text(
1117+
self,
1118+
context: &mut RenderContext<'_, 'gc>,
1119+
render_state: &mut EditTextRenderState,
1120+
) {
11161121
self.render_selection_background(context);
11171122
self.render_lines(context, |context, line| {
1118-
self.render_layout_line(context, line);
1123+
self.render_layout_line(context, line, render_state);
11191124
});
11201125
}
11211126

@@ -1187,14 +1192,24 @@ impl<'gc> EditText<'gc> {
11871192
context.commands.draw_rect(color, selection_box);
11881193
}
11891194

1190-
fn render_layout_line(self, context: &mut RenderContext<'_, 'gc>, line: &LayoutLine<'gc>) {
1195+
fn render_layout_line(
1196+
self,
1197+
context: &mut RenderContext<'_, 'gc>,
1198+
line: &LayoutLine<'gc>,
1199+
render_state: &mut EditTextRenderState,
1200+
) {
11911201
for layout_box in line.boxes_iter() {
1192-
self.render_layout_box(context, layout_box);
1202+
self.render_layout_box(context, layout_box, render_state);
11931203
}
11941204
}
11951205

11961206
/// Render a layout box, plus its children.
1197-
fn render_layout_box(self, context: &mut RenderContext<'_, 'gc>, lbox: &LayoutBox<'gc>) {
1207+
fn render_layout_box(
1208+
self,
1209+
context: &mut RenderContext<'_, 'gc>,
1210+
lbox: &LayoutBox<'gc>,
1211+
render_state: &mut EditTextRenderState,
1212+
) {
11981213
let origin = lbox.bounds().origin();
11991214

12001215
// If text's top is under the textbox's bottom, skip drawing.
@@ -1293,7 +1308,7 @@ impl<'gc> EditText<'gc> {
12931308
);
12941309

12951310
if caret.is_some() {
1296-
self.render_caret(context, caret_x, caret_height, color);
1311+
self.render_caret(context, caret_x, caret_height, color, render_state);
12971312
}
12981313
}
12991314

@@ -1310,6 +1325,7 @@ impl<'gc> EditText<'gc> {
13101325
x: Twips,
13111326
height: Twips,
13121327
color: Color,
1328+
render_state: &mut EditTextRenderState,
13131329
) {
13141330
let mut caret = context.transform_stack.transform().matrix
13151331
* Matrix::create_box_with_rotation(
@@ -1321,7 +1337,12 @@ impl<'gc> EditText<'gc> {
13211337
);
13221338
let pixel_snapping = EditTextPixelSnapping::new(context.stage.quality());
13231339
pixel_snapping.apply(&mut caret);
1324-
context.commands.draw_line(color, caret);
1340+
1341+
// We have to draw the caret outside of the text mask.
1342+
render_state.draw_caret_command = Some(RenderCommand::DrawLine {
1343+
color,
1344+
matrix: caret,
1345+
});
13251346
}
13261347

13271348
/// Attempts to bind this text field to a property of a display object.
@@ -2693,7 +2714,8 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
26932714
..Default::default()
26942715
});
26952716

2696-
self.render_text(context);
2717+
let mut render_state = Default::default();
2718+
self.render_text(context, &mut render_state);
26972719

26982720
self.render_debug_boxes(
26992721
context,
@@ -2709,6 +2731,10 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
27092731
context.transform_stack.transform().matrix * mask,
27102732
);
27112733
context.commands.pop_mask();
2734+
2735+
if let Some(draw_caret_command) = render_state.draw_caret_command {
2736+
context.commands.commands.push(draw_caret_command);
2737+
}
27122738
}
27132739

27142740
fn allow_as_mask(&self) -> bool {
@@ -3615,3 +3641,10 @@ struct ImeData {
36153641
ime_end: usize,
36163642
text: String,
36173643
}
3644+
3645+
#[derive(Clone, Debug, Default)]
3646+
struct EditTextRenderState {
3647+
/// Used for delaying rendering the caret, so that it's
3648+
/// rendered outside of the text mask.
3649+
draw_caret_command: Option<RenderCommand>,
3650+
}

0 commit comments

Comments
 (0)