浏览代码

Merge branches 'kgdev' and 'master' of gitlab.cds.internal.unity3d.com:upm-packages/ui-widgets/com.unity.uiwidgets into kgdev

/main
kg 6 年前
当前提交
ed524f3e
共有 32 个文件被更改,包括 1815 次插入350 次删除
  1. 2
      Runtime/debugger/inspector_treeview.cs
  2. 17
      Runtime/gestures/multitap.cs
  3. 35
      Runtime/painting/matrix_utils.cs
  4. 35
      Runtime/rendering/editable.cs
  5. 198
      Runtime/rendering/layer.cs
  6. 2
      Runtime/rendering/object.cs
  7. 142
      Runtime/rendering/proxy_box.cs
  8. 98
      Runtime/rendering/shifted_box.cs
  9. 360
      Runtime/service/text_input.cs
  10. 75
      Runtime/widgets/basic.cs
  11. 4
      Runtime/widgets/custom_paint.cs
  12. 226
      Runtime/widgets/editable_text.cs
  13. 2
      Runtime/widgets/framework.cs
  14. 1
      Runtime/widgets/page_view.cs
  15. 2
      Samples/ReduxSample/ObjectFinder/ObjectFinderApp.cs
  16. 2
      Samples/ReduxSample/ObjectFinder/Reducer.cs
  17. 2
      Samples/UIWidgetSample/AsScreenCanvas.cs
  18. 13
      Samples/UIWidgetSample/TextInputCanvas.cs
  19. 24
      Samples/UIWidgetSample/ToDoAppCanvas.cs
  20. 2
      Tests/Editor/EditableTextWiget.cs
  21. 2
      Tests/Editor/Gestures.cs
  22. 14
      Tests/Editor/Widgets.cs
  23. 8
      Runtime/material.meta
  24. 42
      Runtime/service/clipboard.cs
  25. 11
      Runtime/service/clipboard.cs.meta
  26. 153
      Runtime/service/text_input_utils.cs
  27. 11
      Runtime/service/text_input_utils.cs.meta
  28. 439
      Runtime/widgets/text_selection.cs
  29. 11
      Runtime/widgets/text_selection.cs.meta
  30. 221
      Runtime/material/text_selection.cs
  31. 11
      Runtime/material/text_selection.cs.meta

2
Runtime/debugger/inspector_treeview.cs


}
xoffset += iconSize;
}
this.labelGUI(xoffset, rect, node.description);
this.labelGUI(xoffset, rect, node.description.Replace("\n", " "));
}
protected override void SelectionChanged(IList<int> selectedIds) {

17
Runtime/gestures/multitap.cs


using Unity.UIWidgets.ui;
namespace Unity.UIWidgets.gestures {
public delegate void GestureDoubleTapCallback();
public delegate void GestureDoubleTapCallback(DoubleTapDetails details);
public class DoubleTapDetails {
public DoubleTapDetails(Offset firstGlobalPosition = null) {
this.firstGlobalPosition = firstGlobalPosition ?? Offset.zero;
}
public readonly Offset firstGlobalPosition;
}
class _TapTracker {
internal _TapTracker(
PointerDownEvent evt = null,

public readonly int pointer;
public readonly GestureArenaEntry entry;
readonly Offset _initialPosition;
internal readonly Offset _initialPosition;
bool _isTrackingPointer = false;

return offset.distance <= tolerance;
}
}
public class DoubleTapGestureRecognizer : GestureRecognizer {
public DoubleTapGestureRecognizer(object debugOwner = null)

}
void _registerSecondTap(_TapTracker tracker) {
var initialPosition = tracker._initialPosition;
this._firstTap.entry.resolve(GestureDisposition.accepted);
tracker.entry.resolve(GestureDisposition.accepted);
this._freezeTracker(tracker);

this.onDoubleTap();
this.onDoubleTap(new DoubleTapDetails(initialPosition));
return null;
});
}

35
Runtime/painting/matrix_utils.cs


using Unity.UIWidgets.ui;
using System.Collections.Generic;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.ui;
}
public static List<string> debugDescribeTransform(Matrix3 transform) {
if (transform == null)
return new List<string> {"null"};
List<string> result = new List<string>(3);
for (int i = 0; i < 3; i++) {
result.Add($"[{i}] {transform[i * 3]}, {transform[i * 3 + 1]}, {transform[i * 3 + 2]}");
}
return result;
}
}
public class TransformProperty : DiagnosticsProperty<Matrix3> {
public TransformProperty(string name, Matrix3 value,
bool showName = true,
object defaultValue = null,
DiagnosticLevel level = DiagnosticLevel.info
): base(name, value, showName: showName, defaultValue: defaultValue??Diagnostics.kNoDefaultValue, level: level) {
}
protected override string valueToString(TextTreeConfiguration parentConfiguration = null) {
if (parentConfiguration != null && !parentConfiguration.lineBreakProperties) {
return this.value == null ? "null" : this.value.ToString();
}
return string.Join("\n", MatrixUtils.debugDescribeTransform(this.value));
}
}
}

35
Runtime/rendering/editable.cs


TextSelection _selection;
bool _obscureText;
TapGestureRecognizer _tap;
LongPressGestureRecognizer _longPress;
DoubleTapGestureRecognizer _doubleTap;
public bool ignorePointer;
public SelectionChangedHandler onSelectionChanged;

