core: Add string_utils char boundary functions

This commit is contained in:
jmckiern 2020-12-15 22:57:28 +00:00 committed by Mike Welsh
parent b81cb1a3eb
commit beed570475
3 changed files with 35 additions and 31 deletions

View File

@ -10,6 +10,7 @@ use crate::font::{round_down_to_pixel, Glyph};
use crate::html::{BoxBounds, FormatSpans, LayoutBox, LayoutContent, TextFormat}; use crate::html::{BoxBounds, FormatSpans, LayoutBox, LayoutContent, TextFormat};
use crate::prelude::*; use crate::prelude::*;
use crate::shape_utils::DrawCommand; use crate::shape_utils::DrawCommand;
use crate::string_utils;
use crate::tag_utils::SwfMovie; use crate::tag_utils::SwfMovie;
use crate::transform::Transform; use crate::transform::Transform;
use crate::types::{Degrees, Percent}; use crate::types::{Degrees, Percent};
@ -1023,11 +1024,7 @@ impl<'gc> EditText<'gc> {
&& local_position.1 <= params.height() && local_position.1 <= params.height()
{ {
if local_position.0 >= x + (advance / 2) { if local_position.0 >= x + (advance / 2) {
let mut idx = pos + 1; result = Some(string_utils::next_char_boundary(text, pos));
while !text.is_char_boundary(idx) {
idx += 1;
}
result = Some(idx);
} else { } else {
result = Some(pos); result = Some(pos);
} }
@ -1065,10 +1062,7 @@ impl<'gc> EditText<'gc> {
if selection.start() > 0 { if selection.start() > 0 {
// Delete previous character // Delete previous character
let text = self.text(); let text = self.text();
let mut start = selection.start() - 1; let start = string_utils::prev_char_boundary(&text, selection.start());
while !text.is_char_boundary(start) {
start -= 1;
}
self.replace_text(start, selection.start(), "", context); self.replace_text(start, selection.start(), "", context);
self.set_selection( self.set_selection(
Some(TextSelection::for_position(start)), Some(TextSelection::for_position(start)),
@ -1082,10 +1076,7 @@ impl<'gc> EditText<'gc> {
if selection.end() < self.text_length() { if selection.end() < self.text_length() {
// Delete next character // Delete next character
let text = self.text(); let text = self.text();
let mut end = selection.start() + 1; let end = string_utils::next_char_boundary(&text, selection.start());
while !text.is_char_boundary(end) {
end += 1;
}
self.replace_text(selection.start(), end, "", context); self.replace_text(selection.start(), end, "", context);
// No need to change selection // No need to change selection
changed = true; changed = true;
@ -1450,19 +1441,13 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
let length = text.len(); let length = text.len();
match key_code { match key_code {
ButtonKeyCode::Left if selection.to > 0 => { ButtonKeyCode::Left if selection.to > 0 => {
selection.to -= 1; selection.to = string_utils::prev_char_boundary(text, selection.to);
while !text.is_char_boundary(selection.to) {
selection.to -= 1;
}
if !context.input.is_key_down(KeyCode::Shift) { if !context.input.is_key_down(KeyCode::Shift) {
selection.from = selection.to; selection.from = selection.to;
} }
} }
ButtonKeyCode::Right if selection.to < length => { ButtonKeyCode::Right if selection.to < length => {
selection.to += 1; selection.to = string_utils::next_char_boundary(text, selection.to);
while !text.is_char_boundary(selection.to) {
selection.to += 1;
}
if !context.input.is_key_down(KeyCode::Shift) { if !context.input.is_key_down(KeyCode::Shift) {
selection.from = selection.to; selection.from = selection.to;
} }

View File

@ -7,6 +7,7 @@ use crate::font::{EvalParameters, Font};
use crate::html::dimensions::{BoxBounds, Position, Size}; use crate::html::dimensions::{BoxBounds, Position, Size};
use crate::html::text_format::{FormatSpans, TextFormat, TextSpan}; use crate::html::text_format::{FormatSpans, TextFormat, TextSpan};
use crate::shape_utils::DrawCommand; use crate::shape_utils::DrawCommand;
use crate::string_utils;
use crate::tag_utils::SwfMovie; use crate::tag_utils::SwfMovie;
use gc_arena::{Collect, GcCell, MutationContext}; use gc_arena::{Collect, GcCell, MutationContext};
use std::cmp::{max, min}; use std::cmp::{max, min};
@ -720,16 +721,10 @@ impl<'gc> LayoutBox<'gc> {
// This ensures that the space causing the line break // This ensures that the space causing the line break
// is included in the line it broke. // is included in the line it broke.
let next_breakpoint = if last_breakpoint + breakpoint + 1 >= text.len() let next_breakpoint = string_utils::next_char_boundary(
{ text,
text.len() last_breakpoint + breakpoint,
} else { );
let mut nb = last_breakpoint + breakpoint + 1;
while !text.is_char_boundary(nb) {
nb += 1;
}
nb
};
layout_context.append_text( layout_context.append_text(
&text[last_breakpoint..next_breakpoint], &text[last_breakpoint..next_breakpoint],

View File

@ -1,5 +1,29 @@
///! Utilities for operating on strings in SWF files. ///! Utilities for operating on strings in SWF files.
/// Gets the position of the previous char
/// `pos` must already lie on a char boundary
pub fn prev_char_boundary(slice: &str, pos: usize) -> usize {
if pos == 0 {
return pos;
}
let mut idx = pos - 1;
while !slice.is_char_boundary(idx) {
idx -= 1;
}
idx
}
/// Gets the byte position of the next char
/// `pos` must already lie on a char boundary
pub fn next_char_boundary(slice: &str, pos: usize) -> usize {
if let Some(c) = slice[pos..].chars().next() {
pos + c.len_utf8()
} else {
slice.len()
}
}
/// Creates a `String` from an iterator of UTF-16 code units. /// Creates a `String` from an iterator of UTF-16 code units.
/// TODO: Unpaired surrogates will get replaced with the Unicode replacement character. /// TODO: Unpaired surrogates will get replaced with the Unicode replacement character.
pub fn utf16_iter_to_string<I: Iterator<Item = u16>>(it: I) -> String { pub fn utf16_iter_to_string<I: Iterator<Item = u16>>(it: I) -> String {