浏览代码

Merge branch 'yczhang' into gallery

/main
Yuncong Zhang 5 年前
当前提交
7bc1cd08
共有 49 个文件被更改,包括 2857 次插入272 次删除
  1. 22
      README-ZH.md
  2. 23
      README.md
  3. 1
      Runtime/Plugins/platform/android/editing/TextInputPlugin.java
  4. 64
      Runtime/animation/animation_controller.cs
  5. 54
      Runtime/animation/curves.cs
  6. 30
      Runtime/animation/listener_helpers.mixin.gen.cs
  7. 12
      Runtime/animation/listener_helpers.mixin.njk
  8. 13
      Runtime/animation/tween.cs
  9. 2
      Runtime/flow/physical_shape_layer.cs
  10. 17
      Runtime/foundation/basic_types.cs
  11. 4
      Runtime/foundation/node.mixin.gen.cs
  12. 24
      Runtime/gestures/binding.cs
  13. 6
      Runtime/gestures/long_press.cs
  14. 46
      Runtime/gestures/monodrag.cs
  15. 16
      Runtime/gestures/multidrag.cs
  16. 6
      Runtime/gestures/multitap.cs
  17. 38
      Runtime/gestures/recognizer.cs
  18. 61
      Runtime/material/text_field.cs
  19. 3
      Runtime/material/tooltip.cs
  20. 1
      Runtime/painting/basic_types.cs
  21. 38
      Runtime/painting/text_span.cs
  22. 199
      Runtime/rendering/editable.cs
  23. 2
      Runtime/rendering/object.mixin.gen.cs
  24. 213
      Runtime/rendering/paragraph.cs
  25. 9
      Runtime/ui/painting/path.cs
  26. 63
      Runtime/ui/text.cs
  27. 80
      Runtime/ui/txt/layout.cs
  28. 57
      Runtime/ui/txt/layout_utils.cs
  29. 16
      Runtime/ui/txt/linebreaker.cs
  30. 10
      Runtime/ui/txt/paragraph.cs
  31. 8
      Runtime/ui/txt/word_separate.cs
  32. 48
      Runtime/ui/txt/wordbreaker.cs
  33. 6
      Runtime/ui/window.cs
  34. 14
      Runtime/widgets/basic.cs
  35. 2
      Runtime/widgets/binding.cs
  36. 220
      Runtime/widgets/text_selection.cs
  37. 2
      Samples/UIWidgetSample/txt/TextSpanGesture.cs
  38. 4
      Samples/UIWidgetSample/txt/TextStyleSample.cs
  39. 22
      Samples/UIWidgetsGallery/demo/shrine/shrine_types.cs
  40. 1001
      Runtime/Plugins/platform/webgl/UIWidgetsCanvasDevicePixelRatio.jslib
  41. 37
      Runtime/Plugins/platform/webgl/UIWidgetsCanvasDevicePixelRatio.jslib.meta
  42. 26
      Runtime/gestures/hover.cs
  43. 11
      Runtime/gestures/hover.cs.meta
  44. 30
      Runtime/ui/txt/text_buff.cs
  45. 11
      Runtime/ui/txt/text_buff.cs.meta
  46. 453
      Runtime/widgets/selectable_text.cs
  47. 11
      Runtime/widgets/selectable_text.cs.meta
  48. 82
      Samples/UIWidgetSample/HoverRecognizerSample.cs
  49. 11
      Samples/UIWidgetSample/HoverRecognizerSample.cs.meta

22
README-ZH.md


#### 七、自动调节帧率
如果要使得构建出的应用能够自动调节帧率,请打开Project Settings,将构建目标平台对应的Quality选项卡中的V Sync Count设置为Don't Sync。
默认的逻辑是在界面静止时将帧率降低为15,在界面变动时将帧率提高至60。
默认的逻辑是在界面静止时将帧率降低为25,在界面变动时将帧率提高至60。
#### 八、WebGL Canvas分辨率调整插件
因为浏览器中Canvas的宽高和其在显示器上的像素数可能不一致,所以构建出的WebGL程序中画面可能会模糊。
插件`Plugins/platform/webgl/UIWidgetsCanvasDevicePixelRatio.jslib`解决了这个问题。
如果您因为任何原因需要禁止此插件,请在Project面板中选中该插件,在Inspector面板中的Define Constraints参数中,输入任意(没有被Unity定义为宏)的字符串(例如`ENABLE_CANVAS_DEVICE_PIXEL_RATIO_PLUGIN`),使得只有在Player Settings中定义了这个宏,此插件才会启用。
此插件覆盖了Unity WebGL构建模块中的如下参数:
```
JS_SystemInfo_GetWidth
JS_SystemInfo_GetHeight
JS_SystemInfo_GetCurrentCanvasWidth
JS_SystemInfo_GetCurrentCanvasHeight
$Browser
$JSEvents
```
如果您需要实现自己的WebGL插件,并且您的插件覆盖了这些参数中的至少一种,您需要采用上文中提到的方法禁用`UIWidgetsCanvasDevicePixelRatio`插件,以防止可能的冲突。
如果您仍然需要此插件所提供的功能,您可以手动将此插件对Unity WebGL构建模块的修改应用到您的插件中。
`UIWidgetsCanvasDevicePixelRatio`插件中所有的修改之处都以`////////// Modification Start ////////////`和`////////// Modification End ////////////`标识。
在被标识的代码中,所有乘/除以`devicePixelRatio`都来自于我们的修改。
若您需要详细了解此插件所修改的脚本,请参考您的Unity Editor安装目录下的`PlaybackEngines/WebGLSupport/BuildTools/lib`文件夹中的`SystemInfo.js`和`UnityNativeJS/UnityNative.js`。
## 调试UIWidgets应用程序

23
README.md


#### Automatically Adjust Frame Rate
To build an App that is able to adjust the frame rate automatically, please open Project Settings, and in the Quality tab, set the "V Sync Count" option of the target platform to "Don't Sync".
The default logic is to set the frame rate to 15 when the screen is static, and change the frame rate to 60 whenever the screen changes.
The default logic is to set the frame rate to 25 when the screen is static, and change the frame rate to 60 whenever the screen changes.
#### WebGL Canvas Device Pixel Ratio Plugin
The width and height of the Canvas in browser may differ from the number of pixels the Canvas occupies on the screen.
Therefore, the image may blur in the builded WebGL program.
The Plugin `Plugins/platform/webgl/UIWidgetsCanvasDevicePixelRatio.jslib` solves this issue.
If you need to disable this plugin for any reason, please select this plugin in the **Project** panel, and put an arbitrary string (that is not defined as a macro by Unity) in the **Define Constraints** field in the **Inspector** panel (for example, `ENABLE_CANVAS_DEVICE_PIXEL_RATIO_PLUGIN`), so that the plugin is enabled only if this macro is defined in **Player Settings**, which effectively disables the plugin.
This plugin overrides the following parameters in the Unity WebGL building module:
```
JS_SystemInfo_GetWidth
JS_SystemInfo_GetHeight
JS_SystemInfo_GetCurrentCanvasWidth
JS_SystemInfo_GetCurrentCanvasHeight
$Browser
$JSEvents
```
If you would like to implement your own WebGL plugin, and your plugin overrides at least one of the above parameters, you need to disable the `UIWidgetsCanvasDevicePixelRatio` plugin in the above mentioned way to avoid possible conflicts.
If you still need the function provided by this plugin, you can mannually apply the modification to Unity WebGL building module introduced in this plugin.
All the modifications introduced in `UIWidgetsCanvasDevicePixelRatio` are marked by `////////// Modifcation Start ////////////` and `////////// Modifcation End ////////////`.
In the marked codes, all the multiplications and divisions with `devicePixelRatio` are introduced by our modification.
To learn about the original script in detail, please refer to `SystemInfo.js` and `UnityNativeJS/UnityNative.js` in `PlaybackEngines/WebGLSupport/BuildTools/lib` in your Unity Editor installation.
## Debug UIWidgets Application

1
Runtime/Plugins/platform/android/editing/TextInputPlugin.java