this._tap.onTapDown = this._handleTapDown;
this._tap.onTap = this._handleTap;
this._doubleTap.onDoubleTap = this._handleDoubleTap;
this._longPress = new LongPressGestureRecognizer(debugOwner: this);
this._longPress.onLongPress = this._handleLongPress;
}
public bool obscureText {

public TextPosition getPositionForPoint(Offset globalPosition) {
this._layoutText(this.constraints.maxWidth);
globalPosition -= this._paintOffset;
return this._textPainter.getPositionForOffset(globalPosition);
return this._textPainter.getPositionForOffset(this.globalToLocal(globalPosition));
}
public Rect getLocalRectForCaret(TextPosition caretPosition) {

if (evt is PointerDownEvent && this.onSelectionChanged != null) {
this._tap.addPointer((PointerDownEvent) evt);
this._doubleTap.addPointer((PointerDownEvent) evt);
// todo long press
this._longPress.addPointer((PointerDownEvent) evt);
}
}

}
}
public void handleDoubleTap() {
public void handleDoubleTap(DoubleTapDetails details) {
this._lastTapDownPosition = details.firstGlobalPosition - this._paintOffset;
this.selectWord(cause: SelectionChangedCause.doubleTap);
}
public void handleLongPress() {
this.selectWord(cause: SelectionChangedCause.longPress);
}
void selectWord(SelectionChangedCause? cause = null) {
var position = this._textPainter.getPositionForOffset(this.globalToLocal(this._lastTapDownPosition));
this.onSelectionChanged(this._selectWordAtOffset(position), this, SelectionChangedCause.doubleTap);
TextPosition position =
this._textPainter.getPositionForOffset(this.globalToLocal(this._lastTapDownPosition));
this.onSelectionChanged(this._selectWordAtOffset(position), this, cause.Value);
protected override void performLayout() {
this._layoutText(this.constraints.maxWidth);
this._caretPrototype = Rect.fromLTWH(0.0, _kCaretHeightOffset, _kCaretWidth,

this.handleTap();
}
void _handleDoubleTap() {
void _handleDoubleTap(DoubleTapDetails details) {
D.assert(!this.ignorePointer);
this.handleDoubleTap(details);
}
void _handleLongPress() {
this.handleDoubleTap();
this.handleLongPress();
}
void markNeedsSemanticsUpdate() {

198
Runtime/rendering/layer.cs


using System.Collections.Generic;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.widgets;
namespace Unity.UIWidgets.rendering {
public abstract class Layer : AbstractNodeMixinDiagnosticableTree {

while (node.parent != null) {
node = node.parent;
}
D.assert(node != newLayer);
return true;
});

while (node.parent != null) {
node = node.parent;
}
D.assert(node != child);
return true;
});

if (child._previousSibling == null) {
D.assert(this.firstChild == child);
this._firstChild = child.nextSibling;
} else {
}
else {
child._previousSibling._nextSibling = child.nextSibling;
}

} else {
}
else {
child._nextSibling._previousSibling = child.previousSibling;
}

properties.add(new IntProperty("alpha", this.alpha));
}
}
public class LayerLink {
public LeaderLayer leader => this._leader;
internal LeaderLayer _leader;
public override string ToString() {
return $"{Diagnostics.describeIdentity(this)}({(this._leader != null ? "<linked>" : "<dangling>")})";
}
}
public class LeaderLayer : ContainerLayer {
public readonly LayerLink link;
public Offset offset;
public LeaderLayer(LayerLink link, Offset offset = null) {
D.assert(link != null);
offset = offset ?? Offset.zero;
this.link = link;
this.offset = offset;
}
public override void attach(object owner) {
base.attach(owner);
D.assert(this.link.leader == null);
this._lastOffset = null;
this.link._leader = this;
}
public override void detach() {
D.assert(this.link.leader == this);
this.link._leader = null;
this._lastOffset = null;
base.detach();
}
internal Offset _lastOffset;
public override void addToScene(SceneBuilder builder, Offset layerOffset) {
D.assert(this.offset != null);
this._lastOffset = this.offset + layerOffset;
if (this._lastOffset != Offset.zero)
builder.pushTransform(Matrix3.makeTrans(this._lastOffset));
this.addChildrenToScene(builder, Offset.zero);
if (this._lastOffset != Offset.zero)
builder.pop();
}
public override void applyTransform(Layer child, Matrix3 transform) {
D.assert(this._lastOffset != null);
if (this._lastOffset != Offset.zero)
transform.preTranslate((float)this._lastOffset.dx, (float)this._lastOffset.dy);
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new DiagnosticsProperty<Offset>("offset", this.offset));
properties.add(new DiagnosticsProperty<LayerLink>("link", this.link));
}
}
public class FollowerLayer : ContainerLayer {
public FollowerLayer(
LayerLink link = null,
bool showWhenUnlinked = true,
Offset unlinkedOffset = null,
Offset linkedOffset = null
) {
D.assert(link != null);
this.link = link;
this.showWhenUnlinked = showWhenUnlinked;
this.unlinkedOffset = unlinkedOffset;
this.linkedOffset = linkedOffset;
}
public readonly LayerLink link;
public readonly bool showWhenUnlinked;
public readonly Offset unlinkedOffset;
public readonly Offset linkedOffset;
Offset _lastOffset;
Matrix3 _lastTransform;
Matrix3 _invertedTransform;
public Matrix3 getLastTransform() {
if (this._lastTransform == null)
return null;
Matrix3 result = Matrix3.makeTrans((float)-this._lastOffset.dx, (float)-this._lastOffset.dy);
result.preConcat(this._lastTransform);
return result;
}
Matrix3 _collectTransformForLayerChain(List<ContainerLayer> layers) {
Matrix3 result = Matrix3.I();
for (int index = layers.Count - 1; index > 0; index -= 1)
layers[index].applyTransform(layers[index - 1], result);
return result;
}
void _establishTransform() {
D.assert(this.link != null);
this._lastTransform = null;
if (this.link._leader == null) {
return;
}
D.assert(this.link.leader.owner == this.owner,
"Linked LeaderLayer anchor is not in the same layer tree as the FollowerLayer.");
D.assert(this.link.leader._lastOffset != null, "LeaderLayer anchor must come before FollowerLayer in paint order, but the reverse was true.");
HashSet<Layer> ancestors = new HashSet<Layer>();
Layer ancestor = this.parent;
while (ancestor != null) {
ancestors.Add(ancestor);
ancestor = ancestor.parent;
}
ContainerLayer layer = this.link.leader;
List<ContainerLayer> forwardLayers = new List<ContainerLayer> {null, layer};
do {
layer = layer.parent;
forwardLayers.Add(layer);
} while (!ancestors.Contains(layer));
ancestor = layer;
layer = this;
List<ContainerLayer> inverseLayers = new List<ContainerLayer>();
do {
layer = layer.parent;
inverseLayers.Add(layer);
} while (layer != ancestor);
Matrix3 forwardTransform = this._collectTransformForLayerChain(forwardLayers);
Matrix3 inverseTransform = this._collectTransformForLayerChain(inverseLayers);
var inverse = Matrix3.I();
var invertible = inverseTransform.invert(inverseTransform);
if (!invertible) {
return;
}
inverseTransform = inverse;
inverseTransform.preConcat(forwardTransform);
inverseTransform.preTranslate((float)this.linkedOffset.dx, (float)this.linkedOffset.dy);
this._lastTransform = inverseTransform;
}
public override void addToScene(SceneBuilder builder, Offset layerOffset) {
D.assert(this.link != null);
if (this.link.leader == null && !this.showWhenUnlinked) {
this._lastTransform = null;
this._lastOffset = null;
return;
}
this._establishTransform();
if (this._lastTransform != null) {
builder.pushTransform(this._lastTransform);
this.addChildrenToScene(builder, Offset.zero);
builder.pop();
this._lastOffset = this.unlinkedOffset + layerOffset;
}
else {
this._lastOffset = null;
var matrix = Matrix3.makeTrans((float)this.unlinkedOffset.dx, (float)this.unlinkedOffset.dy);
builder.pushTransform(matrix);
this.addChildrenToScene(builder, Offset.zero);
builder.pop();
}
}
public override void applyTransform(Layer child, Matrix3 transform) {
D.assert(child != null);
D.assert(transform != null);
if (this._lastTransform != null) {
transform.preConcat(this._lastTransform);
} else {
transform.preConcat(Matrix3.makeTrans((float)this.unlinkedOffset.dx, (float)this.unlinkedOffset.dy));
}
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new DiagnosticsProperty<LayerLink>("link", this.link));
properties.add(new TransformProperty("transform", this.getLastTransform(), defaultValue: null));
}
}
}

2
Runtime/rendering/object.cs


this.appendLayer(layer);
}
void pushLayer(ContainerLayer childLayer, PaintingContextCallback painter, Offset offset,
public void pushLayer(ContainerLayer childLayer, PaintingContextCallback painter, Offset offset,
Rect childPaintBounds = null) {
D.assert(!childLayer.attached);
D.assert(childLayer.parent == null);

142
Runtime/rendering/proxy_box.cs


properties.add(new DiagnosticsProperty<object>("metaData", this.metaData));
}
}
public class RenderLeaderLayer : RenderProxyBox {
public RenderLeaderLayer(
LayerLink link = null,
RenderBox child = null
) : base(child:child) {
D.assert(link != null);
this.link = link;
}
public LayerLink link {
get => this._link;
set {
D.assert(value != null);
if (this._link == value)
return;
this._link = value;
this.markNeedsPaint();
}
}
LayerLink _link;
protected override bool alwaysNeedsCompositing => true;
public override void paint(PaintingContext context, Offset offset) {
context.pushLayer(new LeaderLayer(link: this.link, offset: offset), base.paint, Offset.zero);
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new DiagnosticsProperty<LayerLink>("link", this.link));
}
}
class RenderFollowerLayer : RenderProxyBox {
public RenderFollowerLayer(LayerLink link,
bool showWhenUnlinked = true,
Offset offset = null,
RenderBox child = null
): base(child) {
this.link = link;
this.showWhenUnlinked = showWhenUnlinked;
this.offset = offset;
}
public LayerLink link {
get => this._link;
set {
D.assert(value != null);
if (this._link == value)
return;
this._link = value;
this.markNeedsPaint();
}
}
LayerLink _link;
public bool showWhenUnlinked {
get => this._showWhenUnlinked;
set {
if (this._showWhenUnlinked == value)
return;
this._showWhenUnlinked = value;
this.markNeedsPaint();
}
}
bool _showWhenUnlinked;
public Offset offset {
get => this._offset;
set {
D.assert(value != null);
if (this._offset == value)
return;
this._offset = value;
this.markNeedsPaint();
}
}
Offset _offset;
public override void detach() {
this._layer = null;
base.detach();
}
protected override bool alwaysNeedsCompositing => true;
new FollowerLayer _layer;
Matrix3 getCurrentTransform() {
return this._layer?.getLastTransform() ?? Matrix3.I();
}
public override bool hitTest(HitTestResult result, Offset position) {
return this.hitTestChildren(result, position: position);
}
protected override bool hitTestChildren(HitTestResult result, Offset position) {
Matrix3 inverse = Matrix3.I();
if (!this.getCurrentTransform().invert(inverse)) {
return false;
}
position = inverse.mapPoint(position);
return base.hitTestChildren(result, position: position);
}
public override void paint(PaintingContext context, Offset offset) {
this._layer = new FollowerLayer(
link: this.link,
showWhenUnlinked: this.showWhenUnlinked,
linkedOffset: this.offset,
unlinkedOffset: offset
);
context.pushLayer(this._layer,
base.paint,
Offset.zero,
childPaintBounds: Rect.fromLTRB(
double.NegativeInfinity,
double.NegativeInfinity,
double.PositiveInfinity,
double.PositiveInfinity
)
);
}
public override void applyPaintTransform(RenderObject child, Matrix3 transform) {
transform.preConcat(this.getCurrentTransform());
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new DiagnosticsProperty<LayerLink>("link", this.link));
properties.add(new DiagnosticsProperty<bool>("showWhenUnlinked", this.showWhenUnlinked));
properties.add(new DiagnosticsProperty<Offset>("offset", this.offset));
properties.add(new TransformProperty("current transform matrix", this.getCurrentTransform()));
}
}
}

