您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
671 行
25 KiB
671 行
25 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Unity.UIWidgets.external;
|
|
using Unity.UIWidgets.foundation;
|
|
using Unity.UIWidgets.rendering;
|
|
using Unity.UIWidgets.service;
|
|
using Unity.UIWidgets.ui;
|
|
using Unity.UIWidgets.widgets;
|
|
using UnityEngine;
|
|
using Canvas = Unity.UIWidgets.ui.Canvas;
|
|
using Color = Unity.UIWidgets.ui.Color;
|
|
using Rect = Unity.UIWidgets.ui.Rect;
|
|
using Transform = Unity.UIWidgets.widgets.Transform;
|
|
|
|
namespace Unity.UIWidgets.material {
|
|
public static class MaterialUtils {
|
|
public static readonly TextSelectionControls materialTextSelectionControls =
|
|
new _MaterialTextSelectionControls();
|
|
|
|
internal const float _kHandleSize = 22.0f;
|
|
|
|
internal const float _kToolbarScreenPadding = 8.0f;
|
|
|
|
internal const float _kToolbarHeight = 44.0f;
|
|
|
|
internal const float _kToolbarContentDistanceBelow = _kHandleSize - 2.0f;
|
|
|
|
internal const float _kToolbarContentDistance = 8.0f;
|
|
}
|
|
|
|
public class _TextSelectionToolbar : StatefulWidget {
|
|
public _TextSelectionToolbar(Key key = null, VoidCallback handleCut = null,
|
|
VoidCallback handleCopy = null, VoidCallback handlePaste = null, VoidCallback handleSelectAll = null,
|
|
bool isAbove = false) : base(key: key) {
|
|
this.handleCut = handleCut;
|
|
this.handleCopy = handleCopy;
|
|
this.handlePaste = handlePaste;
|
|
this.handleSelectAll = handleSelectAll;
|
|
this.isAbove = isAbove;
|
|
}
|
|
|
|
public readonly VoidCallback handleCut;
|
|
public readonly VoidCallback handleCopy;
|
|
public readonly VoidCallback handlePaste;
|
|
public readonly VoidCallback handleSelectAll;
|
|
public readonly bool isAbove;
|
|
|
|
public override State createState() {
|
|
return new _TextSelectionToolbarState();
|
|
}
|
|
}
|
|
|
|
public class _TextSelectionToolbarState : TickerProviderStateMixin<_TextSelectionToolbar> {
|
|
bool _overflowOpen = false;
|
|
|
|
UniqueKey _containerKey = new UniqueKey();
|
|
|
|
FlatButton _getItem(VoidCallback onPressed, string label) {
|
|
D.assert(onPressed != null);
|
|
return new FlatButton(
|
|
child: new Text(label),
|
|
onPressed: onPressed
|
|
);
|
|
}
|
|
|
|
public override void didUpdateWidget(StatefulWidget oldWidget) {
|
|
var _oldWidget = (_TextSelectionToolbar) oldWidget;
|
|
if (((widget.handleCut == null) != (_oldWidget.handleCut == null))
|
|
|| ((widget.handleCopy == null) != (_oldWidget.handleCopy == null))
|
|
|| ((widget.handlePaste == null) != (_oldWidget.handlePaste == null))
|
|
|| ((widget.handleSelectAll == null) != (_oldWidget.handleSelectAll == null))) {
|
|
_containerKey = new UniqueKey();
|
|
_overflowOpen = false;
|
|
}
|
|
|
|
base.didUpdateWidget(oldWidget);
|
|
}
|
|
|
|
public override Widget build(BuildContext context) {
|
|
MaterialLocalizations localizations = MaterialLocalizations.of(context);
|
|
List<Widget> items = new List<Widget>();
|
|
|
|
if (widget.handleCut != null) {
|
|
items.Add(_getItem(widget.handleCut, localizations.cutButtonLabel));
|
|
}
|
|
|
|
if (widget.handleCopy != null) {
|
|
items.Add(_getItem(widget.handleCopy, localizations.copyButtonLabel));
|
|
}
|
|
|
|
if (widget.handlePaste != null) {
|
|
items.Add(_getItem(widget.handlePaste, localizations.pasteButtonLabel));
|
|
}
|
|
|
|
if (widget.handleSelectAll != null) {
|
|
items.Add(_getItem(widget.handleSelectAll, localizations.selectAllButtonLabel));
|
|
}
|
|
|
|
if (items.isEmpty()) {
|
|
return new Container(width: 0.0f, height: 0.0f);
|
|
}
|
|
|
|
items.Insert(0, new IconButton(
|
|
icon: new Icon(_overflowOpen ? Icons.arrow_back : Icons.more_vert),
|
|
onPressed: () => { setState(() => { _overflowOpen = !_overflowOpen; }); },
|
|
tooltip: _overflowOpen
|
|
? localizations.backButtonTooltip
|
|
: localizations.moreButtonTooltip
|
|
)
|
|
);
|
|
|
|
return new _TextSelectionToolbarContainer(
|
|
key: _containerKey,
|
|
overflowOpen: _overflowOpen,
|
|
child: new AnimatedSize(
|
|
vsync: this,
|
|
duration: new TimeSpan(0, 0, 0, 0, 140),
|
|
child: new Material(
|
|
elevation: 1.0f,
|
|
child: new _TextSelectionToolbarItems(
|
|
isAbove: widget.isAbove,
|
|
overflowOpen: _overflowOpen,
|
|
children: items)
|
|
)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
class _TextSelectionToolbarContainer : SingleChildRenderObjectWidget {
|
|
public _TextSelectionToolbarContainer(
|
|
Key key = null,
|
|
Widget child = null,
|
|
bool overflowOpen = false
|
|
) : base(key: key, child: child) {
|
|
D.assert(child != null);
|
|
this.overflowOpen = overflowOpen;
|
|
}
|
|
|
|
public readonly bool overflowOpen;
|
|
|
|
public override RenderObject createRenderObject(BuildContext context) {
|
|
return new _TextSelectionToolbarContainerRenderBox(overflowOpen: overflowOpen);
|
|
}
|
|
|
|
public override void updateRenderObject(BuildContext context, RenderObject renderObject) {
|
|
var _renderObject = (_TextSelectionToolbarContainerRenderBox) renderObject;
|
|
_renderObject.overflowOpen = overflowOpen;
|
|
}
|
|
}
|
|
|
|
class _TextSelectionToolbarContainerRenderBox : RenderProxyBox {
|
|
public _TextSelectionToolbarContainerRenderBox(
|
|
bool overflowOpen = false
|
|
) : base() {
|
|
_overflowOpen = overflowOpen;
|
|
}
|
|
|
|
float? _closedWidth;
|
|
|
|
public bool overflowOpen {
|
|
get { return _overflowOpen; }
|
|
set {
|
|
if (value == overflowOpen) {
|
|
return;
|
|
}
|
|
|
|
_overflowOpen = value;
|
|
markNeedsLayout();
|
|
}
|
|
}
|
|
|
|
bool _overflowOpen;
|
|
|
|
protected override void performLayout() {
|
|
child.layout(constraints.loosen(), parentUsesSize: true);
|
|
|
|
if (!overflowOpen && _closedWidth == null) {
|
|
_closedWidth = child.size.width;
|
|
}
|
|
|
|
size = constraints.constrain(new Size(
|
|
(_closedWidth == null || child.size.width > _closedWidth) ? child.size.width : _closedWidth.Value,
|
|
child.size.height
|
|
));
|
|
|
|
_ToolbarParentData childParentData = child.parentData as _ToolbarParentData;
|
|
childParentData.offset = new Offset(
|
|
size.width - child.size.width,
|
|
0.0f
|
|
);
|
|
}
|
|
|
|
public override void paint(PaintingContext context, Offset offset) {
|
|
_ToolbarParentData childParentData = child.parentData as _ToolbarParentData;
|
|
context.paintChild(child, childParentData.offset + offset);
|
|
}
|
|
|
|
protected override bool hitTestChildren(BoxHitTestResult result, Offset position = null) {
|
|
_ToolbarParentData childParentData = child.parentData as _ToolbarParentData;
|
|
return result.addWithPaintOffset(
|
|
offset: childParentData.offset,
|
|
position: position,
|
|
hitTest: (BoxHitTestResult boxResult, Offset boxTransformed) => {
|
|
D.assert(boxTransformed == position - childParentData.offset);
|
|
return child.hitTest(boxResult, position: boxTransformed);
|
|
}
|
|
);
|
|
}
|
|
|
|
public override void setupParentData(RenderObject child) {
|
|
if (!(child.parentData is _ToolbarParentData)) {
|
|
child.parentData = new _ToolbarParentData();
|
|
}
|
|
}
|
|
|
|
public override void applyPaintTransform(RenderObject child, Matrix4 transform) {
|
|
_ToolbarParentData childParentData = child.parentData as _ToolbarParentData;
|
|
transform.translate(childParentData.offset.dx, childParentData.offset.dy);
|
|
base.applyPaintTransform(child, transform);
|
|
}
|
|
}
|
|
|
|
class _TextSelectionToolbarItems : MultiChildRenderObjectWidget {
|
|
public _TextSelectionToolbarItems(
|
|
Key key = null,
|
|
bool isAbove = false,
|
|
bool overflowOpen = false,
|
|
List<Widget> children = null) : base(key: key, children: children) {
|
|
D.assert(children != null);
|
|
this.isAbove = isAbove;
|
|
this.overflowOpen = overflowOpen;
|
|
}
|
|
|
|
public readonly bool isAbove;
|
|
public readonly bool overflowOpen;
|
|
|
|
public override RenderObject createRenderObject(BuildContext context) {
|
|
return new _TextSelectionToolbarItemsRenderBox(
|
|
isAbove: isAbove,
|
|
overflowOpen: overflowOpen
|
|
);
|
|
}
|
|
|
|
public override void updateRenderObject(BuildContext context, RenderObject renderObject) {
|
|
var _renderObject = (_TextSelectionToolbarItemsRenderBox) renderObject;
|
|
_renderObject.isAbove = isAbove;
|
|
_renderObject.overflowOpen = overflowOpen;
|
|
}
|
|
|
|
public override Element createElement() {
|
|
return new _TextSelectionToolbarItemsElement(this);
|
|
}
|
|
}
|
|
|
|
class _ToolbarParentData : ContainerBoxParentData<RenderBox> {
|
|
public bool shouldPaint;
|
|
|
|
public override string ToString() {
|
|
return $"{base.ToString()}; shouldPaint={shouldPaint}";
|
|
}
|
|
}
|
|
|
|
class _TextSelectionToolbarItemsElement : MultiChildRenderObjectElement {
|
|
public _TextSelectionToolbarItemsElement(
|
|
MultiChildRenderObjectWidget widget) :
|
|
base(widget: widget) {
|
|
}
|
|
|
|
static bool _shouldPaint(Element child) {
|
|
return (child.renderObject.parentData as _ToolbarParentData).shouldPaint;
|
|
}
|
|
|
|
public override void debugVisitOnstageChildren(ElementVisitor visitor) {
|
|
foreach (var child in LinqUtils<Element>.WhereList(children,(_shouldPaint))) {
|
|
visitor(child);
|
|
}
|
|
}
|
|
}
|
|
|
|
class _TextSelectionToolbarItemsRenderBox : ContainerRenderObjectMixinRenderBox<RenderBox, _ToolbarParentData> {
|
|
public _TextSelectionToolbarItemsRenderBox(
|
|
bool isAbove = false,
|
|
bool overflowOpen = false
|
|
) : base() {
|
|
_isAbove = isAbove;
|
|
_overflowOpen = overflowOpen;
|
|
}
|
|
|
|
int _lastIndexThatFits = -1;
|
|
|
|
public bool isAbove {
|
|
get { return _isAbove; }
|
|
set {
|
|
if (value == isAbove) {
|
|
return;
|
|
}
|
|
|
|
_isAbove = value;
|
|
markNeedsLayout();
|
|
}
|
|
}
|
|
|
|
bool _isAbove;
|
|
|
|
public bool overflowOpen {
|
|
get { return _overflowOpen; }
|
|
set {
|
|
if (value == overflowOpen) {
|
|
return;
|
|
}
|
|
|
|
_overflowOpen = value;
|
|
markNeedsLayout();
|
|
}
|
|
}
|
|
|
|
bool _overflowOpen;
|
|
|
|
void _layoutChildren() {
|
|
BoxConstraints sizedConstraints = _overflowOpen
|
|
? constraints
|
|
: BoxConstraints.loose(new Size(
|
|
constraints.maxWidth,
|
|
MaterialUtils._kToolbarHeight
|
|
));
|
|
|
|
int i = -1;
|
|
float width = 0.0f;
|
|
visitChildren((RenderObject renderObjectChild) => {
|
|
i++;
|
|
|
|
if (_lastIndexThatFits != -1 && !overflowOpen) {
|
|
return;
|
|
}
|
|
|
|
RenderBox child = renderObjectChild as RenderBox;
|
|
child.layout(sizedConstraints.loosen(), parentUsesSize: true);
|
|
width += child.size.width;
|
|
|
|
if (width > sizedConstraints.maxWidth && _lastIndexThatFits == -1) {
|
|
_lastIndexThatFits = i - 1;
|
|
}
|
|
});
|
|
|
|
RenderBox navButton = firstChild;
|
|
if (_lastIndexThatFits != -1
|
|
&& _lastIndexThatFits == childCount - 2
|
|
&& width - navButton.size.width <= sizedConstraints.maxWidth) {
|
|
_lastIndexThatFits = -1;
|
|
}
|
|
}
|
|
|
|
bool _shouldPaintChild(RenderObject renderObjectChild, int index) {
|
|
if (renderObjectChild == firstChild) {
|
|
return _lastIndexThatFits != -1;
|
|
}
|
|
|
|
if (_lastIndexThatFits == -1) {
|
|
return true;
|
|
}
|
|
|
|
return (index > _lastIndexThatFits) == overflowOpen;
|
|
}
|
|
|
|
void _placeChildren() {
|
|
int i = -1;
|
|
Size nextSize = new Size(0.0f, 0.0f);
|
|
float fitWidth = 0.0f;
|
|
RenderBox navButton = firstChild;
|
|
float overflowHeight = overflowOpen && !isAbove ? navButton.size.height : 0.0f;
|
|
visitChildren((RenderObject renderObjectChild) => {
|
|
i++;
|
|
|
|
RenderBox child = renderObjectChild as RenderBox;
|
|
_ToolbarParentData childParentData = child.parentData as _ToolbarParentData;
|
|
|
|
if (renderObjectChild == navButton) {
|
|
return;
|
|
}
|
|
|
|
if (!_shouldPaintChild(renderObjectChild, i)) {
|
|
childParentData.shouldPaint = false;
|
|
return;
|
|
}
|
|
|
|
childParentData.shouldPaint = true;
|
|
|
|
if (!overflowOpen) {
|
|
childParentData.offset = new Offset(fitWidth, 0.0f);
|
|
fitWidth += child.size.width;
|
|
nextSize = new Size(
|
|
fitWidth,
|
|
Mathf.Max(child.size.height, nextSize.height)
|
|
);
|
|
}
|
|
else {
|
|
childParentData.offset = new Offset(0.0f, overflowHeight);
|
|
overflowHeight += child.size.height;
|
|
nextSize = new Size(
|
|
Mathf.Max(child.size.width, nextSize.width),
|
|
overflowHeight
|
|
);
|
|
}
|
|
});
|
|
|
|
_ToolbarParentData navButtonParentData = navButton.parentData as _ToolbarParentData;
|
|
if (_shouldPaintChild(firstChild, 0)) {
|
|
navButtonParentData.shouldPaint = true;
|
|
if (overflowOpen) {
|
|
navButtonParentData.offset = isAbove
|
|
? new Offset(0.0f, overflowHeight)
|
|
: Offset.zero;
|
|
nextSize = new Size(
|
|
nextSize.width,
|
|
isAbove ? nextSize.height + navButton.size.height : nextSize.height
|
|
);
|
|
}
|
|
else {
|
|
navButtonParentData.offset = new Offset(fitWidth, 0.0f);
|
|
nextSize = new Size(nextSize.width + navButton.size.width, nextSize.height);
|
|
}
|
|
}
|
|
else {
|
|
navButtonParentData.shouldPaint = false;
|
|
}
|
|
|
|
size = nextSize;
|
|
}
|
|
|
|
protected override void performLayout() {
|
|
_lastIndexThatFits = -1;
|
|
if (firstChild == null) {
|
|
performResize();
|
|
return;
|
|
}
|
|
|
|
_layoutChildren();
|
|
_placeChildren();
|
|
}
|
|
|
|
public override void paint(PaintingContext context, Offset offset) {
|
|
visitChildren((RenderObject renderObjectChild) => {
|
|
RenderBox child = renderObjectChild as RenderBox;
|
|
_ToolbarParentData childParentData = child.parentData as _ToolbarParentData;
|
|
if (!childParentData.shouldPaint) {
|
|
return;
|
|
}
|
|
|
|
context.paintChild(child, childParentData.offset + offset);
|
|
});
|
|
}
|
|
|
|
public override void setupParentData(RenderObject child) {
|
|
if (!(child.parentData is _ToolbarParentData)) {
|
|
child.parentData = new _ToolbarParentData();
|
|
}
|
|
}
|
|
|
|
protected override bool hitTestChildren(BoxHitTestResult result, Offset position = null) {
|
|
RenderBox child = lastChild;
|
|
while (child != null) {
|
|
_ToolbarParentData childParentData = child.parentData as _ToolbarParentData;
|
|
|
|
if (!childParentData.shouldPaint) {
|
|
child = childParentData.previousSibling;
|
|
continue;
|
|
}
|
|
|
|
bool isHit = result.addWithPaintOffset(
|
|
offset: childParentData.offset,
|
|
position: position,
|
|
hitTest: (BoxHitTestResult boxResult, Offset boxTransformed) => {
|
|
D.assert(boxTransformed == position - childParentData.offset);
|
|
return child.hitTest(boxResult, position: boxTransformed);
|
|
}
|
|
);
|
|
if (isHit) {
|
|
return true;
|
|
}
|
|
|
|
child = childParentData.previousSibling;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
class _TextSelectionToolbarLayout : SingleChildLayoutDelegate {
|
|
internal _TextSelectionToolbarLayout(Offset anchor, float upperBounds, bool fitsAbove) {
|
|
this.anchor = anchor;
|
|
this.upperBounds = upperBounds;
|
|
this.fitsAbove = fitsAbove;
|
|
}
|
|
|
|
public readonly Offset anchor;
|
|
public readonly float upperBounds;
|
|
public readonly bool fitsAbove;
|
|
|
|
static float _centerOn(float position, float width, float min, float max) {
|
|
if (position - width / 2.0f < min) {
|
|
return min;
|
|
}
|
|
|
|
if (position + width / 2.0f > max) {
|
|
return max - width;
|
|
}
|
|
|
|
return position - width / 2.0f;
|
|
}
|
|
|
|
public override BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
|
|
return constraints.loosen();
|
|
}
|
|
|
|
public override Offset getPositionForChild(Size size, Size childSize) {
|
|
return new Offset(
|
|
_centerOn(
|
|
anchor.dx,
|
|
childSize.width,
|
|
MaterialUtils._kToolbarScreenPadding,
|
|
size.width - MaterialUtils._kToolbarScreenPadding
|
|
),
|
|
fitsAbove
|
|
? Mathf.Max(upperBounds, anchor.dy - childSize.height)
|
|
: anchor.dy
|
|
);
|
|
}
|
|
|
|
public override bool shouldRelayout(SingleChildLayoutDelegate oldDelegate) {
|
|
return anchor != ((_TextSelectionToolbarLayout) oldDelegate).anchor;
|
|
}
|
|
}
|
|
|
|
class _TextSelectionHandlePainter : AbstractCustomPainter {
|
|
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 = color;
|
|
float radius = size.width / 2.0f;
|
|
Rect circle = Rect.fromCircle(center: new Offset(radius, radius), radius: radius);
|
|
Rect point = Rect.fromLTWH(0.0f, 0.0f, radius, radius);
|
|
Path path = new Path();
|
|
path.addOval(circle);
|
|
path.addRect(point);
|
|
canvas.drawPath(path, paint);
|
|
}
|
|
|
|
public override bool shouldRepaint(CustomPainter oldPainter) {
|
|
return color != ((_TextSelectionHandlePainter) oldPainter).color;
|
|
}
|
|
}
|
|
|
|
class _MaterialTextSelectionControls : TextSelectionControls {
|
|
public static readonly TextSelectionControls materialTextSelectionControls =
|
|
new _MaterialTextSelectionControls();
|
|
|
|
public override Size getHandleSize(float textLineHeight) {
|
|
return new Size(MaterialUtils._kHandleSize,
|
|
MaterialUtils._kHandleSize);
|
|
}
|
|
|
|
public override Widget buildToolbar(BuildContext context, Rect globalEditableRegion, float textLineHeight,
|
|
Offset selectionMidpoint, List<TextSelectionPoint> endpoints, TextSelectionDelegate selectionDelegate) {
|
|
D.assert(WidgetsD.debugCheckHasMediaQuery(context));
|
|
D.assert(material_.debugCheckHasMaterialLocalizations(context));
|
|
|
|
TextSelectionPoint startTextSelectionPoint = endpoints[0];
|
|
TextSelectionPoint endTextSelectionPoint = endpoints.Count > 1
|
|
? endpoints[1]
|
|
: endpoints[0];
|
|
const float closedToolbarHeightNeeded = MaterialUtils._kToolbarScreenPadding
|
|
+ MaterialUtils._kToolbarHeight
|
|
+ MaterialUtils._kToolbarContentDistance;
|
|
float paddingTop = MediaQuery.of(context).padding.top;
|
|
float availableHeight = globalEditableRegion.top
|
|
+ startTextSelectionPoint.point.dy
|
|
- textLineHeight
|
|
- paddingTop;
|
|
bool fitsAbove = closedToolbarHeightNeeded <= availableHeight;
|
|
Offset anchor = new Offset(
|
|
globalEditableRegion.left + selectionMidpoint.dx,
|
|
fitsAbove
|
|
? globalEditableRegion.top + startTextSelectionPoint.point.dy - textLineHeight -
|
|
MaterialUtils._kToolbarContentDistance
|
|
: globalEditableRegion.top + endTextSelectionPoint.point.dy +
|
|
MaterialUtils._kToolbarContentDistanceBelow
|
|
);
|
|
|
|
return new Stack(
|
|
children: new List<Widget>() {
|
|
new CustomSingleChildLayout(
|
|
layoutDelegate: new _TextSelectionToolbarLayout(
|
|
anchor,
|
|
MaterialUtils._kToolbarScreenPadding + paddingTop,
|
|
fitsAbove
|
|
),
|
|
child: new _TextSelectionToolbar(
|
|
handleCut: canCut(selectionDelegate)
|
|
? () => handleCut(selectionDelegate)
|
|
: (VoidCallback) null,
|
|
handleCopy: canCopy(selectionDelegate)
|
|
? () => handleCopy(selectionDelegate)
|
|
: (VoidCallback) null,
|
|
handlePaste: canPaste(selectionDelegate)
|
|
? () => handlePaste(selectionDelegate)
|
|
: (VoidCallback) null,
|
|
handleSelectAll: canSelectAll(selectionDelegate)
|
|
? () => handleSelectAll(selectionDelegate)
|
|
: (VoidCallback) null,
|
|
isAbove: fitsAbove
|
|
)
|
|
),
|
|
}
|
|
);
|
|
}
|
|
|
|
public override Widget buildHandle(BuildContext context, TextSelectionHandleType type, float textLineHeight) {
|
|
Widget handle = new SizedBox(
|
|
width: MaterialUtils._kHandleSize,
|
|
height: MaterialUtils._kHandleSize,
|
|
child: new CustomPaint(
|
|
painter: new _TextSelectionHandlePainter(
|
|
color: Theme.of(context).textSelectionHandleColor
|
|
)
|
|
)
|
|
);
|
|
|
|
switch (type) {
|
|
case TextSelectionHandleType.left: // points up-right
|
|
return new Transform(
|
|
transform: Matrix4.rotationZ(Mathf.PI / 2),
|
|
child: handle
|
|
);
|
|
case TextSelectionHandleType.right: // points up-left
|
|
return handle;
|
|
case TextSelectionHandleType.collapsed: // points up
|
|
return new Transform(
|
|
transform: Matrix4.rotationZ(Mathf.PI / 4),
|
|
child: handle
|
|
);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public override Offset getHandleAnchor(TextSelectionHandleType type, float textLineHeight) {
|
|
switch (type) {
|
|
case TextSelectionHandleType.left:
|
|
return new Offset(MaterialUtils._kHandleSize, 0);
|
|
case TextSelectionHandleType.right:
|
|
return Offset.zero;
|
|
default:
|
|
return new Offset(MaterialUtils._kHandleSize / 2, -4);
|
|
}
|
|
}
|
|
|
|
new bool canSelectAll(TextSelectionDelegate selectionDelegate) {
|
|
TextEditingValue value = selectionDelegate.textEditingValue;
|
|
return selectionDelegate.selectAllEnabled &&
|
|
value.text.isNotEmpty() &&
|
|
!(value.selection.start == 0 && value.selection.end == value.text.Length);
|
|
}
|
|
}
|
|
}
|