浏览代码

very basic editable text

/main
fzhangtj 6 年前
当前提交
639c9f04
共有 20 个文件被更改,包括 1525 次插入54 次删除
  1. 6
      Assets/UIWidgets/Tests/RenderEditable.cs
  2. 16
      Assets/UIWidgets/editor/editor_window.cs
  3. 3
      Assets/UIWidgets/foundation/basic_types.cs
  4. 1
      Assets/UIWidgets/painting/basic_types.cs
  5. 8
      Assets/UIWidgets/painting/text_painter.cs
  6. 173
      Assets/UIWidgets/painting/text_style.cs
  7. 33
      Assets/UIWidgets/rendering/editable.cs
  8. 27
      Assets/UIWidgets/service/text_editing.cs
  9. 4
      Assets/UIWidgets/ui/txt/paragraph.cs
  10. 3
      Assets/UIWidgets/ui/window.cs
  11. 2
      Assets/UIWidgets/widgets/binding.cs
  12. 8
      Assets/UIWidgets/widgets/framework.cs
  13. 71
      Assets/UIWidgets/Tests/EditableTextWiget.cs
  14. 3
      Assets/UIWidgets/Tests/EditableTextWiget.cs.meta
  15. 108
      Assets/UIWidgets/service/text_formatter.cs
  16. 3
      Assets/UIWidgets/service/text_formatter.cs.meta
  17. 513
      Assets/UIWidgets/service/text_input.cs
  18. 3
      Assets/UIWidgets/service/text_input.cs.meta
  19. 591
      Assets/UIWidgets/widgets/editable_text.cs
  20. 3
      Assets/UIWidgets/widgets/editable_text.cs.meta

6
Assets/UIWidgets/Tests/RenderEditable.cs


flexbox.add(flexItemBox(
new rendering.RenderEditable(span, TextDirection.ltr,
new _FixedViewportOffset(0.0), new ValueNotifier<bool>(true), this.rendererBindings.rendererBinding,
new _FixedViewportOffset(0.0), new ValueNotifier<bool>(true),
onSelectionChanged: selectionChanged, cursorColor: Color.fromARGB(255, 0, 0, 0),
maxLines: 100,
selectionColor: Color.fromARGB(255, 255, 0, 0))

}, style:new painting.TextStyle(height:1.0));
flexbox.add(flexItemBox(
new rendering.RenderEditable(span, TextDirection.ltr,
new _FixedViewportOffset(0.0), new ValueNotifier<bool>(true), this.rendererBindings.rendererBinding,
new _FixedViewportOffset(0.0), new ValueNotifier<bool>(true),
onSelectionChanged: selectionChanged, cursorColor: Color.fromARGB(255, 0, 0, 0),
maxLines: 100,
selectionColor: Color.fromARGB(255, 255, 0, 0))

}, style:new painting.TextStyle(height:1.0));
flexbox.add(flexItemBox(
new rendering.RenderEditable(span, TextDirection.ltr,
new _FixedViewportOffset(0.0), new ValueNotifier<bool>(true), this.rendererBindings.rendererBinding,
new _FixedViewportOffset(0.0), new ValueNotifier<bool>(true),
onSelectionChanged: selectionChanged, cursorColor: Color.fromARGB(255, 0, 0, 0),
selectionColor: Color.fromARGB(255, 255, 0, 0))
, width:300));

16
Assets/UIWidgets/editor/editor_window.cs


using System.Diagnostics;
using UIWidgets.async;
using UIWidgets.flow;
using UIWidgets.service;
using UIWidgets.ui;
using UnityEditor;
using UnityEngine;

readonly DateTime _epoch = new DateTime(Stopwatch.GetTimestamp());
readonly MicrotaskQueue _microtaskQueue = new MicrotaskQueue();
readonly TimerProvider _timerProvider = new TimerProvider();
readonly TextInput _textInput = new TextInput();
public void OnGUI() {
var evt = Event.current;

}));
}
}
if (_textInput != null)
{
_textInput.OnGUI();
}
if (_textInput != null)
{
_textInput.Update();
}
this.flushMicrotasks();
this._timerProvider.update();

public override Timer run(TimeSpan duration, Action callback) {
return this._timerProvider.run(duration, callback);
}
public override TextInput textInput
{
get { return _textInput; }
}
}
}

3
Assets/UIWidgets/foundation/basic_types.cs