98
Runtime/rendering/shifted_box.cs


}
}
public abstract class SingleChildLayoutDelegate {
public SingleChildLayoutDelegate(Listenable _relayout = null) {
this._relayout = _relayout;
}
public readonly Listenable _relayout;
public virtual Size getSize(BoxConstraints constraints) => constraints.biggest;
public virtual BoxConstraints getConstraintsForChild(BoxConstraints constraints) => constraints;
public virtual Offset getPositionForChild(Size size, Size childSize) => Offset.zero;
public abstract bool shouldRelayout(SingleChildLayoutDelegate oldDelegate);
}
public class RenderCustomSingleChildLayoutBox : RenderShiftedBox {
public RenderCustomSingleChildLayoutBox(RenderBox child = null,
SingleChildLayoutDelegate layoutDelegate = null) : base(child) {
D.assert(layoutDelegate != null);
this._delegate = layoutDelegate;
}
public SingleChildLayoutDelegate layoutDelegate {
get => this._delegate;
set {
var newDelegate = value;
D.assert(newDelegate != null);
if (this._delegate == newDelegate)
return;
SingleChildLayoutDelegate oldDelegate = this._delegate;
if (newDelegate.GetType() != oldDelegate.GetType() || newDelegate.shouldRelayout(oldDelegate)) this.markNeedsLayout();
this._delegate = newDelegate;
if (this.attached) {
oldDelegate?._relayout?.removeListener(this.markNeedsLayout);
newDelegate?._relayout?.addListener(this.markNeedsLayout);
}
}
}
SingleChildLayoutDelegate _delegate;
public override void attach(object owner) {
base.attach(owner);
this._delegate?._relayout?.addListener(this.markNeedsLayout);
}
public override void detach() {
this._delegate?._relayout?.removeListener(this.markNeedsLayout);
base.detach();
}
Size _getSize(BoxConstraints constraints) {
return constraints.constrain(this._delegate.getSize(constraints));
}
protected override double computeMinIntrinsicWidth(double height) {
double width = this._getSize(BoxConstraints.tightForFinite(height: height)).width;
if (width.isFinite())
return width;
return 0.0;
}
protected override double computeMaxIntrinsicWidth(double height) {
double width = this._getSize(BoxConstraints.tightForFinite(height: height)).width;
if (width.isFinite())
return width;
return 0.0;
}
protected override double computeMinIntrinsicHeight(double width) {
double height = this._getSize(BoxConstraints.tightForFinite(width: width)).height;
if (height.isFinite())
return height;
return 0.0;
}
protected override double computeMaxIntrinsicHeight(double width) {
double height = this._getSize(BoxConstraints.tightForFinite(width: width)).height;
if (height.isFinite())
return height;
return 0.0;
}
protected override void performLayout() {
this.size = this._getSize(this.constraints);
if (this.child != null) {
BoxConstraints childConstraints = this.layoutDelegate.getConstraintsForChild(this.constraints);
D.assert(childConstraints.debugAssertIsValid(isAppliedConstraint: true));
this.child.layout(childConstraints, parentUsesSize: !childConstraints.isTight);
BoxParentData childParentData = (BoxParentData)this.child.parentData;
childParentData.offset = this.layoutDelegate.getPositionForChild(this.size,
childConstraints.isTight ? childConstraints.smallest : this.child.size);
}
}
}
public class RenderBaseline : RenderShiftedBox {
public RenderBaseline(
RenderBox child = null,

360
Runtime/service/text_input.cs


using System;
using System.Collections.Generic;
using Unity.UIWidgets.ui;
using UnityEngine;
namespace Unity.UIWidgets.service {

}
}
public interface TextSelectionDelegate {
TextEditingValue textEditingValue { get; set; }
void hideToolbar();
void bringIntoView(TextPosition textPosition);
}
TextEditingValue getValueForOperation(TextEditOp operation);
// void performAction(TextInputAction action);
void performAction(TextInputAction action);
none,
unspecified,
go,
search,
send,
next,
previous,
continueAction,
join,
route,
emergencyCall,
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,
// text client
public class TextInputConnection {
internal TextInputConnection(TextInputClient client, TextInput textInput) {

public class TextInput {
internal TextInputConnection _currentConnection;
internal TextEditingValue _value;
static Dictionary<Event, TextEditOp> s_Keyactions;
string _lastCompositionString;
public TextInputConnection attach(TextInputClient client) {

var currentEvent = Event.current;
if (currentEvent.type == EventType.KeyDown) {
bool handled = this.handleKeyEvent(currentEvent);
if (!handled) {
var action = TextInputUtils.getInputAction(currentEvent);
if (action != null) {
this._performAction(this._currentConnection._id, action.Value);
}
if (action == null || action == TextInputAction.newline) {
this._currentConnection._client.updateEditingValue(this._value);
this._updateEditingState(this._currentConnection._id, this._value);
}
}
currentEvent.Use();

this._lastCompositionString != Input.compositionString) {
this._value = this._value.compose(Input.compositionString);
this._currentConnection._client.updateEditingValue(this._value);
this._updateEditingState(this._currentConnection._id, this._value);
}
this._lastCompositionString = Input.compositionString;

Input.compositionCursorPos = new Vector2((float) x, (float) y);
}
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 = this._currentConnection._client.getValueForOperation(op);
if (this._value != newValue) {
this._value = newValue;
this._currentConnection._client.updateEditingValue(this._value);
void _updateEditingState(int client, TextEditingValue value) {
Window.instance.run(() => {
if (this._currentConnection == null) {
return;
e.modifiers = m;
return true;
}
e.modifiers = m;
return false;
}
TextEditingValue performOperation(TextEditOp operation) {
switch (operation) {
case TextEditOp.MoveLeft:
return this._value.moveLeft();
case TextEditOp.MoveRight:
return this._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 this._value.extendLeft();
case TextEditOp.SelectRight:
return this._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 this._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 this._value;
if (client != this._currentConnection._id) {
return;
}
this._currentConnection._client.updateEditingValue(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);
void _performAction(int client, TextInputAction action) {
Window.instance.run(() => {
if (this._currentConnection == null) {
return;
}
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);
}
if (client != this._currentConnection._id) {
return;
}
this._currentConnection._client.performAction(action);
});
static void mapKey(string key, TextEditOp action) {
s_Keyactions[Event.KeyboardEvent(key)] = action;
}
public 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,
}
}

75
Runtime/widgets/basic.cs


bool _needTextDirection {
get {
D.assert(this.direction != null);
switch (this.direction) {
case Axis.horizontal:
return true;

}
}
public class CompositedTransformTarget : SingleChildRenderObjectWidget {
public CompositedTransformTarget(
Key key = null,
LayerLink link = null,
Widget child = null
) : base(key: key, child: child) {
D.assert(link != null);
this.link = link;
}
public readonly LayerLink link;
public override RenderObject createRenderObject(BuildContext context) {
return new RenderLeaderLayer(
link: this.link
);
}
public override void updateRenderObject(BuildContext context, RenderObject renderObject) {
((RenderLeaderLayer) renderObject).link = this.link;
}
}
public class CompositedTransformFollower : SingleChildRenderObjectWidget {
public CompositedTransformFollower(
Key key = null,
LayerLink link = null,
bool showWhenUnlinked = true,
Offset offset = null,
Widget child = null
) : base(key: key, child: child) {
D.assert(link != null);
this.showWhenUnlinked = showWhenUnlinked;
this.offset = offset ?? Offset.zero;
this.link = link;
}
public readonly LayerLink link;
public readonly bool showWhenUnlinked;
public readonly Offset offset;
public override RenderObject createRenderObject(BuildContext context) {
return new RenderFollowerLayer(
link: this.link,
showWhenUnlinked: this.showWhenUnlinked,
offset: this.offset
);
}
public override void updateRenderObject(BuildContext context, RenderObject renderObject) {
((RenderFollowerLayer) renderObject).link = this.link;
((RenderFollowerLayer) renderObject).showWhenUnlinked = this.showWhenUnlinked;
((RenderFollowerLayer) renderObject).offset = this.offset;
}
}
public class FractionalTranslation : SingleChildRenderObjectWidget {
public FractionalTranslation(Key key = null, Offset translation = null,
bool transformHitTests = true, Widget child = null) : base(key: key, child: child) {

widthFactor: widthFactor,
heightFactor: heightFactor,
child: child) {
}
}
public class CustomSingleChildLayout : SingleChildRenderObjectWidget {
public CustomSingleChildLayout(Key key = null,
SingleChildLayoutDelegate layoutDelegate = null, Widget child = null):base(key:key, child:child) {
D.assert(layoutDelegate != null);
this.layoutDelegate = layoutDelegate;
}
public readonly SingleChildLayoutDelegate layoutDelegate;
public override RenderObject createRenderObject(BuildContext context) {
return new RenderCustomSingleChildLayoutBox(layoutDelegate: this.layoutDelegate);
}
public override void updateRenderObject(BuildContext context, RenderObject renderObject) {
((RenderCustomSingleChildLayoutBox)renderObject).layoutDelegate = this.layoutDelegate;
}
}

4
Runtime/widgets/custom_paint.cs


namespace Unity.UIWidgets.widgets {
public abstract class CustomPainter : Listenable {
public CustomPainter(Listenable repaint) {
public CustomPainter(Listenable repaint = null) {
this._repaint = repaint;
}

public abstract bool shouldRepaint(CustomPainter oldDelegate);
public virtual bool hitTest(Offset position) {
return false;
return true;
}
public override string ToString() {

226
Runtime/widgets/editable_text.cs


public readonly bool autofocus;
public readonly Color selectionColor;
public readonly TextSelectionControls selectionControls;
public readonly VoidCallback onEditingComplete;
public readonly ValueChanged<string> onSubmitted;
public readonly SelectionChangedCallback onSelectionChanged;

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,
bool autofocus = false, Color selectionColor = null, TextSelectionControls selectionControls = null,
ValueChanged<string> onChanged = null, VoidCallback onEditingComplete = null,
ValueChanged<string> onSubmitted = null, SelectionChangedCallback onSelectionChanged = null,
List<TextInputFormatter> inputFormatters = null, bool rendererIgnoresPointer = false,
EdgeInsets scrollPadding = null,

this.onChanged = onChanged;
this.onSubmitted = onSubmitted;
this.onSelectionChanged = onSelectionChanged;
this.onEditingComplete = onEditingComplete;
this.selectionControls = selectionControls;
if (maxLines == 1) {
this.inputFormatters = new List<TextInputFormatter>();
this.inputFormatters.Add(BlacklistingTextInputFormatter.singleLineFormatter);

}
}
public class EditableTextState : AutomaticKeepAliveClientMixin<EditableText>, TextInputClient {
public class EditableTextState : AutomaticKeepAliveClientMixin<EditableText>, TextInputClient, TextSelectionDelegate {
LayerLink _layerLink = new LayerLink();
TextSelectionOverlay _selectionOverlay;
int _obscureShowCharTicksPending = 0;
int _obscureLatestCharIndex;

base.initState();
this.widget.controller.addListener(this._didChangeTextEditingValue);
this.widget.focusNode.addListener(this._handleFocusChanged);
this._scrollController.addListener(() => { this._selectionOverlay?.updateForScroll(); });
}
public override void didChangeDependencies() {

D.assert(!this._hasInputConnection);
this._stopCursorTimer();
D.assert(this._cursorTimer == null);
this._selectionOverlay?.dispose();
this._selectionOverlay = null;
this.widget.focusNode.removeListener(this._handleFocusChanged);
base.dispose();
}

public void updateEditingValue(TextEditingValue value) {
if (value.text != this._value.text) {
// _hideSelectionOverlayIfNeeded();
this._hideSelectionOverlayIfNeeded();
this._showCaretOnScreen();
if (this.widget.obscureText && value.text.Length == this._value.text.Length + 1) {
this._obscureShowCharTicksPending = _kObscureShowLatestCharCursorTicks;
this._obscureLatestCharIndex = this._value.selection.baseOffset;

this._formatAndSetValue(value);
}
public TextEditingValue getValueForOperation(TextEditOp operation) {
public TextEditingValue getValueForAction(TextInputAction operation) {
TextPosition newPosition = null;
TextPosition newExtend = null;
TextEditingValue newValue = null;

case TextEditOp.MoveLeft:
case TextInputAction.moveLeft:
case TextEditOp.MoveRight:
case TextInputAction.moveRight:
case TextEditOp.MoveUp:
case TextInputAction.moveUp:
case TextEditOp.MoveDown:
case TextInputAction.moveDown:
case TextEditOp.MoveLineStart:
case TextInputAction.moveLineStart:
case TextEditOp.MoveLineEnd:
case TextInputAction.moveLineEnd:
case TextEditOp.MoveWordRight:
case TextInputAction.moveWordRight:
case TextEditOp.MoveWordLeft:
case TextInputAction.moveWordLeft:
// case TextEditOp.MoveToStartOfNextWord: MoveToStartOfNextWord(); break;
// case TextEditOp.MoveToEndOfPreviousWord: MoveToEndOfPreviousWord(); break;
case TextEditOp.MoveTextStart:
// case TextInputAction.MoveToStartOfNextWord: MoveToStartOfNextWord(); break;
// case TextInputAction.MoveToEndOfPreviousWord: MoveToEndOfPreviousWord(); break;
case TextInputAction.moveTextStart:
case TextEditOp.MoveTextEnd:
case TextInputAction.moveTextEnd:
case TextEditOp.MoveParagraphForward:
case TextInputAction.moveParagraphForward:
case TextEditOp.MoveParagraphBackward:
case TextInputAction.moveParagraphBackward:
case TextEditOp.MoveGraphicalLineStart:
case TextInputAction.moveGraphicalLineStart:
case TextEditOp.MoveGraphicalLineEnd:
case TextInputAction.moveGraphicalLineEnd:
case TextEditOp.SelectLeft:
case TextInputAction.selectLeft:
case TextEditOp.SelectRight:
case TextInputAction.selectRight:
case TextEditOp.SelectUp:
case TextInputAction.selectUp:
case TextEditOp.SelectDown:
case TextInputAction.selectDown:
case TextEditOp.SelectWordRight:
case TextInputAction.selectWordRight:
case TextEditOp.SelectWordLeft:
case TextInputAction.selectWordLeft:
// case TextEditOp.SelectToEndOfPreviousWord: SelectToEndOfPreviousWord(); break;
// case TextEditOp.SelectToStartOfNextWord: SelectToStartOfNextWord(); break;
// case TextInputAction.SelectToEndOfPreviousWord: SelectToEndOfPreviousWord(); break;
// case TextInputAction.SelectToStartOfNextWord: SelectToStartOfNextWord(); break;
case TextEditOp.SelectTextStart:
case TextInputAction.selectTextStart:
case TextEditOp.SelectTextEnd:
case TextInputAction.selectTextEnd:
case TextEditOp.ExpandSelectGraphicalLineStart:
case TextInputAction.expandSelectGraphicalLineStart:
if (this._value.selection.isCollapsed ||
!this.renderEditable.isLineEndOrStart(this._value.selection.start)) {
newSelection = new TextSelection(this.renderEditable.getLineStartPosition(startPos).offset,

break;
case TextEditOp.ExpandSelectGraphicalLineEnd:
case TextInputAction.expandSelectGraphicalLineEnd:
if (this._value.selection.isCollapsed ||
!this.renderEditable.isLineEndOrStart(this._value.selection.end)) {
newSelection = new TextSelection(this._value.selection.start,

break;
case TextEditOp.SelectParagraphForward:
case TextInputAction.selectParagraphForward:
case TextEditOp.SelectParagraphBackward:
case TextInputAction.selectParagraphBackward:
case TextEditOp.SelectGraphicalLineStart:
case TextInputAction.selectGraphicalLineStart:
case TextEditOp.SelectGraphicalLineEnd:
case TextInputAction.selectGraphicalLineEnd:
case TextEditOp.Delete:
case TextInputAction.delete:
case TextEditOp.Backspace:
case TextInputAction.backspace:
case TextEditOp.SelectAll:
case TextInputAction.selectAll:
newSelection = this._value.selection.copyWith(baseOffset: 0, extentOffset: this._value.text.Length);
break;
}

return this._value;
}
public void performAction(TextInputAction action) {
TextEditingValue newValue;
switch (action) {
case TextInputAction.newline:
if (this.widget.maxLines == 1) this._finalizeEditing(true);
break;
case TextInputAction.done:
case TextInputAction.go:
case TextInputAction.send:
case TextInputAction.search:
this._finalizeEditing(true);
break;
case TextInputAction.next:
case TextInputAction.previous:
case TextInputAction.continueAction:
case TextInputAction.join:
case TextInputAction.route:
case TextInputAction.emergencyCall:
this._finalizeEditing(false);
break;
default:
newValue = this.getValueForAction(action);
if (newValue != this.textEditingValue) {
this.textEditingValue = newValue;
}
break;
}
}
void _finalizeEditing(bool shouldUnfocus) {
if (this.widget.onEditingComplete != null) {
this.widget.onEditingComplete();
} else {
this.widget.controller.clearComposing();
if (shouldUnfocus) this.widget.focusNode.unfocus();
}
if (this.widget.onSubmitted != null) this.widget.onSubmitted(this._value.text);
}
void _updateRemoteEditingValueIfNeeded() {
if (!this._hasInputConnection) {
return;

this._textInputConnection.setEditingState(localValue);
}
// Calculate the new scroll offset so the cursor remains visible.
double _getScrollOffsetForCaret(Rect caretRect) {
double caretStart = this._isMultiline ? caretRect.top : caretRect.left;

}
}
void _hideSelectionOverlayIfNeeded() {
this._selectionOverlay?.hide();
this._selectionOverlay = null;
}
void _updateOrDisposeSelectionOverlayIfNeeded() {
if (this._selectionOverlay != null) {
if (this._hasFocus) {
this._selectionOverlay.update(this._value);
} else {
this._selectionOverlay.dispose();
this._selectionOverlay = null;
}
}
}
this._hideSelectionOverlayIfNeeded();
if (this.widget.selectionControls != null) {
this._selectionOverlay = new TextSelectionOverlay(
context: this.context,
value: this._value,
debugRequiredFor: this.widget,
layerLink: this._layerLink,
renderObject: renderObject,
selectionControls: this.widget.selectionControls,
selectionDelegate: this
);
bool longPress = cause == SelectionChangedCause.longPress;
if (cause != SelectionChangedCause.keyboard && (this._value.text.isNotEmpty() || longPress)) this._selectionOverlay.showHandles();
if (longPress || cause == SelectionChangedCause.doubleTap) this._selectionOverlay.showToolbar();
}
if (this.widget.onSelectionChanged != null) {
this.widget.onSelectionChanged(selection, cause);
}

void _didChangeTextEditingValue() {
this._updateRemoteEditingValueIfNeeded();
this._startOrStopCursorTimerIfNeeded();
this._updateOrDisposeSelectionOverlayIfNeeded();
this._textChangedSinceLastCaretUpdate = true;
this.setState(() => { });
}

this._startOrStopCursorTimerIfNeeded();
this._updateOrDisposeSelectionOverlayIfNeeded();
if (!this._hasFocus) {
this._value = new TextEditingValue(text: this._value.text);
} else if (!this._value.selection.isValid) {

get { return (RenderEditable) this._editableKey.currentContext.findRenderObject(); }
}
public TextEditingValue textEditingValue {
get { return this._value; }
set {
this._selectionOverlay?.update(value);
this._formatAndSetValue(value);
}
}
public void bringIntoView(TextPosition position) {
this._scrollController.jumpTo(this._getScrollOffsetForCaret(this.renderEditable.getLocalRectForCaret(position)));
}
public void hideToolbar() {
this._selectionOverlay?.hide();
}
public override Widget build(BuildContext context) {
FocusScope.of(context).reparentIfNeeded(this.widget.focusNode);
base.build(context); // See AutomaticKeepAliveClientMixin.

controller: this._scrollController,
physics: new ClampingScrollPhysics(),
viewportBuilder: (BuildContext _context, ViewportOffset offset) =>
new _Editable(
key: this._editableKey,
textSpan: this.buildTextSpan(),
value: this._value,
cursorColor: this.widget.cursorColor,
showCursor: this._showCursor,
hasFocus: this._hasFocus,
maxLines: this.widget.maxLines,
selectionColor: this.widget.selectionColor,
textScaleFactor: Window.instance
.devicePixelRatio, // todo widget.textScaleFactor ?? MediaQuery.textScaleFactorOf(context),
textAlign: this.widget.textAlign,
textDirection: this._textDirection,
obscureText: this.widget.obscureText,
autocorrect: this.widget.autocorrect,
offset: offset,
onSelectionChanged: this._handleSelectionChanged,
onCaretChanged: this._handleCaretChanged,
rendererIgnoresPointer: this.widget.rendererIgnoresPointer
new CompositedTransformTarget(
link: this._layerLink,
child: new _Editable(
key: this._editableKey,
textSpan: this.buildTextSpan(),
value: this._value,
cursorColor: this.widget.cursorColor,
showCursor: this._showCursor,
hasFocus: this._hasFocus,
maxLines: this.widget.maxLines,
selectionColor: this.widget.selectionColor,
textScaleFactor: Window.instance
.devicePixelRatio, // todo widget.textScaleFactor ?? MediaQuery.textScaleFactorOf(context),
textAlign: this.widget.textAlign,
textDirection: this._textDirection,
obscureText: this.widget.obscureText,
autocorrect: this.widget.autocorrect,
offset: offset,
onSelectionChanged: this._handleSelectionChanged,
onCaretChanged: this._handleCaretChanged,
rendererIgnoresPointer: this.widget.rendererIgnoresPointer
)
);
}

2
Runtime/widgets/framework.cs


if (haveOldChildren) {
Key key = newWidget.key;
if (key != null) {
oldChild = oldKeyedChildren[key];
oldChild = oldKeyedChildren.getOrDefault(key);
if (oldChild != null) {
if (Widget.canUpdate(oldChild.widget, newWidget)) {
oldKeyedChildren.Remove(key);

1
Runtime/widgets/page_view.cs


oldPosition: oldPosition
) {
D.assert(viewportFraction > 0.0);
this.initialPage = initialPage;
this._viewportFraction = viewportFraction;
this._pageToUseOnStartup = initialPage;
}

2
Samples/ReduxSample/ObjectFinder/ObjectFinderApp.cs


using System.Collections.Generic;
using Unity.UIWidgets.engine;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;

decoration: new BoxDecoration(border: Border.all(new Color(0xFF000000), 1)),
padding: EdgeInsets.only(left: 8, right: 8),
child: new EditableText(
selectionControls: MaterialUtils.materialTextSelectionControls,
controller: this._controller,
focusNode: this._focusNode,
style: new TextStyle(

2
Samples/ReduxSample/ObjectFinder/Reducer.cs


if (action is SearchResultAction) {
var resultAction = (SearchResultAction) action;
var selected = state.selected;
if (selected != null) {
if (selected != 0) {
var obj = resultAction.results.Find(o => o.id == selected);
if (obj == null) {
selected = 0;

2
Samples/UIWidgetSample/AsScreenCanvas.cs


using System.Collections.Generic;
using Unity.UIWidgets.engine;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;

margin: EdgeInsets.only(right: 4),
child: new EditableText(
maxLines: 1,
selectionControls: MaterialUtils.materialTextSelectionControls,
controller: new TextEditingController("Type here to search assets"),
focusNode: new FocusNode(),
style: new TextStyle(

13
Samples/UIWidgetSample/TextInputCanvas.cs


using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Unity.UIWidgets.material;
using Unity.UIWidgets.service;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using TextStyle = Unity.UIWidgets.painting.TextStyle;

}
Widget title() {
return new Text(this.widget.title ?? "", textAlign: TextAlign.center,
style: new TextStyle(fontSize: 24, fontWeight: FontWeight.w700));
return new Container(child:new Text(this.widget.title ?? "", textAlign: TextAlign.center,
style: new TextStyle(fontSize: 24, fontWeight: FontWeight.w700)), margin:EdgeInsets.only(bottom:20));
}
Widget titleInput() {

padding: EdgeInsets.fromLTRB(8, 0, 8, 0),
child: new EditableText(maxLines: 1,
controller: this.titleController,
selectionControls: MaterialUtils.materialTextSelectionControls,
autofocus: true,
focusNode: new FocusNode(),
style: new TextStyle(

padding: EdgeInsets.fromLTRB(8, 0, 8, 0),
child: new EditableText(maxLines: 200,
controller: this.descController,
selectionControls: MaterialUtils.materialTextSelectionControls,
focusNode: new FocusNode(),
style: new TextStyle(
fontSize: 18,

}
}
}
}

24
Samples/UIWidgetSample/ToDoAppCanvas.cs


using Unity.UIWidgets.engine;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.material;
using Color = Unity.UIWidgets.ui.Color;
using TextStyle = Unity.UIWidgets.painting.TextStyle;
namespace UIWidgetsSample {

padding: EdgeInsets.fromLTRB(8, 0, 8, 0),
child: new EditableText(maxLines: 1,
controller: this.controller,
onSubmitted: (text) => {
this.controller.clear();
this._addItem(text);
},
selectionControls: MaterialUtils.materialTextSelectionControls,
autofocus: true,
focusNode: new FocusNode(),
style: new TextStyle(

child: new Text("Add", style: new TextStyle(
fontSize: 20, color: Color.fromARGB(255, 255, 255, 255), fontWeight: FontWeight.w700
)), onPressed: () => {
this.setState(() => {
if (this.controller.text != "") {
this.items.Add(new ToDoItem()
{id = this.nextId++, content = this.controller.text});
}
});
this._addItem();
}
void _addItem(string text = null) {
this.setState(() => {
text = text ?? this.controller.text;
if (text != "") {
this.items.Add(new ToDoItem()
{id = this.nextId++, content = text});
}
});
}
Widget contents() {

2
Tests/Editor/EditableTextWiget.cs


using Unity.UIWidgets.editor;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.widgets;
using UnityEditor;

color: Color.fromARGB(255, 244, 190, 85),
child: new EditableText(
maxLines: 100,
selectionControls: MaterialUtils.materialTextSelectionControls,
controller: new TextEditingController(this.txt),
focusNode: new FocusNode(),
style: new TextStyle(),

2
Tests/Editor/Gestures.cs


this._panRecognizer.onUpdate = (details) => { Debug.Log("onUpdate " + details); };
this._doubleTapGesture = new DoubleTapGestureRecognizer();
this._doubleTapGesture.onDoubleTap = () => { Debug.Log("onDoubleTap"); };
this._doubleTapGesture.onDoubleTap = (detail) => { Debug.Log("onDoubleTap"); };
}
void OnDisable() {

14
Tests/Editor/Widgets.cs


using Unity.UIWidgets.editor;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;

}
void _attachRootWidget(Widget widget) {
this.windowAdapter.attachRootWidget(new WidgetsApp(window: this.windowAdapter, home: widget));
this.windowAdapter.attachRootWidget(new WidgetsApp(window: this.windowAdapter, home: widget,
pageRouteBuilder: (RouteSettings settings, WidgetBuilder builder) =>
new PageRouteBuilder(
settings: settings,
pageBuilder: (BuildContext context, Unity.UIWidgets.animation.Animation<double> animation,
Unity.UIWidgets.animation.Animation<double> secondaryAnimation) => builder(context)
)));
}
void Update() {

}
Widget asPage() {
return new WidgetsApp(home: new AsScreen(), window: this.windowAdapter);
return new AsScreen();
return new WidgetsApp(home: new MouseHoverWidget(), window: this.windowAdapter);
return new MouseHoverWidget();
}
}

margin: EdgeInsets.only(right: 4),
child: new EditableText(
maxLines: 1,
selectionControls: MaterialUtils.materialTextSelectionControls,
controller: new TextEditingController("Type here to search assets"),
focusNode: new FocusNode(),
style: new TextStyle(

8
Runtime/material.meta


fileFormatVersion: 2
guid: ba4b5dab38db54970bf74725b5642400
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

42
Runtime/service/clipboard.cs


using RSG;
using UnityEngine;
namespace Unity.UIWidgets.service {
public class ClipboardData {
public ClipboardData(string text = null) {
this.text = text;
}
public readonly string text;
}
public abstract class Clipboard {
static readonly Clipboard _instance = new UnityGUIClipboard();
public static readonly string kTextPlain = "text/plain";
public static IPromise setData(ClipboardData data) {
return _instance.setClipboardData(data);
}
public static IPromise<ClipboardData> getData(string format) {
return _instance.getClipboardData(format);
}
protected abstract IPromise setClipboardData(ClipboardData data);
protected abstract IPromise<ClipboardData> getClipboardData(string format);
}
public class UnityGUIClipboard : Clipboard {
protected override IPromise setClipboardData(ClipboardData data) {
GUIUtility.systemCopyBuffer = data.text;
return Promise.Resolved();
}
protected override IPromise<ClipboardData> getClipboardData(string format) {
var data = new ClipboardData(text: GUIUtility.systemCopyBuffer);
return Promise<ClipboardData>.Resolved(data);
}
}
}

11
Runtime/service/clipboard.cs.meta


fileFormatVersion: 2
guid: e5edc4624d81f4f78846c2d0b1f1beec
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

153
Runtime/service/text_input_utils.cs


using System.Collections.Generic;
using UnityEngine;
namespace Unity.UIWidgets.service {
public class TextInputUtils {
static Dictionary<Event, TextInputAction> _keyToOperations;
public static TextInputAction? getInputAction(Event evt) {
if (_keyToOperations == null) {
initKeyToOperations();
}
EventModifiers m = evt.modifiers;
evt.modifiers &= ~EventModifiers.CapsLock;
TextInputAction result;
var exists = _keyToOperations.TryGetValue(evt, out result);
evt.modifiers = m;
if (exists) {
return result;
}
return null;
}
public static void initKeyToOperations() {
if (_keyToOperations != null) {
return;
}
_keyToOperations = new Dictionary<Event, TextInputAction>();
// key mappings shared by the platforms
mapKey("return", TextInputAction.newline);
mapKey("left", TextInputAction.moveLeft);
mapKey("right", TextInputAction.moveRight);
mapKey("up", TextInputAction.moveUp);
mapKey("down", TextInputAction.moveDown);
mapKey("#left", TextInputAction.selectLeft);
mapKey("#right", TextInputAction.selectRight);
mapKey("#up", TextInputAction.selectUp);
mapKey("#down", TextInputAction.selectDown);
mapKey("delete", TextInputAction.delete);
mapKey("backspace", TextInputAction.backspace);
mapKey("#backspace", TextInputAction.backspace);
// OSX is the special case for input shortcuts
if (SystemInfo.operatingSystemFamily == OperatingSystemFamily.MacOSX) {
// Keyboard mappings for mac
// TODO mapKey ("home", TextInputAction.scrollStart);
// TODO mapKey ("end", TextInputAction.scrollEnd);
// TODO mapKey ("page up", TextInputAction.scrollPageUp);
// TODO mapKey ("page down", TextInputAction.scrollPageDown);
mapKey("^left", TextInputAction.moveGraphicalLineStart);
mapKey("^right", TextInputAction.moveGraphicalLineEnd);
// TODO mapKey ("^up", TextInputAction.scrollPageUp);
// TODO mapKey ("^down", TextInputAction.scrollPageDown);
mapKey("&left", TextInputAction.moveWordLeft);
mapKey("&right", TextInputAction.moveWordRight);
mapKey("&up", TextInputAction.moveParagraphBackward);
mapKey("&down", TextInputAction.moveParagraphForward);
mapKey("%left", TextInputAction.moveGraphicalLineStart);
mapKey("%right", TextInputAction.moveGraphicalLineEnd);
mapKey("%up", TextInputAction.moveTextStart);
mapKey("%down", TextInputAction.moveTextEnd);
mapKey("#home", TextInputAction.selectTextStart);
mapKey("#end", TextInputAction.selectTextEnd);
// TODO mapKey ("#page up", TextInputAction.selectPageUp);
// TODO mapKey ("#page down", TextInputAction.selectPageDown);
mapKey("#^left", TextInputAction.expandSelectGraphicalLineStart);
mapKey("#^right", TextInputAction.expandSelectGraphicalLineEnd);
mapKey("#^up", TextInputAction.selectParagraphBackward);
mapKey("#^down", TextInputAction.selectParagraphForward);
mapKey("#&left", TextInputAction.selectWordLeft);
mapKey("#&right", TextInputAction.selectWordRight);
mapKey("#&up", TextInputAction.selectParagraphBackward);
mapKey("#&down", TextInputAction.selectParagraphForward);
mapKey("#%left", TextInputAction.expandSelectGraphicalLineStart);
mapKey("#%right", TextInputAction.expandSelectGraphicalLineEnd);
mapKey("#%up", TextInputAction.selectTextStart);
mapKey("#%down", TextInputAction.selectTextEnd);
mapKey("%a", TextInputAction.selectAll);
mapKey("%x", TextInputAction.cut);
mapKey("%c", TextInputAction.copy);
mapKey("%v", TextInputAction.paste);
// emacs-like keybindings
mapKey("^d", TextInputAction.delete);
mapKey("^h", TextInputAction.backspace);
mapKey("^b", TextInputAction.moveLeft);
mapKey("^f", TextInputAction.moveRight);
mapKey("^a", TextInputAction.moveLineStart);
mapKey("^e", TextInputAction.moveLineEnd);
mapKey("&delete", TextInputAction.deleteWordForward);
mapKey("&backspace", TextInputAction.deleteWordBack);
mapKey("%backspace", TextInputAction.deleteLineBack);
} else {
// Windows/Linux keymappings
mapKey("home", TextInputAction.moveGraphicalLineStart);
mapKey("end", TextInputAction.moveGraphicalLineEnd);
// TODO mapKey ("page up", TextInputAction.movePageUp);
// TODO mapKey ("page down", TextInputAction.movePageDown);
mapKey("%left", TextInputAction.moveWordLeft);
mapKey("%right", TextInputAction.moveWordRight);
mapKey("%up", TextInputAction.moveParagraphBackward);
mapKey("%down", TextInputAction.moveParagraphForward);
mapKey("^left", TextInputAction.moveToEndOfPreviousWord);
mapKey("^right", TextInputAction.moveToStartOfNextWord);
mapKey("^up", TextInputAction.moveParagraphBackward);
mapKey("^down", TextInputAction.moveParagraphForward);
mapKey("#^left", TextInputAction.selectToEndOfPreviousWord);
mapKey("#^right", TextInputAction.selectToStartOfNextWord);
mapKey("#^up", TextInputAction.selectParagraphBackward);
mapKey("#^down", TextInputAction.selectParagraphForward);
mapKey("#home", TextInputAction.selectGraphicalLineStart);
mapKey("#end", TextInputAction.selectGraphicalLineEnd);
// TODO mapKey ("#page up", TextInputAction.selectPageUp);
// TODO mapKey ("#page down", TextInputAction.selectPageDown);
mapKey("^delete", TextInputAction.deleteWordForward);
mapKey("^backspace", TextInputAction.deleteWordBack);
mapKey("%backspace", TextInputAction.deleteLineBack);
mapKey("^a", TextInputAction.selectAll);
mapKey("^x", TextInputAction.cut);
mapKey("^c", TextInputAction.copy);
mapKey("^v", TextInputAction.paste);
mapKey("#delete", TextInputAction.cut);
mapKey("^insert", TextInputAction.copy);
mapKey("#insert", TextInputAction.paste);
}
}
static void mapKey(string key, TextInputAction action) {
_keyToOperations[Event.KeyboardEvent(key)] = action;
}
}
}

11
Runtime/service/text_input_utils.cs.meta


fileFormatVersion: 2
guid: 2288dc63570aa4d8db1bd6673b12634f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

439
Runtime/widgets/text_selection.cs


using System;
using System.Collections.Generic;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.scheduler;
using Unity.UIWidgets.service;
using Unity.UIWidgets.ui;
namespace Unity.UIWidgets.widgets {
public enum TextSelectionHandleType {
left,
right,
collapsed,
}
internal enum _TextSelectionHandlePosition {
start,
end
}
public delegate void TextSelectionOverlayChanged(TextEditingValue value, Rect caretRect);
public abstract class TextSelectionControls {
public abstract Widget buildHandle(BuildContext context, TextSelectionHandleType type, double textLineHeight);
public abstract Widget buildToolbar(BuildContext context, Rect globalEditableRegion, Offset position,
TextSelectionDelegate selectionDelegate);
public abstract Size handleSize { get; }
public virtual bool canCut(TextSelectionDelegate selectionDelegate) {
return !selectionDelegate.textEditingValue.selection.isCollapsed;
}
public virtual bool canCopy(TextSelectionDelegate selectionDelegate) {
return !selectionDelegate.textEditingValue.selection.isCollapsed;
}
public virtual bool canPaste(TextSelectionDelegate selectionDelegate) {
// TODO in flutter: return false when clipboard is empty
return true;
}
public virtual bool canSelectAll(TextSelectionDelegate selectionDelegate) {
return selectionDelegate.textEditingValue.text.isEmpty() &&
selectionDelegate.textEditingValue.selection.isCollapsed;
}
public void handleCut(TextSelectionDelegate selectionDelegate) {
TextEditingValue value = selectionDelegate.textEditingValue;
Clipboard.setData(new ClipboardData(
text: value.selection.textInside(value.text)
));
selectionDelegate.textEditingValue = new TextEditingValue(
text: value.selection.textBefore(value.text)
+ value.selection.textAfter(value.text),
selection: TextSelection.collapsed(
offset: value.selection.start
)
);
selectionDelegate.bringIntoView(selectionDelegate.textEditingValue.selection.extendPos);
selectionDelegate.hideToolbar();
}
public void handleCopy(TextSelectionDelegate selectionDelegate) {
TextEditingValue value = selectionDelegate.textEditingValue;
Clipboard.setData(new ClipboardData(
text: value.selection.textInside(value.text)
));
selectionDelegate.textEditingValue = new TextEditingValue(
text: value.text,
selection: TextSelection.collapsed(offset: value.selection.end)
);
selectionDelegate.bringIntoView(selectionDelegate.textEditingValue.selection.extendPos);
selectionDelegate.hideToolbar();
}
public void handlePaste(TextSelectionDelegate selectionDelegate) {
TextEditingValue value = selectionDelegate.textEditingValue; // Snapshot the input before using `await`.
Clipboard.getData(Clipboard.kTextPlain).Then((data) => {
if (data != null) {
selectionDelegate.textEditingValue = new TextEditingValue(
text: value.selection.textBefore(value.text)
+ data.text
+ value.selection.textAfter(value.text),
selection: TextSelection.collapsed(
offset: value.selection.start + data.text.Length
)
);
}
selectionDelegate.bringIntoView(selectionDelegate.textEditingValue.selection.extendPos);
selectionDelegate.hideToolbar();
});
}
public void handleSelectAll(TextSelectionDelegate selectionDelegate) {
selectionDelegate.textEditingValue = new TextEditingValue(
text: selectionDelegate.textEditingValue.text,
selection: new TextSelection(
baseOffset: 0,
extentOffset: selectionDelegate.textEditingValue.text.Length
)
);
selectionDelegate.bringIntoView(selectionDelegate.textEditingValue.selection.extendPos);
}
}
public class TextSelectionOverlay {
public TextSelectionOverlay(TextEditingValue value = null,
BuildContext context = null, Widget debugRequiredFor = null,
LayerLink layerLink = null,
RenderEditable renderObject = null,
TextSelectionControls selectionControls = null,
TextSelectionDelegate selectionDelegate = null) {
D.assert(value != null);
D.assert(context != null);
this.context = context;
this.debugRequiredFor = debugRequiredFor;
this.layerLink = layerLink;
this.renderObject = renderObject;
this.selectionControls = selectionControls;
this.selectionDelegate = selectionDelegate;
this._value = value;
OverlayState overlay = Overlay.of(context);
D.assert(overlay != null);
this._handleController = new AnimationController(duration: _fadeDuration, vsync: overlay);
this._toolbarController = new AnimationController(duration: _fadeDuration, vsync: overlay);
}
public readonly BuildContext context;
public readonly Widget debugRequiredFor;
public readonly LayerLink layerLink;
public readonly RenderEditable renderObject;
public readonly TextSelectionControls selectionControls;
public readonly TextSelectionDelegate selectionDelegate;
public static TimeSpan _fadeDuration = TimeSpan.FromMilliseconds(150);
AnimationController _handleController;
AnimationController _toolbarController;
Animation<double> _handleOpacity => this._handleController.view;
Animation<double> _toolbarOpacity => this._toolbarController.view;
TextEditingValue _value;
List<OverlayEntry> _handles;
OverlayEntry _toolbar;
TextSelection _selection => this._value.selection;
public void showHandles() {
D.assert(this._handles == null);
this._handles = new List<OverlayEntry> {
new OverlayEntry(builder: (BuildContext context) =>
this._buildHandle(context, _TextSelectionHandlePosition.start)),
new OverlayEntry(builder: (BuildContext context) =>
this._buildHandle(context, _TextSelectionHandlePosition.end)),
};
Overlay.of(this.context, debugRequiredFor: this.debugRequiredFor).insertAll(this._handles);
this._handleController.forward(from: 0.0);
}
public void showToolbar() {
D.assert(this._toolbar == null);
this._toolbar = new OverlayEntry(builder: this._buildToolbar);
Overlay.of(this.context, debugRequiredFor: this.debugRequiredFor).insert(this._toolbar);
this._toolbarController.forward(from: 0.0);
}
public void update(TextEditingValue newValue) {
if (this._value == newValue)
return;
this._value = newValue;
if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.persistentCallbacks) {
SchedulerBinding.instance.addPostFrameCallback((duration) => this._markNeedsBuild());
}
else {
this._markNeedsBuild();
}
}
public void updateForScroll() {
this._markNeedsBuild();
}
void _markNeedsBuild() {
if (this._handles != null) {
this._handles[0].markNeedsBuild();
this._handles[1].markNeedsBuild();
}
this._toolbar?.markNeedsBuild();
}
public bool handlesAreVisible => this._handles != null;
public bool toolbarIsVisible => this._toolbar != null;
public void hide() {
if (this._handles != null) {
this._handles[0].remove();
this._handles[1].remove();
this._handles = null;
}
this._toolbar?.remove();
this._toolbar = null;
this._handleController.stop();
this._toolbarController.stop();
}
public void dispose() {
this.hide();
this._handleController.dispose();
this._toolbarController.dispose();
}
Widget _buildHandle(BuildContext context, _TextSelectionHandlePosition position) {
if ((this._selection.isCollapsed && position == _TextSelectionHandlePosition.end) ||
this.selectionControls == null)
return new Container(); // hide the second handle when collapsed
return new FadeTransition(
opacity: this._handleOpacity,
child: new _TextSelectionHandleOverlay(
onSelectionHandleChanged: (TextSelection newSelection) => {
this._handleSelectionHandleChanged(newSelection, position);
},
onSelectionHandleTapped: this._handleSelectionHandleTapped,
layerLink: this.layerLink,
renderObject: this.renderObject,
selection: this._selection,
selectionControls: this.selectionControls,
position: position
)
);
}
Widget _buildToolbar(BuildContext context) {
if (this.selectionControls == null)
return new Container();
// Find the horizontal midpoint, just above the selected text.
List<TextSelectionPoint> endpoints = this.renderObject.getEndpointsForSelection(this._selection);
Offset midpoint = new Offset(
(endpoints.Count == 1) ? endpoints[0].point.dx : (endpoints[0].point.dx + endpoints[1].point.dx) / 2.0,
endpoints[0].point.dy - this.renderObject.preferredLineHeight
);
Rect editingRegion = Rect.fromPoints(this.renderObject.localToGlobal(Offset.zero),
this.renderObject.localToGlobal(this.renderObject.size.bottomRight(Offset.zero))
);
return new FadeTransition(
opacity: this._toolbarOpacity,
child: new CompositedTransformFollower(
link: this.layerLink,
showWhenUnlinked: false,
offset: -editingRegion.topLeft,
child: this.selectionControls.buildToolbar(context, editingRegion, midpoint, this.selectionDelegate)
)
);
}
void _handleSelectionHandleChanged(TextSelection newSelection, _TextSelectionHandlePosition position) {
TextPosition textPosition = null;
switch (position) {
case _TextSelectionHandlePosition.start:
textPosition = newSelection.basePos;
break;
case _TextSelectionHandlePosition.end:
textPosition = newSelection.extendPos;
break;
}
this.selectionDelegate.textEditingValue =
this._value.copyWith(selection: newSelection, composing: TextRange.empty);
this.selectionDelegate.bringIntoView(textPosition);
}
void _handleSelectionHandleTapped() {
if (this._value.selection.isCollapsed) {
if (this._toolbar != null) {
this._toolbar?.remove();
this._toolbar = null;
}
else {
this.showToolbar();
}
}
}
}
internal class _TextSelectionHandleOverlay : StatefulWidget {
internal _TextSelectionHandleOverlay(
Key key = null,
TextSelection selection = null,
_TextSelectionHandlePosition position = _TextSelectionHandlePosition.start,
LayerLink layerLink = null,
RenderEditable renderObject = null,
ValueChanged<TextSelection> onSelectionHandleChanged = null,
VoidCallback onSelectionHandleTapped = null,
TextSelectionControls selectionControls = null
) : base(key: key) {
this.selection = selection;
this.position = position;
this.layerLink = layerLink;
this.renderObject = renderObject;
this.onSelectionHandleChanged = onSelectionHandleChanged;
this.onSelectionHandleTapped = onSelectionHandleTapped;
this.selectionControls = selectionControls;
}
public readonly TextSelection selection;
public readonly _TextSelectionHandlePosition position;
public readonly LayerLink layerLink;
public readonly RenderEditable renderObject;
public readonly ValueChanged<TextSelection> onSelectionHandleChanged;
public readonly VoidCallback onSelectionHandleTapped;
public readonly TextSelectionControls selectionControls;
public override State createState() {
return new _TextSelectionHandleOverlayState();
}
}
internal class _TextSelectionHandleOverlayState : State<_TextSelectionHandleOverlay> {
Offset _dragPosition;
void _handleDragStart(DragStartDetails details) {
this._dragPosition = details.globalPosition +
new Offset(0.0, -this.widget.selectionControls.handleSize.height);
}
void _handleDragUpdate(DragUpdateDetails details) {
this._dragPosition += details.delta;
TextPosition position = this.widget.renderObject.getPositionForPoint(this._dragPosition);
if (this.widget.selection.isCollapsed) {
this.widget.onSelectionHandleChanged(TextSelection.fromPosition(position));
return;
}
TextSelection newSelection = null;
switch (this.widget.position) {
case _TextSelectionHandlePosition.start:
newSelection = new TextSelection(
baseOffset: position.offset,
extentOffset: this.widget.selection.extentOffset
);
break;
case _TextSelectionHandlePosition.end:
newSelection = new TextSelection(
baseOffset: this.widget.selection.baseOffset,
extentOffset: position.offset
);
break;
}
if (newSelection.baseOffset >= newSelection.extentOffset)
return; // don't allow order swapping.
this.widget.onSelectionHandleChanged(newSelection);
}
void _handleTap() {
this.widget.onSelectionHandleTapped();
}
public override Widget build(BuildContext context) {
List<TextSelectionPoint> endpoints =
this.widget.renderObject.getEndpointsForSelection(this.widget.selection);
Offset point = null;
TextSelectionHandleType type = TextSelectionHandleType.left;
switch (this.widget.position) {
case _TextSelectionHandlePosition.start:
point = endpoints[0].point;
type = this._chooseType(endpoints[0], TextSelectionHandleType.left, TextSelectionHandleType.right);
break;
case _TextSelectionHandlePosition.end:
D.assert(endpoints.Count == 2);
point = endpoints[1].point;
type = this._chooseType(endpoints[1], TextSelectionHandleType.right, TextSelectionHandleType.left);
break;
}
return new CompositedTransformFollower(
link: this.widget.layerLink,
showWhenUnlinked: false,
child: new GestureDetector(
onPanStart: this._handleDragStart,
onPanUpdate: this._handleDragUpdate,
onTap: this._handleTap,
child: new Stack(
overflow: Overflow.visible,
children: new List<Widget>() {
new Positioned(
left: point.dx,
top: point.dy,
child: this.widget.selectionControls.buildHandle(context, type,
this.widget.renderObject.preferredLineHeight)
)
}
)
)
);
}
TextSelectionHandleType _chooseType(
TextSelectionPoint endpoint,
TextSelectionHandleType ltrType,
TextSelectionHandleType rtlType
) {
if (this.widget.selection.isCollapsed)
return TextSelectionHandleType.collapsed;
D.assert(endpoint.direction != null);
switch (endpoint.direction) {
case TextDirection.ltr:
return ltrType;
case TextDirection.rtl:
return rtlType;
}
D.assert(() => throw new UIWidgetsError($"invalid endpoint.direction {endpoint.direction}"));
return ltrType;
}
}
}

11
Runtime/widgets/text_selection.cs.meta


fileFormatVersion: 2
guid: af25b0b118f7847c1980a3e9914392f3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

221
Runtime/material/text_selection.cs


using System;
using System.Collections.Generic;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.service;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
// todo using material components: FlatButton & Material ...
namespace Unity.UIWidgets.material {
public static class MaterialUtils {
public readonly static TextSelectionControls materialTextSelectionControls =
new _MaterialTextSelectionControls();
}
internal static class _TextSelectionUtils {
internal const double _kHandleSize = 22.0;
internal const double _kToolbarScreenPadding = 8.0;
}
internal class _TextSelectionToolbar : StatelessWidget {
public _TextSelectionToolbar(Key key = null, Action handleCut = null,
Action handleCopy = null, Action handlePaste = null, Action handleSelectAll = null) : base(key: key) {
this.handleCut = handleCut;
this.handleCopy = handleCopy;
this.handlePaste = handlePaste;
this.handleSelectAll = handleSelectAll;
}
public readonly Action handleCut;
public readonly Action handleCopy;
public readonly Action handlePaste;
public readonly Action handleSelectAll;
public override Widget build(BuildContext context) {
List<Widget> items = new List<Widget>();
if (this.handleCut != null) {
items.Add(new _TempButton(onPressed: () => this.handleCut(), child: new Text("Cut")));
}
if (this.handleCopy != null) {
items.Add(new _TempButton(onPressed: () => this.handleCopy(), child: new Text("Copy")));
}
if (this.handlePaste != null) {
items.Add(new _TempButton(onPressed: () => this.handlePaste(), child: new Text("Past")));
}
if (this.handleSelectAll != null) {
items.Add(new _TempButton(onPressed: () => this.handleSelectAll(), child: new Text("Select All")));
}
return new Container(
color: new Color(0xFFEFEFEF),
height: 44.0, child: new Row(mainAxisSize: MainAxisSize.min, children: items));
}
}
internal class _TextSelectionToolbarLayout : SingleChildLayoutDelegate {
internal _TextSelectionToolbarLayout(Size screenSize = null, Rect globalEditableRegion = null,
Offset position = null) {
this.screenSize = screenSize;
this.globalEditableRegion = globalEditableRegion;
this.position = position;
}
public readonly Size screenSize;
public readonly Rect globalEditableRegion;
public readonly Offset position;
public override BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
return constraints.loosen();
}
public override Offset getPositionForChild(Size size, Size childSize) {
Offset globalPosition = this.globalEditableRegion.topLeft + this.position;
double x = globalPosition.dx - childSize.width / 2.0;
double y = globalPosition.dy - childSize.height;
if (x < _TextSelectionUtils._kToolbarScreenPadding)
x = _TextSelectionUtils._kToolbarScreenPadding;
else if (x + childSize.width > this.screenSize.width - _TextSelectionUtils._kToolbarScreenPadding)
x = this.screenSize.width - childSize.width - _TextSelectionUtils._kToolbarScreenPadding;
if (y < _TextSelectionUtils._kToolbarScreenPadding)
y = _TextSelectionUtils._kToolbarScreenPadding;
else if (y + childSize.height > this.screenSize.height - _TextSelectionUtils._kToolbarScreenPadding)
y = this.screenSize.height - childSize.height - _TextSelectionUtils._kToolbarScreenPadding;
return new Offset(x, y);
}
public override bool shouldRelayout(SingleChildLayoutDelegate oldDelegate) {
return this.position != ((_TextSelectionToolbarLayout) oldDelegate).position;
}
}
internal class _TextSelectionHandlePainter : CustomPainter {
internal _TextSelectionHandlePainter(Color color) {
this.color = color;
}
public readonly Color color;
public override void paint(Canvas canvas, Size size) {
Paint paint = new Paint();
paint.color = this.color;
double radius = size.width / 2.0;
canvas.drawCircle(new Offset(radius, radius), radius, paint);
canvas.drawRect(Rect.fromLTWH(0.0, 0.0, radius, radius), paint);
}
public override bool shouldRepaint(CustomPainter oldPainter) {
return this.color != ((_TextSelectionHandlePainter) oldPainter).color;
}
}
internal class _MaterialTextSelectionControls : TextSelectionControls {
public override Size handleSize {
get => new Size(_TextSelectionUtils._kHandleSize,
_TextSelectionUtils._kHandleSize);
}
public override Widget buildToolbar(BuildContext context, Rect globalEditableRegion, Offset position,
TextSelectionDelegate selectionDelegate) {
return new ConstrainedBox(
constraints: BoxConstraints.tight(globalEditableRegion.size),
child: new CustomSingleChildLayout(
layoutDelegate: new _TextSelectionToolbarLayout(
MediaQuery.of(context).size,
globalEditableRegion,
position
),
child: new _TextSelectionToolbar(
handleCut: this.canCut(selectionDelegate)
? () => this.handleCut(selectionDelegate)
: (Action) null,
handleCopy: this.canCopy(selectionDelegate)
? () => this.handleCopy(selectionDelegate)
: (Action) null,
handlePaste: this.canPaste(selectionDelegate)
? () => this.handlePaste(selectionDelegate)
: (Action) null,
handleSelectAll: this.canSelectAll(selectionDelegate)
? () => this.handleSelectAll(selectionDelegate)
: (Action) null
)
)
);
}
public override Widget buildHandle(BuildContext context, TextSelectionHandleType type, double textLineHeight) {
Widget handle = new Padding(
padding: EdgeInsets.only(right: 26.0, bottom: 26.0),
child: new SizedBox(
width: 20,
height: 20,
child: new CustomPaint(
painter: new _TextSelectionHandlePainter(
color: new Color(0xFFFF0000)
)
)
)
);
switch (type) {
case TextSelectionHandleType.left: // points up-right
return new Transform(
transform: Matrix3.makeRotate(90),
child: handle
);
case TextSelectionHandleType.right: // points up-left
return handle;
case TextSelectionHandleType.collapsed: // points up
return new Transform(
transform: Matrix3.makeRotate(45),
child: handle
);
}
return null;
}
}
public class _TempButton : StatelessWidget {
public _TempButton(
Key key = null,
GestureTapCallback onPressed = null,
EdgeInsets padding = null,
Color backgroundColor = null,
Widget child = null
) : base(key: key) {
this.onPressed = onPressed;
this.padding = padding ?? EdgeInsets.all(8.0);
this.backgroundColor = backgroundColor ?? new Color(0);
this.child = child;
}
public readonly GestureTapCallback onPressed;
public readonly EdgeInsets padding;
public readonly Widget child;
public readonly Color backgroundColor;
public override Widget build(BuildContext context) {
return new GestureDetector(
onTap: this.onPressed,
child: new Container(
padding: this.padding,
color: this.backgroundColor,
child: this.child
)
);
}
}
}

11
Runtime/material/text_selection.cs.meta


fileFormatVersion: 2
guid: 33685d6edaab14322aa1b854fdd7c3e6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
正在加载...
取消
保存