浏览代码
Merge branch 'master' of gitlab.cds.internal.unity3d.com:upm-packages/ui-widgets/com.unity.uiwidgets into dragdrop_fix
Merge branch 'master' of gitlab.cds.internal.unity3d.com:upm-packages/ui-widgets/com.unity.uiwidgets into dragdrop_fix
# Conflicts: # Runtime/material.meta # Runtime/rendering/proxy_box.cs/main
xingwei.zhu
6 年前
当前提交
9dc2eebe
共有 44 个文件被更改,包括 2462 次插入 和 366 次删除
-
2Runtime/debugger/inspector_treeview.cs
-
15Runtime/engine/WidgetCanvas.cs
-
17Runtime/gestures/multitap.cs
-
2Runtime/material.meta
-
35Runtime/painting/matrix_utils.cs
-
35Runtime/rendering/editable.cs
-
198Runtime/rendering/layer.cs
-
2Runtime/rendering/object.cs
-
142Runtime/rendering/proxy_box.cs
-
98Runtime/rendering/shifted_box.cs
-
8Runtime/rendering/sliver_fixed_extent_list.cs
-
360Runtime/service/text_input.cs
-
2Runtime/widgets/app.cs
-
75Runtime/widgets/basic.cs
-
4Runtime/widgets/custom_paint.cs
-
226Runtime/widgets/editable_text.cs
-
2Runtime/widgets/framework.cs
-
4Runtime/widgets/scroll_controller.cs
-
13Runtime/widgets/scroll_metrics.cs
-
8Runtime/widgets/scroll_position.cs
-
20Runtime/widgets/sliver.cs
-
2Samples/ReduxSample/ObjectFinder/ObjectFinderApp.cs
-
2Samples/ReduxSample/ObjectFinder/Reducer.cs
-
2Samples/UIWidgetSample/AsScreenCanvas.cs
-
73Samples/UIWidgetSample/Navigation.unity
-
13Samples/UIWidgetSample/TextInputCanvas.cs
-
24Samples/UIWidgetSample/ToDoAppCanvas.cs
-
2Tests/Editor/EditableTextWiget.cs
-
2Tests/Editor/Gestures.cs
-
14Tests/Editor/Widgets.cs
-
221Runtime/material/text_selection.cs
-
11Runtime/material/text_selection.cs.meta
-
91Runtime/rendering/sliver_fill.cs
-
11Runtime/rendering/sliver_fill.cs.meta
-
42Runtime/service/clipboard.cs
-
11Runtime/service/clipboard.cs.meta
-
153Runtime/service/text_input_utils.cs
-
11Runtime/service/text_input_utils.cs.meta
-
374Runtime/widgets/page_view.cs
-
11Runtime/widgets/page_view.cs.meta
-
439Runtime/widgets/text_selection.cs
-
11Runtime/widgets/text_selection.cs.meta
-
29Samples/UIWidgetSample/PageViewCanvas.cs
-
11Samples/UIWidgetSample/PageViewCanvas.cs.meta
|
|||
using Unity.UIWidgets.ui; |
|||
using System.Collections.Generic; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.ui; |
|||
|
|||
} |
|||
|
|||
public static List<string> debugDescribeTransform(Matrix3 transform) { |
|||
if (transform == null) |
|||
return new List<string> {"null"}; |
|||
|
|||
List<string> result = new List<string>(3); |
|||
for (int i = 0; i < 3; i++) { |
|||
result.Add($"[{i}] {transform[i * 3]}, {transform[i * 3 + 1]}, {transform[i * 3 + 2]}"); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
|
|||
public class TransformProperty : DiagnosticsProperty<Matrix3> { |
|||
|
|||
public TransformProperty(string name, Matrix3 value, |
|||
bool showName = true, |
|||
object defaultValue = null, |
|||
DiagnosticLevel level = DiagnosticLevel.info |
|||
): base(name, value, showName: showName, defaultValue: defaultValue??Diagnostics.kNoDefaultValue, level: level) { |
|||
} |
|||
|
|||
protected override string valueToString(TextTreeConfiguration parentConfiguration = null) { |
|||
if (parentConfiguration != null && !parentConfiguration.lineBreakProperties) { |
|||
return this.value == null ? "null" : this.value.ToString(); |
|||
} |
|||
|
|||
return string.Join("\n", MatrixUtils.debugDescribeTransform(this.value)); |
|||
} |
|||
} |
|||
} |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.gestures; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.service; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
|
|||
|
|||
// todo using material components: FlatButton & Material ...
|
|||
namespace Unity.UIWidgets.material { |
|||
public static class MaterialUtils { |
|||
public readonly static TextSelectionControls materialTextSelectionControls = |
|||
new _MaterialTextSelectionControls(); |
|||
} |
|||
|
|||
internal static class _TextSelectionUtils { |
|||
internal const double _kHandleSize = 22.0; |
|||
internal const double _kToolbarScreenPadding = 8.0; |
|||
} |
|||
|
|||
internal class _TextSelectionToolbar : StatelessWidget { |
|||
public _TextSelectionToolbar(Key key = null, Action handleCut = null, |
|||
Action handleCopy = null, Action handlePaste = null, Action handleSelectAll = null) : base(key: key) { |
|||
this.handleCut = handleCut; |
|||
this.handleCopy = handleCopy; |
|||
this.handlePaste = handlePaste; |
|||
this.handleSelectAll = handleSelectAll; |
|||
} |
|||
|
|||
public readonly Action handleCut; |
|||
public readonly Action handleCopy; |
|||
public readonly Action handlePaste; |
|||
public readonly Action handleSelectAll; |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
List<Widget> items = new List<Widget>(); |
|||
if (this.handleCut != null) { |
|||
items.Add(new _TempButton(onPressed: () => this.handleCut(), child: new Text("Cut"))); |
|||
} |
|||
|
|||
if (this.handleCopy != null) { |
|||
items.Add(new _TempButton(onPressed: () => this.handleCopy(), child: new Text("Copy"))); |
|||
} |
|||
|
|||
if (this.handlePaste != null) { |
|||
items.Add(new _TempButton(onPressed: () => this.handlePaste(), child: new Text("Past"))); |
|||
} |
|||
|
|||
if (this.handleSelectAll != null) { |
|||
items.Add(new _TempButton(onPressed: () => this.handleSelectAll(), child: new Text("Select All"))); |
|||
} |
|||
|
|||
return new Container( |
|||
color: new Color(0xFFEFEFEF), |
|||
height: 44.0, child: new Row(mainAxisSize: MainAxisSize.min, children: items)); |
|||
} |
|||
} |
|||
|
|||
internal class _TextSelectionToolbarLayout : SingleChildLayoutDelegate { |
|||
internal _TextSelectionToolbarLayout(Size screenSize = null, Rect globalEditableRegion = null, |
|||
Offset position = null) { |
|||
this.screenSize = screenSize; |
|||
this.globalEditableRegion = globalEditableRegion; |
|||
this.position = position; |
|||
} |
|||
|
|||
public readonly Size screenSize; |
|||
public readonly Rect globalEditableRegion; |
|||
public readonly Offset position; |
|||
|
|||
public override BoxConstraints getConstraintsForChild(BoxConstraints constraints) { |
|||
return constraints.loosen(); |
|||
} |
|||
|
|||
public override Offset getPositionForChild(Size size, Size childSize) { |
|||
Offset globalPosition = this.globalEditableRegion.topLeft + this.position; |
|||
|
|||
double x = globalPosition.dx - childSize.width / 2.0; |
|||
double y = globalPosition.dy - childSize.height; |
|||
|
|||
if (x < _TextSelectionUtils._kToolbarScreenPadding) |
|||
x = _TextSelectionUtils._kToolbarScreenPadding; |
|||
else if (x + childSize.width > this.screenSize.width - _TextSelectionUtils._kToolbarScreenPadding) |
|||
x = this.screenSize.width - childSize.width - _TextSelectionUtils._kToolbarScreenPadding; |
|||
|
|||
if (y < _TextSelectionUtils._kToolbarScreenPadding) |
|||
y = _TextSelectionUtils._kToolbarScreenPadding; |
|||
else if (y + childSize.height > this.screenSize.height - _TextSelectionUtils._kToolbarScreenPadding) |
|||
y = this.screenSize.height - childSize.height - _TextSelectionUtils._kToolbarScreenPadding; |
|||
|
|||
return new Offset(x, y); |
|||
} |
|||
|
|||
public override bool shouldRelayout(SingleChildLayoutDelegate oldDelegate) { |
|||
return this.position != ((_TextSelectionToolbarLayout) oldDelegate).position; |
|||
} |
|||
} |
|||
|
|||
internal class _TextSelectionHandlePainter : CustomPainter { |
|||
internal _TextSelectionHandlePainter(Color color) { |
|||
this.color = color; |
|||
} |
|||
|
|||
public readonly Color color; |
|||
|
|||
public override void paint(Canvas canvas, Size size) { |
|||
Paint paint = new Paint(); |
|||
paint.color = this.color; |
|||
double radius = size.width / 2.0; |
|||
canvas.drawCircle(new Offset(radius, radius), radius, paint); |
|||
canvas.drawRect(Rect.fromLTWH(0.0, 0.0, radius, radius), paint); |
|||
} |
|||
|
|||
|
|||
public override bool shouldRepaint(CustomPainter oldPainter) { |
|||
return this.color != ((_TextSelectionHandlePainter) oldPainter).color; |
|||
} |
|||
} |
|||
|
|||
internal class _MaterialTextSelectionControls : TextSelectionControls { |
|||
public override Size handleSize { |
|||
get => new Size(_TextSelectionUtils._kHandleSize, |
|||
_TextSelectionUtils._kHandleSize); |
|||
} |
|||
|
|||
public override Widget buildToolbar(BuildContext context, Rect globalEditableRegion, Offset position, |
|||
TextSelectionDelegate selectionDelegate) { |
|||
return new ConstrainedBox( |
|||
constraints: BoxConstraints.tight(globalEditableRegion.size), |
|||
child: new CustomSingleChildLayout( |
|||
layoutDelegate: new _TextSelectionToolbarLayout( |
|||
MediaQuery.of(context).size, |
|||
globalEditableRegion, |
|||
position |
|||
), |
|||
child: new _TextSelectionToolbar( |
|||
handleCut: this.canCut(selectionDelegate) |
|||
? () => this.handleCut(selectionDelegate) |
|||
: (Action) null, |
|||
handleCopy: this.canCopy(selectionDelegate) |
|||
? () => this.handleCopy(selectionDelegate) |
|||
: (Action) null, |
|||
handlePaste: this.canPaste(selectionDelegate) |
|||
? () => this.handlePaste(selectionDelegate) |
|||
: (Action) null, |
|||
handleSelectAll: this.canSelectAll(selectionDelegate) |
|||
? () => this.handleSelectAll(selectionDelegate) |
|||
: (Action) null |
|||
) |
|||
) |
|||
); |
|||
} |
|||
|
|||
public override Widget buildHandle(BuildContext context, TextSelectionHandleType type, double textLineHeight) { |
|||
Widget handle = new Padding( |
|||
padding: EdgeInsets.only(right: 26.0, bottom: 26.0), |
|||
child: new SizedBox( |
|||
width: 20, |
|||
height: 20, |
|||
child: new CustomPaint( |
|||
painter: new _TextSelectionHandlePainter( |
|||
color: new Color(0xFFFF0000) |
|||
) |
|||
) |
|||
) |
|||
); |
|||
|
|||
switch (type) { |
|||
case TextSelectionHandleType.left: // points up-right
|
|||
return new Transform( |
|||
transform: Matrix3.makeRotate(90), |
|||
child: handle |
|||
); |
|||
case TextSelectionHandleType.right: // points up-left
|
|||
return handle; |
|||
case TextSelectionHandleType.collapsed: // points up
|
|||
return new Transform( |
|||
transform: Matrix3.makeRotate(45), |
|||
child: handle |
|||
); |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
} |
|||
|
|||
|
|||
public class _TempButton : StatelessWidget { |
|||
public _TempButton( |
|||
Key key = null, |
|||
GestureTapCallback onPressed = null, |
|||
EdgeInsets padding = null, |
|||
Color backgroundColor = null, |
|||
Widget child = null |
|||
) : base(key: key) { |
|||
this.onPressed = onPressed; |
|||
this.padding = padding ?? EdgeInsets.all(8.0); |
|||
this.backgroundColor = backgroundColor ?? new Color(0); |
|||
this.child = child; |
|||
} |
|||
|
|||
public readonly GestureTapCallback onPressed; |
|||
public readonly EdgeInsets padding; |
|||
public readonly Widget child; |
|||
public readonly Color backgroundColor; |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
return new GestureDetector( |
|||
onTap: this.onPressed, |
|||
child: new Container( |
|||
padding: this.padding, |
|||
color: this.backgroundColor, |
|||
child: this.child |
|||
) |
|||
); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 33685d6edaab14322aa1b854fdd7c3e6 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.ui; |
|||
using UnityEngine; |
|||
|
|||
namespace Unity.UIWidgets.rendering { |
|||
public class RenderSliverFillViewport : RenderSliverFixedExtentBoxAdaptor { |
|||
public RenderSliverFillViewport( |
|||
RenderSliverBoxChildManager childManager = null, |
|||
double viewportFraction = 1.0 |
|||
) : |
|||
base(childManager: childManager) { |
|||
D.assert(viewportFraction > 0.0); |
|||
this._viewportFraction = viewportFraction; |
|||
} |
|||
|
|||
public override double itemExtent { |
|||
get => this.constraints.viewportMainAxisExtent * this.viewportFraction; |
|||
set {} |
|||
} |
|||
|
|||
double _viewportFraction; |
|||
|
|||
public double viewportFraction { |
|||
get => this._viewportFraction; |
|||
set { |
|||
if (this._viewportFraction == value) |
|||
return; |
|||
this._viewportFraction = value; |
|||
this.markNeedsLayout(); |
|||
} |
|||
} |
|||
|
|||
|
|||
double _padding => (1.0 - this.viewportFraction) * this.constraints.viewportMainAxisExtent * 0.5; |
|||
|
|||
protected override double indexToLayoutOffset(double itemExtent, int index) { |
|||
return this._padding + base.indexToLayoutOffset(itemExtent, index); |
|||
} |
|||
|
|||
protected override int getMinChildIndexForScrollOffset(double scrollOffset, double itemExtent) { |
|||
return base.getMinChildIndexForScrollOffset(Math.Max(scrollOffset - this._padding, 0.0), itemExtent); |
|||
} |
|||
|
|||
protected override int getMaxChildIndexForScrollOffset(double scrollOffset, double itemExtent) { |
|||
return base.getMaxChildIndexForScrollOffset(Math.Max(scrollOffset - this._padding, 0.0), itemExtent); |
|||
} |
|||
|
|||
protected override double estimateMaxScrollOffset(SliverConstraints constraints, |
|||
int firstIndex = 0, |
|||
int lastIndex = 0, |
|||
double leadingScrollOffset = 0.0, |
|||
double trailingScrollOffset = 0.0 |
|||
) { |
|||
double padding = this._padding; |
|||
return this.childManager.estimateMaxScrollOffset( |
|||
constraints, |
|||
firstIndex: firstIndex, |
|||
lastIndex: lastIndex, |
|||
leadingScrollOffset: leadingScrollOffset - padding, |
|||
trailingScrollOffset: trailingScrollOffset - padding |
|||
) + padding + padding; |
|||
} |
|||
} |
|||
|
|||
public class RenderSliverFillRemaining : RenderSliverSingleBoxAdapter { |
|||
public RenderSliverFillRemaining( |
|||
RenderBox child |
|||
) : base(child: child) { |
|||
} |
|||
|
|||
protected override void performLayout() { |
|||
double extent = this.constraints.remainingPaintExtent - Math.Min(this.constraints.overlap, 0.0); |
|||
if (this.child != null) |
|||
this.child.layout(this.constraints.asBoxConstraints(minExtent: extent, maxExtent: extent), |
|||
parentUsesSize: true); |
|||
double paintedChildSize = this.calculatePaintOffset(this.constraints, from: 0.0, to: extent); |
|||
Debug.Log("size" + paintedChildSize); |
|||
D.assert(paintedChildSize.isFinite()); |
|||
D.assert(paintedChildSize >= 0.0); |
|||
this.geometry = new SliverGeometry( |
|||
scrollExtent: this.constraints.viewportMainAxisExtent, |
|||
paintExtent: paintedChildSize, |
|||
maxPaintExtent: paintedChildSize, |
|||
hasVisualOverflow: extent > this.constraints.remainingPaintExtent || |
|||
this.constraints.scrollOffset > 0.0 |
|||
); |
|||
if (this.child != null) this.setChildParentData(this.child, this.constraints, this.geometry); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 2172361941dad4eb2acd42f7d238704c |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using RSG; |
|||
using UnityEngine; |
|||
|
|||
namespace Unity.UIWidgets.service { |
|||
public class ClipboardData { |
|||
public ClipboardData(string text = null) { |
|||
this.text = text; |
|||
} |
|||
|
|||
public readonly string text; |
|||
} |
|||
|
|||
public abstract class Clipboard { |
|||
static readonly Clipboard _instance = new UnityGUIClipboard(); |
|||
|
|||
public static readonly string kTextPlain = "text/plain"; |
|||
|
|||
public static IPromise setData(ClipboardData data) { |
|||
return _instance.setClipboardData(data); |
|||
} |
|||
|
|||
public static IPromise<ClipboardData> getData(string format) { |
|||
return _instance.getClipboardData(format); |
|||
} |
|||
|
|||
protected abstract IPromise setClipboardData(ClipboardData data); |
|||
protected abstract IPromise<ClipboardData> getClipboardData(string format); |
|||
} |
|||
|
|||
public class UnityGUIClipboard : Clipboard { |
|||
|
|||
protected override IPromise setClipboardData(ClipboardData data) { |
|||
GUIUtility.systemCopyBuffer = data.text; |
|||
return Promise.Resolved(); |
|||
} |
|||
|
|||
protected override IPromise<ClipboardData> getClipboardData(string format) { |
|||
var data = new ClipboardData(text: GUIUtility.systemCopyBuffer); |
|||
return Promise<ClipboardData>.Resolved(data); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: e5edc4624d81f4f78846c2d0b1f1beec |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
|
|||
namespace Unity.UIWidgets.service { |
|||
|
|||
public class TextInputUtils { |
|||
|
|||
static Dictionary<Event, TextInputAction> _keyToOperations; |
|||
|
|||
public static TextInputAction? getInputAction(Event evt) { |
|||
if (_keyToOperations == null) { |
|||
initKeyToOperations(); |
|||
} |
|||
EventModifiers m = evt.modifiers; |
|||
evt.modifiers &= ~EventModifiers.CapsLock; |
|||
TextInputAction result; |
|||
var exists = _keyToOperations.TryGetValue(evt, out result); |
|||
evt.modifiers = m; |
|||
if (exists) { |
|||
return result; |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
public static void initKeyToOperations() { |
|||
if (_keyToOperations != null) { |
|||
return; |
|||
} |
|||
_keyToOperations = new Dictionary<Event, TextInputAction>(); |
|||
|
|||
// key mappings shared by the platforms
|
|||
mapKey("return", TextInputAction.newline); |
|||
mapKey("left", TextInputAction.moveLeft); |
|||
mapKey("right", TextInputAction.moveRight); |
|||
mapKey("up", TextInputAction.moveUp); |
|||
mapKey("down", TextInputAction.moveDown); |
|||
|
|||
mapKey("#left", TextInputAction.selectLeft); |
|||
mapKey("#right", TextInputAction.selectRight); |
|||
mapKey("#up", TextInputAction.selectUp); |
|||
mapKey("#down", TextInputAction.selectDown); |
|||
|
|||
mapKey("delete", TextInputAction.delete); |
|||
mapKey("backspace", TextInputAction.backspace); |
|||
mapKey("#backspace", TextInputAction.backspace); |
|||
|
|||
// OSX is the special case for input shortcuts
|
|||
if (SystemInfo.operatingSystemFamily == OperatingSystemFamily.MacOSX) { |
|||
// Keyboard mappings for mac
|
|||
// TODO mapKey ("home", TextInputAction.scrollStart);
|
|||
// TODO mapKey ("end", TextInputAction.scrollEnd);
|
|||
// TODO mapKey ("page up", TextInputAction.scrollPageUp);
|
|||
// TODO mapKey ("page down", TextInputAction.scrollPageDown);
|
|||
|
|||
mapKey("^left", TextInputAction.moveGraphicalLineStart); |
|||
mapKey("^right", TextInputAction.moveGraphicalLineEnd); |
|||
// TODO mapKey ("^up", TextInputAction.scrollPageUp);
|
|||
// TODO mapKey ("^down", TextInputAction.scrollPageDown);
|
|||
|
|||
mapKey("&left", TextInputAction.moveWordLeft); |
|||
mapKey("&right", TextInputAction.moveWordRight); |
|||
mapKey("&up", TextInputAction.moveParagraphBackward); |
|||
mapKey("&down", TextInputAction.moveParagraphForward); |
|||
|
|||
mapKey("%left", TextInputAction.moveGraphicalLineStart); |
|||
mapKey("%right", TextInputAction.moveGraphicalLineEnd); |
|||
mapKey("%up", TextInputAction.moveTextStart); |
|||
mapKey("%down", TextInputAction.moveTextEnd); |
|||
|
|||
mapKey("#home", TextInputAction.selectTextStart); |
|||
mapKey("#end", TextInputAction.selectTextEnd); |
|||
// TODO mapKey ("#page up", TextInputAction.selectPageUp);
|
|||
// TODO mapKey ("#page down", TextInputAction.selectPageDown);
|
|||
|
|||
mapKey("#^left", TextInputAction.expandSelectGraphicalLineStart); |
|||
mapKey("#^right", TextInputAction.expandSelectGraphicalLineEnd); |
|||
mapKey("#^up", TextInputAction.selectParagraphBackward); |
|||
mapKey("#^down", TextInputAction.selectParagraphForward); |
|||
|
|||
mapKey("#&left", TextInputAction.selectWordLeft); |
|||
mapKey("#&right", TextInputAction.selectWordRight); |
|||
mapKey("#&up", TextInputAction.selectParagraphBackward); |
|||
mapKey("#&down", TextInputAction.selectParagraphForward); |
|||
|
|||
mapKey("#%left", TextInputAction.expandSelectGraphicalLineStart); |
|||
mapKey("#%right", TextInputAction.expandSelectGraphicalLineEnd); |
|||
mapKey("#%up", TextInputAction.selectTextStart); |
|||
mapKey("#%down", TextInputAction.selectTextEnd); |
|||
|
|||
mapKey("%a", TextInputAction.selectAll); |
|||
mapKey("%x", TextInputAction.cut); |
|||
mapKey("%c", TextInputAction.copy); |
|||
mapKey("%v", TextInputAction.paste); |
|||
|
|||
// emacs-like keybindings
|
|||
mapKey("^d", TextInputAction.delete); |
|||
mapKey("^h", TextInputAction.backspace); |
|||
mapKey("^b", TextInputAction.moveLeft); |
|||
mapKey("^f", TextInputAction.moveRight); |
|||
mapKey("^a", TextInputAction.moveLineStart); |
|||
mapKey("^e", TextInputAction.moveLineEnd); |
|||
|
|||
mapKey("&delete", TextInputAction.deleteWordForward); |
|||
mapKey("&backspace", TextInputAction.deleteWordBack); |
|||
mapKey("%backspace", TextInputAction.deleteLineBack); |
|||
} else { |
|||
// Windows/Linux keymappings
|
|||
mapKey("home", TextInputAction.moveGraphicalLineStart); |
|||
mapKey("end", TextInputAction.moveGraphicalLineEnd); |
|||
// TODO mapKey ("page up", TextInputAction.movePageUp);
|
|||
// TODO mapKey ("page down", TextInputAction.movePageDown);
|
|||
|
|||
mapKey("%left", TextInputAction.moveWordLeft); |
|||
mapKey("%right", TextInputAction.moveWordRight); |
|||
mapKey("%up", TextInputAction.moveParagraphBackward); |
|||
mapKey("%down", TextInputAction.moveParagraphForward); |
|||
|
|||
mapKey("^left", TextInputAction.moveToEndOfPreviousWord); |
|||
mapKey("^right", TextInputAction.moveToStartOfNextWord); |
|||
mapKey("^up", TextInputAction.moveParagraphBackward); |
|||
mapKey("^down", TextInputAction.moveParagraphForward); |
|||
|
|||
mapKey("#^left", TextInputAction.selectToEndOfPreviousWord); |
|||
mapKey("#^right", TextInputAction.selectToStartOfNextWord); |
|||
mapKey("#^up", TextInputAction.selectParagraphBackward); |
|||
mapKey("#^down", TextInputAction.selectParagraphForward); |
|||
|
|||
mapKey("#home", TextInputAction.selectGraphicalLineStart); |
|||
mapKey("#end", TextInputAction.selectGraphicalLineEnd); |
|||
// TODO mapKey ("#page up", TextInputAction.selectPageUp);
|
|||
// TODO mapKey ("#page down", TextInputAction.selectPageDown);
|
|||
|
|||
mapKey("^delete", TextInputAction.deleteWordForward); |
|||
mapKey("^backspace", TextInputAction.deleteWordBack); |
|||
mapKey("%backspace", TextInputAction.deleteLineBack); |
|||
|
|||
mapKey("^a", TextInputAction.selectAll); |
|||
mapKey("^x", TextInputAction.cut); |
|||
mapKey("^c", TextInputAction.copy); |
|||
mapKey("^v", TextInputAction.paste); |
|||
mapKey("#delete", TextInputAction.cut); |
|||
mapKey("^insert", TextInputAction.copy); |
|||
mapKey("#insert", TextInputAction.paste); |
|||
} |
|||
|
|||
} |
|||
|
|||
static void mapKey(string key, TextInputAction action) { |
|||
_keyToOperations[Event.KeyboardEvent(key)] = action; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 2288dc63570aa4d8db1bd6673b12634f |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using RSG; |
|||
using Unity.UIWidgets.animation; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.painting; |
|||
using Unity.UIWidgets.physics; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.ui; |
|||
|
|||
namespace Unity.UIWidgets.widgets { |
|||
public class PageController : ScrollController { |
|||
|
|||
public PageController( |
|||
int initialPage = 0, |
|||
bool keepPage = true, |
|||
double viewportFraction = 1.0 |
|||
) { |
|||
this.initialPage = initialPage; |
|||
this.keepPage = keepPage; |
|||
this.viewportFraction = viewportFraction; |
|||
D.assert(viewportFraction > 0.0); |
|||
} |
|||
|
|||
public readonly int initialPage; |
|||
|
|||
public readonly bool keepPage; |
|||
|
|||
public readonly double viewportFraction; |
|||
|
|||
|
|||
public double page { |
|||
get { |
|||
D.assert(this.positions.isNotEmpty(), |
|||
"PageController.page cannot be accessed before a PageView is built with it." |
|||
); |
|||
D.assert(this.positions.Count == 1, |
|||
"The page property cannot be read when multiple PageViews are attached to " + |
|||
"the same PageController." |
|||
); |
|||
_PagePosition position = (_PagePosition) this.position; |
|||
return position.page; |
|||
} |
|||
} |
|||
|
|||
public IPromise animateToPage(int page, TimeSpan duration, Curve curve) { |
|||
_PagePosition position = (_PagePosition) this.position; |
|||
return position.animateTo( |
|||
position.getPixelsFromPage(page), |
|||
duration, |
|||
curve |
|||
); |
|||
} |
|||
|
|||
public void jumpToPage(int page) { |
|||
_PagePosition position = (_PagePosition) this.position; |
|||
position.jumpTo(position.getPixelsFromPage(page)); |
|||
} |
|||
|
|||
public IPromise nextPage(TimeSpan duration, Curve curve) { |
|||
return this.animateToPage(this.page.round() + 1, duration: duration, curve: curve); |
|||
} |
|||
|
|||
public IPromise previousPage(TimeSpan duration, Curve curve) { |
|||
return this.animateToPage(this.page.round() - 1, duration: duration, curve: curve); |
|||
} |
|||
|
|||
public override ScrollPosition createScrollPosition(ScrollPhysics physics, ScrollContext context, |
|||
ScrollPosition oldPosition) { |
|||
return new _PagePosition( |
|||
physics: physics, |
|||
context: context, |
|||
initialPage: this.initialPage, |
|||
keepPage: this.keepPage, |
|||
viewportFraction: this.viewportFraction, |
|||
oldPosition: oldPosition |
|||
); |
|||
} |
|||
|
|||
public override void attach(ScrollPosition position) { |
|||
base.attach(position); |
|||
_PagePosition pagePosition = (_PagePosition) position; |
|||
pagePosition.viewportFraction = this.viewportFraction; |
|||
} |
|||
} |
|||
|
|||
public interface IPageMetrics : ScrollMetrics { |
|||
double page { get; } |
|||
double viewportFraction { get; } |
|||
} |
|||
|
|||
public class PageMetrics : FixedScrollMetrics, IPageMetrics { |
|||
public PageMetrics( |
|||
double minScrollExtent = 0.0, |
|||
double maxScrollExtent = 0.0, |
|||
double pixels = 0.0, |
|||
double viewportDimension = 0.0, |
|||
AxisDirection axisDirection = AxisDirection.down, |
|||
double viewportFraction = 0.0 |
|||
) : base( |
|||
minScrollExtent: minScrollExtent, |
|||
maxScrollExtent: maxScrollExtent, |
|||
pixels: pixels, |
|||
viewportDimension: viewportDimension, |
|||
axisDirection: axisDirection |
|||
) { |
|||
this._viewportFraction = viewportFraction; |
|||
} |
|||
|
|||
public readonly double _viewportFraction; |
|||
|
|||
public double page => Math.Max(0.0, this.pixels.clamp(this.minScrollExtent, this.maxScrollExtent)) / |
|||
Math.Max(1.0, this.viewportDimension * this.viewportFraction); |
|||
|
|||
public double viewportFraction => this._viewportFraction; |
|||
} |
|||
|
|||
internal class _PagePosition : ScrollPositionWithSingleContext, IPageMetrics { |
|||
internal _PagePosition( |
|||
ScrollPhysics physics = null, |
|||
ScrollContext context = null, |
|||
int initialPage = 0, |
|||
bool keepPage = true, |
|||
double viewportFraction = 1.0, |
|||
ScrollPosition oldPosition = null |
|||
) : |
|||
base( |
|||
physics: physics, |
|||
context: context, |
|||
initialPixels: null, |
|||
keepScrollOffset: keepPage, |
|||
oldPosition: oldPosition |
|||
) { |
|||
D.assert(viewportFraction > 0.0); |
|||
this.initialPage = initialPage; |
|||
this._viewportFraction = viewportFraction; |
|||
this._pageToUseOnStartup = initialPage; |
|||
} |
|||
|
|||
public readonly int initialPage; |
|||
double _pageToUseOnStartup; |
|||
|
|||
public double viewportFraction { |
|||
get => this._viewportFraction; |
|||
set { |
|||
if (this._viewportFraction == value) |
|||
return; |
|||
double oldPage = this.page; |
|||
this._viewportFraction = value; |
|||
this.forcePixels(this.getPixelsFromPage(oldPage)); |
|||
} |
|||
} |
|||
|
|||
double _viewportFraction; |
|||
|
|||
public double getPageFromPixels(double pixels, double viewportDimension) { |
|||
return Math.Max(0.0, pixels) / Math.Max(1.0, viewportDimension * this.viewportFraction); |
|||
} |
|||
|
|||
public double getPixelsFromPage(double page) { |
|||
return page * this.viewportDimension * this.viewportFraction; |
|||
} |
|||
|
|||
public double page { |
|||
get { |
|||
return this.getPageFromPixels(this.pixels.clamp(this.minScrollExtent, this.maxScrollExtent), |
|||
this.viewportDimension); |
|||
} |
|||
} |
|||
|
|||
protected override void saveScrollOffset() { |
|||
PageStorage.of(this.context.storageContext)?.writeState(this.context.storageContext, |
|||
this.getPageFromPixels(this.pixels, this.viewportDimension)); |
|||
} |
|||
|
|||
protected override void restoreScrollOffset() { |
|||
object value = PageStorage.of(this.context.storageContext)?.readState(this.context.storageContext); |
|||
if (value != null) this._pageToUseOnStartup = (double) value; |
|||
} |
|||
|
|||
public override bool applyViewportDimension(double viewportDimension) { |
|||
double oldViewportDimensions = 0.0; |
|||
if (this.haveDimensions) { |
|||
oldViewportDimensions = this.viewportDimension; |
|||
} |
|||
bool result = base.applyViewportDimension(viewportDimension); |
|||
double? oldPixels = null; |
|||
if (this.hasPixles) { |
|||
oldPixels = this.pixels; |
|||
} |
|||
double page = (oldPixels == null || oldViewportDimensions == 0.0) |
|||
? this._pageToUseOnStartup |
|||
: this.getPageFromPixels(oldPixels.Value, oldViewportDimensions); |
|||
double newPixels = this.getPixelsFromPage(page); |
|||
if (newPixels != oldPixels) { |
|||
this.correctPixels(newPixels); |
|||
return false; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
|
|||
public class PageScrollPhysics : ScrollPhysics { |
|||
public PageScrollPhysics(ScrollPhysics parent = null) : base(parent: parent) { |
|||
} |
|||
|
|||
public override ScrollPhysics applyTo(ScrollPhysics ancestor) { |
|||
return new PageScrollPhysics(parent: this.buildParent(ancestor)); |
|||
} |
|||
|
|||
double _getPage(ScrollPosition position) { |
|||
if (position is _PagePosition) |
|||
return ((_PagePosition) position).page; |
|||
return position.pixels / position.viewportDimension; |
|||
} |
|||
|
|||
double _getPixels(ScrollPosition position, double page) { |
|||
if (position is _PagePosition) |
|||
return ((_PagePosition) position).getPixelsFromPage(page); |
|||
return page * position.viewportDimension; |
|||
} |
|||
|
|||
double _getTargetPixels(ScrollPosition position, Tolerance tolerance, double velocity) { |
|||
double page = this._getPage(position); |
|||
if (velocity < -tolerance.velocity) |
|||
page -= 0.5; |
|||
else if (velocity > tolerance.velocity) |
|||
page += 0.5; |
|||
return this._getPixels(position, page.round()); |
|||
} |
|||
|
|||
public override Simulation createBallisticSimulation(ScrollMetrics position, double velocity) { |
|||
if ((velocity <= 0.0 && position.pixels <= position.minScrollExtent) || |
|||
(velocity >= 0.0 && position.pixels >= position.maxScrollExtent)) |
|||
return base.createBallisticSimulation(position, velocity); |
|||
Tolerance tolerance = this.tolerance; |
|||
double target = this._getTargetPixels((ScrollPosition) position, tolerance, velocity); |
|||
if (target != position.pixels) |
|||
return new ScrollSpringSimulation(this.spring, position.pixels, target, velocity, tolerance: tolerance); |
|||
return null; |
|||
} |
|||
|
|||
public override bool allowImplicitScrolling => false; |
|||
} |
|||
|
|||
public static class PageViewUtils { |
|||
internal static PageController _defaultPageController = new PageController(); |
|||
internal static PageScrollPhysics _kPagePhysics = new PageScrollPhysics(); |
|||
} |
|||
|
|||
|
|||
public class PageView : StatefulWidget { |
|||
public PageView( |
|||
Key key = null, |
|||
Axis scrollDirection = Axis.horizontal, |
|||
bool reverse = false, |
|||
PageController controller = null, |
|||
ScrollPhysics physics = null, |
|||
bool pageSnapping = true, |
|||
ValueChanged<int> onPageChanged = null, |
|||
List<Widget> children = null, |
|||
IndexedWidgetBuilder itemBuilder = null, |
|||
SliverChildDelegate childDelegate = null, |
|||
int itemCount = 0 |
|||
) : base(key: key) { |
|||
this.scrollDirection = scrollDirection; |
|||
this.reverse = reverse; |
|||
this.physics = physics; |
|||
this.pageSnapping = pageSnapping; |
|||
this.onPageChanged = onPageChanged; |
|||
this.controller = controller ?? PageViewUtils._defaultPageController; |
|||
if (itemBuilder != null) |
|||
this.childrenDelegate = new SliverChildBuilderDelegate(itemBuilder, childCount: itemCount); |
|||
else if (childDelegate != null) |
|||
this.childrenDelegate = childDelegate; |
|||
else |
|||
this.childrenDelegate = new SliverChildListDelegate(children ?? new List<Widget>()); |
|||
} |
|||
|
|||
public readonly Axis scrollDirection; |
|||
|
|||
public readonly bool reverse; |
|||
|
|||
public readonly PageController controller; |
|||
|
|||
public readonly ScrollPhysics physics; |
|||
|
|||
public readonly bool pageSnapping; |
|||
|
|||
public readonly ValueChanged<int> onPageChanged; |
|||
|
|||
public readonly SliverChildDelegate childrenDelegate; |
|||
|
|||
public override State createState() { |
|||
return new _PageViewState(); |
|||
} |
|||
} |
|||
|
|||
internal class _PageViewState : State<PageView> { |
|||
int _lastReportedPage = 0; |
|||
|
|||
public override void initState() { |
|||
base.initState(); |
|||
this._lastReportedPage = this.widget.controller.initialPage; |
|||
} |
|||
|
|||
AxisDirection _getDirection(BuildContext context) { |
|||
switch (this.widget.scrollDirection) { |
|||
case Axis.horizontal: |
|||
D.assert(WidgetsD.debugCheckHasDirectionality(context)); |
|||
TextDirection textDirection = Directionality.of(context); |
|||
AxisDirection axisDirection = AxisUtils.textDirectionToAxisDirection(textDirection); |
|||
return this.widget.reverse ? AxisUtils.flipAxisDirection(axisDirection) : axisDirection; |
|||
case Axis.vertical: |
|||
return this.widget.reverse ? AxisDirection.up : AxisDirection.down; |
|||
} |
|||
|
|||
throw new UIWidgetsError("fail to get axis direction"); |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
AxisDirection axisDirection = this._getDirection(context); |
|||
ScrollPhysics physics = this.widget.pageSnapping |
|||
? PageViewUtils._kPagePhysics.applyTo(this.widget.physics) |
|||
: this.widget.physics; |
|||
|
|||
return new NotificationListener<ScrollNotification>( |
|||
onNotification: (ScrollNotification notification) => { |
|||
if (notification.depth == 0 && this.widget.onPageChanged != null && |
|||
notification is ScrollUpdateNotification) { |
|||
IPageMetrics metrics = (IPageMetrics) notification.metrics; |
|||
int currentPage = metrics.page.round(); |
|||
if (currentPage != this._lastReportedPage) { |
|||
this._lastReportedPage = currentPage; |
|||
this.widget.onPageChanged(currentPage); |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
}, |
|||
child: new Scrollable( |
|||
axisDirection: axisDirection, |
|||
controller: this.widget.controller, |
|||
physics: physics, |
|||
viewportBuilder: (BuildContext _context, ViewportOffset position) => { |
|||
return new Viewport( |
|||
cacheExtent: 0.0, |
|||
axisDirection: axisDirection, |
|||
offset: position, |
|||
slivers: new List<Widget> { |
|||
new SliverFillViewport( |
|||
viewportFraction: this.widget.controller.viewportFraction, |
|||
del: this.widget.childrenDelegate |
|||
) |
|||
} |
|||
); |
|||
} |
|||
) |
|||
); |
|||
} |
|||
|
|||
public override void debugFillProperties(DiagnosticPropertiesBuilder description) { |
|||
base.debugFillProperties(description); |
|||
description.add(new EnumProperty<Axis>("scrollDirection", this.widget.scrollDirection)); |
|||
description.add(new FlagProperty("reverse", value: this.widget.reverse, ifTrue: "reversed")); |
|||
description.add( |
|||
new DiagnosticsProperty<PageController>("controller", this.widget.controller, showName: false)); |
|||
description.add(new DiagnosticsProperty<ScrollPhysics>("physics", this.widget.physics, showName: false)); |
|||
description.add(new FlagProperty("pageSnapping", value: this.widget.pageSnapping, |
|||
ifFalse: "snapping disabled")); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 00d46a618968045298e84302af711284 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Unity.UIWidgets.animation; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.gestures; |
|||
using Unity.UIWidgets.rendering; |
|||
using Unity.UIWidgets.scheduler; |
|||
using Unity.UIWidgets.service; |
|||
using Unity.UIWidgets.ui; |
|||
|
|||
namespace Unity.UIWidgets.widgets { |
|||
public enum TextSelectionHandleType { |
|||
left, |
|||
right, |
|||
collapsed, |
|||
} |
|||
|
|||
internal enum _TextSelectionHandlePosition { |
|||
start, |
|||
end |
|||
} |
|||
|
|||
public delegate void TextSelectionOverlayChanged(TextEditingValue value, Rect caretRect); |
|||
|
|||
public abstract class TextSelectionControls { |
|||
public abstract Widget buildHandle(BuildContext context, TextSelectionHandleType type, double textLineHeight); |
|||
|
|||
public abstract Widget buildToolbar(BuildContext context, Rect globalEditableRegion, Offset position, |
|||
TextSelectionDelegate selectionDelegate); |
|||
|
|||
public abstract Size handleSize { get; } |
|||
|
|||
public virtual bool canCut(TextSelectionDelegate selectionDelegate) { |
|||
return !selectionDelegate.textEditingValue.selection.isCollapsed; |
|||
} |
|||
|
|||
public virtual bool canCopy(TextSelectionDelegate selectionDelegate) { |
|||
return !selectionDelegate.textEditingValue.selection.isCollapsed; |
|||
} |
|||
|
|||
public virtual bool canPaste(TextSelectionDelegate selectionDelegate) { |
|||
// TODO in flutter: return false when clipboard is empty
|
|||
return true; |
|||
} |
|||
|
|||
public virtual bool canSelectAll(TextSelectionDelegate selectionDelegate) { |
|||
return selectionDelegate.textEditingValue.text.isEmpty() && |
|||
selectionDelegate.textEditingValue.selection.isCollapsed; |
|||
} |
|||
|
|||
public void handleCut(TextSelectionDelegate selectionDelegate) { |
|||
TextEditingValue value = selectionDelegate.textEditingValue; |
|||
Clipboard.setData(new ClipboardData( |
|||
text: value.selection.textInside(value.text) |
|||
)); |
|||
selectionDelegate.textEditingValue = new TextEditingValue( |
|||
text: value.selection.textBefore(value.text) |
|||
+ value.selection.textAfter(value.text), |
|||
selection: TextSelection.collapsed( |
|||
offset: value.selection.start |
|||
) |
|||
); |
|||
selectionDelegate.bringIntoView(selectionDelegate.textEditingValue.selection.extendPos); |
|||
selectionDelegate.hideToolbar(); |
|||
} |
|||
|
|||
public void handleCopy(TextSelectionDelegate selectionDelegate) { |
|||
TextEditingValue value = selectionDelegate.textEditingValue; |
|||
Clipboard.setData(new ClipboardData( |
|||
text: value.selection.textInside(value.text) |
|||
)); |
|||
selectionDelegate.textEditingValue = new TextEditingValue( |
|||
text: value.text, |
|||
selection: TextSelection.collapsed(offset: value.selection.end) |
|||
); |
|||
selectionDelegate.bringIntoView(selectionDelegate.textEditingValue.selection.extendPos); |
|||
selectionDelegate.hideToolbar(); |
|||
} |
|||
|
|||
public void handlePaste(TextSelectionDelegate selectionDelegate) { |
|||
TextEditingValue value = selectionDelegate.textEditingValue; // Snapshot the input before using `await`.
|
|||
Clipboard.getData(Clipboard.kTextPlain).Then((data) => { |
|||
if (data != null) { |
|||
selectionDelegate.textEditingValue = new TextEditingValue( |
|||
text: value.selection.textBefore(value.text) |
|||
+ data.text |
|||
+ value.selection.textAfter(value.text), |
|||
selection: TextSelection.collapsed( |
|||
offset: value.selection.start + data.text.Length |
|||
) |
|||
); |
|||
|
|||
} |
|||
selectionDelegate.bringIntoView(selectionDelegate.textEditingValue.selection.extendPos); |
|||
selectionDelegate.hideToolbar(); |
|||
}); |
|||
} |
|||
|
|||
public void handleSelectAll(TextSelectionDelegate selectionDelegate) { |
|||
selectionDelegate.textEditingValue = new TextEditingValue( |
|||
text: selectionDelegate.textEditingValue.text, |
|||
selection: new TextSelection( |
|||
baseOffset: 0, |
|||
extentOffset: selectionDelegate.textEditingValue.text.Length |
|||
) |
|||
); |
|||
selectionDelegate.bringIntoView(selectionDelegate.textEditingValue.selection.extendPos); |
|||
} |
|||
} |
|||
|
|||
public class TextSelectionOverlay { |
|||
|
|||
public TextSelectionOverlay(TextEditingValue value = null, |
|||
BuildContext context = null, Widget debugRequiredFor = null, |
|||
LayerLink layerLink = null, |
|||
RenderEditable renderObject = null, |
|||
TextSelectionControls selectionControls = null, |
|||
TextSelectionDelegate selectionDelegate = null) { |
|||
|
|||
D.assert(value != null); |
|||
D.assert(context != null); |
|||
this.context = context; |
|||
this.debugRequiredFor = debugRequiredFor; |
|||
this.layerLink = layerLink; |
|||
this.renderObject = renderObject; |
|||
this.selectionControls = selectionControls; |
|||
this.selectionDelegate = selectionDelegate; |
|||
this._value = value; |
|||
OverlayState overlay = Overlay.of(context); |
|||
D.assert(overlay != null); |
|||
this._handleController = new AnimationController(duration: _fadeDuration, vsync: overlay); |
|||
this._toolbarController = new AnimationController(duration: _fadeDuration, vsync: overlay); |
|||
} |
|||
|
|||
public readonly BuildContext context; |
|||
public readonly Widget debugRequiredFor; |
|||
public readonly LayerLink layerLink; |
|||
public readonly RenderEditable renderObject; |
|||
public readonly TextSelectionControls selectionControls; |
|||
public readonly TextSelectionDelegate selectionDelegate; |
|||
|
|||
public static TimeSpan _fadeDuration = TimeSpan.FromMilliseconds(150); |
|||
AnimationController _handleController; |
|||
AnimationController _toolbarController; |
|||
|
|||
Animation<double> _handleOpacity => this._handleController.view; |
|||
Animation<double> _toolbarOpacity => this._toolbarController.view; |
|||
|
|||
TextEditingValue _value; |
|||
|
|||
List<OverlayEntry> _handles; |
|||
|
|||
OverlayEntry _toolbar; |
|||
|
|||
TextSelection _selection => this._value.selection; |
|||
|
|||
public void showHandles() { |
|||
D.assert(this._handles == null); |
|||
this._handles = new List<OverlayEntry> { |
|||
new OverlayEntry(builder: (BuildContext context) => |
|||
this._buildHandle(context, _TextSelectionHandlePosition.start)), |
|||
new OverlayEntry(builder: (BuildContext context) => |
|||
this._buildHandle(context, _TextSelectionHandlePosition.end)), |
|||
}; |
|||
Overlay.of(this.context, debugRequiredFor: this.debugRequiredFor).insertAll(this._handles); |
|||
this._handleController.forward(from: 0.0); |
|||
} |
|||
|
|||
public void showToolbar() { |
|||
D.assert(this._toolbar == null); |
|||
this._toolbar = new OverlayEntry(builder: this._buildToolbar); |
|||
Overlay.of(this.context, debugRequiredFor: this.debugRequiredFor).insert(this._toolbar); |
|||
this._toolbarController.forward(from: 0.0); |
|||
} |
|||
|
|||
public void update(TextEditingValue newValue) { |
|||
if (this._value == newValue) |
|||
return; |
|||
this._value = newValue; |
|||
if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.persistentCallbacks) { |
|||
SchedulerBinding.instance.addPostFrameCallback((duration) => this._markNeedsBuild()); |
|||
} |
|||
else { |
|||
this._markNeedsBuild(); |
|||
} |
|||
} |
|||
|
|||
public void updateForScroll() { |
|||
this._markNeedsBuild(); |
|||
} |
|||
|
|||
void _markNeedsBuild() { |
|||
if (this._handles != null) { |
|||
this._handles[0].markNeedsBuild(); |
|||
this._handles[1].markNeedsBuild(); |
|||
} |
|||
|
|||
this._toolbar?.markNeedsBuild(); |
|||
} |
|||
|
|||
public bool handlesAreVisible => this._handles != null; |
|||
|
|||
|
|||
public bool toolbarIsVisible => this._toolbar != null; |
|||
|
|||
public void hide() { |
|||
if (this._handles != null) { |
|||
this._handles[0].remove(); |
|||
this._handles[1].remove(); |
|||
this._handles = null; |
|||
} |
|||
|
|||
this._toolbar?.remove(); |
|||
this._toolbar = null; |
|||
|
|||
this._handleController.stop(); |
|||
this._toolbarController.stop(); |
|||
} |
|||
|
|||
public void dispose() { |
|||
this.hide(); |
|||
this._handleController.dispose(); |
|||
this._toolbarController.dispose(); |
|||
} |
|||
|
|||
Widget _buildHandle(BuildContext context, _TextSelectionHandlePosition position) { |
|||
if ((this._selection.isCollapsed && position == _TextSelectionHandlePosition.end) || |
|||
this.selectionControls == null) |
|||
return new Container(); // hide the second handle when collapsed
|
|||
|
|||
return new FadeTransition( |
|||
opacity: this._handleOpacity, |
|||
child: new _TextSelectionHandleOverlay( |
|||
onSelectionHandleChanged: (TextSelection newSelection) => { |
|||
this._handleSelectionHandleChanged(newSelection, position); |
|||
}, |
|||
onSelectionHandleTapped: this._handleSelectionHandleTapped, |
|||
layerLink: this.layerLink, |
|||
renderObject: this.renderObject, |
|||
selection: this._selection, |
|||
selectionControls: this.selectionControls, |
|||
position: position |
|||
) |
|||
); |
|||
} |
|||
|
|||
Widget _buildToolbar(BuildContext context) { |
|||
if (this.selectionControls == null) |
|||
return new Container(); |
|||
|
|||
// Find the horizontal midpoint, just above the selected text.
|
|||
List<TextSelectionPoint> endpoints = this.renderObject.getEndpointsForSelection(this._selection); |
|||
Offset midpoint = new Offset( |
|||
(endpoints.Count == 1) ? endpoints[0].point.dx : (endpoints[0].point.dx + endpoints[1].point.dx) / 2.0, |
|||
endpoints[0].point.dy - this.renderObject.preferredLineHeight |
|||
); |
|||
|
|||
Rect editingRegion = Rect.fromPoints(this.renderObject.localToGlobal(Offset.zero), |
|||
this.renderObject.localToGlobal(this.renderObject.size.bottomRight(Offset.zero)) |
|||
); |
|||
|
|||
return new FadeTransition( |
|||
opacity: this._toolbarOpacity, |
|||
child: new CompositedTransformFollower( |
|||
link: this.layerLink, |
|||
showWhenUnlinked: false, |
|||
offset: -editingRegion.topLeft, |
|||
child: this.selectionControls.buildToolbar(context, editingRegion, midpoint, this.selectionDelegate) |
|||
) |
|||
); |
|||
} |
|||
|
|||
void _handleSelectionHandleChanged(TextSelection newSelection, _TextSelectionHandlePosition position) { |
|||
TextPosition textPosition = null; |
|||
switch (position) { |
|||
case _TextSelectionHandlePosition.start: |
|||
textPosition = newSelection.basePos; |
|||
break; |
|||
case _TextSelectionHandlePosition.end: |
|||
textPosition = newSelection.extendPos; |
|||
break; |
|||
} |
|||
|
|||
this.selectionDelegate.textEditingValue = |
|||
this._value.copyWith(selection: newSelection, composing: TextRange.empty); |
|||
this.selectionDelegate.bringIntoView(textPosition); |
|||
} |
|||
|
|||
void _handleSelectionHandleTapped() { |
|||
if (this._value.selection.isCollapsed) { |
|||
if (this._toolbar != null) { |
|||
this._toolbar?.remove(); |
|||
this._toolbar = null; |
|||
} |
|||
else { |
|||
this.showToolbar(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
internal class _TextSelectionHandleOverlay : StatefulWidget { |
|||
internal _TextSelectionHandleOverlay( |
|||
Key key = null, |
|||
TextSelection selection = null, |
|||
_TextSelectionHandlePosition position = _TextSelectionHandlePosition.start, |
|||
LayerLink layerLink = null, |
|||
RenderEditable renderObject = null, |
|||
ValueChanged<TextSelection> onSelectionHandleChanged = null, |
|||
VoidCallback onSelectionHandleTapped = null, |
|||
TextSelectionControls selectionControls = null |
|||
) : base(key: key) { |
|||
this.selection = selection; |
|||
this.position = position; |
|||
this.layerLink = layerLink; |
|||
this.renderObject = renderObject; |
|||
this.onSelectionHandleChanged = onSelectionHandleChanged; |
|||
this.onSelectionHandleTapped = onSelectionHandleTapped; |
|||
this.selectionControls = selectionControls; |
|||
} |
|||
|
|||
public readonly TextSelection selection; |
|||
public readonly _TextSelectionHandlePosition position; |
|||
public readonly LayerLink layerLink; |
|||
public readonly RenderEditable renderObject; |
|||
public readonly ValueChanged<TextSelection> onSelectionHandleChanged; |
|||
public readonly VoidCallback onSelectionHandleTapped; |
|||
public readonly TextSelectionControls selectionControls; |
|||
|
|||
|
|||
public override State createState() { |
|||
return new _TextSelectionHandleOverlayState(); |
|||
} |
|||
} |
|||
|
|||
internal class _TextSelectionHandleOverlayState : State<_TextSelectionHandleOverlay> { |
|||
Offset _dragPosition; |
|||
|
|||
void _handleDragStart(DragStartDetails details) { |
|||
this._dragPosition = details.globalPosition + |
|||
new Offset(0.0, -this.widget.selectionControls.handleSize.height); |
|||
} |
|||
|
|||
void _handleDragUpdate(DragUpdateDetails details) { |
|||
this._dragPosition += details.delta; |
|||
TextPosition position = this.widget.renderObject.getPositionForPoint(this._dragPosition); |
|||
|
|||
if (this.widget.selection.isCollapsed) { |
|||
this.widget.onSelectionHandleChanged(TextSelection.fromPosition(position)); |
|||
return; |
|||
} |
|||
|
|||
TextSelection newSelection = null; |
|||
switch (this.widget.position) { |
|||
case _TextSelectionHandlePosition.start: |
|||
newSelection = new TextSelection( |
|||
baseOffset: position.offset, |
|||
extentOffset: this.widget.selection.extentOffset |
|||
); |
|||
break; |
|||
case _TextSelectionHandlePosition.end: |
|||
newSelection = new TextSelection( |
|||
baseOffset: this.widget.selection.baseOffset, |
|||
extentOffset: position.offset |
|||
); |
|||
break; |
|||
} |
|||
|
|||
if (newSelection.baseOffset >= newSelection.extentOffset) |
|||
return; // don't allow order swapping.
|
|||
|
|||
this.widget.onSelectionHandleChanged(newSelection); |
|||
} |
|||
|
|||
void _handleTap() { |
|||
this.widget.onSelectionHandleTapped(); |
|||
} |
|||
|
|||
public override Widget build(BuildContext context) { |
|||
List<TextSelectionPoint> endpoints = |
|||
this.widget.renderObject.getEndpointsForSelection(this.widget.selection); |
|||
Offset point = null; |
|||
TextSelectionHandleType type = TextSelectionHandleType.left; |
|||
|
|||
switch (this.widget.position) { |
|||
case _TextSelectionHandlePosition.start: |
|||
point = endpoints[0].point; |
|||
type = this._chooseType(endpoints[0], TextSelectionHandleType.left, TextSelectionHandleType.right); |
|||
break; |
|||
case _TextSelectionHandlePosition.end: |
|||
D.assert(endpoints.Count == 2); |
|||
point = endpoints[1].point; |
|||
type = this._chooseType(endpoints[1], TextSelectionHandleType.right, TextSelectionHandleType.left); |
|||
break; |
|||
} |
|||
|
|||
return new CompositedTransformFollower( |
|||
link: this.widget.layerLink, |
|||
showWhenUnlinked: false, |
|||
child: new GestureDetector( |
|||
onPanStart: this._handleDragStart, |
|||
onPanUpdate: this._handleDragUpdate, |
|||
onTap: this._handleTap, |
|||
child: new Stack( |
|||
overflow: Overflow.visible, |
|||
children: new List<Widget>() { |
|||
new Positioned( |
|||
left: point.dx, |
|||
top: point.dy, |
|||
child: this.widget.selectionControls.buildHandle(context, type, |
|||
this.widget.renderObject.preferredLineHeight) |
|||
) |
|||
} |
|||
) |
|||
) |
|||
); |
|||
} |
|||
|
|||
TextSelectionHandleType _chooseType( |
|||
TextSelectionPoint endpoint, |
|||
TextSelectionHandleType ltrType, |
|||
TextSelectionHandleType rtlType |
|||
) { |
|||
if (this.widget.selection.isCollapsed) |
|||
return TextSelectionHandleType.collapsed; |
|||
|
|||
D.assert(endpoint.direction != null); |
|||
switch (endpoint.direction) { |
|||
case TextDirection.ltr: |
|||
return ltrType; |
|||
case TextDirection.rtl: |
|||
return rtlType; |
|||
} |
|||
|
|||
D.assert(() => throw new UIWidgetsError($"invalid endpoint.direction {endpoint.direction}")); |
|||
return ltrType; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: af25b0b118f7847c1980a3e9914392f3 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System.Collections.Generic; |
|||
using Unity.UIWidgets.engine; |
|||
using Unity.UIWidgets.ui; |
|||
using Unity.UIWidgets.widgets; |
|||
|
|||
namespace UIWidgetsSample { |
|||
|
|||
public class PageViewCanvas : WidgetCanvas { |
|||
|
|||
protected override Widget getWidget() { |
|||
return new Container( |
|||
width: 200, |
|||
height: 400, |
|||
child: new PageView( |
|||
children: new List<Widget>(){ |
|||
new Container( |
|||
color: new Color(0xFFE91E63) |
|||
), |
|||
new Container( |
|||
color: new Color(0xFF00BCD4) |
|||
), |
|||
new Container( |
|||
color: new Color(0xFF673AB7) |
|||
) |
|||
} |
|||
)); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: a8dea5be0500345ccb20b797d19e741c |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
撰写
预览
正在加载...
取消
保存
Reference in new issue