using System.Collections.Generic;
namespace UIWidgets.foundation {
public delegate void ValueChanged<T>(T value);
public delegate IEnumerable<T> EnumerableFilter<T>(IEnumerable<T> input);
public static class CollectionUtils {

1
Assets/UIWidgets/painting/basic_types.cs


using System;
using System.ComponentModel;
using UIWidgets.foundation;
using UIWidgets.ui;
using UIWidgets.widgets;

8
Assets/UIWidgets/painting/text_painter.cs


{
Debug.Assert(text != null, "TextPainter.text must be set to a non-null value before using the TextPainter.");
Debug.Assert(textDirection != null, "TextPainter.textDirection must be set to a non-null value before using the TextPainter.");
if (_needsLayout && minWidth == _lastMaxWidth && maxWidth == _lastMaxWidth)
if (!_needsLayout && minWidth == _lastMaxWidth && maxWidth == _lastMaxWidth)
_needsLayout = false;
if (_paragraph == null)
{

{
D.assert(!_needsLayout);
var results = _paragraph.getRectsForRange(selection.start, selection.end);
if (results.Count > 0)
{
Debug.Log(string.Format(" getBoxesForSelection {0}", results[0]));
}
return results;
}

173
Assets/UIWidgets/painting/text_style.cs


using System;
using System.Collections.Generic;
using System.Linq;
using UIWidgets.foundation;
public class TextStyle
public class TextStyle : Diagnosticable
public readonly bool? inherit;
public readonly bool inherit;
public readonly Color color;
public readonly double? fontSize;
public readonly FontWeight? fontWeight;

public readonly double? height;
public readonly TextDecoration decoration;
public readonly string fontFamily;
public readonly string debugLabel;
const string _kDefaultDebugLabel = "unknown";
public TextStyle(bool? inherit = null, Color color = null, double? fontSize = null, FontWeight? fontWeight = null,
FontStyle? fontStyle = null, double? letterSpacing = null, double? wordSpacing = null,
TextBaseline? textBaseline = null, double? height = null, TextDecoration decoration = null, string fontFamily = null)
public TextStyle(bool inherit = true, Color color = null, double? fontSize = null,
FontWeight? fontWeight = null,
FontStyle? fontStyle = null, double? letterSpacing = null, double? wordSpacing = null,
TextBaseline? textBaseline = null, double? height = null, TextDecoration decoration = null,
string fontFamily = null, string debugLabel = null)
{
this.inherit = inherit;
this.color = color;

this.height = height;
this.decoration = decoration;
this.fontFamily = fontFamily;
this.debugLabel = debugLabel;
}
public ui.TextStyle getTextStyle(double textScaleFactor = 1.0)

textBaseline: textBaseline,
height: height,
fontFamily: fontFamily
);
);
}
public RenderComparison compareTo(TextStyle other)

{
return RenderComparison.paint;
}
public ParagraphStyle getParagraphStyle(TextAlign textAlign,
TextDirection textDirection, string ellipsis, int maxLines, double textScaleFactor = 1.0)
public ParagraphStyle getParagraphStyle(TextAlign textAlign,
TextDirection textDirection, string ellipsis, int maxLines, double textScaleFactor = 1.0)
return new ParagraphStyle(
textAlign, textDirection, fontWeight, fontStyle,
maxLines, (fontSize ?? _defaultFontSize) * textScaleFactor,
fontFamily, height, ellipsis
);
}
public TextStyle merge(TextStyle other)
{
if (other == null)
{
return this;
}
if (!other.inherit)
{
return other;
}
return new ParagraphStyle(
textAlign, textDirection, fontWeight, fontStyle,
maxLines, (fontSize ?? _defaultFontSize) * textScaleFactor,
fontFamily, height, ellipsis
);
string mergedDebugLabel = null;
D.assert(() =>
{
if (other.debugLabel != null || debugLabel != null)
{
mergedDebugLabel = string.Format("({0}).merge({1})", debugLabel??_kDefaultDebugLabel, other.debugLabel ?? _kDefaultDebugLabel);
}
return true;
});
return copyWith(
color: other.color,
fontFamily: other.fontFamily,
fontSize: other.fontSize,
fontWeight: other.fontWeight,
fontStyle: other.fontStyle,
letterSpacing: other.letterSpacing,
wordSpacing: other.wordSpacing,
textBaseline: other.textBaseline,
height: other.height,
decoration: other.decoration,
debugLabel: mergedDebugLabel
);
}
public TextStyle copyWith(Color color,
String fontFamily,
double? fontSize,
FontWeight? fontWeight,
FontStyle? fontStyle,
double? letterSpacing,
double? wordSpacing,
TextBaseline? textBaseline = null,
double? height = null,
Paint background = null,
TextDecoration decoration = null,
Color decorationColor = null,
string debugLabel = null)
{
string newDebugLabel = null;
D.assert(() => {
if (this.debugLabel != null)
{
newDebugLabel = debugLabel ?? string.Format("({0}).copyWith", this.debugLabel);
}
return true;
});
return new TextStyle(
inherit: inherit,
color: color ?? this.color,
fontFamily: fontFamily ?? this.fontFamily,
fontSize: fontSize ?? this.fontSize,
fontWeight: fontWeight ?? this.fontWeight,
fontStyle: fontStyle ?? this.fontStyle,
letterSpacing: letterSpacing ?? this.letterSpacing,
wordSpacing: wordSpacing ?? this.wordSpacing,
textBaseline: textBaseline ?? this.textBaseline,
height: height ?? this.height,
decoration: decoration ?? this.decoration,
debugLabel: newDebugLabel
);
public override void debugFillProperties(DiagnosticPropertiesBuilder properties)
{
base.debugFillProperties(properties);
List<DiagnosticsNode> styles = new List<DiagnosticsNode>();
styles.Add(new DiagnosticsProperty<Color>("color", color, defaultValue: null));
styles.Add(new StringProperty("family", fontFamily, defaultValue: null, quoted: false));
styles.Add(new DiagnosticsProperty<double?>("size", fontSize, defaultValue: null));
string weightDescription = "";
if (fontWeight != null)
{
switch (fontWeight)
{
case FontWeight.w400:
weightDescription = "400";
break;
case FontWeight.w700:
weightDescription = "700";
break;
}
}
styles.Add(new DiagnosticsProperty<FontWeight?>(
"weight",
fontWeight,
description: weightDescription,
defaultValue: null
));
styles.Add(new EnumProperty<FontStyle?>("style", fontStyle, defaultValue: null));
styles.Add(new DiagnosticsProperty<double?>("letterSpacing", letterSpacing, defaultValue: null));
styles.Add(new DiagnosticsProperty<double?>("wordSpacing", wordSpacing, defaultValue: null));
styles.Add(new EnumProperty<TextBaseline?>("baseline", textBaseline, defaultValue: null));
styles.Add(new DiagnosticsProperty<double?>("height", height, defaultValue: null));
if (decoration != null)
{
List<String> decorationDescription = new List<String>();
styles.Add(new DiagnosticsProperty<TextDecoration>("decoration", decoration, defaultValue: null,
level: DiagnosticLevel.hidden));
if (decoration != null)
decorationDescription.Add("$decoration");
D.assert(decorationDescription.isNotEmpty);
styles.Add(new MessageProperty("decoration", string.Join(" ", decorationDescription.ToArray())));
}
bool styleSpecified = styles.Any((DiagnosticsNode n) => !n.isFiltered(DiagnosticLevel.info));
properties.add(new DiagnosticsProperty<bool>("inherit", inherit,
level: (!styleSpecified && inherit) ? DiagnosticLevel.fine : DiagnosticLevel.info));
foreach (var style in styles)
{
properties.add(style);
}
if (!styleSpecified)
properties.add(new FlagProperty("inherit", value: inherit, ifTrue: "<all styles inherited>",
ifFalse: "<no style specified>"));
}
}
}

33
Assets/UIWidgets/rendering/editable.cs


public class RenderEditable : RenderBox
{
public static readonly string obscuringCharacter = "•";
public static readonly char obscuringCharacter = '•';
private static readonly double _kCaretGap = 1.0;
private static readonly double _kCaretHeightOffset = 2.0;
private static readonly double _kCaretWidth = 1.0;

private bool _obscureText;
private TapGestureRecognizer _tap;
private DoubleTapGestureRecognizer _doubleTap;
private bool ignorePointer;
private SelectionChangedHandler onSelectionChanged;
private CaretChangedHandler onCaretChanged;
public bool ignorePointer;
public SelectionChangedHandler onSelectionChanged;
public CaretChangedHandler onCaretChanged;
private Rect _lastCaretRect;
private double? _textLayoutLastWidth;
private List<TextBox> _selectionRects;

public RenderEditable(TextSpan text, TextDirection textDirection, ViewportOffset offset,
ValueNotifier<bool> showCursor,
GestureBinding binding,
TextAlign textAlign = TextAlign.left, double textScaleFactor = 1.0, Color cursorColor = null,
bool? hasFocus = null, int maxLines = 1, Color selectionColor = null,
TextSelection selection = null, bool obscureText = false, SelectionChangedHandler onSelectionChanged = null,

D.assert(_showCursor != null);
D.assert(!_showCursor.value || cursorColor != null);
_tap = new TapGestureRecognizer(binding, this);
_doubleTap = new DoubleTapGestureRecognizer(binding, this);
_tap.onTapDown = this._handleTapDown;
_tap.onTap = this._handleTap;
_doubleTap.onDoubleTap = this._handleDoubleTap;
// _tap = new TapGestureRecognizer(owner.binding, this);
// _doubleTap = new DoubleTapGestureRecognizer(owner.binding, this);
// _tap.onTapDown = this._handleTapDown;
// _tap.onTap = this._handleTap;
// _doubleTap.onDoubleTap = this._handleDoubleTap;
}
public bool obscureText

return;
}
hasFocus = value;
_hasFocus = value;
markNeedsSemanticsUpdate();
}
}

public override void attach(object ownerObject)
{
base.attach(ownerObject);
_tap = new TapGestureRecognizer(owner.binding, this);
_doubleTap = new DoubleTapGestureRecognizer(owner.binding, this);
_tap.onTapDown = this._handleTapDown;
_tap.onTap = this._handleTap;
_doubleTap.onDoubleTap = this._handleDoubleTap;
_offset.addListener(markNeedsLayout);
_showCursor.addListener(markNeedsPaint);
}

