core: Fix caret and selection rendering

This commit fixes issues with caret and selection rendering:
1. They had the wrong height and were rendered lower than expected
   for some fonts and sizes.
2. The caret was not being rendered at all when there was no text,
   but only when the text was set earlier and then deleted.
3. The selection was rendered with translate_x=-1,
   which caused overlap over some glyphs.
This commit is contained in:
Kamil Jarosz 2024-01-10 00:20:41 +01:00 committed by TÖRÖK Attila
parent 6b7bca1fd6
commit 5ece6981ce
1 changed files with 67 additions and 58 deletions

View File

@ -934,7 +934,7 @@ impl<'gc> EditText<'gc> {
&& visible_selection.end() <= *end && visible_selection.end() <= *end
&& Utc::now().timestamp_subsec_millis() / 500 == 0 && Utc::now().timestamp_subsec_millis() / 500 == 0
{ {
Some((visible_selection.start() - start, end - start)) Some(visible_selection.start() - start)
} else { } else {
None None
} }
@ -958,8 +958,11 @@ impl<'gc> EditText<'gc> {
if let Some((text, _tf, font, params, color)) = if let Some((text, _tf, font, params, color)) =
lbox.as_renderable_text(edit_text.text_spans.displayed_text()) lbox.as_renderable_text(edit_text.text_spans.displayed_text())
{ {
let baseline_adjustment = let baseline = font.get_baseline_for_height(params.height());
font.get_baseline_for_height(params.height()) - params.height(); let descent = font.get_descent_for_height(params.height());
let baseline_adjustment = baseline - params.height();
let caret_height = baseline + descent;
let mut caret_x = Twips::ZERO;
font.evaluate( font.evaluate(
text, text,
self.text_transform(color, baseline_adjustment), self.text_transform(color, baseline_adjustment),
@ -967,29 +970,18 @@ impl<'gc> EditText<'gc> {
|pos, transform, glyph: &Glyph, advance, x| { |pos, transform, glyph: &Glyph, advance, x| {
if let Some(glyph_shape_handle) = glyph.shape_handle(context.renderer) { if let Some(glyph_shape_handle) = glyph.shape_handle(context.renderer) {
// If it's highlighted, override the color. // If it's highlighted, override the color.
match visible_selection { if matches!(visible_selection, Some(visible_selection) if visible_selection.contains(start + pos)) {
Some(visible_selection) if visible_selection.contains(start + pos) => {
// Draw black selection rect // Draw black selection rect
let selection_box = context.transform_stack.transform().matrix self.render_selection(context, x, advance, caret_height);
* Matrix::create_box(
advance.to_pixels() as f32,
params.height().to_pixels() as f32,
0.0,
x + Twips::from_pixels(-1.0),
Twips::from_pixels(2.0),
);
context.commands.draw_rect(Color::BLACK, selection_box);
// Set text color to white // Set text color to white
context.transform_stack.push(&Transform { context.transform_stack.push(&Transform {
matrix: transform.matrix, matrix: transform.matrix,
color_transform: ColorTransform::IDENTITY, color_transform: ColorTransform::IDENTITY,
}); });
} } else {
_ => {
context.transform_stack.push(transform); context.transform_stack.push(transform);
} }
}
// Render glyph. // Render glyph.
context context
@ -998,31 +990,21 @@ impl<'gc> EditText<'gc> {
context.transform_stack.pop(); context.transform_stack.pop();
} }
if let Some((caret_pos, length)) = caret { // Update caret position
if caret_pos == pos { if let Some(caret) = caret {
let caret = context.transform_stack.transform().matrix if pos == caret {
* Matrix::create_box( caret_x = x;
1.0, } else if caret > 0 && pos == caret - 1 {
params.height().to_pixels() as f32, // The caret may be rendered at the end, after all glyphs.
0.0, caret_x = x + advance;
x + Twips::from_pixels(-1.0),
Twips::from_pixels(2.0),
);
context.commands.draw_rect(color, caret);
} else if pos == length - 1 && caret_pos == length {
let caret = context.transform_stack.transform().matrix
* Matrix::create_box(
1.0,
params.height().to_pixels() as f32,
0.0,
x + advance,
Twips::from_pixels(2.0),
);
context.commands.draw_rect(color, caret);
} }
} }
}, },
); );
if caret.is_some() {
self.render_caret(context, caret_x, caret_height, color);
}
} }
if let Some(drawing) = lbox.as_renderable_drawing() { if let Some(drawing) = lbox.as_renderable_drawing() {
@ -1032,6 +1014,43 @@ impl<'gc> EditText<'gc> {
context.transform_stack.pop(); context.transform_stack.pop();
} }
fn render_selection(
self,
context: &mut RenderContext<'_, 'gc>,
x: Twips,
width: Twips,
height: Twips,
) {
let selection_box = context.transform_stack.transform().matrix
* Matrix::create_box(
width.to_pixels() as f32,
height.to_pixels() as f32,
0.0,
x,
Twips::ZERO,
);
context.commands.draw_rect(Color::BLACK, selection_box);
}
fn render_caret(
self,
context: &mut RenderContext<'_, 'gc>,
x: Twips,
height: Twips,
color: Color,
) {
let cursor_width = Twips::from_pixels(1.0);
let caret = context.transform_stack.transform().matrix
* Matrix::create_box(
cursor_width.to_pixels() as f32,
height.to_pixels() as f32,
0.0,
x - cursor_width,
Twips::ZERO,
);
context.commands.draw_rect(color, caret);
}
/// Attempts to bind this text field to a property of a display object. /// Attempts to bind this text field to a property of a display object.
/// If we find a parent display object matching the given path, we register oursevles and a property name with it. /// If we find a parent display object matching the given path, we register oursevles and a property name with it.
/// `set_text` will be called by the stage object whenever the property changes. /// `set_text` will be called by the stage object whenever the property changes.
@ -1977,19 +1996,9 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
&& visible_selection.start() == 0 && visible_selection.start() == 0
&& Utc::now().timestamp_subsec_millis() / 500 == 0 && Utc::now().timestamp_subsec_millis() / 500 == 0
{ {
let caret = context.transform_stack.transform().matrix let format = edit_text.text_spans.default_format();
* Matrix::create_box( let caret_height = format.size.map(Twips::from_pixels).unwrap_or_default();
1.0, self.render_caret(context, Twips::ZERO, caret_height, Color::BLACK);
edit_text
.text_spans
.default_format()
.size
.unwrap_or_default() as f32,
0.0,
Twips::from_pixels(-1.0),
Twips::from_pixels(2.0),
);
context.commands.draw_rect(Color::BLACK, caret);
} }
} }
} else { } else {