浏览代码

Merge branch 'yczhang' into webgl

/main
Yuncong Zhang 6 年前
当前提交
6df79256
共有 14 个文件被更改,包括 999 次插入49 次删除
  1. 1
      Runtime/Plugins/platform/android/editing/TextInputPlugin.java
  2. 6
      Runtime/gestures/long_press.cs
  3. 46
      Runtime/gestures/monodrag.cs
  4. 16
      Runtime/gestures/multidrag.cs
  5. 6
      Runtime/gestures/multitap.cs
  6. 38
      Runtime/gestures/recognizer.cs
  7. 43
      Runtime/material/text_field.cs
  8. 34
      Runtime/rendering/editable.cs
  9. 158
      Runtime/rendering/paragraph.cs
  10. 2
      Runtime/ui/window.cs
  11. 14
      Runtime/widgets/basic.cs
  12. 220
      Runtime/widgets/text_selection.cs
  13. 453
      Runtime/widgets/selectable_text.cs
  14. 11
      Runtime/widgets/selectable_text.cs.meta

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);

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;

43
Runtime/material/text_field.cs


}
}
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;

void _handleTapDown(TapDownDetails details) {
this._renderEditable.handleTapDown(details);
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(
behavior: HitTestBehavior.translucent,
child: new TextSelectionGestureDetector(
onTap: this._handleTap,
onTapCancel: this._handleTapCancel,
onLongPress: this._handleLongPress,
onSingleTapUp: this._handleSingleTapUp,
onSingleTapCancel: this._handleSingleTapCancel,
onSingleLongTapStart: this._handleLongPress,
onDragSelectionStart: this._handleDragSelectionStart,
onDragSelectionUpdate: this._handleDragSelectionUpdate,
behavior: HitTestBehavior.translucent,
child: child
)
);

34
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 {

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);
}
}
}
void selectPosition(SelectionChangedCause? cause = null) {

158
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;
}
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 {

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;
}
}
}
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();
}
Offset offset = ((BoxHitTestEntry)entry).localPosition;
Offset offset = ((BoxHitTestEntry) entry).localPosition;
span?.recognizer?.addPointer((PointerDownEvent)evt);
span?.recognizer?.addPointer((PointerDownEvent) 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) {

2
Runtime/ui/window.cs


}
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) {

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
);
}
}
}

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:
正在加载...
取消
保存