_tap.dispose();
_tap = null;
_doubleTap.dispose();
_doubleTap = null;
_offset.removeListener(markNeedsLayout);
_showCursor.removeListener(markNeedsPaint);
base.detach();

foreach (var box in _selectionRects)
{
Debug.Log(string.Format("draw box {0}", box.toRect().shift(effectiveOffset)));
canvas.drawRect(box.toRect().shift(effectiveOffset), BorderWidth.zero, BorderRadius.zero, paint);
}
}

{
return TextSelection.fromPosition(position);
}
return new TextSelection(baseOffset: word.start, extendOffset: word.end);
return new TextSelection(baseOffset: word.start, extentOffset: word.end);
}
private bool _isMultiline

27
Assets/UIWidgets/service/text_editing.cs


public class TextSelection : TextRange, IEquatable<TextSelection>
{
public readonly int baseOffset;
public readonly int extendOffset;
public readonly int extentOffset;
public TextSelection(int baseOffset, int extendOffset, TextAffinity affinity = TextAffinity.downstream,
bool isDirectional = false):base(baseOffset < extendOffset ? baseOffset: extendOffset,
baseOffset < extendOffset ? extendOffset : baseOffset )
public TextSelection(int baseOffset, int extentOffset, TextAffinity affinity = TextAffinity.downstream,
bool isDirectional = false):base(baseOffset < extentOffset ? baseOffset: extentOffset,
baseOffset < extentOffset ? extentOffset : baseOffset )
this.extendOffset = extendOffset;
this.extentOffset = extentOffset;
this.affinity = affinity;
this.isDirectional = isDirectional;
}

{
get
{
return new TextPosition(offset: extendOffset, affinity: affinity);
return new TextPosition(offset: extentOffset, affinity: affinity);
}
}

if (ReferenceEquals(this, other)) return true;
return baseOffset == other.baseOffset && extendOffset == other.extendOffset &&
return baseOffset == other.baseOffset && extentOffset == other.extentOffset &&
affinity == other.affinity && isDirectional == other.isDirectional;
}

{
unchecked
{
int hashCode = 0;
var hashCode = base.GetHashCode();
hashCode = (hashCode * 397) ^ extendOffset;
hashCode = (hashCode * 397) ^ extentOffset;
hashCode = (hashCode * 397) ^ (int) affinity;
hashCode = (hashCode * 397) ^ isDirectional.GetHashCode();
return hashCode;

return !Equals(left, right);
}
public TextSelection copyWith(int? baseOffset = null, int? extendOffset = null, TextAffinity? affinity = null,
public TextSelection copyWith(int? baseOffset = null, int? extentOffset = null, TextAffinity? affinity = null,
baseOffset??this.baseOffset, extendOffset??this.extendOffset, affinity??this.affinity,
baseOffset??this.baseOffset, extentOffset??this.extentOffset, affinity??this.affinity,
}
public override string ToString()
{
return string.Format("{0}, BaseOffset: {1}, ExtentOffset: {2}, Affinity: {3}, IsDirectional: {4}", base.ToString(), baseOffset, extentOffset, affinity, isDirectional);
}
}
}

4
Assets/UIWidgets/ui/txt/paragraph.cs


for (;;)
{
var run = _runs.getRun(runIndex);
if (run.start < run.end && run.start < line.end && run.end > line.start)
var run = runIndex < _runs.size ? _runs.getRun(runIndex) : null;
if (run != null && run.start < run.end && run.start < line.end && run.end > line.start)
{
var font = FontManager.instance.getOrCreate(run.style.safeFontFamily, run.style.UnityFontSize);
var ascent = font.ascent * (run.style.height??1.0);

3
Assets/UIWidgets/ui/window.cs


using System;
using UIWidgets.async;
using UIWidgets.service;
namespace UIWidgets.ui {
public delegate void VoidCallback();

public Timer run(Action callback) {
return this.run(TimeSpan.Zero, callback);
}
public abstract TextInput textInput { get; }
}
}

2
Assets/UIWidgets/widgets/binding.cs


public class WidgetsBinding : RendererBinding {
public WidgetsBinding(Window window) : base(window) {
_buildOwner = new BuildOwner(window);
_buildOwner = new BuildOwner(window);
}
public BuildOwner buildOwner {

8
Assets/UIWidgets/widgets/framework.cs


}
internal Element _currentElement {
get { return _registry[this]; }
get
{
Element result;
_registry.TryGetValue(this, out result);
return result;
}
}
public BuildContext currentContext {

D.assert(_child != null);
}
catch (Exception e) {
Debug.LogError(e);
built = ErrorWidget.builder(WidgetsD._debugReportException("building " + this, e));
this._child = this.updateChild(null, built, this.slot);
}

71
Assets/UIWidgets/Tests/EditableTextWiget.cs


using UIWidgets.editor;
using UIWidgets.painting;
using UIWidgets.widgets;
using UnityEditor;
using Color = UIWidgets.ui.Color;
namespace UIWidgets.Tests
{
public class EditableTextWiget: EditorWindow
{
private WindowAdapter windowAdapter;
private WidgetsBindings widgetsBindings;
private PaintingBinding paintingBinding;
private Widget root;
private Widget image;
[MenuItem("UIWidgetsTests/EditableTextWiget")]
public static void renderWidgets() {
EditorWindow.GetWindow(typeof(EditableTextWiget));
}
EditableTextWiget() {
}
void OnGUI() {
if (this.windowAdapter != null) {
this.windowAdapter.OnGUI();
}
}
private void Update() {
if (this.windowAdapter != null) {
this.windowAdapter.Update();
}
}
private void OnEnable() {
this.paintingBinding = new PaintingBinding(null);
paintingBinding.initInstances();
this.windowAdapter = new WindowAdapter(this);
this.root = new widgets.Container(
width: 200,
height: 200,
margin: EdgeInsets.all(30.0),
padding: EdgeInsets.all(15.0),
color: ui.Color.fromARGB(255, 244, 190, 85),
child: new EditableText(
maxLines: 100,
textInput: windowAdapter.textInput,
controller: new TextEditingController("click to edit"),
focusNode: new FocusNode(),
style: new TextStyle(),
selectionColor: Color.fromARGB(255, 255, 0, 0),
cursorColor: Color.fromARGB(255, 0, 0, 0)
)
);
this.widgetsBindings = new WidgetsBindings(windowAdapter);
if (widgetsBindings != null) {
widgetsBindings.attachRootWidget(root);
}
}
void OnDestroy() {
this.windowAdapter = null;
this.widgetsBindings = null;
}
}
}

3
Assets/UIWidgets/Tests/EditableTextWiget.cs.meta


fileFormatVersion: 2
guid: 1438fd697c854ab783eb666a6a62115c
timeCreated: 1537236780

108
Assets/UIWidgets/service/text_formatter.cs


using System;
using System.Text.RegularExpressions;
using UIWidgets.foundation;
namespace UIWidgets.service
{
public abstract class TextInputFormatter
{
public delegate TextEditingValue TextInputFormatFunction(TextEditingValue oldValue,
TextEditingValue newValue);
public abstract TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue);
static TextInputFormatter withFunction(TextInputFormatFunction formatFunction)
{
return new _SimpleTextInputFormatter(formatFunction);
}
}
internal class _SimpleTextInputFormatter : TextInputFormatter
{
public readonly TextInputFormatFunction formatFunction;
internal _SimpleTextInputFormatter(TextInputFormatFunction formatFunction)
{
D.assert(formatFunction != null);
this.formatFunction = formatFunction;
}
public override TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue)
{
return formatFunction(oldValue, newValue);
}
}
public class BlacklistingTextInputFormatter : TextInputFormatter
{
public readonly Regex blacklistedPattern;
public readonly string replacementString;
public static readonly BlacklistingTextInputFormatter singleLineFormatter
= new BlacklistingTextInputFormatter(new Regex(@"\n"));
public BlacklistingTextInputFormatter(Regex blacklistedPattern, string replacementString = "")
{
D.assert(blacklistedPattern != null);
this.blacklistedPattern = blacklistedPattern;
this.replacementString = replacementString;
}
public override TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue)
{
return Util._selectionAwareTextManipulation(newValue,
(substring) => blacklistedPattern.Replace(substring, replacementString));
}
}
internal static class Util
{
internal static TextEditingValue _selectionAwareTextManipulation(TextEditingValue value,
Func<string, string> substringManipulation)
{
int selectionStartIndex = value.selection.start;
int selectionEndIndex = value.selection.end;
string manipulatedText;
TextSelection manipulatedSelection = null;
if (selectionStartIndex < 0 || selectionEndIndex < 0)
{
manipulatedText = substringManipulation(value.text);
}
else
{
var beforeSelection = substringManipulation(
value.text.Substring(0, selectionStartIndex)
);
var inSelection = substringManipulation(
value.text.Substring(selectionStartIndex, selectionEndIndex - selectionStartIndex)
);
var afterSelection = substringManipulation(
value.text.Substring(selectionEndIndex)
);
manipulatedText = beforeSelection + inSelection + afterSelection;
if (value.selection.baseOffset > value.selection.extentOffset)
{
manipulatedSelection = value.selection.copyWith(
baseOffset: beforeSelection.Length + inSelection.Length,
extentOffset: beforeSelection.Length
);
}
else
{
manipulatedSelection = value.selection.copyWith(
baseOffset: beforeSelection.Length,
extentOffset: beforeSelection.Length + inSelection.Length
);
}
}
return new TextEditingValue(
text: manipulatedText,
selection: manipulatedSelection ?? TextSelection.collapsed(offset: -1),
composing: manipulatedText == value.text ? value.composing : TextRange.empty
);
}
}
}