public TextInputPlugin() {
ViewGroup contentView = (ViewGroup)UnityPlayer.currentActivity.findViewById(android.R.id.content);
mView = new TextInputView(UnityPlayer.currentActivity);
mView.requestFocus();
contentView.addView(mView, 0, 0);
mImm = (InputMethodManager) mView.getContext().getSystemService(
Context.INPUT_METHOD_SERVICE);

64
Runtime/animation/animation_controller.cs


return true;
});
D.assert(
this._ticker != null,
"AnimationController.forward() called after AnimationController.dispose()\n" +
"AnimationController methods should not be used after calling dispose."
);
this._direction = _AnimationDirection.forward;
if (from != null) {
this.setValue(from.Value);

return true;
});
D.assert(
this._ticker != null,
"AnimationController.reverse() called after AnimationController.dispose()\n" +
"AnimationController methods should not be used after calling dispose."
);
this._direction = _AnimationDirection.reverse;
if (from != null) {
this.setValue(from.Value);

}
public TickerFuture animateTo(float target, TimeSpan? duration = null, Curve curve = null) {
D.assert(
this._ticker != null,
"AnimationController.animateTo() called after AnimationController.dispose()\n" +
"AnimationController methods should not be used after calling dispose."
);
curve = curve ?? Curves.linear;
this._direction = _AnimationDirection.forward;

public TickerFuture animateBack(float target, TimeSpan? duration, Curve curve = null) {
D.assert(
this._ticker != null,
"AnimationController.animateBack() called after AnimationController.dispose()\n" +
"AnimationController methods should not be used after calling dispose."
);
curve = curve ?? Curves.linear;
this._direction = _AnimationDirection.reverse;
return this._animateToInternal(target, duration, curve);
}
TickerFuture _animateToInternal(float target, TimeSpan? duration = null, Curve curve = null) {
curve = curve ?? Curves.linear;

new _InterpolationSimulation(this._value, target, simulationDuration.Value, curve));
}
public TickerFuture repeat(float? min = null, float? max = null, TimeSpan? period = null) {
public TickerFuture repeat(float? min = null, float? max = null, bool reverse = false,
TimeSpan? period = null) {
min = min ?? this.lowerBound;
max = max ?? this.upperBound;
period = period ?? this.duration;

return true;
});
return this.animateWith(new _RepeatingSimulation(min.Value, max.Value, period.Value));
D.assert(max >= min);
D.assert(max <= this.upperBound && min >= this.lowerBound);
return this.animateWith(new _RepeatingSimulation(this._value, min.Value, max.Value, reverse, period.Value));
}
public TickerFuture fling(float velocity = 1.0f) {

public TickerFuture animateWith(Simulation simulation) {
D.assert(
this._ticker != null,
"AnimationController.animateWith() called after AnimationController.dispose()\n" +
"AnimationController methods should not be used after calling dispose."
);
this.stop();
return this._startSimulation(simulation);
}

}
public void stop(bool canceled = true) {
D.assert(
this._ticker != null,
"AnimationController.stop() called after AnimationController.dispose()\n" +
"AnimationController methods should not be used after calling dispose."
);
this._simulation = null;
this._lastElapsedDuration = null;
this._ticker.stop(canceled: canceled);

}
class _RepeatingSimulation : Simulation {
internal _RepeatingSimulation(float min, float max, TimeSpan period) {
internal _RepeatingSimulation(float initialValue, float min, float max, bool reverse, TimeSpan period) {
this._initialT =
(max == min) ? 0.0f : (initialValue / (max - min)) * (period.Ticks / TimeSpan.TicksPerSecond);
this._reverse = reverse;
D.assert(this._initialT >= 0.0f);
readonly bool _reverse;
readonly float _initialT;
float t = (timeInSeconds / this._periodInSeconds) % 1.0f;
return MathUtils.lerpFloat(this._min, this._max, t);
float totalTimeInSeconds = timeInSeconds + this._initialT;
float t = (totalTimeInSeconds / this._periodInSeconds) % 1.0f;
bool _isPlayingReverse = ((int) (totalTimeInSeconds / this._periodInSeconds)) % 2 == 1;
if (this._reverse && _isPlayingReverse) {
return MathUtils.lerpFloat(this._max, this._min, t);
}
else {
return MathUtils.lerpFloat(this._min, this._max, t);
}
}
public override float dx(float timeInSeconds) {

54
Runtime/animation/curves.cs


public static readonly Curve decelerate = new _DecelerateCurve();
public static readonly Cubic fastLinearToSlowEaseIn = new Cubic(0.18f, 1.0f, 0.04f, 1.0f);
public static readonly Cubic easeInToLinear = new Cubic(0.67f, 0.03f, 0.65f, 0.09f);
public static readonly Cubic easeInSine = new Cubic(0.47f, 0, 0.745f, 0.715f);
public static readonly Cubic easeInQuad = new Cubic(0.55f, 0.085f, 0.68f, 0.53f);
public static readonly Cubic easeInCubic = new Cubic(0.55f, 0.055f, 0.675f, 0.19f);
public static readonly Cubic easeInQuart = new Cubic(0.895f, 0.03f, 0.685f, 0.22f);
public static readonly Cubic easeInQuint = new Cubic(0.755f, 0.05f, 0.855f, 0.06f);
public static readonly Cubic easeInExpo = new Cubic(0.95f, 0.05f, 0.795f, 0.035f);
public static readonly Cubic easeInCirc = new Cubic(0.6f, 0.04f, 0.98f, 0.335f);
public static readonly Cubic easeInBack = new Cubic(0.6f, -0.28f, 0.735f, 0.045f);
public static readonly Cubic linearToEaseOut = new Cubic(0.35f, 0.91f, 0.33f, 0.97f);
public static readonly Cubic easeOutSine = new Cubic(0.39f, 0.575f, 0.565f, 1.0f);
public static readonly Cubic easeOutQuad = new Cubic(0.25f, 0.46f, 0.45f, 0.94f);
public static readonly Cubic easeOutCubic = new Cubic(0.215f, 0.61f, 0.355f, 1.0f);
public static readonly Cubic easeOutQuart = new Cubic(0.165f, 0.84f, 0.44f, 1.0f);
public static readonly Cubic easeOutQuint = new Cubic(0.23f, 1.0f, 0.32f, 1.0f);
public static readonly Cubic easeOutExpo = new Cubic(0.19f, 1.0f, 0.22f, 1.0f);
public static readonly Cubic easeOutCirc = new Cubic(0.075f, 0.82f, 0.165f, 1.0f);
public static readonly Cubic easeOutBack = new Cubic(0.175f, 0.885f, 0.32f, 1.275f);
public static readonly Cubic easeInOutSine = new Cubic(0.445f, 0.05f, 0.55f, 0.95f);
public static readonly Cubic easeInOutQuad = new Cubic(0.455f, 0.03f, 0.515f, 0.955f);
public static readonly Cubic easeInOutCubic = new Cubic(0.645f, 0.045f, 0.355f, 1.0f);
public static readonly Cubic easeInOutQuart = new Cubic(0.77f, 0, 0.175f, 1.0f);
public static readonly Cubic easeInOutQuint = new Cubic(0.86f, 0, 0.07f, 1.0f);
public static readonly Cubic easeInOutExpo = new Cubic(1.0f, 0, 0, 1.0f);
public static readonly Cubic easeInOutCirc = new Cubic(0.785f, 0.135f, 0.15f, 0.86f);
public static readonly Cubic easeInOutBack = new Cubic(0.68f, -0.55f, 0.265f, 1.55f);
public static readonly Curve fastOutSlowIn = new Cubic(0.4f, 0.0f, 0.2f, 1.0f);

30
Runtime/animation/listener_helpers.mixin.gen.cs


}
public override void removeListener(VoidCallback listener) {
this._listeners.Remove(listener);
this.didUnregisterListener();
bool removed = this._listeners.Remove(listener);
if (removed) {
this.didUnregisterListener();
}
}
public void notifyListeners() {

}
public override void removeListener(VoidCallback listener) {
this._listeners.Remove(listener);
this.didUnregisterListener();
bool removed = this._listeners.Remove(listener);
if (removed) {
this.didUnregisterListener();
}
}
public void notifyListeners() {

}
public override void removeStatusListener(AnimationStatusListener listener) {
this._statusListeners.Remove(listener);
this.didUnregisterListener();
bool removed = this._statusListeners.Remove(listener);
if (removed) {
this.didUnregisterListener();
}
}
public void notifyStatusListeners(AnimationStatus status) {

}
public override void removeStatusListener(AnimationStatusListener listener) {
this._statusListeners.Remove(listener);
this.didUnregisterListener();
bool removed = this._statusListeners.Remove(listener);
if (removed) {
this.didUnregisterListener();
}
}
public void notifyStatusListeners(AnimationStatus status) {

}
public override void removeStatusListener(AnimationStatusListener listener) {
this._statusListeners.Remove(listener);
this.didUnregisterListener();
bool removed = this._statusListeners.Remove(listener);
if (removed) {
this.didUnregisterListener();
}
}
public void notifyStatusListeners(AnimationStatus status) {

12
Runtime/animation/listener_helpers.mixin.njk


}
public override void removeListener(VoidCallback listener) {
this._listeners.Remove(listener);
this.didUnregisterListener();
bool removed = this._listeners.Remove(listener);
if (removed) {
this.didUnregisterListener();
}
}
public void notifyListeners() {

}
public override void removeStatusListener(AnimationStatusListener listener) {
this._statusListeners.Remove(listener);
this.didUnregisterListener();
bool removed = this._statusListeners.Remove(listener);
if (removed) {
this.didUnregisterListener();
}
}
public void notifyStatusListeners(AnimationStatus status) {

13
Runtime/animation/tween.cs


}
}
class ConstantTween<T> : Tween<T> {
public ConstantTween(T value) : base(begin: value, end: value) {
}
public override T lerp(float t) {
return this.begin;
}
public override string ToString() {
return $"{this.GetType()}(value: {this.begin})";
}
}
public class CurveTween : Animatable<float> {
public CurveTween(Curve curve = null) {
D.assert(curve != null);

2
Runtime/flow/physical_shape_layer.cs


public static void drawShadow(Canvas canvas, Path path, Color color, float elevation, bool transparentOccluder,
float dpr) {
float kAmbientAlpha = 0.039f;
float kSpotAlpha = 0.25f;
float kSpotAlpha = ShadowUtils.kUseFastShadow ? 0.1f : 0.25f;
float kLightHeight = 600f;
float kLightRadius = 800f;

17
Runtime/foundation/basic_types.cs


}
return "{ " + string.Join(", ", it.Select(item => item.ToString())) + " }";
}
public static void resize<T>(this List<T> list, int size, T value) {
int curSize = list.Count;
if (size < curSize) {
list.RemoveRange(size, curSize - size);
} else if(size > curSize) {
if (size > list.Capacity) {
list.Capacity = size;
}
list.AddRange(Enumerable.Repeat(value, size - curSize));
}
int remains = Math.Min(curSize, size);
for (int i = 0; i < remains; ++i) {
list[i] = value;
}
}
}
}

4
Runtime/foundation/node.mixin.gen.cs


