Add line boundary information to LineMetrics. (#13727)

This exposes the line boundary information a line by adding getLineBoundary to return the indices corresponding to a line around a TextPosition. The information is already calculated when calculating line metrics, so that we can enable moving the selection/cursor to the beginning/end of a line in a text field.
This commit is contained in:
Greg Spencer 2019-11-11 16:42:40 -08:00 committed by GitHub
parent ce0e9e79e6
commit f68de3f451
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 79 additions and 38 deletions

View File

@ -1866,23 +1866,32 @@ class Paragraph extends NativeFieldWrapperClass2 {
}
List<int> _getPositionForOffset(double dx, double dy) native 'Paragraph_getPositionForOffset';
/// Returns the [start, end] of the word at the given offset. Characters not
/// part of a word, such as spaces, symbols, and punctuation, have word breaks
/// on both sides. In such cases, this method will return [offset, offset+1].
/// Word boundaries are defined more precisely in Unicode Standard Annex #29
/// http://www.unicode.org/reports/tr29/#Word_Boundaries
List<int> getWordBoundary(dynamic position) {
// TODO(gspencergoog): have this take only a TextPosition once the framework
// code is calling it with that.
if (position is TextPosition) {
return _getWordBoundary(position.offset);
} else {
final int offset = position;
return _getWordBoundary(offset);
}
/// Returns the [TextRange] of the word at the given [TextPosition].
///
/// Characters not part of a word, such as spaces, symbols, and punctuation,
/// have word breaks on both sides. In such cases, this method will return
/// [offset, offset+1]. Word boundaries are defined more precisely in Unicode
/// Standard Annex #29 http://www.unicode.org/reports/tr29/#Word_Boundaries
TextRange getWordBoundary(TextPosition position) {
final List<int> boundary = _getWordBoundary(position.offset);
return TextRange(start: boundary[0], end: boundary[1]);
}
List<int> _getWordBoundary(int offset) native 'Paragraph_getWordBoundary';
/// Returns the [TextRange] of the line at the given [TextPosition].
///
/// The newline (if any) is returned as part of the range.
///
/// Not valid until after layout.
///
/// This can potentially be expensive, since it needs to compute the line
/// metrics, so use it sparingly.
TextRange getLineBoundary(TextPosition position) {
final List<int> boundary = _getLineBoundary(position.offset);
return TextRange(start: boundary[0], end: boundary[1]);
}
List<int> _getLineBoundary(int offset) native 'Paragraph_getLineBoundary';
// Redirecting the paint function in this way solves some dependency problems
// in the C++ code. If we straighten out the C++ dependencies, we can remove
// this indirection.

View File

@ -31,6 +31,7 @@ IMPLEMENT_WRAPPERTYPEINFO(ui, Paragraph);
V(Paragraph, layout) \
V(Paragraph, paint) \
V(Paragraph, getWordBoundary) \
V(Paragraph, getLineBoundary) \
V(Paragraph, getRectsForRange) \
V(Paragraph, getRectsForPlaceholders) \
V(Paragraph, getPositionForOffset) \
@ -134,6 +135,23 @@ Dart_Handle Paragraph::getWordBoundary(unsigned offset) {
return result;
}
Dart_Handle Paragraph::getLineBoundary(unsigned offset) {
std::vector<txt::LineMetrics> metrics = m_paragraph->GetLineMetrics();
int line_start = -1;
int line_end = -1;
for (txt::LineMetrics& line : metrics) {
if (offset >= line.start_index && offset < line.end_index) {
line_start = line.start_index;
line_end = line.end_index;
break;
}
}
Dart_Handle result = Dart_NewListOf(Dart_CoreType_Int, 2);
Dart_ListSetAt(result, 0, ToDart(line_start));
Dart_ListSetAt(result, 1, ToDart(line_end));
return result;
}
std::vector<LineMetrics> Paragraph::computeLineMetrics() {
std::vector<LineMetrics> result;
std::vector<txt::LineMetrics> metrics = m_paragraph->GetLineMetrics();

View File

@ -49,6 +49,7 @@ class Paragraph : public RefCountedDartWrappable<Paragraph> {
std::vector<TextBox> getRectsForPlaceholders();
Dart_Handle getPositionForOffset(double dx, double dy);
Dart_Handle getWordBoundary(unsigned offset);
Dart_Handle getLineBoundary(unsigned offset);
std::vector<LineMetrics> computeLineMetrics();
size_t GetAllocationSize() override;

View File

@ -281,27 +281,22 @@ class EngineParagraph implements ui.Paragraph {
}
@override
List<int> getWordBoundary(dynamic position) {
// TODO(gspencergoog): have this take only a TextPosition once the framework
// code is calling it with that.
if (position is ui.TextPosition) {
ui.TextPosition textPosition = position;
if (_plainText == null) {
return <int>[textPosition.offset, textPosition.offset];
}
final int start = WordBreaker.prevBreakIndex(_plainText, textPosition.offset);
final int end = WordBreaker.nextBreakIndex(_plainText, textPosition.offset);
return <int>[start, end];
}
ui.TextRange getWordBoundary(ui.TextPosition position) {
ui.TextPosition textPosition = position;
if (_plainText == null) {
return <int>[position, position];
return ui.TextRange(start: textPosition.offset, end: textPosition.offset);
}
final int start = WordBreaker.prevBreakIndex(_plainText, position);
final int end = WordBreaker.nextBreakIndex(_plainText, position);
return <int>[start, end];
final int start = WordBreaker.prevBreakIndex(_plainText, textPosition.offset);
final int end = WordBreaker.nextBreakIndex(_plainText, textPosition.offset);
return ui.TextRange(start: start, end: end);
}
@override
ui.TextRange getLineBoundary(ui.TextPosition position) {
// TODO(flutter_web): https://github.com/flutter/flutter/issues/39537
// Depends upon LineMetrics measurement.
return null;
}
@override

View File

@ -1237,12 +1237,23 @@ abstract class Paragraph {
/// within the text.
TextPosition getPositionForOffset(Offset offset);
/// Returns the [start, end] of the word at the given offset. Characters not
/// part of a word, such as spaces, symbols, and punctuation, have word breaks
/// on both sides. In such cases, this method will return [offset, offset+1].
/// Word boundaries are defined more precisely in Unicode Standard Annex #29
/// http://www.unicode.org/reports/tr29/#Word_Boundaries
List<int> getWordBoundary(dynamic position);
/// Returns the [TextRange] of the word at the given [TextPosition].
///
/// Characters not part of a word, such as spaces, symbols, and punctuation,
/// have word breaks on both sides. In such cases, this method will return
/// [offset, offset+1]. Word boundaries are defined more precisely in Unicode
/// Standard Annex #29 http://www.unicode.org/reports/tr29/#Word_Boundaries
TextRange getWordBoundary(TextPosition position);
/// Returns the [TextRange] of the line at the given [TextPosition].
///
/// The newline (if any) is returned as part of the range.
///
/// Not valid until after layout.
///
/// This can potentially be expensive, since it needs to compute the line
/// metrics, so use it sparingly.
TextRange getLineBoundary(TextPosition position);
/// Returns a list of text boxes that enclose all placeholders in the paragraph.
///
@ -1252,6 +1263,13 @@ abstract class Paragraph {
/// where positive y values indicate down.
List<TextBox> getBoxesForPlaceholders();
/// Returns the full list of [LineMetrics] that describe in detail the various
/// metrics of each laid out line.
///
/// Not valid until after layout.
///
/// This can potentially return a large amount of data, so it is not recommended
/// to repeatedly call this. Instead, cache the results.
List<LineMetrics> computeLineMetrics();
}