3
Assets/UIWidgets/service/text_formatter.cs.meta


fileFormatVersion: 2
guid: 6fbc4090466b444f8423c68d5928dfa7
timeCreated: 1537166345

513
Assets/UIWidgets/service/text_input.cs


using System;
using System.Collections.Generic;
using System.Text;
using UIWidgets.foundation;
using UnityEngine;
namespace UIWidgets.service
{
public class TextEditingValue:IEquatable<TextEditingValue>
{
public readonly string text;
public readonly TextSelection selection;
public readonly TextRange composing;
public TextEditingValue(string text = "", TextSelection selection = null, TextRange composing = null)
{
this.text = text;
this.selection = selection ?? TextSelection.collapsed(-1);
this.composing = composing ?? TextRange.empty;
}
public TextEditingValue copyWith(string text = null, TextSelection selection = null, TextRange composing = null)
{
return new TextEditingValue(
text??this.text, selection??this.selection, composing??this.composing
);
}
public TextEditingValue insert(string text)
{
string newText;
TextSelection newSelection;
if (string.IsNullOrEmpty(text))
{
return this;
}
newText = selection.textBefore(this.text) + text + selection.textAfter(this.text);
newSelection = TextSelection.collapsed(selection.start + text.Length);
return new TextEditingValue(
text: newText, selection: newSelection,composing:TextRange.empty
);
}
public TextEditingValue deleteSelection()
{
if (selection.isCollapsed)
{
if (selection.start == 0)
{
return this;
}
return this.copyWith(text: text.Substring(0, selection.start - 1) + selection.textAfter(this.text),
selection: TextSelection.collapsed(selection.start - 1));
}
else
{
var newText = selection.textBefore(this.text) + selection.textAfter(this.text);
return this.copyWith(text: newText, selection: TextSelection.collapsed(selection.start));
}
}
public TextEditingValue moveLeft()
{
return moveSelection(-1);
}
public TextEditingValue moveRight()
{
return moveSelection(1);
}
public TextEditingValue extendLeft()
{
return moveExtent(-1);
}
public TextEditingValue extendRight()
{
return moveExtent(1);
}
public TextEditingValue moveExtent(int move)
{
int offset = selection.extentOffset + move;
offset = Math.Max(0, offset);
offset = Math.Min(offset, text.Length);
return this.copyWith(selection: selection.copyWith(extentOffset: offset));
}
public TextEditingValue moveSelection(int move)
{
int offset = selection.baseOffset + move;
offset = Math.Max(0, offset);
offset = Math.Min(offset, text.Length);
return this.copyWith(selection: TextSelection.collapsed(offset));
}
public static readonly TextEditingValue empty = new TextEditingValue();
public bool Equals(TextEditingValue other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return string.Equals(text, other.text) && Equals(selection, other.selection) && Equals(composing, other.composing);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((TextEditingValue) obj);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = (text != null ? text.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (selection != null ? selection.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (composing != null ? composing.GetHashCode() : 0);
return hashCode;
}
}
public static bool operator ==(TextEditingValue left, TextEditingValue right)
{
return Equals(left, right);
}
public static bool operator !=(TextEditingValue left, TextEditingValue right)
{
return !Equals(left, right);
}
public override string ToString()
{
return string.Format("Text: {0}, Selection: {1}, Composing: {2}", text, selection, composing);
}
}
public interface TextInputClient
{
void updateEditingValue(TextEditingValue value);
// void performAction(TextInputAction action);
}
public enum TextInputAction {
done,
newline,
}
public class TextInputConnection
{
internal TextInputConnection(TextInputClient client, TextInput textInput)
{
D.assert(client != null);
D.assert(textInput != null);
_client = client;
_textInput = textInput;
_id = _nextId++;
}
public bool attached
{
get { return _textInput._currentConnection == this; }
}
public void setEditingState(TextEditingValue value)
{
D.assert(attached);
_textInput._value = value;
}
public void close()
{
if (attached)
{
_textInput._currentConnection = null;
_textInput._value = null;
}
D.assert(!attached);
}
private static int _nextId = 1;
internal readonly int _id;
internal readonly TextInputClient _client;
internal readonly TextInput _textInput;
}
public class TextInput
{
internal TextInputConnection _currentConnection;
internal TextEditingValue _value;
private List<char> inputValue = new List<char>();
static Dictionary<Event, TextEditOp> s_Keyactions;
public TextInputConnection attach(TextInputClient client)
{
D.assert(client != null);
var connection = new TextInputConnection(client, this);
_currentConnection = connection;
return connection;
}
public void OnGUI()
{
if (_currentConnection == null)
{
return;
}
var currentEvent = Event.current;
if (currentEvent.type == EventType.KeyDown)
{
bool handled = handleKeyEvent(currentEvent);
if (!handled && currentEvent.keyCode == KeyCode.None)
{
inputValue.Add(currentEvent.character);
}
currentEvent.Use();
}
}
public void Update()
{
if (_currentConnection == null)
{
return;
}
if (inputValue.Count == 0)
{
return;
}
_value = _value.insert(new string(inputValue.ToArray()));
//_value = _value.copyWith(text: _value.text + new string(inputValue.ToArray()));
_currentConnection._client.updateEditingValue(_value);
inputValue.Clear();
}
private bool handleKeyEvent(Event e)
{
initKeyActions();
EventModifiers m = e.modifiers;
e.modifiers &= ~EventModifiers.CapsLock;
if (s_Keyactions.ContainsKey(e))
{
TextEditOp op = s_Keyactions[e];
var newValue = performOperation(op);
if (_value != newValue)
{
_value = newValue;
_currentConnection._client.updateEditingValue(_value);
}
e.modifiers = m;
return true;
}
e.modifiers = m;
return false;
}
TextEditingValue performOperation(TextEditOp operation)
{
// m_RevealCursor = true;
TextEditingValue newValue = null;
switch (operation)
{
case TextEditOp.MoveLeft:
return _value.moveLeft();
case TextEditOp.MoveRight:
return _value.moveRight();
// case TextEditOp.MoveUp: MoveUp(); break;
// case TextEditOp.MoveDown: MoveDown(); break;
// case TextEditOp.MoveLineStart: MoveLineStart(); break;
// case TextEditOp.MoveLineEnd: MoveLineEnd(); break;
// case TextEditOp.MoveWordRight: MoveWordRight(); break;
// case TextEditOp.MoveToStartOfNextWord: MoveToStartOfNextWord(); break;
// case TextEditOp.MoveToEndOfPreviousWord: MoveToEndOfPreviousWord(); break;
// case TextEditOp.MoveWordLeft: MoveWordLeft(); break;
// case TextEditOp.MoveTextStart: MoveTextStart(); break;
// case TextEditOp.MoveTextEnd: MoveTextEnd(); break;
// case TextEditOp.MoveParagraphForward: MoveParagraphForward(); break;
// case TextEditOp.MoveParagraphBackward: MoveParagraphBackward(); break;
// case TextEditOp.MoveGraphicalLineStart: MoveGraphicalLineStart(); break;
// case TextEditOp.MoveGraphicalLineEnd: MoveGraphicalLineEnd(); break;
case TextEditOp.SelectLeft:
return _value.extendLeft();
case TextEditOp.SelectRight:
return _value.extendRight();
// case TextEditOp.SelectUp: SelectUp(); break;
// case TextEditOp.SelectDown: SelectDown(); break;
// case TextEditOp.SelectWordRight: SelectWordRight(); break;
// case TextEditOp.SelectWordLeft: SelectWordLeft(); break;
// case TextEditOp.SelectToEndOfPreviousWord: SelectToEndOfPreviousWord(); break;
// case TextEditOp.SelectToStartOfNextWord: SelectToStartOfNextWord(); break;
//
// case TextEditOp.SelectTextStart: SelectTextStart(); break;
// case TextEditOp.SelectTextEnd: SelectTextEnd(); break;
// case TextEditOp.ExpandSelectGraphicalLineStart: ExpandSelectGraphicalLineStart(); break;
// case TextEditOp.ExpandSelectGraphicalLineEnd: ExpandSelectGraphicalLineEnd(); break;
// case TextEditOp.SelectParagraphForward: SelectParagraphForward(); break;
// case TextEditOp.SelectParagraphBackward: SelectParagraphBackward(); break;
// case TextEditOp.SelectGraphicalLineStart: SelectGraphicalLineStart(); break;
// case TextEditOp.SelectGraphicalLineEnd: SelectGraphicalLineEnd(); break;
// case TextEditOp.Delete: return Delete();
case TextEditOp.Backspace:
return _value.deleteSelection();
// _value.composing
// _value = _value.
// case TextEditOp.Cut: return Cut();
// case TextEditOp.Copy: Copy(); break;
// case TextEditOp.Paste: return Paste();
// case TextEditOp.SelectAll: SelectAll(); break;
// case TextEditOp.SelectNone: SelectNone(); break;
// case TextEditOp.DeleteWordBack: return DeleteWordBack(); // break; // The uncoditional return makes the "break;" issue a warning about unreachable code
// case TextEditOp.DeleteLineBack: return DeleteLineBack();
// case TextEditOp.DeleteWordForward: return DeleteWordForward(); // break; // The uncoditional return makes the "break;" issue a warning about unreachable code
default:
Debug.Log("Unimplemented: " + operation);
break;
}
return _value;
}
static void initKeyActions()
{
if (s_Keyactions != null)
return;
s_Keyactions = new Dictionary<Event, TextEditOp>();
// key mappings shared by the platforms
mapKey("left", TextEditOp.MoveLeft);
mapKey("right", TextEditOp.MoveRight);
mapKey("up", TextEditOp.MoveUp);
mapKey("down", TextEditOp.MoveDown);
mapKey("#left", TextEditOp.SelectLeft);
mapKey("#right", TextEditOp.SelectRight);
mapKey("#up", TextEditOp.SelectUp);
mapKey("#down", TextEditOp.SelectDown);
mapKey("delete", TextEditOp.Delete);
mapKey("backspace", TextEditOp.Backspace);
mapKey("#backspace", TextEditOp.Backspace);
// OSX is the special case for input shortcuts
if (SystemInfo.operatingSystemFamily == OperatingSystemFamily.MacOSX)
{
// Keyboard mappings for mac
// TODO mapKey ("home", TextEditOp.ScrollStart);
// TODO mapKey ("end", TextEditOp.ScrollEnd);
// TODO mapKey ("page up", TextEditOp.ScrollPageUp);
// TODO mapKey ("page down", TextEditOp.ScrollPageDown);
mapKey("^left", TextEditOp.MoveGraphicalLineStart);
mapKey("^right", TextEditOp.MoveGraphicalLineEnd);
// TODO mapKey ("^up", TextEditOp.ScrollPageUp);
// TODO mapKey ("^down", TextEditOp.ScrollPageDown);
mapKey("&left", TextEditOp.MoveWordLeft);
mapKey("&right", TextEditOp.MoveWordRight);
mapKey("&up", TextEditOp.MoveParagraphBackward);
mapKey("&down", TextEditOp.MoveParagraphForward);
mapKey("%left", TextEditOp.MoveGraphicalLineStart);
mapKey("%right", TextEditOp.MoveGraphicalLineEnd);
mapKey("%up", TextEditOp.MoveTextStart);
mapKey("%down", TextEditOp.MoveTextEnd);
mapKey("#home", TextEditOp.SelectTextStart);
mapKey("#end", TextEditOp.SelectTextEnd);
// TODO mapKey ("#page up", TextEditOp.SelectPageUp);
// TODO mapKey ("#page down", TextEditOp.SelectPageDown);
mapKey("#^left", TextEditOp.ExpandSelectGraphicalLineStart);
mapKey("#^right", TextEditOp.ExpandSelectGraphicalLineEnd);
mapKey("#^up", TextEditOp.SelectParagraphBackward);
mapKey("#^down", TextEditOp.SelectParagraphForward);
mapKey("#&left", TextEditOp.SelectWordLeft);
mapKey("#&right", TextEditOp.SelectWordRight);
mapKey("#&up", TextEditOp.SelectParagraphBackward);
mapKey("#&down", TextEditOp.SelectParagraphForward);
mapKey("#%left", TextEditOp.ExpandSelectGraphicalLineStart);
mapKey("#%right", TextEditOp.ExpandSelectGraphicalLineEnd);
mapKey("#%up", TextEditOp.SelectTextStart);
mapKey("#%down", TextEditOp.SelectTextEnd);
mapKey("%a", TextEditOp.SelectAll);
mapKey("%x", TextEditOp.Cut);
mapKey("%c", TextEditOp.Copy);
mapKey("%v", TextEditOp.Paste);
// emacs-like keybindings
mapKey("^d", TextEditOp.Delete);
mapKey("^h", TextEditOp.Backspace);
mapKey("^b", TextEditOp.MoveLeft);
mapKey("^f", TextEditOp.MoveRight);
mapKey("^a", TextEditOp.MoveLineStart);
mapKey("^e", TextEditOp.MoveLineEnd);
mapKey("&delete", TextEditOp.DeleteWordForward);
mapKey("&backspace", TextEditOp.DeleteWordBack);
mapKey("%backspace", TextEditOp.DeleteLineBack);
}
else
{
// Windows/Linux keymappings
mapKey("home", TextEditOp.MoveGraphicalLineStart);
mapKey("end", TextEditOp.MoveGraphicalLineEnd);
// TODO mapKey ("page up", TextEditOp.MovePageUp);
// TODO mapKey ("page down", TextEditOp.MovePageDown);
mapKey("%left", TextEditOp.MoveWordLeft);
mapKey("%right", TextEditOp.MoveWordRight);
mapKey("%up", TextEditOp.MoveParagraphBackward);
mapKey("%down", TextEditOp.MoveParagraphForward);
mapKey("^left", TextEditOp.MoveToEndOfPreviousWord);
mapKey("^right", TextEditOp.MoveToStartOfNextWord);
mapKey("^up", TextEditOp.MoveParagraphBackward);
mapKey("^down", TextEditOp.MoveParagraphForward);
mapKey("#^left", TextEditOp.SelectToEndOfPreviousWord);
mapKey("#^right", TextEditOp.SelectToStartOfNextWord);
mapKey("#^up", TextEditOp.SelectParagraphBackward);
mapKey("#^down", TextEditOp.SelectParagraphForward);
mapKey("#home", TextEditOp.SelectGraphicalLineStart);
mapKey("#end", TextEditOp.SelectGraphicalLineEnd);
// TODO mapKey ("#page up", TextEditOp.SelectPageUp);
// TODO mapKey ("#page down", TextEditOp.SelectPageDown);
mapKey("^delete", TextEditOp.DeleteWordForward);
mapKey("^backspace", TextEditOp.DeleteWordBack);
mapKey("%backspace", TextEditOp.DeleteLineBack);
mapKey("^a", TextEditOp.SelectAll);
mapKey("^x", TextEditOp.Cut);
mapKey("^c", TextEditOp.Copy);
mapKey("^v", TextEditOp.Paste);
mapKey("#delete", TextEditOp.Cut);
mapKey("^insert", TextEditOp.Copy);
mapKey("#insert", TextEditOp.Paste);
}
}
static void mapKey(string key, TextEditOp action)
{
s_Keyactions[Event.KeyboardEvent(key)] = action;
}
}
internal enum TextEditOp
{
MoveLeft,
MoveRight,
MoveUp,
MoveDown,
MoveLineStart,
MoveLineEnd,
MoveTextStart,
MoveTextEnd,
MovePageUp,
MovePageDown,
MoveGraphicalLineStart,
MoveGraphicalLineEnd,
MoveWordLeft,
MoveWordRight,
MoveParagraphForward,
MoveParagraphBackward,
MoveToStartOfNextWord,
MoveToEndOfPreviousWord,
SelectLeft,
SelectRight,
SelectUp,
SelectDown,
SelectTextStart,
SelectTextEnd,
SelectPageUp,
SelectPageDown,
ExpandSelectGraphicalLineStart,
ExpandSelectGraphicalLineEnd,
SelectGraphicalLineStart,
SelectGraphicalLineEnd,
SelectWordLeft,
SelectWordRight,
SelectToEndOfPreviousWord,
SelectToStartOfNextWord,
SelectParagraphBackward,
SelectParagraphForward,
Delete,
Backspace,
DeleteWordBack,
DeleteWordForward,
DeleteLineBack,
Cut,
Copy,
Paste,
SelectAll,
SelectNone,
ScrollStart,
ScrollEnd,
ScrollPageUp,
ScrollPageDown,
}
}

3
Assets/UIWidgets/service/text_input.cs.meta


fileFormatVersion: 2
guid: 74a6b923efea4cc6bc243bd2ee179330
timeCreated: 1537164543

591
Assets/UIWidgets/widgets/editable_text.cs


using System;
using System.Collections.Generic;
using RSG;
using UIWidgets.animation;
using UIWidgets.foundation;
using UIWidgets.gestures;
using UIWidgets.painting;
using UIWidgets.rendering;
using UIWidgets.service;
using UIWidgets.ui;
using UnityEngine;
using Color = UIWidgets.ui.Color;
using Rect = UIWidgets.ui.Rect;
using TextStyle = UIWidgets.painting.TextStyle;
namespace UIWidgets.widgets
{
public delegate void SelectionChangedCallback(TextSelection selection, SelectionChangedCause cause);
public class TextEditingController : ValueNotifier<TextEditingValue>
{
public TextEditingController(string text) : base(text == null
? TextEditingValue.empty
: new TextEditingValue(text))
{
}
private TextEditingController(TextEditingValue value) : base(value ?? TextEditingValue.empty)
{
}
public TextEditingController fromValue(TextEditingValue value)
{
return new TextEditingController(value);
}
public string text
{
get { return value.text; }
set
{
this.value = this.value.copyWith(text: value, selection: TextSelection.collapsed(-1),
composing: TextRange.empty);
}
}
public TextSelection selection
{
get { return value.selection; }
set
{
if (value.start > text.Length || value.end > text.Length)
{
throw new UIWidgetsError(string.Format("invalid text selection: {0}", value));
}
this.value = this.value.copyWith(selection: value, composing: TextRange.empty);
}
}
public void clear()
{
value = TextEditingValue.empty;
}
public void clearComposing()
{
value = value.copyWith(composing: TextRange.empty);
}
}
public class EditableText : StatefulWidget
{
public readonly TextInput textInput;
public readonly TextEditingController controller;
public readonly FocusNode focusNode;
public readonly bool obscureText;
public readonly bool autocorrect;
public readonly TextStyle style;
public readonly TextAlign textAlign;
public readonly TextDirection? textDirection;
public readonly double textScaleFactor;
public readonly Color cursorColor;
public readonly int maxLines;
public readonly bool autofocus;
public readonly Color selectionColor;
public readonly ValueChanged<string> onChanged;
public readonly ValueChanged<string> onSubmitted;
public readonly SelectionChangedCallback onSelectionChanged;
public readonly List<TextInputFormatter> inputFormatters;
public readonly bool rendererIgnoresPointer;
public EditableText(TextInput textInput, TextEditingController controller, FocusNode focusNode, TextStyle style,
Color cursorColor, bool obscureText = false, bool autocorrect = false,
TextAlign textAlign = TextAlign.left, TextDirection? textDirection = null,
double textScaleFactor = 1.0, int maxLines = 1,
bool autofocus = false, Color selectionColor = null, ValueChanged<string> onChanged = null,
ValueChanged<string> onSubmitted = null, SelectionChangedCallback onSelectionChanged = null,
List<TextInputFormatter> inputFormatters = null, bool rendererIgnoresPointer = false,
Key key = null) : base(key)
{
D.assert(controller != null);
D.assert(focusNode != null);
D.assert(style != null);
D.assert(cursorColor != null);
D.assert(textInput != null);
this.textInput = textInput;
this.controller = controller;
this.focusNode = focusNode;
this.obscureText = obscureText;
this.autocorrect = autocorrect;
this.style = style;
this.textAlign = textAlign;
this.textDirection = textDirection;
this.textScaleFactor = textScaleFactor;
this.cursorColor = cursorColor;
this.maxLines = maxLines;
this.autofocus = autofocus;
this.selectionColor = selectionColor;
this.onChanged = onChanged;
this.onSubmitted = onSubmitted;
this.onSelectionChanged = onSelectionChanged;
this.rendererIgnoresPointer = rendererIgnoresPointer;
if (maxLines == 1)
{
this.inputFormatters = new List<TextInputFormatter>();
this.inputFormatters.Add(BlacklistingTextInputFormatter.singleLineFormatter);
if (inputFormatters != null)
{
this.inputFormatters.AddRange(inputFormatters);
}
}
else
{
this.inputFormatters = inputFormatters;
}
}
public override State createState()
{
return new EditableTextState();
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new DiagnosticsProperty<TextEditingController>("controller", controller));
properties.add(new DiagnosticsProperty<FocusNode>("focusNode", focusNode));
properties.add(new DiagnosticsProperty<bool>("obscureText", obscureText, defaultValue: false));
properties.add(new DiagnosticsProperty<bool>("autocorrect", autocorrect, defaultValue: true));
if (style != null)
{
style.debugFillProperties(properties);
}
properties.add(new EnumProperty<TextAlign>("textAlign", textAlign, defaultValue: null));
properties.add(new EnumProperty<TextDirection?>("textDirection", textDirection, defaultValue: null));
properties.add(new DiagnosticsProperty<double>("textScaleFactor", textScaleFactor, defaultValue: null));
properties.add(new DiagnosticsProperty<int>("maxLines", maxLines, defaultValue: 1));
properties.add(new DiagnosticsProperty<bool>("autofocus", autofocus, defaultValue: false));
}
}
public class EditableTextState: State<EditableText>, TextInputClient
{
const int _kObscureShowLatestCharCursorTicks = 3;
private ValueNotifier<bool> _showCursor = new ValueNotifier<bool>(true); // todo
private GlobalKey _editableKey = GlobalKey.key();
private bool _didAutoFocus = false;
TextInputConnection _textInputConnection;
private int _obscureShowCharTicksPending = 0;
private int _obscureLatestCharIndex;
bool _textChangedSinceLastCaretUpdate = false;
public override void initState()
{
base.initState();
widget.controller.addListener(_didChangeTextEditingValue);
widget.focusNode.addListener(_handleFocusChanged);
}
public override void didChangeDependencies()
{
base.didChangeDependencies();
if (!_didAutoFocus && widget.autofocus)
{
FocusScope.of(context).autofocus(widget.focusNode);
_didAutoFocus = true;
}
}
public override void didUpdateWidget(StatefulWidget old)
{
EditableText oldWidget = (EditableText) old;
base.didUpdateWidget(oldWidget);
if (widget.controller != oldWidget.controller)
{
oldWidget.controller.removeListener(_didChangeTextEditingValue);
widget.controller.addListener(_didChangeTextEditingValue);
_updateRemoteEditingValueIfNeeded();
}
if (widget.focusNode != oldWidget.focusNode) {
oldWidget.focusNode.removeListener(_handleFocusChanged);
widget.focusNode.addListener(_handleFocusChanged);
}
}
public override void dispose()
{
widget.controller.removeListener(_didChangeTextEditingValue);
_closeInputConnectionIfNeeded();
D.assert(!_hasInputConnection);
widget.focusNode.removeListener(_handleFocusChanged);
base.dispose();
}
TextEditingValue _lastKnownRemoteTextEditingValue;
public void updateEditingValue(TextEditingValue value) {
if (value.text != _value.text) {
// _hideSelectionOverlayIfNeeded();
if (widget.obscureText && value.text.Length == _value.text.Length + 1) {
_obscureShowCharTicksPending = _kObscureShowLatestCharCursorTicks;
_obscureLatestCharIndex = _value.selection.baseOffset;
}
}
_lastKnownRemoteTextEditingValue = value;
_formatAndSetValue(value);
}
void _updateRemoteEditingValueIfNeeded()
{
if (!_hasInputConnection)
return;
var localValue = _value;
if (localValue == _lastKnownRemoteTextEditingValue)
return;
_lastKnownRemoteTextEditingValue = localValue;
_textInputConnection.setEditingState(localValue);
}
bool _hasInputConnection
{
get
{
return _textInputConnection != null && _textInputConnection.attached;
}
}
void _openInputConnection() {
if (!_hasInputConnection) {
TextEditingValue localValue = _value;
_lastKnownRemoteTextEditingValue = localValue;
_textInputConnection = widget.textInput.attach(this);
_textInputConnection.setEditingState(localValue);
}
}
void _closeInputConnectionIfNeeded() {
if (_hasInputConnection) {
_textInputConnection.close();
_textInputConnection = null;
_lastKnownRemoteTextEditingValue = null;
}
}
void _openOrCloseInputConnectionIfNeeded() {
if (_hasFocus && widget.focusNode.consumeKeyboardToken()) {
_openInputConnection();
} else if (!_hasFocus) {
_closeInputConnectionIfNeeded();
widget.controller.clearComposing();
}
}
public void requestKeyboard()
{
if (_hasFocus)
{
_openInputConnection();
}
else
{
FocusScope.of(context).requestFocus(widget.focusNode);
}
}
private void _handleSelectionChanged(TextSelection selection, RenderEditable renderObject, SelectionChangedCause cause) {
widget.controller.selection = selection;
requestKeyboard();
if (widget.onSelectionChanged != null)
{
widget.onSelectionChanged(selection, cause);
}
}
void _handleCaretChanged(Rect caretRect) {
if (_textChangedSinceLastCaretUpdate) {
_textChangedSinceLastCaretUpdate = false;
// scheduleMicrotask(() { // todo
// _scrollController.animateTo(
// _getScrollOffsetForCaret(caretRect),
// curve: Curves.fastOutSlowIn,
// duration: const Duration(milliseconds: 50),
// );
// });
}
}
private void _formatAndSetValue(TextEditingValue value) {
var textChanged = (_value == null ? null : _value.text) != (value == null ? null : value.text);
if (widget.inputFormatters != null && widget.inputFormatters.isNotEmpty()) {
foreach (var formatter in widget.inputFormatters)
{
value = formatter.formatEditUpdate(_value, value);
}
_value = value;
_updateRemoteEditingValueIfNeeded();
} else {
_value = value;
}
if (textChanged && widget.onChanged != null)
{
widget.onChanged(value.text);
}
}
private TextEditingValue _value
{
get { return widget.controller.value; }
set
{
widget.controller.value = value;
}
}
private bool _hasFocus
{
get { return widget.focusNode.hasFocus; }
}
private bool _isMultiline
{
get { return widget.maxLines != 1; }
}
private void _didChangeTextEditingValue()
{
_updateRemoteEditingValueIfNeeded();
_textChangedSinceLastCaretUpdate = true;
setState(() => {});
}
private void _handleFocusChanged()
{
_openOrCloseInputConnectionIfNeeded();
if (!_hasFocus) {
_value = new TextEditingValue(text: _value.text);
} else if (!_value.selection.isValid) {
widget.controller.selection = TextSelection.collapsed(offset: _value.text.Length);
}
}
private TextDirection? _textDirection
{
get
{
TextDirection? result = widget.textDirection ?? Directionality.of(context);
D.assert(result != null,
string.Format("{0} created without a textDirection and with no ambient Directionality.", GetType().FullName));
return result;
}
}
public RenderEditable renderEditable
{
get { return (RenderEditable)_editableKey.currentContext.findRenderObject(); }
}
public override Widget build(BuildContext context)
{
FocusScope.of(context).reparentIfNeeded(widget.focusNode);
// todo base.build(context); See AutomaticKeepAliveClientMixin.
return new _Editable(
key: _editableKey,
textSpan: buildTextSpan(),
value: _value,
cursorColor: widget.cursorColor,
showCursor: _showCursor,
hasFocus: _hasFocus,
maxLines: widget.maxLines,
selectionColor: widget.selectionColor,
textScaleFactor: 1.0, // todo widget.textScaleFactor ?? MediaQuery.textScaleFactorOf(context),
textAlign: widget.textAlign,
textDirection: _textDirection,
obscureText: widget.obscureText,
autocorrect: widget.autocorrect,
offset: new _FixedViewportOffset(0.0),
onSelectionChanged: _handleSelectionChanged,
onCaretChanged: _handleCaretChanged,
rendererIgnoresPointer: widget.rendererIgnoresPointer
);
}
public TextSpan buildTextSpan() {
if (!widget.obscureText && _value.composing.isValid) {
TextStyle composingStyle = widget.style.merge(
new TextStyle(decoration: TextDecoration.underline)
);
return new TextSpan(
style: widget.style,
children: new List<TextSpan>
{
new TextSpan(text: _value.composing.textBefore(_value.text)),
new TextSpan(
style: composingStyle,
text: _value.composing.textInside(_value.text)
),
new TextSpan(text: _value.composing.textAfter(_value.text)),
});
}
var text = _value.text;
if (widget.obscureText) {
text = new string(RenderEditable.obscuringCharacter, text.Length);
int o =
_obscureShowCharTicksPending > 0 ? _obscureLatestCharIndex : -1;
if (o >= 0 && o < text.Length)
text = text.Substring(0, o) + _value.text.Substring(o, 1) + text.Substring(o + 1);
}
return new TextSpan(style: widget.style, text: text);
}
}
internal class _Editable : LeafRenderObjectWidget
{
public readonly TextSpan textSpan;
public readonly TextEditingValue value;
public readonly Color cursorColor;
public readonly ValueNotifier<bool> showCursor;
public readonly bool hasFocus;
public readonly int maxLines;
public readonly Color selectionColor;
public readonly double textScaleFactor;
public readonly TextAlign textAlign;
public readonly TextDirection? textDirection;
public readonly bool obscureText;
public readonly bool autocorrect;
public readonly ViewportOffset offset;
public readonly SelectionChangedHandler onSelectionChanged;
public readonly CaretChangedHandler onCaretChanged;
public readonly bool rendererIgnoresPointer;
public _Editable(TextSpan textSpan = null, TextEditingValue value = null,
Color cursorColor = null, ValueNotifier<bool> showCursor = null, bool hasFocus = false,
int maxLines = 0, Color selectionColor = null, double textScaleFactor = 1.0,
TextDirection? textDirection = null, bool obscureText = false, TextAlign textAlign = TextAlign.left,
bool autocorrect = false, ViewportOffset offset = null, SelectionChangedHandler onSelectionChanged = null,
CaretChangedHandler onCaretChanged = null, bool rendererIgnoresPointer = false, Key key = null) : base(key)
{
this.textSpan = textSpan;
this.value = value;
this.cursorColor = cursorColor;
this.showCursor = showCursor;
this.hasFocus = hasFocus;
this.maxLines = maxLines;
this.selectionColor = selectionColor;
this.textScaleFactor = textScaleFactor;
this.textDirection = textDirection;
this.obscureText = obscureText;
this.autocorrect = autocorrect;
this.offset = offset;
this.onSelectionChanged = onSelectionChanged;
this.onCaretChanged = onCaretChanged;
this.rendererIgnoresPointer = rendererIgnoresPointer;
}
public override RenderObject createRenderObject(BuildContext context)
{
return new RenderEditable(
text: textSpan,
textDirection: textDirection??TextDirection.ltr,
offset: offset,
showCursor: showCursor,
cursorColor: cursorColor,
hasFocus: hasFocus,
maxLines: maxLines,
selectionColor: selectionColor,
textScaleFactor: textScaleFactor,
textAlign: textAlign,
selection: value.selection,
obscureText: obscureText,
onSelectionChanged: onSelectionChanged,
onCaretChanged: onCaretChanged,
ignorePointer: rendererIgnoresPointer
);
}
public override void updateRenderObject(BuildContext context, RenderObject renderObject)
{
var edit = (RenderEditable) renderObject;
edit.text = textSpan;
edit.cursorColor = cursorColor;
edit.showCursor = showCursor;
edit.hasFocus = hasFocus;
edit.maxLines = maxLines;
edit.selectionColor = selectionColor;
edit.textScaleFactor = textScaleFactor;
edit.textAlign = textAlign;
edit.textDirection = textDirection;
edit.selection = value.selection;
edit.offset = offset;
edit.onSelectionChanged = onSelectionChanged;
edit.onCaretChanged = onCaretChanged;
edit.ignorePointer = rendererIgnoresPointer;
edit.obscureText = obscureText;
}
}
class _FixedViewportOffset : ViewportOffset {
internal _FixedViewportOffset(double _pixels) {
this._pixels = _pixels;
}
internal new static _FixedViewportOffset zero() {
return new _FixedViewportOffset(0.0);
}
double _pixels;
public override double pixels {
get { return this._pixels; }
}
public override bool applyViewportDimension(double viewportDimension) {
return true;
}
public override bool applyContentDimensions(double minScrollExtent, double maxScrollExtent) {
return true;
}
public override void correctBy(double correction) {
this._pixels += correction;
}
public override void jumpTo(double pixels) {
}
public override IPromise animateTo(double to, TimeSpan duration, Curve curve) {
return Promise.Resolved();
}
public override ScrollDirection userScrollDirection {
get { return ScrollDirection.idle; }
}
public override bool allowImplicitScrolling {
get { return false; }
}
}
}

3
Assets/UIWidgets/widgets/editable_text.cs.meta


fileFormatVersion: 2
guid: b400bbae381e45f8b7242bb9e881bcd5
timeCreated: 1537163865
正在加载...
取消
保存