static readonly Dictionary<_DependencyList, WeakReference> _canonicalObjects =
new Dictionary<_DependencyList, WeakReference>();
public bool pureWidget { get; set; } // pure = false, if canonicalEquals should not be used.
public bool pureWidget { get; set; } // if canonicalEquals should not be used.
public override bool Equals(object obj) {
if (ReferenceEquals(null, obj)) {

24
Runtime/gestures/binding.cs


public readonly HashSet<HitTestTarget> lastMoveTargets = new HashSet<HitTestTarget>();
readonly HashSet<HitTestEntry> _enteredTargets = new HashSet<HitTestEntry>();
void _handlePointerEvent(PointerEvent evt) {
if (evt is PointerScrollEvent) {
this._handlePointerScrollEvent(evt);

HitTestResult result = new HitTestResult();
this.hitTest(result, evt.position);
// enter event
D.assert(this._enteredTargets.Count == 0);
foreach (var hitTestEntry in result.path) {
if (this.lastMoveTargets.Contains(hitTestEntry.target)) {
hitTestEntry.target.handleEvent(evt, hitTestEntry);

hitTestEntry.target.handleEvent(new PointerEnterEvent(
timeStamp: evt.timeStamp,
pointer: evt.pointer,
device: evt.device,
kind: evt.kind
), hitTestEntry);
this._enteredTargets.Add(hitTestEntry);
//leave events
foreach (var lastMoveTarget in this.lastMoveTargets) {
lastMoveTarget.handleEvent(new PointerLeaveEvent(
timeStamp: evt.timeStamp,

), null);
}
//enter events
foreach (var hitTestEntry in this._enteredTargets) {
hitTestEntry.target.handleEvent(new PointerEnterEvent(
timeStamp: evt.timeStamp,
pointer: evt.pointer,
device: evt.device,
kind: evt.kind
), hitTestEntry);
}
this._enteredTargets.Clear();
this.dispatchEvent(evt, result);
}

6
Runtime/gestures/long_press.cs


using Unity.UIWidgets.ui;
public LongPressGestureRecognizer(object debugOwner = null) :
base(deadline: Constants.kLongPressTimeout, debugOwner: debugOwner) {
public LongPressGestureRecognizer(object debugOwner = null, PointerDeviceKind? kind = null) :
base(deadline: Constants.kLongPressTimeout, debugOwner: debugOwner, kind: kind) {
}
public GestureLongPressCallback onLongPress;

46
Runtime/gestures/monodrag.cs


public delegate void GestureDragCancelCallback();
public abstract class DragGestureRecognizer : OneSequenceGestureRecognizer {
public DragGestureRecognizer(object debugOwner = null)
: base(debugOwner: debugOwner) {
public DragGestureRecognizer(
object debugOwner = null,
PointerDeviceKind? kind = null,
DragStartBehavior dragStartBehavior = DragStartBehavior.start)
: base(debugOwner: debugOwner, kind: kind) {
this.dragStartBehavior = dragStartBehavior;
public DragStartBehavior dragStartBehavior;
public GestureDragDownCallback onDown;

}
}
public override void addPointer(PointerDownEvent evt) {
public override void addAllowedPointer(PointerDownEvent evt) {
this.startTrackingPointer(evt.pointer);
this._velocityTrackers[evt.pointer] = new VelocityTracker();
if (this._state == _DragState.ready) {

this._state = _DragState.accepted;
Offset delta = this._pendingDragOffset;
var timestamp = this._lastPendingEventTimestamp;
Offset updateDelta = null;
switch (this.dragStartBehavior) {
case DragStartBehavior.start:
this._initialPosition = this._initialPosition + delta;
updateDelta = Offset.zero;
break;
case DragStartBehavior.down:
updateDelta = this._getDeltaForDetails(delta);
break;
}
D.assert(updateDelta != null);
this._pendingDragOffset = Offset.zero;
this._lastPendingEventTimestamp = default(TimeSpan);
if (this.onStart != null) {

});
}
if (delta != Offset.zero && this.onUpdate != null) {
if (updateDelta != Offset.zero && this.onUpdate != null) {
delta: this._getDeltaForDetails(delta),
primaryDelta: this._getPrimaryValueFromOffset(delta),
globalPosition: this._initialPosition
delta: updateDelta,
primaryDelta: this._getPrimaryValueFromOffset(updateDelta),
globalPosition: this._initialPosition + updateDelta
));
return null;
});

}
public class VerticalDragGestureRecognizer : DragGestureRecognizer {
public VerticalDragGestureRecognizer(object debugOwner = null)
: base(debugOwner: debugOwner) {
public VerticalDragGestureRecognizer(object debugOwner = null, PointerDeviceKind? kind = null)
: base(debugOwner: debugOwner, kind: kind) {
}
protected override bool _isFlingGesture(VelocityEstimate estimate) {

}
public class HorizontalDragGestureRecognizer : DragGestureRecognizer {
public HorizontalDragGestureRecognizer(object debugOwner = null)
: base(debugOwner: debugOwner) {
public HorizontalDragGestureRecognizer(object debugOwner = null, PointerDeviceKind? kind = null)
: base(debugOwner: debugOwner, kind: kind) {
}
protected override bool _isFlingGesture(VelocityEstimate estimate) {

}
public class PanGestureRecognizer : DragGestureRecognizer {
public PanGestureRecognizer(object debugOwner = null)
: base(debugOwner: debugOwner) {
public PanGestureRecognizer(object debugOwner = null, PointerDeviceKind? kind = null)
: base(debugOwner: debugOwner, kind: kind) {
}
protected override bool _isFlingGesture(VelocityEstimate estimate) {

16
Runtime/gestures/multidrag.cs


public abstract class MultiDragGestureRecognizer<T> : GestureRecognizer where T : MultiDragPointerState {
protected MultiDragGestureRecognizer(
object debugOwner) : base(debugOwner: debugOwner) {
object debugOwner, PointerDeviceKind? kind = null) : base(debugOwner: debugOwner, kind: kind) {
}
public GestureMultiDragStartCallback onStart;

public override void addPointer(PointerDownEvent pEvent) {
public override void addAllowedPointer(PointerDownEvent pEvent) {
D.assert(this._pointers != null);
D.assert(pEvent.position != null);
D.assert(!this._pointers.ContainsKey(pEvent.pointer));

public class ImmediateMultiDragGestureRecognizer : MultiDragGestureRecognizer<_ImmediatePointerState> {
public ImmediateMultiDragGestureRecognizer(object debugOwner) : base(debugOwner: debugOwner) {
public ImmediateMultiDragGestureRecognizer(object debugOwner, PointerDeviceKind? kind = null) : base(
debugOwner: debugOwner, kind: kind) {
}
public override _ImmediatePointerState createNewPointerState(PointerDownEvent pEvent) {

}
public class HorizontalMultiDragGestureRecognizer : MultiDragGestureRecognizer<_HorizontalPointerState> {
public HorizontalMultiDragGestureRecognizer(object debugOwner) : base(debugOwner: debugOwner) {
public HorizontalMultiDragGestureRecognizer(object debugOwner, PointerDeviceKind? kind = null) : base(
debugOwner: debugOwner, kind: kind) {
}
public override _HorizontalPointerState createNewPointerState(PointerDownEvent pEvent) {

public class VerticalMultiDragGestureRecognizer : MultiDragGestureRecognizer<_VerticalPointerState> {
public VerticalMultiDragGestureRecognizer(object debugOwner) : base(debugOwner: debugOwner) {
public VerticalMultiDragGestureRecognizer(object debugOwner, PointerDeviceKind? kind = null) : base(
debugOwner: debugOwner, kind: kind) {
}
public override _VerticalPointerState createNewPointerState(PointerDownEvent pEvent) {

public class DelayedMultiDragGestureRecognizer : MultiDragGestureRecognizer<_DelayedPointerState> {
public DelayedMultiDragGestureRecognizer(
TimeSpan? delay = null,
object debugOwner = null) : base(debugOwner: debugOwner) {
object debugOwner = null,
PointerDeviceKind? kind = null) : base(debugOwner: debugOwner, kind: kind) {
if (delay == null) {
delay = Constants.kLongPressTimeout;
}

6
Runtime/gestures/multitap.cs


public class DoubleTapGestureRecognizer : GestureRecognizer {
public DoubleTapGestureRecognizer(object debugOwner = null)
: base(debugOwner: debugOwner) {
public DoubleTapGestureRecognizer(object debugOwner = null, PointerDeviceKind? kind = null)
: base(debugOwner: debugOwner, kind: kind) {
}
public GestureDoubleTapCallback onDoubleTap;

readonly Dictionary<int, _TapTracker> _trackers = new Dictionary<int, _TapTracker>();
public override void addPointer(PointerDownEvent evt) {
public override void addAllowedPointer(PointerDownEvent evt) {
if (this._firstTap != null &&
!this._firstTap.isWithinTolerance(evt, Constants.kDoubleTapSlop)) {
return;

38
Runtime/gestures/recognizer.cs


namespace Unity.UIWidgets.gestures {
public delegate T RecognizerCallback<T>();
public enum DragStartBehavior {
down,
start
}
protected GestureRecognizer(object debugOwner = null) {
protected GestureRecognizer(object debugOwner = null, PointerDeviceKind? kind = null) {
this._kind = kind;
public abstract void addPointer(PointerDownEvent evt);
readonly PointerDeviceKind? _kind;
public void addPointer(PointerDownEvent evt) {
if (this.isPointerAllowed(evt)) {
this.addAllowedPointer(evt);
}
else {
this.handleNonAllowedPointer(evt);
}
}
public abstract void addAllowedPointer(PointerDownEvent evt);
protected virtual void handleNonAllowedPointer(PointerDownEvent evt) {
}
protected bool isPointerAllowed(PointerDownEvent evt) {
return this._kind == null || this._kind == evt.kind;
}
public virtual void addScrollPointer(PointerScrollEvent evt) {
}

}
public abstract class OneSequenceGestureRecognizer : GestureRecognizer {
protected OneSequenceGestureRecognizer(object debugOwner = null) : base(debugOwner) {
protected OneSequenceGestureRecognizer(object debugOwner = null, PointerDeviceKind? kind = null) : base(
debugOwner, kind) {
}
readonly Dictionary<int, GestureArenaEntry> _entries = new Dictionary<int, GestureArenaEntry>();

public abstract class PrimaryPointerGestureRecognizer : OneSequenceGestureRecognizer {
protected PrimaryPointerGestureRecognizer(
TimeSpan? deadline = null,
object debugOwner = null
) : base(debugOwner: debugOwner) {
object debugOwner = null,
PointerDeviceKind? kind = null
) : base(debugOwner: debugOwner, kind: kind) {
this.deadline = deadline;
}

Timer _timer;
public override void addPointer(PointerDownEvent evt) {
public override void addAllowedPointer(PointerDownEvent evt) {
this.startTrackingPointer(evt.pointer);
if (this.state == GestureRecognizerState.ready) {
this.state = GestureRecognizerState.possible;

61
Runtime/material/text_field.cs


using System;
using System.Collections.Generic;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;

namespace Unity.UIWidgets.material {
public class TextField : StatefulWidget {
public TextField(Key key = null, TextEditingController controller = null, FocusNode focusNode = null,
InputDecoration decoration = null, bool noDecoration = false, TextInputType keyboardType = null,
TextInputAction? textInputAction = null,

public readonly VoidCallback onEditingComplete;
public readonly ValueChanged<String> onSubmitted;
public readonly ValueChanged<string> onSubmitted;
public readonly List<TextInputFormatter> inputFormatters;

ifTrue: "max length enforced"));
properties.add(new DiagnosticsProperty<GestureTapCallback>("onTap", this.onTap, defaultValue: null));
}
readonly GlobalKey<EditableTextState> _editableTextKey = new LabeledGlobalKey<EditableTextState>();
HashSet<InteractiveInkFeature> _splashes;

}
}
InteractiveInkFeature _createInkFeature(TapDownDetails details) {
InteractiveInkFeature _createInkFeature(Offset globalPosition) {
Offset position = referenceBox.globalToLocal(details.globalPosition);
Offset position = referenceBox.globalToLocal(globalPosition);
Color color = Theme.of(this.context).splashColor;
InteractiveInkFeature splash = null;

D.assert(this._splashes.Contains(splash));
this._splashes.Remove(splash);
if (this._currentSplash == splash) this._currentSplash = null;
if (this._currentSplash == splash) {
this._currentSplash = null;
}
this.updateKeepAlive();
} // else we're probably in deactivate()
}

}
RenderEditable _renderEditable {
get {
return this._editableTextKey.currentState.renderEditable;
}
}
get { return this._editableTextKey.currentState.renderEditable; }
}
this._startSplash(details);
this._startSplash(details.globalPosition);
void _handleTap() {
void _handleSingleTapUp(TapUpDetails details) {
if (this.widget.enableInteractiveSelection) {
this._renderEditable.handleTap();
}

}
}
void _handleTapCancel() {
void _handleSingleTapCancel() {
this._cancelCurrentSplash();
}

this._confirmCurrentSplash();
}
void _startSplash(TapDownDetails details) {
void _handleDragSelectionStart(DragStartDetails details) {
this._renderEditable.selectPositionAt(
from: details.globalPosition,
cause: SelectionChangedCause.drag);
this._startSplash(details.globalPosition);
}
void _handleDragSelectionUpdate(DragStartDetails startDetails,
DragUpdateDetails updateDetails) {
this._renderEditable.selectPositionAt(
from: startDetails.globalPosition,
to: updateDetails.globalPosition,
cause: SelectionChangedCause.drag);
}
void _startSplash(Offset globalPosition) {
InteractiveInkFeature splash = this._createInkFeature(details);
InteractiveInkFeature splash = this._createInkFeature(globalPosition);
this._splashes = this._splashes ?? new HashSet<InteractiveInkFeature>();
this._splashes.Add(splash);
this._currentSplash = splash;

return new IgnorePointer(
ignoring: !(this.widget.enabled ?? this.widget.decoration?.enabled ?? true),
child: new GestureDetector(
child: new TextSelectionGestureDetector(
onTapDown: this._handleTapDown,
onSingleTapUp: this._handleSingleTapUp,
onSingleTapCancel: this._handleSingleTapCancel,
onSingleLongTapStart: this._handleLongPress,
onDragSelectionStart: this._handleDragSelectionStart,
onDragSelectionUpdate: this._handleDragSelectionUpdate,
onTapDown: this._handleTapDown,
onTap: this._handleTap,
onTapCancel: this._handleTapCancel,
onLongPress: this._handleLongPress,
}
}
}

3
Runtime/material/tooltip.cs


void _handleLongPress() {
bool tooltipCreated = this.ensureTooltipVisible();
if (tooltipCreated)
if (tooltipCreated) {
}
}

1
Runtime/painting/basic_types.cs


public enum RenderComparison {
identical,
metadata,
function,
paint,
layout,
}

38
Runtime/painting/text_span.cs


public readonly string text;
public readonly List<TextSpan> children;
public readonly GestureRecognizer recognizer;
public readonly HoverRecognizer hoverRecognizer;
public TextSpan(string text = "", TextStyle style = null, List<TextSpan> children = null,
GestureRecognizer recognizer = null) {
public TextSpan(string text = "", TextStyle style = null, List<TextSpan> children = null,
GestureRecognizer recognizer = null, HoverRecognizer hoverRecognizer = null) {
this.hoverRecognizer = hoverRecognizer;
}
public void build(ParagraphBuilder builder, float textScaleFactor = 1.0f) {

}
}
public bool hasHoverRecognizer {
get {
bool need = false;
this.visitTextSpan((text) => {
if (text.hoverRecognizer != null) {
need = true;
return false;
}
return true;
});
return need;
}
}
bool visitTextSpan(Visitor visitor) {
if (!string.IsNullOrEmpty(this.text)) {
if (!visitor.Invoke(this)) {

});
return result;
}
public string toPlainText() {
var sb = new StringBuilder();
this.visitTextSpan((span) => {

if (!this.visitTextSpan(span => {
if (span.children != null) {
foreach (TextSpan child in span.children) {
if (child == null)
if (child == null) {
}
"The full text in question was:\n" +
this.toStringDeep(prefixLineOne:" "));
"The full text in question was:\n" +
this.toStringDeep(prefixLineOne: " "));
public RenderComparison compareTo(TextSpan other) {
if (this.Equals(other)) {
return RenderComparison.identical;

RenderComparison result = Equals(this.recognizer, other.recognizer)
? RenderComparison.identical
: RenderComparison.metadata;
if (!Equals(this.hoverRecognizer, other.hoverRecognizer)) {
result = RenderComparison.function > result ? RenderComparison.function : result;
}
if (this.style != null) {
var candidate = this.style.compareTo(other.style);
if (candidate > result) {

199
Runtime/rendering/editable.cs


using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.painting;

doubleTap,
longPress,
keyboard,
drag
}
public class TextSelectionPoint {

int _previousCursorLocation;
bool _resetCursor = false;
void _handleKeyEvent(RawKeyEvent keyEvent) {
if (keyEvent is RawKeyUpEvent) {
return;

this._extentOffset = this.selection.extentOffset;
this._baseOffset = this.selection.baseOffset;
}
bool shift = (modifiers & (int)EventModifiers.Shift) > 0;
bool ctrl = (modifiers & (int)EventModifiers.Control) > 0;
bool alt = (modifiers & (int)EventModifiers.Alt) > 0;
bool cmd = (modifiers & (int)EventModifiers.Command) > 0;
bool shift = (modifiers & (int) EventModifiers.Shift) > 0;
bool ctrl = (modifiers & (int) EventModifiers.Control) > 0;
bool alt = (modifiers & (int) EventModifiers.Alt) > 0;
bool cmd = (modifiers & (int) EventModifiers.Command) > 0;
bool rightArrow = pressedKeyCode == KeyCode.RightArrow;
bool leftArrow = pressedKeyCode == KeyCode.LeftArrow;
bool upArrow = pressedKeyCode == KeyCode.UpArrow;

bool del = pressedKeyCode == KeyCode.Delete;
bool isMac = SystemInfo.operatingSystemFamily == OperatingSystemFamily.MacOSX;
if (keyEvent is RawKeyCommandEvent) { // editor case
this._handleShortcuts(((RawKeyCommandEvent)keyEvent).command);
if (keyEvent is RawKeyCommandEvent) {
// editor case
this._handleShortcuts(((RawKeyCommandEvent) keyEvent).command);
if ((ctrl || (isMac && cmd)) && (xKey || vKey || cKey || aKey)) { // runtime case
if ((ctrl || (isMac && cmd)) && (xKey || vKey || cKey || aKey)) {
// runtime case
} else if (aKey) {
}
else if (aKey) {
} else if (vKey) {
}
else if (vKey) {
} else if (cKey) {
}
else if (cKey) {
if (arrow) {
int newOffset = this._extentOffset;
var word = (isMac && alt) || ctrl;

if (downArrow || upArrow)
if (downArrow || upArrow) {
}
newOffset = this._handleShift(rightArrow, leftArrow, shift, newOffset);
this._extentOffset = newOffset;

// If control is pressed, we will decide which way to look for a word
// based on which arrow is pressed.
if (leftArrow && this._extentOffset > 2) {
TextSelection textSelection = this._selectWordAtOffset(new TextPosition(offset: this._extentOffset - 2));
TextSelection textSelection =
this._selectWordAtOffset(new TextPosition(offset: this._extentOffset - 2));
} else if (rightArrow && this._extentOffset < this.text.text.Length - 2) {
TextSelection textSelection = this._selectWordAtOffset(new TextPosition(offset: this._extentOffset + 1));
}
else if (rightArrow && this._extentOffset < this.text.text.Length - 2) {
TextSelection textSelection =
this._selectWordAtOffset(new TextPosition(offset: this._extentOffset + 1));
int _handleHorizontalArrows(bool rightArrow, bool leftArrow, bool shift, int newOffset) {
if (rightArrow && this._extentOffset < this.text.text.Length) {
newOffset += 1;

}
if (leftArrow && this._extentOffset > 0) {
newOffset -= 1;
if (shift) {

Offset caretOffset = this._textPainter.getOffsetForCaret(new TextPosition(offset: this._extentOffset), this._caretPrototype);
Offset caretOffset =
this._textPainter.getOffsetForCaret(new TextPosition(offset: this._extentOffset), this._caretPrototype);
if (downArrow)
if (downArrow) {
else if (upArrow)
}
else if (upArrow) {
}
} else if (this._resetCursor && shift) {
}
else if (this._resetCursor && shift) {
} else {
}
else {
if (this.onSelectionChanged == null)
if (this.onSelectionChanged == null) {
}
if (shift) {
if (this._baseOffset < newOffset) {
this.onSelectionChanged(

this,
SelectionChangedCause.keyboard
);
} else {
}
else {
this.onSelectionChanged(
new TextSelection(
baseOffset: newOffset,

SelectionChangedCause.keyboard
);
}
} else {
}
else {
if (leftArrow)
if (leftArrow) {
else if (rightArrow)
}
else if (rightArrow) {
}
}
this.onSelectionChanged(

SelectionChangedCause.keyboard
);
}
return newOffset;
}

Clipboard.setData(
new ClipboardData(text: this.selection.textInside(this.text.text)));
}
break;
case KeyCommand.Cut:
if (!this.selection.isCollapsed) {

selection: TextSelection.collapsed(offset: this.selection.start)
);
}
break;
case KeyCommand.Paste:
TextEditingValue value = this.textSelectionDelegate.textEditingValue;

);
}
});
break;
case KeyCommand.SelectAll:
this._baseOffset = 0;

+ selection.textAfter(this.text.text).Substring(1),
selection: TextSelection.collapsed(offset: selection.start)
);
} else {
}
else {
this.textSelectionDelegate.textEditingValue = new TextEditingValue(
text: selection.textBefore(this.text.text),
selection: TextSelection.collapsed(offset: selection.start)

this._textLayoutLastWidth = null;
this.markNeedsLayout();
}
public TextSpan text {
get { return this._textPainter.text; }
set {

bool _hasFocus;
bool _listenerAttached = false;
public bool hasFocus {
get { return this._hasFocus; }
set {

RawKeyboard.instance.removeListener(this._handleKeyEvent);
this._listenerAttached = false;
}
this.markNeedsSemanticsUpdate();
}
}

this.markNeedsLayout();
}
}
set {
if (this._cursorWidth == value)
set {
if (this._cursorWidth == value) {
}
set {
if (this._cursorRadius == value)
set {
if (this._cursorRadius == value) {
}
set {
if (this._enableInteractiveSelection == value)
set {
if (this._enableInteractiveSelection == value) {
}
this._enableInteractiveSelection = value;
this.markNeedsTextLayout();
this.markNeedsSemanticsUpdate();

if (this._listenerAttached) {
RawKeyboard.instance.removeListener(this._handleKeyEvent);
}
base.detach();
}

this._longPress.addPointer((PointerDownEvent) evt);
}
}
this._lastTapDownPosition = details.globalPosition + - this._paintOffset;
this._lastTapDownPosition = details.globalPosition + -this._paintOffset;
public void handleTap() {
this.selectPosition(cause: SelectionChangedCause.tap);
}

D.assert(!this.ignorePointer);
this.handleDoubleTap(details);
}
public void handleDoubleTap(DoubleTapDetails details) {
// need set _lastTapDownPosition, otherwise it would be last single tap position
this._lastTapDownPosition = details.firstGlobalPosition - this._paintOffset;

public void handleLongPress() {
this.selectWord(cause: SelectionChangedCause.longPress);
}
public void selectPositionAt(Offset from = null, Offset to = null, SelectionChangedCause? cause = null) {
D.assert(cause != null);
D.assert(from != null);
this._layoutText(this.constraints.maxWidth);
if (this.onSelectionChanged != null) {
TextPosition fromPosition =
this._textPainter.getPositionForOffset(this.globalToLocal(from - this._paintOffset));
TextPosition toPosition = to == null
? null
: this._textPainter.getPositionForOffset(this.globalToLocal(to - this._paintOffset));
int baseOffset = fromPosition.offset;
int extentOffset = fromPosition.offset;
if (toPosition != null) {
baseOffset = Math.Min(fromPosition.offset, toPosition.offset);
extentOffset = Math.Max(fromPosition.offset, toPosition.offset);
}
TextSelection newSelection = new TextSelection(
baseOffset: baseOffset,
extentOffset: extentOffset,
affinity: fromPosition.affinity);
if (newSelection != this._selection) {
this.onSelectionChanged(newSelection, this, cause.Value);
}
}
}
TextPosition position = this._textPainter.getPositionForOffset(this.globalToLocal(this._lastTapDownPosition));
TextPosition position =
this._textPainter.getPositionForOffset(this.globalToLocal(this._lastTapDownPosition));
void selectWord(SelectionChangedCause? cause = null) {
this._layoutText(this.constraints.maxWidth);
D.assert(this._lastTapDownPosition != null);

this._layoutText(this.constraints.maxWidth);
D.assert(this._lastTapDownPosition != null);
if (this.onSelectionChanged != null) {
TextPosition position = this._textPainter.getPositionForOffset(this.globalToLocal(this._lastTapDownPosition));
TextPosition position =
this._textPainter.getPositionForOffset(this.globalToLocal(this._lastTapDownPosition));
TextRange word = this._textPainter.getWordBoundary(position);
if (position.offset - word.start <= 1) {
this.onSelectionChanged(

);
} else {
}
else {
this.onSelectionChanged(
TextSelection.collapsed(offset: word.end, affinity: TextAffinity.upstream),
this,

}
}
TextSelection _selectWordAtOffset(TextPosition position) {
D.assert(this._textLayoutLastWidth == this.constraints.maxWidth);
var word = this._textPainter.getWordBoundary(position);

return new TextSelection(baseOffset: word.start, extentOffset: word.end);
}
void _layoutText(float constraintWidth) {
if (this._textLayoutLastWidth == constraintWidth) {
return;

this._textLayoutLastWidth = constraintWidth;
}
protected override void performLayout() {
this._layoutText(this.constraints.maxWidth);
this._caretPrototype = Rect.fromLTWH(0.0f, _kCaretHeightOffset, this.cursorWidth,

RRect caretRRect = RRect.fromRectAndRadius(caretRect, this.cursorRadius);
canvas.drawRRect(caretRRect, paint);
}
if (!caretRect.Equals(this._lastCaretRect)) {
this._lastCaretRect = caretRect;
if (this.onCaretChanged != null) {

public override Rect describeApproximatePaintClip(RenderObject child) {
return this._hasVisualOverflow ? Offset.zero & this.size : null;
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new DiagnosticsProperty<Color>("cursorColor", this.cursorColor));

2
Runtime/rendering/object.mixin.gen.cs


get { return this._childCount; }
}
public new bool debugValidateChild(RenderObject child) {
public bool debugValidateChild(RenderObject child) {
D.assert(() => {
if (!(child is ChildType)) {
throw new UIWidgetsError(

213
Runtime/rendering/paragraph.cs


using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.service;
using Canvas = Unity.UIWidgets.ui.Canvas;
using Color = Unity.UIWidgets.ui.Color;
using Rect = Unity.UIWidgets.ui.Rect;
namespace Unity.UIWidgets.rendering {

readonly TextPainter _textPainter;
bool _hasVisualOverflow = false;
List<TextBox> _selectionRects;
public RenderParagraph(TextSpan text,
TextAlign textAlign = TextAlign.left,
TextDirection textDirection = TextDirection.ltr,

int? maxLines = null
int? maxLines = null,
Action onSelectionChanged = null,
Color selectionColor = null
) {
D.assert(maxLines == null || maxLines > 0);
this._softWrap = softWrap;

maxLines,
overflow == TextOverflow.ellipsis ? _kEllipsis : ""
);
this._selection = null;
this.onSelectionChanged = onSelectionChanged;
this.selectionColor = selectionColor;
this._resetHoverHandler();
}
public Action onSelectionChanged;
public Color selectionColor;
public TextSelection selection {
get { return this._selection; }
set {
if (this._selection == value) {
return;
}
this._selection = value;
this._selectionRects = null;
this.markNeedsPaint();
}
}
public TextSpan text {

case RenderComparison.identical:
case RenderComparison.metadata:
return;
case RenderComparison.function:
this._textPainter.text = value;
break;
case RenderComparison.paint:
this._textPainter.text = value;
this.markNeedsPaint();

this.markNeedsLayout();
break;
}
this._resetHoverHandler();
}
}

return true;
}
bool _hasFocus = false;
bool _listenerAttached = false;
public bool hasFocus {
get { return this._hasFocus; }
set {
if (this._hasFocus == value) {
return;
}
this._hasFocus = value;
if (this._hasFocus) {
D.assert(!this._listenerAttached);
RawKeyboard.instance.addListener(this._handleKeyEvent);
this._listenerAttached = true;
}
else {
this.selection = null;
D.assert(this._listenerAttached);
RawKeyboard.instance.removeListener(this._handleKeyEvent);
this._listenerAttached = false;
}
}
}
TextSpan _previousHoverSpan;
bool _pointerHoverInside;
bool _hasHoverRecognizer;
void _resetHoverHandler() {
this._hasHoverRecognizer = this._textPainter.text.hasHoverRecognizer;
this._previousHoverSpan = null;
this._pointerHoverInside = false;
}
void _handleKeyEvent(RawKeyEvent keyEvent) {
//only allow KCommand.copy
if (keyEvent is RawKeyUpEvent) {
return;
}
if (this.selection.isCollapsed) {
return;
}
KeyCode pressedKeyCode = keyEvent.data.unityEvent.keyCode;
int modifiers = (int) keyEvent.data.unityEvent.modifiers;
bool ctrl = (modifiers & (int) EventModifiers.Control) > 0;
bool cmd = (modifiers & (int) EventModifiers.Command) > 0;
bool cKey = pressedKeyCode == KeyCode.C;
bool isMac = SystemInfo.operatingSystemFamily == OperatingSystemFamily.MacOSX;
KeyCommand? kcmd = keyEvent is RawKeyCommandEvent
? ((RawKeyCommandEvent) keyEvent).command
: ((ctrl || (isMac && cmd)) && cKey)
? KeyCommand.Copy
: (KeyCommand?) null;
if (kcmd == KeyCommand.Copy) {
Clipboard.setData(
new ClipboardData(text: this.selection.textInside(this.text.toPlainText()))
);
}
}
public override void detach() {
if (this._listenerAttached) {
RawKeyboard.instance.removeListener(this._handleKeyEvent);
}
base.detach();
}
TextSelection _selection;
public void selectPositionAt(Offset from = null, Offset to = null, SelectionChangedCause? cause = null) {
D.assert(cause != null);
D.assert(from != null);
if (true) {
TextPosition fromPosition =
this._textPainter.getPositionForOffset(this.globalToLocal(from));
TextPosition toPosition = to == null
? null
: this._textPainter.getPositionForOffset(this.globalToLocal(to));
int baseOffset = fromPosition.offset;
int extentOffset = fromPosition.offset;
if (toPosition != null) {
baseOffset = Math.Min(fromPosition.offset, toPosition.offset);
extentOffset = Math.Max(fromPosition.offset, toPosition.offset);
}
TextSelection newSelection = new TextSelection(
baseOffset: baseOffset,
extentOffset: extentOffset,
affinity: fromPosition.affinity);
if (newSelection != this._selection) {
this._handleSelectionChanged(newSelection, cause.Value);
}
}
}
void _handleSelectionChanged(TextSelection selection,
SelectionChangedCause cause) {
this.selection = selection;
this.onSelectionChanged?.Invoke();
}
void _handlePointerHover(PointerEvent evt) {
if (!this._hasHoverRecognizer) {
return;
}
if (evt is PointerEnterEvent) {
this._pointerHoverInside = true;
}
else if (evt is PointerLeaveEvent) {
this._pointerHoverInside = false;
this._previousHoverSpan?.hoverRecognizer?.OnPointerLeave?.Invoke();
this._previousHoverSpan = null;
}
else if (evt is PointerHoverEvent && this._pointerHoverInside) {
this._layoutTextWithConstraints(this.constraints);
Offset offset = this.globalToLocal(evt.position);
TextPosition position = this._textPainter.getPositionForOffset(offset);
TextSpan span = this._textPainter.text.getSpanForPosition(position);
if (this._previousHoverSpan != span) {
this._previousHoverSpan?.hoverRecognizer?.OnPointerLeave?.Invoke();
span?.hoverRecognizer?.OnPointerEnter?.Invoke((PointerHoverEvent) evt);
this._previousHoverSpan = span;
}
}
}
if (!(evt is PointerDownEvent)) {
if (evt is PointerDownEvent) {
this._layoutTextWithConstraints(this.constraints);
Offset offset = ((BoxHitTestEntry) entry).localPosition;
TextPosition position = this._textPainter.getPositionForOffset(offset);
TextSpan span = this._textPainter.text.getSpanForPosition(position);
span?.recognizer?.addPointer((PointerDownEvent) evt);
this._layoutTextWithConstraints(this.constraints);
Offset offset = ((BoxHitTestEntry)entry).localPosition;
TextPosition position = this._textPainter.getPositionForOffset(offset);
TextSpan span = this._textPainter.text.getSpanForPosition(position);
span?.recognizer?.addPointer((PointerDownEvent)evt);
this._handlePointerHover(evt);
}
protected override void performLayout() {

var didOverflowWidth = this.size.width < textSize.width;
this._hasVisualOverflow = didOverflowWidth || didOverflowHeight;
this._selectionRects = null;
}
public override void paint(PaintingContext context, Offset offset) {

canvas.clipRect(bounds);
}
if (this._selection != null && this.selectionColor != null && this._selection.isValid) {
if (!this._selection.isCollapsed) {
this._selectionRects =
this._selectionRects ?? this._textPainter.getBoxesForSelection(this._selection);
this._paintSelection(canvas, offset);
}
}
}
void _paintSelection(Canvas canvas, Offset effectiveOffset) {
D.assert(this._selectionRects != null);
D.assert(this.selectionColor != null);
var paint = new Paint {color = this.selectionColor};
Path barPath = new Path();
foreach (var box in this._selectionRects) {
barPath.addRect(box.toRect().shift(effectiveOffset));
}
canvas.drawPath(barPath, paint);
}
void _layoutText(float minWidth = 0.0f, float maxWidth = float.PositiveInfinity) {

9
Runtime/ui/painting/path.cs


}
}
}
// Split arc into max 90 degree segments.
int ndivs = Mathf.Max(1, Mathf.Min((int) (Mathf.Abs(da) / (Mathf.PI * 0.5f) + 0.5f), 5));
float hda = (da / ndivs) / 2.0f;

this.addPoint(0, 0, PointFlags.corner);
}
var path = this._paths.Last();
var path = this._paths[this._paths.Count - 1];
var pt = this._points.Last();
var pt = this._points[this._points.Count - 1];
if (PathUtils.ptEquals(pt.x, pt.y, point.x, point.y, this._distTol)) {
pt.flags |= point.flags;
return;

y1 = 0;
}
else {
var pt = this._points.Last();
var pt = this._points[this._points.Count - 1];
x1 = pt.x;
y1 = pt.y;
}

return;
}
var path = this._paths.Last();
var path = this._paths[this._paths.Count - 1];
path.winding = winding;
}

63
Runtime/ui/text.cs


}
}
internal class TextStyle : IEquatable<TextStyle> {
class TextStyle : IEquatable<TextStyle> {
public readonly Color color = Color.fromARGB(255, 0, 0, 0);
public readonly float fontSize = 14.0f;
public readonly FontWeight fontWeight = FontWeight.w400;

public static TextStyle applyStyle(TextStyle currentStyle, painting.TextStyle style, float textScaleFactor) {
if (currentStyle != null) {
return new ui.TextStyle(
return new TextStyle(
color: style.color ?? currentStyle.color,
fontSize: style.fontSize != null ? style.fontSize * textScaleFactor : currentStyle.fontSize,
fontWeight: style.fontWeight ?? currentStyle.fontWeight,

);
}
return new ui.TextStyle(
return new TextStyle(
color: style.color,
fontSize: style.fontSize * textScaleFactor,
fontWeight: style.fontWeight,

downstream,
}
public class FontWeight: IEquatable<FontWeight> {
private FontWeight(int index) {
public class FontWeight : IEquatable<FontWeight> {
FontWeight(int index) {
public static readonly FontWeight w100 = new FontWeight(0);
public static readonly FontWeight w200 = new FontWeight(1);
public static readonly FontWeight w300 = new FontWeight(2);
public static readonly FontWeight w400 = new FontWeight(3);
public static readonly FontWeight w500 = new FontWeight(4);
public static readonly FontWeight w600 = new FontWeight(5);
public static readonly FontWeight w700 = new FontWeight(6);
public static readonly FontWeight w800 = new FontWeight(7);
public static readonly FontWeight w900 = new FontWeight(8);
public static readonly FontWeight normal = w400;
public static readonly FontWeight bold = w700;
public static readonly List<FontWeight> values = new List<FontWeight>{
public static readonly FontWeight w100 = new FontWeight(0);
public static readonly FontWeight w200 = new FontWeight(1);
public static readonly FontWeight w300 = new FontWeight(2);
public static readonly FontWeight w400 = new FontWeight(3);
public static readonly FontWeight w500 = new FontWeight(4);
public static readonly FontWeight w600 = new FontWeight(5);
public static readonly FontWeight w700 = new FontWeight(6);
public static readonly FontWeight w800 = new FontWeight(7);
public static readonly FontWeight w900 = new FontWeight(8);
public static readonly FontWeight normal = w400;
public static readonly FontWeight bold = w700;
public static readonly List<FontWeight> values = new List<FontWeight> {
public static readonly Dictionary<int, int> indexToFontWeight = new Dictionary<int, int> {
{0, 100},
{1, 200},

{7, 800},
{8, 900},
};
public bool Equals(FontWeight other) {
if (ReferenceEquals(null, other)) {
return false;

public override string ToString() {
return $"FontWeight.w{this.weightValue}";
}
get {
return indexToFontWeight[this.index];
}
get { return indexToFontWeight[this.index]; }
}
}

80
Runtime/ui/txt/layout.cs


using System;
using UnityEngine;
using Unity.UIWidgets.foundation;
namespace Unity.UIWidgets.ui {

List<float> _positions = new List<float>();
float _advance;
Rect _bounds;
string _text;
public static float measureText(float offset, string buf, int start, int count, TextStyle style,
public static float measureText(float offset, TextBuff buff, int start, int count, TextStyle style,
layout.doLayout(offset, buf, start, count, style);
layout.doLayout(offset, buff, start, count, style);
if (advances != null) {
var layoutAdv = layout.getAdvances();
for (int i = 0; i < count; i++) {

return layout.getAdvance();
}
public void doLayout(float offset, string text, int start, int count, TextStyle style) {
this._text = text;
this._advances.Clear();
this._positions.Clear();
public void doLayout(float offset, TextBuff buff, int start, int count, TextStyle style) {
this._start = start;
this._advances.resize(count, 0);
this._positions.resize(count, 0);
this._advance = 0;
this._bounds = null;
int wordstart = start == buff.size
? start
: LayoutUtils.getPrevWordBreakForCache(buff, start + 1);
int wordend;
for (int iter = start; iter < start + count; iter = wordend) {
wordend = LayoutUtils.getNextWordBreakForCache(buff, iter);
int wordCount = Math.Min(start + count, wordend) - iter;
this.layoutWord(offset, iter - start, buff.subBuff(wordstart, wordend - wordstart),
iter - wordstart, wordCount, style);
wordstart = wordend;
}
this._count = count;
}
void layoutWord(float offset, int layoutOffset,
TextBuff buff, int start, int wordCount, TextStyle style) {
float wordSpacing =
wordCount == 1 && LayoutUtils.isWordSpace(buff.charAt(start)) ? style.wordSpacing : 0;
font.RequestCharactersInTextureSafe(this._text.Substring(start, count),
font.RequestCharactersInTextureSafe(buff.subBuff(start, wordCount).getString(),
float x = this._advance;
float letterSpace = style.letterSpacing;
float letterSpaceHalfLeft = letterSpace * 0.5f;
float letterSpaceHalfRight = letterSpace - letterSpaceHalfLeft;
for (int i = 0; i < wordCount; i++) {
var ch = buff.charAt(start + i);
if (i == 0) {
x += letterSpaceHalfLeft + wordSpacing;
this._advances[i + layoutOffset] += letterSpaceHalfLeft + wordSpacing;
}
else {
this._advances[i - 1 + layoutOffset] += letterSpaceHalfRight;
this._advances[i + layoutOffset] += letterSpaceHalfLeft;
x += letterSpace;
}
this._advance = 0;
this._bounds = null;
for (int i = 0; i < count; i++) {
int charIndex = start + i;
var ch = text[charIndex];
rect = rect.translate(this._advance, 0);
rect = rect.translate(x, 0);
if (this._bounds == null || this._bounds.isEmpty) {
this._bounds = rect;
}

this._positions.Add(this._advance);
this._positions[i + layoutOffset] = x;
this._advances.Add(advance);
this._advance += advance;
x += advance;
this._advances[i + layoutOffset] += advance;
if (i + 1 == wordCount) {
this._advances[i + layoutOffset] += letterSpaceHalfRight;
x += letterSpaceHalfRight;
}
this._advance = x;
}
public void setTabStops(TabStops tabStops) {

57
Runtime/ui/txt/layout_utils.cs


namespace Unity.UIWidgets.ui {
public static class LayoutUtils {
static class LayoutUtils {
public static bool isWordSpace(char ch) {
public static bool isWordSpace(ushort ch) {
return ch == ' ' || ch == CHAR_NBSP;
}

}
public static int getPrevWordBreakForCache(TextBuff buff, int offset) {
int len = buff.size;
if (offset == 0) {
return 0;
}
if (offset > len) {
offset = len;
}
if (isWordBreakBefore(buff.charAt(offset - 1))) {
return offset - 1;
}
for (int i = offset - 1; i > 0; i--) {
if (isWordBreakBefore(buff.charAt(i)) || isWordBreakAfter(buff.charAt(i - 1))) {
return i;
}
}
return 0;
}
public static int getNextWordBreakForCache(TextBuff buff, int offset) {
int len = buff.size;
if (offset >= len) {
return len;
}
if (isWordBreakAfter(buff.charAt(offset))) {
return offset + 1;
}
for (int i = offset + 1; i < len; i++) {
if (isWordBreakBefore(buff.charAt(i))) {
return i;
}
}
return len;
}
public static bool isWordBreakAfter(ushort c) {
if (isWordSpace(c) || (c >= 0x2000 && c <= 0x200a) || c == 0x3000) {
// spaces
return true;
}
return false;
}
public static bool isWordBreakBefore(ushort c) {
return isWordBreakAfter(c) || (c >= 0x3400 && c <= 0x9fff);
}
}
}

16
Runtime/ui/txt/linebreaker.cs


const float ScoreInfty = float.MaxValue;
const float ScoreDesperate = 1e10f;
string _textBuf;
int _textOffset;
int _textLength;
TextBuff _textBuf;
List<float> _charWidths = new List<float>();
List<int> _breaks = new List<int>();
List<float> _widths = new List<float>();

TabStops _tabStops;
int mFirstTabIndex;
List<Candidate> _candidates = new List<Candidate>();
public int computeBreaks() {
int nCand = this._candidates.Count;
if (nCand > 0 && (nCand == 1 || this._lastBreak != nCand - 1)) {

}
public void setText(string text, int textOffset, int textLength) {
this._textBuf = text;
this._textOffset = textOffset;
this._textLength = textLength;
this._wordBreaker.setText(this._textBuf, textOffset, textLength);
this._textBuf = new TextBuff(text, textOffset, textLength);
this._wordBreaker.setText(this._textBuf);
this._wordBreaker.next();
this._candidates.Clear();
Candidate can = new Candidate {

float width = 0.0f;
if (style != null) {
width = Layout.measureText(this._width - this._preBreak, this._textBuf,
start + this._textOffset, end - start, style,
start, end - start, style,
this._charWidths, start, this._tabStops);
}

int postSpaceCount = this._spaceCount;
for (int i = start; i < end; i++) {
char c = this._textBuf[i + this._textOffset];
char c = this._textBuf.charAt(i);
if (c == '\t') {
this._width = this._preBreak + this._tabStops.nextTab((this._width - this._preBreak));
if (this.mFirstTabIndex == int.MaxValue) {

10
Runtime/ui/txt/paragraph.cs


if (!string.IsNullOrEmpty(ellipsis) && !this._width.isInfinite() && !lineRange.hardBreak
&& i == lineRuns.Count - 1 && (lineNumber == lineLimit - 1 || this._paragraphStyle.maxLines == null)) {
float ellipsisWidth = Layout.measureText(runXOffset, ellipsis, 0,
float ellipsisWidth = Layout.measureText(runXOffset, new TextBuff(ellipsis), 0,
float textWidth = Layout.measureText(runXOffset, this._text, textStart, textCount,
float textWidth = Layout.measureText(runXOffset, new TextBuff(text), textStart, textCount,
run.style, textAdvances, 0, this._tabStops);
int truncateCount = 0;

}
}
layout.doLayout(runXOffset, text, textStart, textCount, run.style);
layout.doLayout(runXOffset, new TextBuff(text), textStart, textCount, run.style);
// var layoutAdvances = layout.getAdvances();
builder.setBounds(layout.getBounds());
builder.setBounds(layout.getBounds().translate(-layout.getX(0), 0)); // bounds relative to first character
glyphPositions.Clear();
for (int glyphIndex = 0; glyphIndex < textCount; ++glyphIndex) {

8
Runtime/ui/txt/word_separate.cs


internal static characterType classifyChar(string text, int index) {
if (char.IsWhiteSpace(text, index)) {
return classifyChar(text[index]);
}
internal static characterType classifyChar(char ch) {
if (char.IsWhiteSpace(ch)) {
if (char.IsLetterOrDigit(text, index) || text[index] == '\'') {
if (char.IsLetterOrDigit(ch) || ch == '\'') {
return characterType.LetterLike;
}

48
Runtime/ui/txt/wordbreaker.cs


namespace Unity.UIWidgets.ui {
class WordBreaker {
public const uint U16_SURROGATE_OFFSET = ((0xd800 << 10) + 0xdc00 - 0x10000);
string _text;
int _offset;
int _size;
TextBuff _text;
int _current;
int _last;
int _scanOffset;

return this._current;
}
public void setText(string data, int offset, int size) {
this._text = data;
this._offset = offset;
this._size = size;
public void setText(TextBuff text) {
this._text = text;
this._last = 0;
this._current = 0;
this._scanOffset = 0;

}
int _findNextBoundaryNormal() {
if (this._current == this._size) {
if (this._current == this._text.size) {
WordSeparate.characterType preType = WordSeparate.classifyChar(this._text, this._current + this._offset);
bool preBoundaryChar = isBoundaryChar(this._text[this._current + this._offset]);
WordSeparate.characterType preType = WordSeparate.classifyChar(this._text.charAt(this._current));
bool preBoundaryChar = isBoundaryChar(this._text.charAt(this._current));
for (; this._current < this._size; ++this._current) {
for (; this._current < this._text.size; ++this._current) {
if (this._current >= this._size) {
if (this._current >= this._text.size) {
if (isBoundaryChar(this._text[this._current + this._offset])) {
if (isBoundaryChar(this._text.charAt(this._current))) {
var currentType = WordSeparate.classifyChar(this._text, this._current + this._offset);
var currentType = WordSeparate.classifyChar(this._text.charAt(this._current));
if ((currentType == WordSeparate.characterType.WhiteSpace)
!= (preType == WordSeparate.characterType.WhiteSpace)) {
break;

void _detectEmailOrUrl() {
}
static uint nextCode(string text, ref int index, int end) {
uint ch = text[index++];
static uint nextCode(TextBuff text, ref int index, int end) {
uint ch = text.charAt(index);
index++;
if (index < end && isTrailSurrogate(text[index])) {
char ch2 = text[index];
if (index < end && isTrailSurrogate(text.charAt(index))) {
char ch2 = text.charAt(index);
index++;
ch = getSupplementary(ch, ch2);
}

}
static uint preCode(string text, ref int index, int start) {
uint ch = text[--index];
static uint preCode(TextBuff text, ref int index, int start) {
--index;
uint ch = text.charAt(index);
if (index > start && isLeadSurrogate(text[index - 1])) {
ch = getSupplementary(text[index - 1], ch);
if (index > start && isLeadSurrogate(text.charAt(index - 1))) {
ch = getSupplementary(text.charAt(index - 1), ch);
--index;
}
}

}
void nextUntilCodePoint() {
while (this._current < this._size
&& (char.IsLowSurrogate(this._text[this._current + this._offset])
|| char.IsHighSurrogate(this._text[this._current + this._offset]))) {
while (this._current < this._text.size
&& (char.IsLowSurrogate(this._text.charAt(this._current))
|| char.IsHighSurrogate(this._text.charAt(this._current)))) {
this._current++;
}
}

6
Runtime/ui/window.cs


public abstract Timer run(TimeSpan duration, Action callback, bool periodic = false);
public Timer periodic(TimeSpan duration, Action callback) {
return this.run(duration, callback, true);
}
public Timer run(Action callback) {
return this.run(TimeSpan.Zero, callback);
}

}
public const int defaultMaxTargetFrameRate = 60;
public const int defaultMinTargetFrameRate = 15;
public const int defaultMinTargetFrameRate = 25;
static Action _onFrameRateSpeedUp = defaultFrameRateSpeedUp;

14
Runtime/widgets/basic.cs


bool softWrap = true,
TextOverflow overflow = TextOverflow.clip,
float textScaleFactor = 1.0f,
int? maxLines = null
int? maxLines = null,
Action onSelectionChanged = null,
Color selectionColor = null
) : base(key: key) {
D.assert(text != null);
D.assert(maxLines == null || maxLines > 0);

this.overflow = overflow;
this.textScaleFactor = textScaleFactor;
this.maxLines = maxLines;
this.onSelectionChanged = onSelectionChanged;
this.selectionColor = selectionColor;
}
public readonly TextSpan text;

public readonly float textScaleFactor;
public readonly int? maxLines;
public readonly Action onSelectionChanged;
public readonly Color selectionColor;
public override RenderObject createRenderObject(BuildContext context) {
return new RenderParagraph(

overflow: this.overflow,
textScaleFactor: this.textScaleFactor,
maxLines: this.maxLines
maxLines: this.maxLines,
onSelectionChanged: this.onSelectionChanged,
selectionColor: this.selectionColor
);
}

renderObject.overflow = this.overflow;
renderObject.textScaleFactor = this.textScaleFactor;
renderObject.maxLines = this.maxLines;
renderObject.onSelectionChanged = this.onSelectionChanged;
renderObject.selectionColor = this.selectionColor;
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {

2
Runtime/widgets/binding.cs


return;
}
//The former widget tree must be layout first before its destruction
this.drawFrame();
this.attachRootWidget(null);
this.buildOwner.buildScope(this._renderViewElement);
this.buildOwner.finalizeTree();

220
Runtime/widgets/text_selection.cs


using System;
using System.Collections.Generic;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.async;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.rendering;

namespace Unity.UIWidgets.widgets {
static class TextSelectionUtils {
public static TimeSpan _kDragSelectionUpdateThrottle = new TimeSpan(0, 0, 0, 0, 50);
}
public enum TextSelectionHandleType {
left,
right,

}
public delegate void TextSelectionOverlayChanged(TextEditingValue value, Rect caretRect);
public delegate void DragSelectionUpdateCallback(DragStartDetails startDetails, DragUpdateDetails updateDetails);
public abstract class TextSelectionControls {
public abstract Widget buildHandle(BuildContext context, TextSelectionHandleType type, float textLineHeight);

D.assert(() => throw new UIWidgetsError($"invalid endpoint.direction {endpoint.direction}"));
return ltrType;
}
}
public class TextSelectionGestureDetector : StatefulWidget {
public TextSelectionGestureDetector(
Key key = null,
GestureTapDownCallback onTapDown = null,
GestureTapUpCallback onSingleTapUp = null,
GestureTapCancelCallback onSingleTapCancel = null,
GestureLongPressCallback onSingleLongTapStart = null,
GestureTapDownCallback onDoubleTapDown = null,
GestureDragStartCallback onDragSelectionStart = null,
DragSelectionUpdateCallback onDragSelectionUpdate = null,
GestureDragEndCallback onDragSelectionEnd = null,
HitTestBehavior? behavior = null,
Widget child = null
) : base(key: key) {
D.assert(child != null);
this.onTapDown = onTapDown;
this.onSingleTapUp = onSingleTapUp;
this.onSingleTapCancel = onSingleTapCancel;
this.onSingleLongTapStart = onSingleLongTapStart;
this.onDoubleTapDown = onDoubleTapDown;
this.onDragSelectionStart = onDragSelectionStart;
this.onDragSelectionUpdate = onDragSelectionUpdate;
this.onDragSelectionEnd = onDragSelectionEnd;
this.behavior = behavior;
this.child = child;
}
public readonly GestureTapDownCallback onTapDown;
public readonly GestureTapUpCallback onSingleTapUp;
public readonly GestureTapCancelCallback onSingleTapCancel;
public readonly GestureLongPressCallback onSingleLongTapStart;
public readonly GestureTapDownCallback onDoubleTapDown;
public readonly GestureDragStartCallback onDragSelectionStart;
public readonly DragSelectionUpdateCallback onDragSelectionUpdate;
public readonly GestureDragEndCallback onDragSelectionEnd;
public HitTestBehavior? behavior;
public readonly Widget child;
public override State createState() {
return new _TextSelectionGestureDetectorState();
}
}
class _TextSelectionGestureDetectorState : State<TextSelectionGestureDetector> {
Timer _doubleTapTimer;
Offset _lastTapOffset;
bool _isDoubleTap = false;
public override void dispose() {
this._doubleTapTimer?.cancel();
this._dragUpdateThrottleTimer?.cancel();
base.dispose();
}
void _handleTapDown(TapDownDetails details) {
if (this.widget.onTapDown != null) {
this.widget.onTapDown(details);
}
if (this._doubleTapTimer != null &&
this._isWithinDoubleTapTolerance(details.globalPosition)) {
if (this.widget.onDoubleTapDown != null) {
this.widget.onDoubleTapDown(details);
}
this._doubleTapTimer.cancel();
this._doubleTapTimeout();
this._isDoubleTap = true;
}
}
void _handleTapUp(TapUpDetails details) {
if (!this._isDoubleTap) {
if (this.widget.onSingleTapUp != null) {
this.widget.onSingleTapUp(details);
}
this._lastTapOffset = details.globalPosition;
this._doubleTapTimer = Window.instance.run(Constants.kDoubleTapTimeout, this._doubleTapTimeout);
}
this._isDoubleTap = false;
}
void _handleTapCancel() {
if (this.widget.onSingleTapCancel != null) {
this.widget.onSingleTapCancel();
}
}
DragStartDetails _lastDragStartDetails;
DragUpdateDetails _lastDragUpdateDetails;
Timer _dragUpdateThrottleTimer;
void _handleDragStart(DragStartDetails details) {
D.assert(this._lastDragStartDetails == null);
this._lastDragStartDetails = details;
if (this.widget.onDragSelectionStart != null) {
this.widget.onDragSelectionStart(details);
}
}
void _handleDragUpdate(DragUpdateDetails details) {
this._lastDragUpdateDetails = details;
this._dragUpdateThrottleTimer = this._dragUpdateThrottleTimer ??
Window.instance.run(TextSelectionUtils._kDragSelectionUpdateThrottle,
this._handleDragUpdateThrottled);
}
void _handleDragUpdateThrottled() {
D.assert(this._lastDragStartDetails != null);
D.assert(this._lastDragUpdateDetails != null);
if (this.widget.onDragSelectionUpdate != null) {
this.widget.onDragSelectionUpdate(this._lastDragStartDetails, this._lastDragUpdateDetails);
}
this._dragUpdateThrottleTimer = null;
this._lastDragUpdateDetails = null;
}
void _handleDragEnd(DragEndDetails details) {
D.assert(this._lastDragStartDetails != null);
if (this._lastDragUpdateDetails != null) {
this._dragUpdateThrottleTimer.cancel();
this._handleDragUpdateThrottled();
}
if (this.widget.onDragSelectionEnd != null) {
this.widget.onDragSelectionEnd(details);
}
this._dragUpdateThrottleTimer = null;
this._lastDragStartDetails = null;
this._lastDragUpdateDetails = null;
}
void _handleLongPressStart() {
if (!this._isDoubleTap && this.widget.onSingleLongTapStart != null) {
this.widget.onSingleLongTapStart();
}
}
void _doubleTapTimeout() {
this._doubleTapTimer = null;
this._lastTapOffset = null;
}
bool _isWithinDoubleTapTolerance(Offset secondTapOffset) {
D.assert(secondTapOffset != null);
if (this._lastTapOffset == null) {
return false;
}
Offset difference = secondTapOffset - this._lastTapOffset;
return difference.distance <= Constants.kDoubleTapSlop;
}
public override Widget build(BuildContext context) {
Dictionary<Type, GestureRecognizerFactory> gestures = new Dictionary<Type, GestureRecognizerFactory>();
gestures.Add(typeof(TapGestureRecognizer), new GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>(
() => new TapGestureRecognizer(debugOwner: this),
instance => {
instance.onTapDown = this._handleTapDown;
instance.onTapUp = this._handleTapUp;
instance.onTapCancel = this._handleTapCancel;
}
)
);
if (this.widget.onSingleLongTapStart != null) {
gestures[typeof(LongPressGestureRecognizer)] = new GestureRecognizerFactoryWithHandlers<LongPressGestureRecognizer>(
() => new LongPressGestureRecognizer(debugOwner: this, kind: PointerDeviceKind.touch),
instance => {
instance.onLongPress = this._handleLongPressStart;
});
}
if (this.widget.onDragSelectionStart != null ||
this.widget.onDragSelectionUpdate != null ||
this.widget.onDragSelectionEnd != null) {
gestures.Add(typeof(HorizontalDragGestureRecognizer),
new GestureRecognizerFactoryWithHandlers<HorizontalDragGestureRecognizer>(
() => new HorizontalDragGestureRecognizer(debugOwner: this, kind: PointerDeviceKind.mouse),
instance => {
instance.dragStartBehavior = DragStartBehavior.down;
instance.onStart = this._handleDragStart;
instance.onUpdate = this._handleDragUpdate;
instance.onEnd = this._handleDragEnd;
}
)
);
}
return new RawGestureDetector(
gestures: gestures,
behavior: this.widget.behavior,
child: this.widget.child
);
}
}
}

2
Samples/UIWidgetSample/txt/TextSpanGesture.cs


color: Colors.green,
decoration: TextDecoration.underline
)
// recognizer: this._longPressRecognizer
//recognizer: this._longPressRecognizer
),
new TextSpan(
text: " secret?"

4
Samples/UIWidgetSample/txt/TextStyleSample.cs


new Text("text with font size 0.3f", style: new TextStyle(fontSize: 0.3f)),
new Text("Text with background", style: new TextStyle(fontSize: 14, background:
new Paint(){color = new Color(0xFF00FF00)})),
new Text("positive letter spacing", style: new TextStyle(fontSize: 14, letterSpacing:5)),
new Text("negative letter spacing", style: new TextStyle(fontSize: 14, letterSpacing:-1)),
new Text("positive word spacing test", style: new TextStyle(fontSize: 14, wordSpacing: 20f)),
new Text("negative word spacing test", style: new TextStyle(fontSize: 14, wordSpacing: -4f)),
};
return new Scaffold(

22
Samples/UIWidgetsGallery/demo/shrine/shrine_types.cs


}
public override string ToString() {
return "Vendor($name)";
return $"Vendor({this.name})";
}
}

}
public static bool operator ==(Order left, Order right) {
if (left is null && right is null) return true;
if (left is null || right is null) return false;
if (left is null && right is null) {
return true;
}
if (left is null || right is null) {
return false;
}
if (left is null && right is null) return false;
if (left is null || right is null) return true;
if (left is null && right is null) {
return false;
}
if (left is null || right is null) {
return true;
}
return !left.Equals(right);
}

1001
Runtime/Plugins/platform/webgl/UIWidgetsCanvasDevicePixelRatio.jslib
文件差异内容过多而无法显示
查看文件

37
Runtime/Plugins/platform/webgl/UIWidgetsCanvasDevicePixelRatio.jslib.meta


fileFormatVersion: 2
guid: 9e688991bb495461b8404d9fb47c6684
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
Facebook: WebGL
second:
enabled: 1
settings: {}
- first:
WebGL: WebGL
second:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:

26
Runtime/gestures/hover.cs


using Unity.UIWidgets.foundation;
namespace Unity.UIWidgets.gestures {
public delegate void PointerHoverEnterCallback(PointerHoverEvent evt);
public delegate void PointerHoverLeaveCallback();
public class HoverRecognizer : DiagnosticableTree {
public HoverRecognizer(object debugOwner = null) {
this.debugOwner = debugOwner;
}
readonly object debugOwner;
public PointerHoverEnterCallback OnPointerEnter;
public PointerHoverLeaveCallback OnPointerLeave;
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new DiagnosticsProperty<object>("debugOwner", this.debugOwner,
defaultValue: Diagnostics.kNullDefaultValue));
}
}
}

11
Runtime/gestures/hover.cs.meta


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

30
Runtime/ui/txt/text_buff.cs


using System;
using Unity.UIWidgets.foundation;
namespace Unity.UIWidgets.ui {
class TextBuff {
public readonly string text;
public readonly int offset;
public readonly int size;
public TextBuff(string text, int? offset = null, int? size = null) {
this.text = text;
this.offset = offset ?? 0;
this.size = size ?? text.Length - this.offset;
}
public char charAt(int index) {
return this.text[this.offset + index];
}
public TextBuff subBuff(int shift, int size) {
D.assert(shift >= 0 && shift <= this.size);
D.assert(shift + size <= this.size);
return new TextBuff(this.text, this.offset + shift, size);
}
public String getString() {
return this.text.Substring(this.offset, this.size);
}
}
}

11
Runtime/ui/txt/text_buff.cs.meta


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

453
Runtime/widgets/selectable_text.cs


using System;
using System.Collections.Generic;
using RSG;
using Unity.UIWidgets.async;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Constants = Unity.UIWidgets.gestures.Constants;
using TextStyle = Unity.UIWidgets.painting.TextStyle;
namespace Unity.UIWidgets.widgets {
public class SelectableText : StatefulWidget {
public SelectableText(string data,
Key key = null,
TextStyle style = null,
TextAlign? textAlign = null,
bool? softWrap = null,
TextOverflow? overflow = null,
float? textScaleFactor = null,
int? maxLines = null,
FocusNode focusNode = null,
Color selectionColor = null) : base(key) {
D.assert(data != null);
this.textSpan = null;
this.data = data;
this.style = style;
this.textAlign = textAlign;
this.softWrap = softWrap;
this.overflow = overflow;
this.textScaleFactor = textScaleFactor;
this.maxLines = maxLines;
this.focusNode = focusNode ?? new FocusNode();
this.selectionColor = selectionColor;
}
public SelectableText(TextSpan textSpan,
Key key = null,
TextStyle style = null,
TextAlign? textAlign = null,
bool? softWrap = null,
TextOverflow? overflow = null,
float? textScaleFactor = null,
int? maxLines = null,
FocusNode focusNode = null,
Color selectionColor = null) : base(key) {
D.assert(textSpan != null);
this.textSpan = textSpan;
this.data = null;
this.style = style;
this.textAlign = textAlign;
this.softWrap = softWrap;
this.overflow = overflow;
this.textScaleFactor = textScaleFactor;
this.maxLines = maxLines;
this.focusNode = focusNode ?? new FocusNode();
this.selectionColor = selectionColor;
}
public static SelectableText rich(TextSpan textSpan,
Key key = null,
TextStyle style = null,
TextAlign? textAlign = null,
bool? softWrap = null,
TextOverflow? overflow = null,
float? textScaleFactor = null,
int? maxLines = null,
FocusNode focusNode = null,
Color selectionColor = null) {
return new SelectableText(
textSpan, key,
style,
textAlign,
softWrap,
overflow,
textScaleFactor,
maxLines,
focusNode,
selectionColor);
}
public readonly string data;
public readonly FocusNode focusNode;
public readonly TextSpan textSpan;
public readonly TextStyle style;
public readonly TextAlign? textAlign;
public readonly bool? softWrap;
public readonly TextOverflow? overflow;
public readonly float? textScaleFactor;
public readonly int? maxLines;
public readonly Color selectionColor;
public override State createState() {
return new _SelectableTextState();
}
}
class _SelectableTextState : State<SelectableText>, WidgetsBindingObserver {
readonly GlobalKey _richTextKey = GlobalKey.key();
RenderParagraph _renderParagragh {
get { return (RenderParagraph) this._richTextKey.currentContext.findRenderObject(); }
}
public override void initState() {
base.initState();
this.widget.focusNode.addListener(this._handleFocusChanged);
}
public override void didUpdateWidget(StatefulWidget old) {
SelectableText oldWidget = (SelectableText) old;
base.didUpdateWidget(oldWidget);
if (oldWidget.focusNode != this.widget.focusNode) {
oldWidget.focusNode.removeListener(this._handleFocusChanged);
this.widget.focusNode.addListener(this._handleFocusChanged);
}
}
public override void dispose() {
this.widget.focusNode.removeListener(this._handleFocusChanged);
base.dispose();
}
bool _hasFocus {
get { return this.widget.focusNode.hasFocus; }
}
void _handleFocusChanged() {
if (this._hasFocus) {
WidgetsBinding.instance.addObserver(this);
this._renderParagragh.hasFocus = true;
}
else {
WidgetsBinding.instance.removeObserver(this);
this._renderParagragh.hasFocus = false;
}
}
public void didChangeMetrics() {
}
public void didChangeTextScaleFactor() {
}
public void didChangeLocales(List<Locale> locale) {
}
public IPromise<bool> didPopRoute() {
return Promise<bool>.Resolved(false);
}
public IPromise<bool> didPushRoute(string route) {
return Promise<bool>.Resolved(false);
}
void _handleTapDown(TapDownDetails details) {
}
void _handleSingleTapUp(TapUpDetails details) {
}
void _handleSingleTapCancel() {
}
void _handleLongPress() {
}
void _handleDragSelectionStart(DragStartDetails details) {
this._renderParagragh.selectPositionAt(
from: details.globalPosition,
cause: SelectionChangedCause.drag);
}
void _handleDragSelectionUpdate(DragStartDetails startDetails,
DragUpdateDetails updateDetails) {
this._renderParagragh.selectPositionAt(
from: startDetails.globalPosition,
to: updateDetails.globalPosition,
cause: SelectionChangedCause.drag);
}
public override Widget build(BuildContext context) {
FocusScope.of(context).reparentIfNeeded(this.widget.focusNode);
DefaultTextStyle defaultTextStyle = DefaultTextStyle.of(context);
TextStyle effectiveTextStyle = this.widget.style;
if (this.widget.style == null || this.widget.style.inherit) {
effectiveTextStyle = defaultTextStyle.style.merge(this.widget.style);
}
Widget child = new RichText(
key: this._richTextKey,
textAlign: this.widget.textAlign ?? defaultTextStyle.textAlign ?? TextAlign.left,
softWrap: this.widget.softWrap ?? defaultTextStyle.softWrap,
overflow: this.widget.overflow ?? defaultTextStyle.overflow,
textScaleFactor: this.widget.textScaleFactor ?? MediaQuery.textScaleFactorOf(context),
maxLines: this.widget.maxLines ?? defaultTextStyle.maxLines,
text: new TextSpan(
style: effectiveTextStyle,
text: this.widget.data,
children: this.widget.textSpan != null ? new List<TextSpan> {this.widget.textSpan} : null
),
onSelectionChanged: () => {
if (this._hasFocus) {
return;
}
FocusScope.of(this.context).requestFocus(this.widget.focusNode);
},
selectionColor: this.widget.selectionColor ?? Colors.blue);
return new IgnorePointer(
ignoring: false,
child: new RichTextSelectionGestureDetector(
onTapDown: this._handleTapDown,
onSingleTapUp: this._handleSingleTapUp,
onSingleTapCancel: this._handleSingleTapCancel,
onSingleLongTapStart: this._handleLongPress,
onDragSelectionStart: this._handleDragSelectionStart,
onDragSelectionUpdate: this._handleDragSelectionUpdate,
behavior: HitTestBehavior.translucent,
child: child
)
);
}
}
public class RichTextSelectionGestureDetector : StatefulWidget {
public RichTextSelectionGestureDetector(
Key key = null,
GestureTapDownCallback onTapDown = null,
GestureTapUpCallback onSingleTapUp = null,
GestureTapCancelCallback onSingleTapCancel = null,
GestureLongPressCallback onSingleLongTapStart = null,
GestureTapDownCallback onDoubleTapDown = null,
GestureDragStartCallback onDragSelectionStart = null,
DragSelectionUpdateCallback onDragSelectionUpdate = null,
GestureDragEndCallback onDragSelectionEnd = null,
HitTestBehavior? behavior = null,
Widget child = null
) : base(key: key) {
D.assert(child != null);
this.onTapDown = onTapDown;
this.onSingleTapUp = onSingleTapUp;
this.onSingleTapCancel = onSingleTapCancel;
this.onSingleLongTapStart = onSingleLongTapStart;
this.onDoubleTapDown = onDoubleTapDown;
this.onDragSelectionStart = onDragSelectionStart;
this.onDragSelectionUpdate = onDragSelectionUpdate;
this.onDragSelectionEnd = onDragSelectionEnd;
this.behavior = behavior;
this.child = child;
}
public readonly GestureTapDownCallback onTapDown;
public readonly GestureTapUpCallback onSingleTapUp;
public readonly GestureTapCancelCallback onSingleTapCancel;
public readonly GestureLongPressCallback onSingleLongTapStart;
public readonly GestureTapDownCallback onDoubleTapDown;
public readonly GestureDragStartCallback onDragSelectionStart;
public readonly DragSelectionUpdateCallback onDragSelectionUpdate;
public readonly GestureDragEndCallback onDragSelectionEnd;
public HitTestBehavior? behavior;
public readonly Widget child;
public override State createState() {
return new _RichTextSelectionGestureDetectorState();
}
}
class _RichTextSelectionGestureDetectorState : State<RichTextSelectionGestureDetector> {
Timer _doubleTapTimer;
Offset _lastTapOffset;
bool _isDoubleTap = false;
public override void dispose() {
this._doubleTapTimer?.cancel();
this._dragUpdateThrottleTimer?.cancel();
base.dispose();
}
void _handleTapDown(TapDownDetails details) {
if (this.widget.onTapDown != null) {
this.widget.onTapDown(details);
}
if (this._doubleTapTimer != null &&
this._isWithinDoubleTapTolerance(details.globalPosition)) {
if (this.widget.onDoubleTapDown != null) {
this.widget.onDoubleTapDown(details);
}
this._doubleTapTimer.cancel();
this._doubleTapTimeout();
this._isDoubleTap = true;
}
}
void _handleTapUp(TapUpDetails details) {
if (!this._isDoubleTap) {
if (this.widget.onSingleTapUp != null) {
this.widget.onSingleTapUp(details);
}
this._lastTapOffset = details.globalPosition;
this._doubleTapTimer = Window.instance.run(Constants.kDoubleTapTimeout, this._doubleTapTimeout);
}
this._isDoubleTap = false;
}
void _handleTapCancel() {
if (this.widget.onSingleTapCancel != null) {
this.widget.onSingleTapCancel();
}
}
DragStartDetails _lastDragStartDetails;
DragUpdateDetails _lastDragUpdateDetails;
Timer _dragUpdateThrottleTimer;
void _handleDragStart(DragStartDetails details) {
D.assert(this._lastDragStartDetails == null);
this._lastDragStartDetails = details;
if (this.widget.onDragSelectionStart != null) {
this.widget.onDragSelectionStart(details);
}
}
void _handleDragUpdate(DragUpdateDetails details) {
this._lastDragUpdateDetails = details;
this._dragUpdateThrottleTimer = this._dragUpdateThrottleTimer ??
Window.instance.run(TextSelectionUtils._kDragSelectionUpdateThrottle,
this._handleDragUpdateThrottled);
}
void _handleDragUpdateThrottled() {
D.assert(this._lastDragStartDetails != null);
D.assert(this._lastDragUpdateDetails != null);
if (this.widget.onDragSelectionUpdate != null) {
this.widget.onDragSelectionUpdate(this._lastDragStartDetails, this._lastDragUpdateDetails);
}
this._dragUpdateThrottleTimer = null;
this._lastDragUpdateDetails = null;
}
void _handleDragEnd(DragEndDetails details) {
D.assert(this._lastDragStartDetails != null);
if (this._lastDragUpdateDetails != null) {
this._dragUpdateThrottleTimer.cancel();
this._handleDragUpdateThrottled();
}
if (this.widget.onDragSelectionEnd != null) {
this.widget.onDragSelectionEnd(details);
}
this._dragUpdateThrottleTimer = null;
this._lastDragStartDetails = null;
this._lastDragUpdateDetails = null;
}
void _handleLongPressStart() {
if (!this._isDoubleTap && this.widget.onSingleLongTapStart != null) {
this.widget.onSingleLongTapStart();
}
}
void _doubleTapTimeout() {
this._doubleTapTimer = null;
this._lastTapOffset = null;
}
bool _isWithinDoubleTapTolerance(Offset secondTapOffset) {
D.assert(secondTapOffset != null);
if (this._lastTapOffset == null) {
return false;
}
Offset difference = secondTapOffset - this._lastTapOffset;
return difference.distance <= Constants.kDoubleTapSlop;
}
public override Widget build(BuildContext context) {
Dictionary<Type, GestureRecognizerFactory> gestures = new Dictionary<Type, GestureRecognizerFactory>();
gestures.Add(typeof(TapGestureRecognizer), new GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>(
() => new TapGestureRecognizer(debugOwner: this),
instance => {
instance.onTapDown = this._handleTapDown;
instance.onTapUp = this._handleTapUp;
instance.onTapCancel = this._handleTapCancel;
}
)
);
if (this.widget.onSingleLongTapStart != null) {
gestures[typeof(LongPressGestureRecognizer)] =
new GestureRecognizerFactoryWithHandlers<LongPressGestureRecognizer>(
() => new LongPressGestureRecognizer(debugOwner: this, kind: PointerDeviceKind.touch),
instance => { instance.onLongPress = this._handleLongPressStart; });
}
if (this.widget.onDragSelectionStart != null ||
this.widget.onDragSelectionUpdate != null ||
this.widget.onDragSelectionEnd != null) {
gestures.Add(typeof(PanGestureRecognizer),
new GestureRecognizerFactoryWithHandlers<PanGestureRecognizer>(
() => new PanGestureRecognizer(debugOwner: this, kind: PointerDeviceKind.mouse),
instance => {
instance.dragStartBehavior = DragStartBehavior.down;
instance.onStart = this._handleDragStart;
instance.onUpdate = this._handleDragUpdate;
instance.onEnd = this._handleDragEnd;
}
)
);
}
return new RawGestureDetector(
gestures: gestures,
behavior: this.widget.behavior,
child: this.widget.child
);
}
}
}

11
Runtime/widgets/selectable_text.cs.meta


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

82
Samples/UIWidgetSample/HoverRecognizerSample.cs


using System.Collections.Generic;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
namespace UIWidgetsSample {
public class HoverRecognizerSample : UIWidgetsSamplePanel {
protected override Widget createWidget() {
return new MaterialApp(
showPerformanceOverlay: false,
home: new HoverMainPanel()
);
}
protected override void OnEnable() {
FontManager.instance.addFont(Resources.Load<Font>(path: "MaterialIcons-Regular"), "Material Icons");
base.OnEnable();
}
}
class HoverMainPanel : StatefulWidget {
public override State createState() {
return new HoverMainPanelState();
}
}
class HoverMainPanelState : State<HoverMainPanel> {
bool hoverActivated = false;
public override Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Center(
child: new Text("Test Hover Widget")
)
),
body: new Card(
color: Colors.white,
child: new Center(
child: new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: new List<Widget> {
new Icon(this.hoverActivated ? Unity.UIWidgets.material.Icons.pool : Unity.UIWidgets.material.Icons.directions_walk, size: 128.0f),
new RichText(
text: new TextSpan(
text: "Test <",
style: new TextStyle(color: Colors.black),
children: new List<TextSpan>() {
new TextSpan(
text: "Hover Me",
style: new TextStyle(
color: Colors.green,
decoration: TextDecoration.underline
),
hoverRecognizer: new HoverRecognizer {
OnPointerEnter = evt => {
this.setState(() => { this.hoverActivated = true; });
},
OnPointerLeave = () => {
this.setState(() => { this.hoverActivated = false;});
}
}
),
new TextSpan(
text: ">"
)
}
)
)
}
)
)
)
);
}
}
}

11
Samples/UIWidgetSample/HoverRecognizerSample.cs.meta


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