diff --git a/com.unity.uiwidgets/Runtime/painting/text_painter.cs b/com.unity.uiwidgets/Runtime/painting/text_painter.cs index 1a48eedf0..479f8123e 100644 --- a/com.unity.uiwidgets/Runtime/painting/text_painter.cs +++ b/com.unity.uiwidgets/Runtime/painting/text_painter.cs @@ -56,6 +56,7 @@ public class TextPainter { TextDirection? _textDirection; float _textScaleFactor; Paragraph _layoutTemplate; + Paragraph _layoutCursorTemplate; Paragraph _paragraph; bool _needsLayout = true; int? _maxLines; @@ -106,6 +107,7 @@ public float textScaleFactor { _textScaleFactor = value; markNeedsLayout(); _layoutTemplate = null; + _layoutCursorTemplate = null; } } @@ -131,6 +133,7 @@ public InlineSpan text { if (!Equals(_text == null ? null : _text.style, value == null ? null : value.style)) { _layoutTemplate = null; + _layoutCursorTemplate = null; } _text = value; @@ -155,6 +158,7 @@ public TextDirection? textDirection { _textDirection = value; markNeedsLayout(); _layoutTemplate = null; + _layoutCursorTemplate = null; } } @@ -451,6 +455,45 @@ ParagraphStyle _createParagraphStyle(TextDirection defaultTextDirection = TextDi ); } + ParagraphStyle _createParagraphStyleWithNoStrutStyle(TextDirection defaultTextDirection = TextDirection.ltr) { + D.assert(textDirection != null, + () => "TextPainter.textDirection must be set to a non-null value before using the TextPainter."); + return _text.style?.getParagraphStyle( + textAlign: textAlign, + textDirection: textDirection ?? defaultTextDirection, + textScaleFactor: textScaleFactor, + maxLines: _maxLines, + textHeightBehavior: _textHeightBehavior, + ellipsis: _ellipsis, + locale: _locale + ) ?? new ParagraphStyle( + textAlign: textAlign, + textDirection: textDirection ?? defaultTextDirection, + maxLines: maxLines, + textHeightBehavior: _textHeightBehavior, + ellipsis: ellipsis, + locale: locale + ); + } + + public float preferredCursorHeight { + get { + if (_layoutCursorTemplate == null) { + var builder = new ParagraphBuilder(_createParagraphStyleWithNoStrutStyle(TextDirection.ltr) + ); // direction doesn't matter, text is just a space + if (text != null && text.style != null) { + builder.pushStyle(text.style.getTextStyle(textScaleFactor: textScaleFactor)); + } + + builder.addText(" "); + _layoutCursorTemplate = builder.build(); + _layoutCursorTemplate.layout(new ParagraphConstraints(float.PositiveInfinity)); + } + + return _layoutCursorTemplate.height(); + } + } + public float preferredLineHeight { get { if (_layoutTemplate == null) { diff --git a/com.unity.uiwidgets/Runtime/rendering/editable.cs b/com.unity.uiwidgets/Runtime/rendering/editable.cs index 8c755ae08..8a7e6a6a4 100644 --- a/com.unity.uiwidgets/Runtime/rendering/editable.cs +++ b/com.unity.uiwidgets/Runtime/rendering/editable.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text; using Unity.UIWidgets.async; using Unity.UIWidgets.foundation; using Unity.UIWidgets.gestures; @@ -385,16 +386,18 @@ void _handleSelectionChange( TextSelection nextSelection, SelectionChangedCause cause ) { - - bool focusingEmpty = nextSelection.baseOffset == 0 - && nextSelection.extentOffset == 0 - && !hasFocus; + //force to update selection no matter whether the selection really changes + //it should not affect the ui logics except some performance drop + /*bool focusingEmpty = nextSelection.baseOffset == 0 + && nextSelection.extentOffset == 0 + && !hasFocus; if (nextSelection == selection && cause != SelectionChangedCause.keyboard && !focusingEmpty) { + Debug.Log("[ZXW2] handle double tap6 <>>>"); return; - } - + }*/ + onSelectionChanged?.Invoke(nextSelection, this, cause); } @@ -1290,6 +1293,10 @@ public float preferredLineHeight { get { return _textPainter.preferredLineHeight; } } + private float preferredCursorHeight { + get { return _textPainter.preferredCursorHeight; } + } + float _preferredHeight(float width) { bool lockedMax = maxLines != null && minLines == null; bool lockedBoth = maxLines != null && minLines == maxLines; @@ -1454,7 +1461,7 @@ public void selectWordsInRange(Offset from = null, Offset to = null, SelectionCh ? firstWord : _selectWordAtOffset( _textPainter.getPositionForOffset(globalToLocal(to - _paintOffset))); - + _handleSelectionChange( new TextSelection( baseOffset: firstWord.baseOffset, @@ -1537,14 +1544,26 @@ Rect _getCaretPrototype { switch (Application.platform) { case RuntimePlatform.IPhonePlayer: return Rect.fromLTWH(0.0f, 0.0f, cursorWidth, - preferredLineHeight + 2.0f); + preferredCursorHeight + 2.0f); default: - return Rect.fromLTWH(0.0f, EditableUtils._kCaretHeightOffset, cursorWidth, - preferredLineHeight - 2.0f * EditableUtils._kCaretHeightOffset); + return Rect.fromLTWH(0.0f, 0.0f, cursorWidth, + preferredCursorHeight); + } + } + } + + Rect _getCaretPrototypeForEmptyLine { + get { + switch (Application.platform) { + case RuntimePlatform.IPhonePlayer: + return Rect.fromLTWH(0.0f, (preferredLineHeight - preferredCursorHeight) / 2, cursorWidth, + preferredCursorHeight + 2.0f); + default: + return Rect.fromLTWH(0.0f, (preferredLineHeight - preferredCursorHeight) / 2, cursorWidth, + preferredCursorHeight); } } } - protected override void performLayout() { BoxConstraints constraints = this.constraints; @@ -1577,13 +1596,37 @@ void _paintCaret(Canvas canvas, Offset effectiveOffset, TextPosition textPositio _textLayoutLastMinWidth == constraints.minWidth, () => $"Last width ({_textLayoutLastMinWidth}, {_textLayoutLastMaxWidth}) not the same as max width constraint ({constraints.minWidth}, {constraints.maxWidth})."); var paint = new Paint() {color = _floatingCursorOn ? backgroundCursorColor : _cursorColor}; - var caretOffset = _textPainter.getOffsetForCaret(textPosition, _caretPrototype) + effectiveOffset; - Rect caretRect = _caretPrototype.shift(caretOffset); + + //force offset to 0 (at the beginning of the text if the text is currently focused + if (hasFocus && textPosition.offset == -1) + { + textPosition = new TextPosition(0, textPosition.affinity); + } + //if there is no contents in this editable yet, we use the _getCaretPrototypeForEmptyLine API to fetch the proper caret height + var currentCaretPrototype = _caretPrototype; + var content = new StringBuilder(); + text.computeToPlainText(content, false, false); + + //Debug.Log("[ZXW] text is " + content.ToString()); + if (string.IsNullOrEmpty(content.ToString())) { + currentCaretPrototype = _getCaretPrototypeForEmptyLine; + } + + //Debug.Log("[ZXW] caret offset is " + currentCaretPrototype.top + " " + _caretPrototype.top + " " + _getCaretPrototypeForEmptyLine.top); + + + var caretOffset = _textPainter.getOffsetForCaret(textPosition, currentCaretPrototype) + effectiveOffset; + + Rect caretRect = currentCaretPrototype.shift(caretOffset); + + //Debug.Log("[ZXW] caretRect is " + effectiveOffset.dy + " " + caretOffset.dy + " " + caretRect.top); + + if (_cursorOffset != null) { caretRect = caretRect.shift(_cursorOffset); + //Debug.Log("[ZXW] caretRect shift " + _cursorOffset.dy + " " + caretRect.top); } - - float? caretHeight = _textPainter.getFullHeightForCaret(textPosition, _caretPrototype); + float? caretHeight = _textPainter.getFullHeightForCaret(textPosition, currentCaretPrototype); if (caretHeight != null) { switch (Application.platform) { case RuntimePlatform.IPhonePlayer: @@ -1598,16 +1641,20 @@ void _paintCaret(Canvas canvas, Offset effectiveOffset, TextPosition textPositio default: caretRect = Rect.fromLTWH( caretRect.left, - caretRect.top - EditableUtils._kCaretHeightOffset, + caretRect.top, caretRect.width, caretHeight.Value ); + + //Debug.Log("[ZXW] caretRect final " + caretRect.height + " " + caretRect.top); break; } } caretRect = caretRect.shift(_getPixelPerfectCursorOffset(caretRect)); - + + //Debug.Log("[ZXW] caretRect pixel perfect " + caretRect.height + " " + caretRect.top); + if (cursorRadius == null) { canvas.drawRect(caretRect, paint); }