Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit f68de3f

Browse files
authored
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.
1 parent ce0e9e7 commit f68de3f

File tree

5 files changed

+79
-38
lines changed

5 files changed

+79
-38
lines changed

lib/ui/text.dart

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1866,23 +1866,32 @@ class Paragraph extends NativeFieldWrapperClass2 {
18661866
}
18671867
List<int> _getPositionForOffset(double dx, double dy) native 'Paragraph_getPositionForOffset';
18681868

1869-
/// Returns the [start, end] of the word at the given offset. Characters not
1870-
/// part of a word, such as spaces, symbols, and punctuation, have word breaks
1871-
/// on both sides. In such cases, this method will return [offset, offset+1].
1872-
/// Word boundaries are defined more precisely in Unicode Standard Annex #29
1873-
/// http://www.unicode.org/reports/tr29/#Word_Boundaries
1874-
List<int> getWordBoundary(dynamic position) {
1875-
// TODO(gspencergoog): have this take only a TextPosition once the framework
1876-
// code is calling it with that.
1877-
if (position is TextPosition) {
1878-
return _getWordBoundary(position.offset);
1879-
} else {
1880-
final int offset = position;
1881-
return _getWordBoundary(offset);
1882-
}
1869+
/// Returns the [TextRange] of the word at the given [TextPosition].
1870+
///
1871+
/// Characters not part of a word, such as spaces, symbols, and punctuation,
1872+
/// have word breaks on both sides. In such cases, this method will return
1873+
/// [offset, offset+1]. Word boundaries are defined more precisely in Unicode
1874+
/// Standard Annex #29 http://www.unicode.org/reports/tr29/#Word_Boundaries
1875+
TextRange getWordBoundary(TextPosition position) {
1876+
final List<int> boundary = _getWordBoundary(position.offset);
1877+
return TextRange(start: boundary[0], end: boundary[1]);
18831878
}
18841879
List<int> _getWordBoundary(int offset) native 'Paragraph_getWordBoundary';
18851880

1881+
/// Returns the [TextRange] of the line at the given [TextPosition].
1882+
///
1883+
/// The newline (if any) is returned as part of the range.
1884+
///
1885+
/// Not valid until after layout.
1886+
///
1887+
/// This can potentially be expensive, since it needs to compute the line
1888+
/// metrics, so use it sparingly.
1889+
TextRange getLineBoundary(TextPosition position) {
1890+
final List<int> boundary = _getLineBoundary(position.offset);
1891+
return TextRange(start: boundary[0], end: boundary[1]);
1892+
}
1893+
List<int> _getLineBoundary(int offset) native 'Paragraph_getLineBoundary';
1894+
18861895
// Redirecting the paint function in this way solves some dependency problems
18871896
// in the C++ code. If we straighten out the C++ dependencies, we can remove
18881897
// this indirection.

lib/ui/text/paragraph.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ IMPLEMENT_WRAPPERTYPEINFO(ui, Paragraph);
3131
V(Paragraph, layout) \
3232
V(Paragraph, paint) \
3333
V(Paragraph, getWordBoundary) \
34+
V(Paragraph, getLineBoundary) \
3435
V(Paragraph, getRectsForRange) \
3536
V(Paragraph, getRectsForPlaceholders) \
3637
V(Paragraph, getPositionForOffset) \
@@ -134,6 +135,23 @@ Dart_Handle Paragraph::getWordBoundary(unsigned offset) {
134135
return result;
135136
}
136137

138+
Dart_Handle Paragraph::getLineBoundary(unsigned offset) {
139+
std::vector<txt::LineMetrics> metrics = m_paragraph->GetLineMetrics();
140+
int line_start = -1;
141+
int line_end = -1;
142+
for (txt::LineMetrics& line : metrics) {
143+
if (offset >= line.start_index && offset < line.end_index) {
144+
line_start = line.start_index;
145+
line_end = line.end_index;
146+
break;
147+
}
148+
}
149+
Dart_Handle result = Dart_NewListOf(Dart_CoreType_Int, 2);
150+
Dart_ListSetAt(result, 0, ToDart(line_start));
151+
Dart_ListSetAt(result, 1, ToDart(line_end));
152+
return result;
153+
}
154+
137155
std::vector<LineMetrics> Paragraph::computeLineMetrics() {
138156
std::vector<LineMetrics> result;
139157
std::vector<txt::LineMetrics> metrics = m_paragraph->GetLineMetrics();

lib/ui/text/paragraph.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ class Paragraph : public RefCountedDartWrappable<Paragraph> {
4949
std::vector<TextBox> getRectsForPlaceholders();
5050
Dart_Handle getPositionForOffset(double dx, double dy);
5151
Dart_Handle getWordBoundary(unsigned offset);
52+
Dart_Handle getLineBoundary(unsigned offset);
5253
std::vector<LineMetrics> computeLineMetrics();
5354

5455
size_t GetAllocationSize() override;

lib/web_ui/lib/src/engine/text/paragraph.dart

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -281,27 +281,22 @@ class EngineParagraph implements ui.Paragraph {
281281
}
282282

283283
@override
284-
List<int> getWordBoundary(dynamic position) {
285-
// TODO(gspencergoog): have this take only a TextPosition once the framework
286-
// code is calling it with that.
287-
if (position is ui.TextPosition) {
288-
ui.TextPosition textPosition = position;
289-
if (_plainText == null) {
290-
return <int>[textPosition.offset, textPosition.offset];
291-
}
292-
293-
final int start = WordBreaker.prevBreakIndex(_plainText, textPosition.offset);
294-
final int end = WordBreaker.nextBreakIndex(_plainText, textPosition.offset);
295-
return <int>[start, end];
296-
}
297-
284+
ui.TextRange getWordBoundary(ui.TextPosition position) {
285+
ui.TextPosition textPosition = position;
298286
if (_plainText == null) {
299-
return <int>[position, position];
287+
return ui.TextRange(start: textPosition.offset, end: textPosition.offset);
300288
}
301289

302-
final int start = WordBreaker.prevBreakIndex(_plainText, position);
303-
final int end = WordBreaker.nextBreakIndex(_plainText, position);
304-
return <int>[start, end];
290+
final int start = WordBreaker.prevBreakIndex(_plainText, textPosition.offset);
291+
final int end = WordBreaker.nextBreakIndex(_plainText, textPosition.offset);
292+
return ui.TextRange(start: start, end: end);
293+
}
294+
295+
@override
296+
ui.TextRange getLineBoundary(ui.TextPosition position) {
297+
// TODO(flutter_web): https://github.com/flutter/flutter/issues/39537
298+
// Depends upon LineMetrics measurement.
299+
return null;
305300
}
306301

307302
@override

lib/web_ui/lib/src/ui/text.dart

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1237,12 +1237,23 @@ abstract class Paragraph {
12371237
/// within the text.
12381238
TextPosition getPositionForOffset(Offset offset);
12391239

1240-
/// Returns the [start, end] of the word at the given offset. Characters not
1241-
/// part of a word, such as spaces, symbols, and punctuation, have word breaks
1242-
/// on both sides. In such cases, this method will return [offset, offset+1].
1243-
/// Word boundaries are defined more precisely in Unicode Standard Annex #29
1244-
/// http://www.unicode.org/reports/tr29/#Word_Boundaries
1245-
List<int> getWordBoundary(dynamic position);
1240+
/// Returns the [TextRange] of the word at the given [TextPosition].
1241+
///
1242+
/// Characters not part of a word, such as spaces, symbols, and punctuation,
1243+
/// have word breaks on both sides. In such cases, this method will return
1244+
/// [offset, offset+1]. Word boundaries are defined more precisely in Unicode
1245+
/// Standard Annex #29 http://www.unicode.org/reports/tr29/#Word_Boundaries
1246+
TextRange getWordBoundary(TextPosition position);
1247+
1248+
/// Returns the [TextRange] of the line at the given [TextPosition].
1249+
///
1250+
/// The newline (if any) is returned as part of the range.
1251+
///
1252+
/// Not valid until after layout.
1253+
///
1254+
/// This can potentially be expensive, since it needs to compute the line
1255+
/// metrics, so use it sparingly.
1256+
TextRange getLineBoundary(TextPosition position);
12461257

12471258
/// Returns a list of text boxes that enclose all placeholders in the paragraph.
12481259
///
@@ -1252,6 +1263,13 @@ abstract class Paragraph {
12521263
/// where positive y values indicate down.
12531264
List<TextBox> getBoxesForPlaceholders();
12541265

1266+
/// Returns the full list of [LineMetrics] that describe in detail the various
1267+
/// metrics of each laid out line.
1268+
///
1269+
/// Not valid until after layout.
1270+
///
1271+
/// This can potentially return a large amount of data, so it is not recommended
1272+
/// to repeatedly call this. Instead, cache the results.
12551273
List<LineMetrics> computeLineMetrics();
12561274
}
12571275

0 commit comments

Comments
 (0)