|
|
|
|
|
|
using System.Collections.Generic; |
|
|
|
using System.Xml.Schema; |
|
|
|
using Unity.UIWidgets.foundation; |
|
|
|
using Unity.UIWidgets.gestures; |
|
|
|
using Unity.UIWidgets.painting; |
|
|
|
|
|
|
using Rect = Unity.UIWidgets.ui.Rect; |
|
|
|
|
|
|
|
namespace Unity.UIWidgets.rendering { |
|
|
|
class EditableUtils { |
|
|
|
public static readonly float _kCaretGap = 1.0f; |
|
|
|
public static readonly float _kCaretHeightOffset = 2.0f; |
|
|
|
public static readonly Offset _kFloatingCaretSizeIncrease = new Offset(0.5f, 1.0f); |
|
|
|
public static readonly float _kFloatingCaretRadius = 1.0f; |
|
|
|
} |
|
|
|
|
|
|
|
public delegate void SelectionChangedHandler(TextSelection selection, RenderEditable renderObject, |
|
|
|
SelectionChangedCause cause); |
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
public class RenderEditable : RenderBox { |
|
|
|
public static readonly char obscuringCharacter = '•'; |
|
|
|
static readonly float _kCaretGap = 1.0f; |
|
|
|
static readonly float _kCaretHeightOffset = 2.0f; |
|
|
|
static readonly Offset _kFloatingCaretSizeIncrease = new Offset(0.5f, 1.0f); |
|
|
|
static readonly float _kFloatingCaretRadius = 1.0f; |
|
|
|
|
|
|
|
TextPainter _textPainter; |
|
|
|
Color _cursorColor; |
|
|
|
int? _maxLines; |
|
|
|
int? _minLines; |
|
|
|
bool _expands; |
|
|
|
Color _selectionColor; |
|
|
|
ViewportOffset _offset; |
|
|
|
ValueNotifier<bool> _showCursor; |
|
|
|
TextSelection _selection; |
|
|
|
bool _obscureText; |
|
|
|
TapGestureRecognizer _tap; |
|
|
|
LongPressGestureRecognizer _longPress; |
|
|
|
DoubleTapGestureRecognizer _doubleTap; |
|
|
|
public bool ignorePointer; |
|
|
|
public SelectionChangedHandler onSelectionChanged; |
|
|
|
public CaretChangedHandler onCaretChanged; |
|
|
|
Rect _lastCaretRect; |
|
|
|
float? _textLayoutLastWidth; |
|
|
|
List<TextBox> _selectionRects; |
|
|
|
Rect _caretPrototype; |
|
|
|
bool _hasVisualOverflow = false; |
|
|
|
Offset _lastTapDownPosition; |
|
|
|
|
|
|
|
ViewportOffset offset, |
|
|
|
ValueNotifier<bool> showCursor, |
|
|
|
float textScaleFactor = 1.0f, |
|
|
|
ValueNotifier<bool> showCursor = null, |
|
|
|
bool? hasFocus = null, |
|
|
|
int? maxLines = 1, |
|
|
|
int? minLines = null, |
|
|
|
|
|
|
float textScaleFactor = 1.0f, |
|
|
|
bool obscureText = false, |
|
|
|
ViewportOffset offset = null, |
|
|
|
bool obscureText = false, |
|
|
|
float cursorWidth = 1.0f, |
|
|
|
Radius cursorRadius = null, |
|
|
|
bool paintCursorAboveText = false, |
|
|
|
|
|
|
floatingCursorAddedMargin = floatingCursorAddedMargin ?? EdgeInsets.fromLTRB(4, 4, 4, 5); |
|
|
|
D.assert(textSelectionDelegate != null); |
|
|
|
D.assert(minLines == null || minLines > 0); |
|
|
|
D.assert((minLines == null) || maxLines >= minLines, () => "minLines can't be greater than maxLines"); |
|
|
|
this._textPainter = new TextPainter(text: text, textAlign: textAlign, textDirection: textDirection, |
|
|
|
textScaleFactor: textScaleFactor, strutStyle: strutStyle); |
|
|
|
D.assert(maxLines == null || maxLines > 0); |
|
|
|
D.assert((maxLines == null) || (minLines == null) || maxLines >= minLines, |
|
|
|
() => "minLines can't be greater than maxLines"); |
|
|
|
D.assert(offset != null); |
|
|
|
D.assert(cursorWidth >= 0.0f); |
|
|
|
this._textPainter = new TextPainter( |
|
|
|
text: text, |
|
|
|
textAlign: textAlign, |
|
|
|
textDirection: textDirection, |
|
|
|
textScaleFactor: textScaleFactor, |
|
|
|
strutStyle: strutStyle); |
|
|
|
|
|
|
|
this._backgroundCursorColor = backgroundCursorColor; |
|
|
|
this._showCursor = showCursor ?? new ValueNotifier<bool>(false); |
|
|
|
this._hasFocus = hasFocus ?? false; |
|
|
|
this._maxLines = maxLines; |
|
|
|
|
|
|
this._longPress = new LongPressGestureRecognizer(debugOwner: this); |
|
|
|
this._longPress.onLongPress = this._handleLongPress; |
|
|
|
|
|
|
|
this._backgroundCursorColor = backgroundCursorColor; |
|
|
|
|
|
|
|
public static readonly char obscuringCharacter = '•'; |
|
|
|
public SelectionChangedHandler onSelectionChanged; |
|
|
|
float? _textLayoutLastWidth; |
|
|
|
public CaretChangedHandler onCaretChanged; |
|
|
|
public bool ignorePointer; |
|
|
|
|
|
|
|
float _devicePixelRatio; |
|
|
|
|
|
|
|
public float devicePixelRatio { |
|
|
|
get { return this._devicePixelRatio; } |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
float _devicePixelRatio; |
|
|
|
|
|
|
|
public Color backgroundCursorColor { |
|
|
|
get { return this._backgroundCursorColor; } |
|
|
|
set { |
|
|
|
if (this.backgroundCursorColor == value) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
this._backgroundCursorColor = value; |
|
|
|
this.markNeedsPaint(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
Color _backgroundCursorColor; |
|
|
|
|
|
|
|
public bool paintCursorAboveText { |
|
|
|
get { return this._paintCursorOnTop; } |
|
|
|
set { |
|
|
|
if (this._paintCursorOnTop == value) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
this._paintCursorOnTop = value; |
|
|
|
this.markNeedsLayout(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
bool _paintCursorOnTop; |
|
|
|
|
|
|
|
public Offset cursorOffset { |
|
|
|
get { return this._cursorOffset; } |
|
|
|
set { |
|
|
|
if (this._cursorOffset == value) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
this._cursorOffset = value; |
|
|
|
this.markNeedsLayout(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
Offset _cursorOffset; |
|
|
|
|
|
|
|
public EdgeInsets floatingCursorAddedMargin { |
|
|
|
get { return this._floatingCursorAddedMargin; } |
|
|
|
set { |
|
|
|
if (this._floatingCursorAddedMargin == value) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
this._floatingCursorAddedMargin = value; |
|
|
|
this.markNeedsPaint(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
EdgeInsets _floatingCursorAddedMargin; |
|
|
|
|
|
|
|
bool _floatingCursorOn = false; |
|
|
|
Offset _floatingCursorOffset; |
|
|
|
TextPosition _floatingCursorTextPosition; |
|
|
|
|
|
|
|
public bool selectionEnabled { |
|
|
|
get { return this.enableInteractiveSelection ?? !this.obscureText; } |
|
|
|
} |
|
|
|
bool _obscureText; |
|
|
|
|
|
|
|
public bool obscureText { |
|
|
|
get { return this._obscureText; } |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
public TextSelectionDelegate textSelectionDelegate; |
|
|
|
Rect _lastCaretRect; |
|
|
|
|
|
|
|
|
|
|
|
public ValueListenable<bool> selectionStartInViewport { |
|
|
|
get { return this._selectionStartInViewport; } |
|
|
|
|
|
|
|
|
|
|
readonly ValueNotifier<bool> _selectionEndInViewport = new ValueNotifier<bool>(true); |
|
|
|
|
|
|
|
|
|
|
|
DoubleTapGestureRecognizer _doubleTap; |
|
|
|
|
|
|
|
void _updateSelectionExtentsVisibility(Offset effectiveOffset) { |
|
|
|
Rect visibleRegion = Offset.zero & this.size; |
|
|
|
Offset startOffset = this._textPainter.getOffsetForCaret( |
|
|
|
|
|
|
.contains(startOffset + effectiveOffset); |
|
|
|
|
|
|
|
Offset endOffset = this._textPainter.getOffsetForCaret( |
|
|
|
new TextPosition(offset: this._selection.end, affinity: this._selection.affinity), |
|
|
|
new TextPosition(offset: this._selection.end, affinity: this._selection.affinity), |
|
|
|
Rect.zero |
|
|
|
); |
|
|
|
this._selectionEndInViewport.value = visibleRegion |
|
|
|
|
|
|
|
|
|
|
int _baseOffset = -1; |
|
|
|
|
|
|
|
int _previousCursorLocation; |
|
|
|
int _previousCursorLocation = -1; |
|
|
|
|
|
|
|
bool _resetCursor = false; |
|
|
|
|
|
|
|
|
|
|
this.markNeedsLayout(); |
|
|
|
} |
|
|
|
|
|
|
|
TextPainter _textPainter; |
|
|
|
|
|
|
|
public TextSpan text { |
|
|
|
get { return this._textPainter.text; } |
|
|
|
set { |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
Color _cursorColor; |
|
|
|
|
|
|
|
public Color cursorColor { |
|
|
|
get { return this._cursorColor; } |
|
|
|
set { |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
Color _backgroundCursorColor; |
|
|
|
|
|
|
|
public Color backgroundCursorColor { |
|
|
|
get { return this._backgroundCursorColor; } |
|
|
|
set { |
|
|
|
if (this.backgroundCursorColor == value) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
this._backgroundCursorColor = value; |
|
|
|
this.markNeedsPaint(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
ValueNotifier<bool> _showCursor; |
|
|
|
|
|
|
|
public ValueNotifier<bool> showCursor { |
|
|
|
get { return this._showCursor; } |
|
|
|
set { |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
int? _maxLines; |
|
|
|
|
|
|
|
public int? maxLines { |
|
|
|
get { return this._maxLines; } |
|
|
|
set { |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
int? _minLines; |
|
|
|
|
|
|
|
public int? minLines { |
|
|
|
get { return this._minLines; } |
|
|
|
set { |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
bool _expands; |
|
|
|
|
|
|
|
public bool expands { |
|
|
|
get { return this._expands; } |
|
|
|
set { |
|
|
|
|
|
|
this.markNeedsTextLayout(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
Color _selectionColor; |
|
|
|
|
|
|
|
public Color selectionColor { |
|
|
|
get { return this._selectionColor; } |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
List<TextBox> _selectionRects; |
|
|
|
|
|
|
|
TextSelection _selection; |
|
|
|
|
|
|
|
public TextSelection selection { |
|
|
|
get { return this._selection; } |
|
|
|
set { |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
ViewportOffset _offset; |
|
|
|
|
|
|
|
public ViewportOffset offset { |
|
|
|
get { return this._offset; } |
|
|
|
set { |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
this._cursorWidth = value; |
|
|
|
this.markNeedsLayout(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool _paintCursorOnTop; |
|
|
|
|
|
|
|
public bool paintCursorAboveText { |
|
|
|
get { return this._paintCursorOnTop; } |
|
|
|
set { |
|
|
|
if (this._paintCursorOnTop == value) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
this._paintCursorOnTop = value; |
|
|
|
this.markNeedsLayout(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
Offset _cursorOffset; |
|
|
|
|
|
|
|
public Offset cursorOffset { |
|
|
|
get { return this._cursorOffset; } |
|
|
|
set { |
|
|
|
if (this._cursorOffset == value) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
this._cursorOffset = value; |
|
|
|
this.markNeedsLayout(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public EdgeInsets floatingCursorAddedMargin { |
|
|
|
get { return this._floatingCursorAddedMargin; } |
|
|
|
set { |
|
|
|
if (this._floatingCursorAddedMargin == value) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
this._floatingCursorAddedMargin = value; |
|
|
|
this.markNeedsPaint(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
EdgeInsets _floatingCursorAddedMargin; |
|
|
|
|
|
|
|
bool _floatingCursorOn = false; |
|
|
|
Offset _floatingCursorOffset; |
|
|
|
TextPosition _floatingCursorTextPosition; |
|
|
|
|
|
|
|
|
|
|
|
bool? _enableInteractiveSelection; |
|
|
|
|
|
|
|
public bool? enableInteractiveSelection { |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public float preferredLineHeight { |
|
|
|
get { return this._textPainter.preferredLineHeight; } |
|
|
|
public bool selectionEnabled { |
|
|
|
get { return this.enableInteractiveSelection ?? !this.obscureText; } |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
base.detach(); |
|
|
|
} |
|
|
|
|
|
|
|
bool _isMultiline { |
|
|
|
get { return this._maxLines != 1; } |
|
|
|
} |
|
|
|
|
|
|
|
Axis _viewportAxis { |
|
|
|
get { return this._isMultiline ? Axis.vertical : Axis.horizontal; } |
|
|
|
} |
|
|
|
|
|
|
|
Offset _paintOffset { |
|
|
|
get { |
|
|
|
switch (this._viewportAxis) { |
|
|
|
case Axis.horizontal: |
|
|
|
return new Offset(-this.offset.pixels, 0.0f); |
|
|
|
case Axis.vertical: |
|
|
|
return new Offset(0.0f, -this.offset.pixels); |
|
|
|
} |
|
|
|
|
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
float _viewportExtent { |
|
|
|
get { |
|
|
|
D.assert(this.hasSize); |
|
|
|
switch (this._viewportAxis) { |
|
|
|
case Axis.horizontal: |
|
|
|
return this.size.width; |
|
|
|
case Axis.vertical: |
|
|
|
return this.size.height; |
|
|
|
} |
|
|
|
|
|
|
|
return 0.0f; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
float _getMaxScrollExtent(Size contentSize) { |
|
|
|
D.assert(this.hasSize); |
|
|
|
switch (this._viewportAxis) { |
|
|
|
case Axis.horizontal: |
|
|
|
return Mathf.Max(0.0f, contentSize.width - this.size.width); |
|
|
|
case Axis.vertical: |
|
|
|
return Mathf.Max(0.0f, contentSize.height - this.size.height); |
|
|
|
} |
|
|
|
|
|
|
|
return 0.0f; |
|
|
|
} |
|
|
|
|
|
|
|
float _maxScrollExtent = 0; |
|
|
|
|
|
|
|
bool _hasVisualOverflow { |
|
|
|
get { return this._maxScrollExtent > 0 || this._paintOffset != Offset.zero; } |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Returns the local coordinates of the endpoints of the given selection.
|
|
|
|
///
|
|
|
|
/// If the selection is collapsed (and therefore occupies a single point), the
|
|
|
|
|
|
|
return this._textPainter.maxIntrinsicWidth + this.cursorWidth; |
|
|
|
} |
|
|
|
|
|
|
|
public float preferredLineHeight { |
|
|
|
get { return this._textPainter.preferredLineHeight; } |
|
|
|
} |
|
|
|
|
|
|
|
float _preferredHeight(float width) { |
|
|
|
bool lockedMax = this.maxLines != null && this.minLines == null; |
|
|
|
bool lockedBoth = this.maxLines != null && this.minLines == this.maxLines; |
|
|
|
bool singleLine = this.maxLines == 1; |
|
|
|
if (singleLine || lockedMax || lockedBoth) { |
|
|
|
return this.preferredLineHeight * this.maxLines.Value; |
|
|
|
} |
|
|
|
|
|
|
|
bool minLimited = this.minLines != null && this.minLines > 1; |
|
|
|
bool maxLimited = this.maxLines != null; |
|
|
|
if (minLimited || maxLimited) { |
|
|
|
this._layoutText(width); |
|
|
|
if (minLimited && this._textPainter.height < this.preferredLineHeight * this.minLines.Value) { |
|
|
|
return this.preferredLineHeight * this.minLines.Value; |
|
|
|
} |
|
|
|
|
|
|
|
if (maxLimited && this._textPainter.height > this.preferredLineHeight * this.maxLines.Value) { |
|
|
|
return this.preferredLineHeight * this.maxLines.Value; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (!width.isFinite()) { |
|
|
|
var text = this._textPainter.text.text; |
|
|
|
int lines = 1; |
|
|
|
for (int index = 0; index < text.Length; ++index) { |
|
|
|
if (text[index] == 0x0A) { |
|
|
|
lines += 1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return this.preferredLineHeight * lines; |
|
|
|
} |
|
|
|
|
|
|
|
this._layoutText(width); |
|
|
|
return Mathf.Max(this.preferredLineHeight, this._textPainter.height); |
|
|
|
} |
|
|
|
|
|
|
|
protected override float computeMinIntrinsicHeight(float width) { |
|
|
|
return this._preferredHeight(width); |
|
|
|
} |
|
|
|
|
|
|
return this._textPainter.computeDistanceToActualBaseline(baseline); |
|
|
|
} |
|
|
|
|
|
|
|
protected override bool hitTestSelf(Offset position) { |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
TapGestureRecognizer _tap; |
|
|
|
LongPressGestureRecognizer _longPress; |
|
|
|
|
|
|
|
public override void handleEvent(PointerEvent evt, HitTestEntry entry) { |
|
|
|
if (this.ignorePointer) { |
|
|
|
return; |
|
|
|
|
|
|
this._longPress.addPointer((PointerDownEvent) evt); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
Offset _lastTapDownPosition; |
|
|
|
|
|
|
|
public void handleTapDown(TapDownDetails details) { |
|
|
|
this._lastTapDownPosition = details.globalPosition; |
|
|
|
|
|
|
D.assert(this._lastTapDownPosition != null); |
|
|
|
if (this.onSelectionChanged != null) { |
|
|
|
TextPosition position = |
|
|
|
this._textPainter.getPositionForOffset(this.globalToLocal(this._lastTapDownPosition - this._paintOffset)); |
|
|
|
this._textPainter.getPositionForOffset( |
|
|
|
this.globalToLocal(this._lastTapDownPosition - this._paintOffset)); |
|
|
|
TextRange word = this._textPainter.getWordBoundary(position); |
|
|
|
if (position.offset - word.start <= 1) { |
|
|
|
this.onSelectionChanged( |
|
|
|
|
|
|
return new TextSelection(baseOffset: word.start, extentOffset: word.end); |
|
|
|
} |
|
|
|
|
|
|
|
Rect _caretPrototype; |
|
|
|
|
|
|
|
var caretMargin = _kCaretGap + this.cursorWidth; |
|
|
|
var caretMargin = EditableUtils._kCaretGap + this.cursorWidth; |
|
|
|
var avialableWidth = Mathf.Max(0.0f, constraintWidth - caretMargin); |
|
|
|
var maxWidth = this._isMultiline ? avialableWidth : float.PositiveInfinity; |
|
|
|
this._textPainter.layout(minWidth: avialableWidth, maxWidth: maxWidth); |
|
|
|
|
|
|
get { |
|
|
|
switch (Application.platform) { |
|
|
|
case RuntimePlatform.IPhonePlayer: |
|
|
|
return Rect.fromLTWH(0.0f, -_kCaretHeightOffset + 0.5f, this.cursorWidth, |
|
|
|
return Rect.fromLTWH(0.0f, -EditableUtils._kCaretHeightOffset + 0.5f, this.cursorWidth, |
|
|
|
return Rect.fromLTWH(0.0f, _kCaretHeightOffset, this.cursorWidth, |
|
|
|
this.preferredLineHeight - 2.0f * _kCaretHeightOffset); |
|
|
|
return Rect.fromLTWH(0.0f, EditableUtils._kCaretHeightOffset, this.cursorWidth, |
|
|
|
this.preferredLineHeight - 2.0f * EditableUtils._kCaretHeightOffset); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
var textPainterSize = this._textPainter.size; |
|
|
|
this.size = new Size(this.constraints.maxWidth, |
|
|
|
this.constraints.constrainHeight(this._preferredHeight(this.constraints.maxWidth))); |
|
|
|
var contentSize = new Size(textPainterSize.width + _kCaretGap + this.cursorWidth, |
|
|
|
var contentSize = new Size(textPainterSize.width + EditableUtils._kCaretGap + this.cursorWidth, |
|
|
|
var _maxScrollExtent = this._getMaxScrollExtend(contentSize); |
|
|
|
this._hasVisualOverflow = _maxScrollExtent > 0.0; |
|
|
|
this.offset.applyViewportDimension(this._viewportExtend); |
|
|
|
this.offset.applyContentDimensions(0.0f, _maxScrollExtent); |
|
|
|
} |
|
|
|
|
|
|
|
public override void paint(PaintingContext context, Offset offset) { |
|
|
|
this._layoutText(this.constraints.maxWidth); |
|
|
|
if (this._hasVisualOverflow) { |
|
|
|
context.pushClipRect(this.needsCompositing, offset, Offset.zero & this.size, this._paintContents); |
|
|
|
} |
|
|
|
else { |
|
|
|
this._paintContents(context, offset); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
protected override bool hitTestSelf(Offset position) { |
|
|
|
return true; |
|
|
|
this._maxScrollExtent = this._getMaxScrollExtent(contentSize); |
|
|
|
this.offset.applyViewportDimension(this._viewportExtent); |
|
|
|
this.offset.applyContentDimensions(0.0f, this._maxScrollExtent); |
|
|
|
// describeSemanticsConfiguration todo
|
|
|
|
|
|
|
|
Offset _getPixelPerfectCursorOffset(Rect caretRect) { |
|
|
|
Offset caretPosition = this.localToGlobal(caretRect.topLeft); |
|
|
|
float pixelMultiple = 1.0f / this._devicePixelRatio; |
|
|
|
|
|
|
if (this._cursorOffset != null) { |
|
|
|
caretRect = caretRect.shift(this._cursorOffset); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
caretRect.top - _kCaretHeightOffset, |
|
|
|
caretRect.top - EditableUtils._kCaretHeightOffset, |
|
|
|
caretRect.width, |
|
|
|
this._textPainter.getFullHeightForCaret(textPosition, this._caretPrototype).Value |
|
|
|
); |
|
|
|
|
|
|
this.markNeedsPaint(); |
|
|
|
} |
|
|
|
|
|
|
|
// describeSemanticsConfiguration todo
|
|
|
|
|
|
|
|
void _paintFloatingCaret(Canvas canvas, Offset effectiveOffset) { |
|
|
|
D.assert(this._textLayoutLastWidth == this.constraints.maxWidth); |
|
|
|
D.assert(this._floatingCursorOn); |
|
|
|
|
|
|
float sizeAdjustmentX = _kFloatingCaretSizeIncrease.dx; |
|
|
|
float sizeAdjustmentY = _kFloatingCaretSizeIncrease.dy; |
|
|
|
float sizeAdjustmentX = EditableUtils._kFloatingCaretSizeIncrease.dx; |
|
|
|
float sizeAdjustmentY = EditableUtils._kFloatingCaretSizeIncrease.dy; |
|
|
|
|
|
|
|
if (this._resetFloatingCursorAnimationValue != null) { |
|
|
|
sizeAdjustmentX = |
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
Rect caretRect = floatingCaretPrototype.shift(effectiveOffset); |
|
|
|
Radius floatingCursorRadius = Radius.circular(_kFloatingCaretRadius); |
|
|
|
Radius floatingCursorRadius = Radius.circular(EditableUtils._kFloatingCaretRadius); |
|
|
|
RRect caretRRect = RRect.fromRectAndRadius(caretRect, floatingCursorRadius); |
|
|
|
canvas.drawRRect(caretRRect, paint); |
|
|
|
} |
|
|
|
|
|
|
// todo
|
|
|
|
} |
|
|
|
|
|
|
|
float _preferredHeight(float width) { |
|
|
|
bool lockedMax = this.maxLines != null && this.minLines == null; |
|
|
|
bool lockedBoth = this.maxLines != null && this.minLines == this.maxLines; |
|
|
|
bool singleLine = this.maxLines == 1; |
|
|
|
if (singleLine || lockedMax || lockedBoth) { |
|
|
|
return this.preferredLineHeight * this.maxLines.Value; |
|
|
|
} |
|
|
|
|
|
|
|
bool minLimited = this.minLines != null && this.minLines > 1; |
|
|
|
bool maxLimited = this.maxLines != null; |
|
|
|
if (minLimited || maxLimited) { |
|
|
|
this._layoutText(width); |
|
|
|
if (minLimited && this._textPainter.height < this.preferredLineHeight * this.minLines.Value) { |
|
|
|
return this.preferredLineHeight * this.minLines.Value; |
|
|
|
} |
|
|
|
|
|
|
|
if (maxLimited && this._textPainter.height > this.preferredLineHeight * this.maxLines.Value) { |
|
|
|
return this.preferredLineHeight * this.maxLines.Value; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (!width.isFinite()) { |
|
|
|
var text = this._textPainter.text.text; |
|
|
|
int lines = 1; |
|
|
|
for (int index = 0; index < text.Length; ++index) { |
|
|
|
if (text[index] == 0x0A) { |
|
|
|
lines += 1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return this.preferredLineHeight * lines; |
|
|
|
} |
|
|
|
|
|
|
|
this._layoutText(width); |
|
|
|
return Mathf.Max(this.preferredLineHeight, this._textPainter.height); |
|
|
|
public override Rect describeApproximatePaintClip(RenderObject child) { |
|
|
|
return this._hasVisualOverflow ? Offset.zero & this.size : null; |
|
|
|
bool _isMultiline { |
|
|
|
get { return this._maxLines != 1; } |
|
|
|
} |
|
|
|
|
|
|
|
Axis _viewportAxis { |
|
|
|
get { return this._isMultiline ? Axis.vertical : Axis.horizontal; } |
|
|
|
} |
|
|
|
|
|
|
|
Offset _paintOffset { |
|
|
|
get { |
|
|
|
switch (this._viewportAxis) { |
|
|
|
case Axis.horizontal: |
|
|
|
return new Offset(-this.offset.pixels, 0.0f); |
|
|
|
case Axis.vertical: |
|
|
|
return new Offset(0.0f, -this.offset.pixels); |
|
|
|
} |
|
|
|
|
|
|
|
return null; |
|
|
|
public override void paint(PaintingContext context, Offset offset) { |
|
|
|
this._layoutText(this.constraints.maxWidth); |
|
|
|
if (this._hasVisualOverflow) { |
|
|
|
context.pushClipRect(this.needsCompositing, offset, Offset.zero & this.size, this._paintContents); |
|
|
|
} |
|
|
|
|
|
|
|
float _viewportExtend { |
|
|
|
get { |
|
|
|
D.assert(this.hasSize); |
|
|
|
switch (this._viewportAxis) { |
|
|
|
case Axis.horizontal: |
|
|
|
return this.size.width; |
|
|
|
case Axis.vertical: |
|
|
|
return this.size.height; |
|
|
|
} |
|
|
|
|
|
|
|
return 0.0f; |
|
|
|
else { |
|
|
|
this._paintContents(context, offset); |
|
|
|
} |
|
|
|
|
|
|
|
float _getMaxScrollExtend(Size contentSize) { |
|
|
|
D.assert(this.hasSize); |
|
|
|
switch (this._viewportAxis) { |
|
|
|
case Axis.horizontal: |
|
|
|
return Mathf.Max(0.0f, contentSize.width - this.size.width); |
|
|
|
case Axis.vertical: |
|
|
|
return Mathf.Max(0.0f, contentSize.height - this.size.height); |
|
|
|
} |
|
|
|
|
|
|
|
return 0.0f; |
|
|
|
} |
|
|
|
|
|
|
|
public override Rect describeApproximatePaintClip(RenderObject child) { |
|
|
|
return this._hasVisualOverflow ? Offset.zero & this.size : null; |
|
|
|
} |
|
|
|
|
|
|
|
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
|
|
|