Commit e3cdd454 authored by Jason Simmons's avatar Jason Simmons Committed by GitHub

Calculate the caret position based on the next or previous rune (#7432)

The indexes passed to paragraph.getBoxesForRange are based on UTF-16 code
units.  If the caret is at a character that is encoded as multiple code units,
then the start and end indexes should be adjusted accordingly.
parent 0c7a9da9
......@@ -212,8 +212,16 @@ class TextPainter {
canvas.drawParagraph(_paragraph, offset);
}
bool _isUtf16Surrogate(int value) {
return value & 0xF800 == 0xD800;
}
Offset _getOffsetFromUpstream(int offset, Rect caretPrototype) {
List<ui.TextBox> boxes = _paragraph.getBoxesForRange(offset - 1, offset);
int prevCodeUnit = _text.codeUnitAt(offset - 1);
if (prevCodeUnit == null)
return null;
int prevRuneOffset = _isUtf16Surrogate(prevCodeUnit) ? offset - 2 : offset - 1;
List<ui.TextBox> boxes = _paragraph.getBoxesForRange(prevRuneOffset, offset);
if (boxes.isEmpty)
return null;
ui.TextBox box = boxes[0];
......@@ -223,7 +231,11 @@ class TextPainter {
}
Offset _getOffsetFromDownstream(int offset, Rect caretPrototype) {
List<ui.TextBox> boxes = _paragraph.getBoxesForRange(offset, offset + 1);
int nextCodeUnit = _text.codeUnitAt(offset + 1);
if (nextCodeUnit == null)
return null;
int nextRuneOffset = _isUtf16Surrogate(nextCodeUnit) ? offset + 2 : offset + 1;
List<ui.TextBox> boxes = _paragraph.getBoxesForRange(offset, nextRuneOffset);
if (boxes.isEmpty)
return null;
ui.TextBox box = boxes[0];
......
......@@ -160,6 +160,24 @@ class TextSpan {
return buffer.toString();
}
/// Returns the UTF-16 code unit at the given index in the flattened string.
/// Returns null if the index is out of bounds.
int codeUnitAt(int index) {
if (index < 0)
return null;
int offset = 0;
int result;
visitTextSpan((TextSpan span) {
if (index - offset < span.text.length) {
result = span.text.codeUnitAt(index - offset);
return false;
}
offset += span.text.length;
return true;
});
return result;
}
@override
String toString([String prefix = '']) {
StringBuffer buffer = new StringBuffer();
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui' as ui;
import 'package:flutter/painting.dart';
import 'package:test/test.dart';
void main() {
test("TextPainter caret test", () {
TextPainter painter = new TextPainter();
String text = 'A';
painter.text = new TextSpan(text: text);
painter.layout();
Offset caretOffset = painter.getOffsetForCaret(new ui.TextPosition(offset: 0), ui.Rect.zero);
expect(caretOffset.dx, 0);
caretOffset = painter.getOffsetForCaret(new ui.TextPosition(offset: text.length), ui.Rect.zero);
expect(caretOffset.dx, painter.width);
// Check that getOffsetForCaret handles a character that is encoded as a surrogate pair.
text = 'A\u{1F600}';
painter.text = new TextSpan(text: text);
painter.layout();
caretOffset = painter.getOffsetForCaret(new ui.TextPosition(offset: text.length), ui.Rect.zero);
expect(caretOffset.dx, painter.width);
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment