浏览代码

upgrade text stuffs

/siyaoH-1.17-PlatformMessage
xingweizhu 4 年前
当前提交
865298ed
共有 4 个文件被更改,包括 804 次插入314 次删除
  1. 463
      com.unity.uiwidgets/Runtime/material/text_field.cs
  2. 32
      com.unity.uiwidgets/Runtime/material/text_form_field.cs
  3. 615
      com.unity.uiwidgets/Runtime/material/text_selection.cs
  4. 8
      com.unity.uiwidgets/Runtime/widgets/text_selection.cs

463
com.unity.uiwidgets/Runtime/material/text_field.cs


using System.Collections.Generic;
using System.Text;
using uiwidgets;
using Unity.UIWidgets.cupertino;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.painting;

using Unity.UIWidgets.widgets;
using UnityEngine;
using Color = Unity.UIWidgets.ui.Color;
class _TextFieldSelectionGestureDetectorBuilder : TextSelectionGestureDetectorBuilder {
public _TextFieldSelectionGestureDetectorBuilder(
_TextFieldState state) : base(_delegate: state) {
_state = state;
}
public readonly _TextFieldState _state;
protected override void onForcePressStart(ForcePressDetails details) {
base.onForcePressStart(details);
if (_delegate.selectionEnabled && shouldShowSelectionToolbar) {
editableText.showToolbar();
}
}
protected override void onForcePressEnd(ForcePressDetails details) {
// Not required.
}
protected override void onSingleTapUp(TapUpDetails details) {
editableText.hideToolbar();
if (_delegate.selectionEnabled) {
//use android by default
renderEditable.selectPosition(cause: SelectionChangedCause.tap);
}
_state._requestKeyboard();
if (_state.widget.onTap != null) {
_state.widget.onTap();
}
}
protected override void onSingleLongTapStart(LongPressStartDetails details) {
if (_delegate.selectionEnabled) {
//use android by default
renderEditable.selectWord(cause: SelectionChangedCause.longPress);
Feedback.forLongPress(_state.context);
}
}
}
public class TextField : StatefulWidget {
public TextField(Key key = null,
TextEditingController controller = null,

TextStyle style = null,
StrutStyle strutStyle = null,
TextAlign textAlign = TextAlign.left,
TextAlignVertical textAlignVertical = null,
bool readOnly = false,
ToolbarOptions toolbarOptions = null,
bool? showCursor = null,
SmartDashesType? smartDashesType = null,
SmartQuotesType? smartQuotesType = null,
bool enableSuggestions = true,
int? maxLines = 1,
int? minLines = null,
bool expands = false,

float? cursorWidth = 2.0f,
Radius cursorRadius = null,
Color cursorColor = null,
BoxHeightStyle selectionHeightStyle = BoxHeightStyle.tight,
BoxWidthStyle selectionWidthStyle = BoxWidthStyle.tight,
bool? enableInteractiveSelection = null,
bool enableInteractiveSelection = true,
ScrollController scrollController = null,
ScrollPhysics scrollPhysics = null
) : base(key: key) {
D.assert(maxLines == null || maxLines > 0);

D.assert(!expands || (maxLines == null && minLines == null),
() => "minLines and maxLines must be null when expands is true.");
D.assert(maxLength == null || maxLength == noMaxLength || maxLength > 0);
D.assert(!obscureText || maxLines == 1, () => "Obscured fields cannot be multiline.");
this.smartDashesType =
smartDashesType ?? (obscureText ? SmartDashesType.disabled : SmartDashesType.enabled);
this.smartQuotesType =
smartQuotesType ?? (obscureText ? SmartQuotesType.disabled : SmartQuotesType.enabled);
this.controller = controller;
this.focusNode = focusNode;

this.style = style;
this.strutStyle = strutStyle;
this.textAlign = textAlign;
this.textAlignVertical = textAlignVertical;
this.readOnly = readOnly;
this.showCursor = showCursor;
this.enableSuggestions = enableSuggestions;
this.maxLines = maxLines;
this.minLines = minLines;
this.expands = expands;

this.enabled = enabled;
this.cursorWidth = cursorWidth;
this.cursorColor = cursorColor;
this.selectionHeightStyle = selectionHeightStyle;
this.selectionWidthStyle = selectionWidthStyle;
this.cursorRadius = cursorRadius;
this.onSubmitted = onSubmitted;
this.keyboardAppearance = keyboardAppearance;

this.toolbarOptions = toolbarOptions ?? (obscureText
? new ToolbarOptions(
selectAll: true,
paste: true
)
: new ToolbarOptions(
copy: true,
cut: true,
selectAll: true,
paste: true
));
this.enableInteractiveSelection = enableInteractiveSelection;
this.scrollController = scrollController;
}
public readonly TextEditingController controller;

public readonly TextAlign textAlign;
public readonly TextAlignVertical textAlignVertical;
public readonly TextDirection textDirection;
public readonly bool autofocus;

public readonly bool autocorrect;
public readonly SmartDashesType smartDashesType;
public readonly SmartQuotesType smartQuotesType;
public readonly bool enableSuggestions;
public readonly int? maxLines;

public readonly bool readOnly;
public readonly ToolbarOptions toolbarOptions;
public readonly bool? showCursor;
public const long noMaxLength = -1;
public readonly int? maxLength;

public readonly Color cursorColor;
public readonly BoxHeightStyle selectionHeightStyle;
public readonly BoxWidthStyle selectionWidthStyle;
public readonly bool? enableInteractiveSelection;
public readonly bool enableInteractiveSelection;
public readonly ScrollController scrollController;
get {
return enableInteractiveSelection ?? !obscureText;
}
get { return enableInteractiveSelection; }
}
public readonly GestureTapCallback onTap;

new DiagnosticsProperty<TextEditingController>("controller", controller, defaultValue: null));
properties.add(new DiagnosticsProperty<FocusNode>("focusNode", focusNode, defaultValue: null));
properties.add(new DiagnosticsProperty<bool?>("enabled", enabled, defaultValue: null));
properties.add(new DiagnosticsProperty<InputDecoration>("decoration", decoration, defaultValue: new InputDecoration()));
properties.add(new DiagnosticsProperty<InputDecoration>("decoration", decoration,
defaultValue: new InputDecoration()));
properties.add(new DiagnosticsProperty<TextInputType>("keyboardType", keyboardType,
defaultValue: TextInputType.text));
properties.add(new DiagnosticsProperty<TextStyle>("style", style, defaultValue: null));

properties.add(new EnumProperty<SmartDashesType>("smartDashesType", smartDashesType,
defaultValue: obscureText ? SmartDashesType.disabled : SmartDashesType.enabled));
properties.add(new EnumProperty<SmartQuotesType>("smartQuotesType", smartQuotesType,
defaultValue: obscureText ? SmartQuotesType.disabled : SmartQuotesType.enabled));
properties.add(new DiagnosticsProperty<bool>("enableSuggestions", enableSuggestions, defaultValue: true));
properties.add(new IntProperty("maxLines", maxLines, defaultValue: 1));
properties.add(new IntProperty("minLines", minLines, defaultValue: null));
properties.add(new DiagnosticsProperty<bool>("expands", expands, defaultValue: false));

properties.add(new EnumProperty<TextInputAction?>("textInputAction", textInputAction, defaultValue: null));
properties.add(new EnumProperty<TextCapitalization>("textCapitalization", textCapitalization, defaultValue: TextCapitalization.none));
properties.add(new EnumProperty<TextCapitalization>("textCapitalization", textCapitalization,
defaultValue: TextCapitalization.none));
properties.add(new DiagnosticsProperty<TextAlignVertical>("textAlignVertical", textAlignVertical,
defaultValue: null));
properties.add(new DiagnosticsProperty<Color>("cursorColor", cursorColor, defaultValue: null));
properties.add(new DiagnosticsProperty<Brightness?>("keyboardAppearance", keyboardAppearance, defaultValue: null));
properties.add(new DiagnosticsProperty<EdgeInsets>("scrollPadding", scrollPadding, defaultValue: EdgeInsets.all(20.0f)));
properties.add(new FlagProperty("selectionEnabled", value: selectionEnabled, defaultValue: true, ifFalse: "selection disabled"));
properties.add(new ColorProperty("cursorColor", cursorColor, defaultValue: null));
properties.add(new DiagnosticsProperty<Brightness?>("keyboardAppearance", keyboardAppearance,
defaultValue: null));
properties.add(new DiagnosticsProperty<EdgeInsets>("scrollPadding", scrollPadding,
defaultValue: EdgeInsets.all(20.0f)));
properties.add(new FlagProperty("selectionEnabled", value: selectionEnabled, defaultValue: true,
ifFalse: "selection disabled"));
properties.add(
new DiagnosticsProperty<ScrollController>("scrollController", scrollController, defaultValue: null));
class _TextFieldState : AutomaticKeepAliveClientMixin<TextField> {
readonly GlobalKey<EditableTextState> _editableTextKey = new LabeledGlobalKey<EditableTextState>();
HashSet<InteractiveInkFeature> _splashes;
InteractiveInkFeature _currentSplash;
class _TextFieldState : State<TextField>, TextSelectionGestureDetectorBuilderDelegate {
TextEditingController _controller;
TextEditingController _effectiveController {

}
}
bool _isHovering = false;
bool needsCounter {
get {
return widget.maxLength != null

}
bool _showSelectionHandles = false;
_TextFieldSelectionGestureDetectorBuilder _selectionGestureDetectorBuilder;
bool _forcePressEnabled;
public bool forcePressEnabled {
get { return _forcePressEnabled; }
}
readonly GlobalKey<EditableTextState> _editableTextKey = new LabeledGlobalKey<EditableTextState>();
public GlobalKey<EditableTextState> editableTextKey {
get { return _editableTextKey; }
}
public bool selectionEnabled {
get { return widget.selectionEnabled; }
}
bool _isEnabled {
get { return widget.enabled ?? widget.decoration?.enabled ?? true; }
}
int _currentLength {
get { return _effectiveController.value.text.Length; }
}
InputDecoration _getEffectiveDecoration() {
MaterialLocalizations localizations = MaterialLocalizations.of(context);
ThemeData themeData = Theme.of(context);

return effectiveDecoration;
}
Widget counter;
int currentLength = _effectiveController.value.text.Length;
Widget counter = null;
int currentLength = _currentLength;
counter = widget.buildCounter(
Widget builtCounter = widget.buildCounter(
if (builtCounter != null) {
counter = builtCounter;
}
return effectiveDecoration.copyWith(counter: counter);
}

}
}
// Handle length exceeds maxLength
return effectiveDecoration.copyWith(
counterText: counterText
);

base.initState();
_selectionGestureDetectorBuilder = new _TextFieldSelectionGestureDetectorBuilder(state: this);
_effectiveFocusNode.canRequestFocus = _isEnabled;
}
public override void didUpdateWidget(StatefulWidget oldWidget) {

_controller = null;
}
bool isEnabled = widget.enabled ?? widget.decoration?.enabled ?? true;
bool wasEnabled = ((TextField) oldWidget).enabled ?? ((TextField) oldWidget).decoration?.enabled ?? true;
if (wasEnabled && !isEnabled) {
_effectiveFocusNode.unfocus();
_effectiveFocusNode.canRequestFocus = _isEnabled;
if (_effectiveFocusNode.hasFocus && widget.readOnly != ((TextField) oldWidget).readOnly) {
if (_effectiveController.selection.isCollapsed) {
_showSelectionHandles = !widget.readOnly;
}
}
}

}
void _requestKeyboard() {
_editableTextKey.currentState?.requestKeyboard();
EditableTextState _editableText {
get { return editableTextKey.currentState; }
void _handleSelectionChanged(TextSelection selection, SelectionChangedCause cause) {
switch (Theme.of(context).platform) {
case RuntimePlatform.IPhonePlayer:
if (cause == SelectionChangedCause.longPress) {
_editableTextKey.currentState?.bringIntoView(selection.basePos);
}
return;
case RuntimePlatform.Android:
break;
}
internal void _requestKeyboard() {
_editableText?.requestKeyboard();
InteractiveInkFeature _createInkFeature(Offset globalPosition) {
MaterialInkController inkController = Material.of(context);
ThemeData themeData = Theme.of(context);
BuildContext editableContext = _editableTextKey.currentContext;
RenderBox referenceBox =
(RenderBox) (InputDecorator.containerOf(editableContext) ?? editableContext.findRenderObject());
Offset position = referenceBox.globalToLocal(globalPosition);
Color color = themeData.splashColor;
InteractiveInkFeature splash = null;
void handleRemoved() {
if (_splashes != null) {
D.assert(_splashes.Contains(splash));
_splashes.Remove(splash);
if (_currentSplash == splash) {
_currentSplash = null;
}
updateKeepAlive();
} // else we're probably in deactivate()
bool _shouldShowSelectionHandles(SelectionChangedCause cause) {
if (!_selectionGestureDetectorBuilder.shouldShowSelectionToolbar) {
return false;
splash = themeData.splashFactory.create(
controller: inkController,
referenceBox: referenceBox,
position: position,
color: color,
containedInkWell: true,
borderRadius: BorderRadius.zero,
onRemoved: handleRemoved
);
return splash;
}
RenderEditable _renderEditable {
get { return _editableTextKey.currentState.renderEditable; }
}
void _handleTapDown(TapDownDetails details) {
_renderEditable.handleTapDown(details);
_startSplash(details.globalPosition);
}
void _handleSingleTapUp(TapUpDetails details) {
if (widget.enableInteractiveSelection == true) {
_renderEditable.handleTap();
if (cause == SelectionChangedCause.keyboard) {
return false;
_requestKeyboard();
_confirmCurrentSplash();
if (widget.onTap != null) {
widget.onTap();
if (widget.readOnly && _effectiveController.selection.isCollapsed) {
return false;
}
void _handleSingleTapCancel() {
_cancelCurrentSplash();
}
void _handleSingleLongTapStart(LongPressStartDetails details) {
if (widget.selectionEnabled) {
switch (Theme.of(context).platform) {
case RuntimePlatform.IPhonePlayer:
_renderEditable.selectPositionAt(
from: details.globalPosition,
cause: SelectionChangedCause.longPress
);
break;
case RuntimePlatform.Android:
_renderEditable.selectWord(cause: SelectionChangedCause.longPress);
Feedback.forLongPress(context);
break;
}
if (cause == SelectionChangedCause.longPress) {
return true;
_confirmCurrentSplash();
}
void _handleSingleLongTapMoveUpdate(LongPressMoveUpdateDetails details) {
if (widget.selectionEnabled) {
switch (Theme.of(context).platform) {
case RuntimePlatform.IPhonePlayer:
_renderEditable.selectPositionAt(
from: details.globalPosition,
cause: SelectionChangedCause.longPress
);
break;
case RuntimePlatform.Android:
_renderEditable.selectWordsInRange(
from: details.globalPosition - details.offsetFromOrigin,
to: details.globalPosition,
cause: SelectionChangedCause.longPress);
Feedback.forLongPress(context);
break;
}
if (_effectiveController.text.isNotEmpty()) {
return true;
}
void _handleSingleLongTapEnd(LongPressEndDetails details) {
_editableTextKey.currentState.showToolbar();
return false;
void _handleDoubleTapDown(TapDownDetails details) {
if (widget.selectionEnabled) {
_renderEditable.selectWord(cause: SelectionChangedCause.doubleTap);
_editableTextKey.currentState.showToolbar();
void _handleSelectionChanged(TextSelection selection, SelectionChangedCause cause) {
bool willShowSelectionHandles = _shouldShowSelectionHandles(cause);
if (willShowSelectionHandles != _showSelectionHandles) {
setState(() => { _showSelectionHandles = willShowSelectionHandles; });
}
void _handleMouseDragSelectionStart(DragStartDetails details) {
_renderEditable.selectPositionAt(
from: details.globalPosition,
cause: SelectionChangedCause.drag);
_startSplash(details.globalPosition);
//use the code path for android by default
return;
void _handleMouseDragSelectionUpdate(DragStartDetails startDetails,
DragUpdateDetails updateDetails) {
_renderEditable.selectPositionAt(
from: startDetails.globalPosition,
to: updateDetails.globalPosition,
cause: SelectionChangedCause.drag);
}
void _startSplash(Offset globalPosition) {
if (_effectiveFocusNode.hasFocus) {
return;
void _handleSelectionHandleTapped() {
if (_effectiveController.selection.isCollapsed) {
_editableText.toggleToolbar();
InteractiveInkFeature splash = _createInkFeature(globalPosition);
_splashes = _splashes ?? new HashSet<InteractiveInkFeature>();
_splashes.Add(splash);
_currentSplash = splash;
updateKeepAlive();
void _confirmCurrentSplash() {
_currentSplash?.confirm();
_currentSplash = null;
}
void _cancelCurrentSplash() {
_currentSplash?.cancel();
}
protected override bool wantKeepAlive {
get { return _splashes != null && _splashes.isNotEmpty(); }
}
public override void deactivate() {
if (_splashes != null) {
HashSet<InteractiveInkFeature> splashes = _splashes;
_splashes = null;
foreach (InteractiveInkFeature splash in splashes) {
splash.dispose();
}
_currentSplash = null;
void _handleHover(bool hovering) {
if (hovering != _isHovering) {
setState(() => { _isHovering = hovering; });
D.assert(_currentSplash == null);
base.deactivate();
base.build(context); // See AutomaticKeepAliveClientMixin.
!(widget.style != null && widget.style.inherit == false &&
(widget.style.fontSize == null || widget.style.textBaseline == null)),
() => "inherit false style must supply fontSize and textBaseline"
!(widget.style != null && widget.style.inherit == false &&
(widget.style.fontSize == null || widget.style.textBaseline == null)),
() => "inherit false style must supply fontSize and textBaseline"
TextStyle style = themeData.textTheme.subhead.merge(widget.style);
TextStyle style = themeData.textTheme.subtitle1.merge(widget.style);
Brightness keyboardAppearance = widget.keyboardAppearance ?? themeData.primaryColorBrightness;
TextEditingController controller = _effectiveController;
FocusNode focusNode = _effectiveFocusNode;

}
// bool forcePressEnabled = false; // TODO: wait for force press is ready
TextSelectionControls textSelectionControls = MaterialUtils.materialTextSelectionControls;;
TextSelectionControls textSelectionControls = MaterialUtils.materialTextSelectionControls;
;
bool paintCursorAboveText = false;
bool cursorOpacityAnimates = false;
Offset cursorOffset = null;

_forcePressEnabled = false;
textSelectionControls = _MaterialTextSelectionControls.materialTextSelectionControls;
paintCursorAboveText = false;
cursorOpacityAnimates = false;
cursorColor ??= themeData.cursorColor;
key: _editableTextKey,
key: editableTextKey,
readOnly: widget.readOnly,
toolbarOptions: widget.toolbarOptions,
showCursor: widget.showCursor,
showSelectionHandles: _showSelectionHandles,
controller: controller,
focusNode: focusNode,
keyboardType: widget.keyboardType,

autofocus: widget.autofocus,
obscureText: widget.obscureText,
autocorrect: widget.autocorrect,
smartDashesType: widget.smartDashesType,
smartQuotesType: widget.smartQuotesType,
enableSuggestions: widget.enableSuggestions,
maxLines: widget.maxLines,
minLines: widget.minLines,
expands: widget.expands,

onSelectionChanged: _handleSelectionChanged,
onEditingComplete: widget.onEditingComplete,
onSubmitted: widget.onSubmitted,
onSelectionHandleTapped: _handleSelectionHandleTapped,
selectionHeightStyle: widget.selectionHeightStyle,
selectionWidthStyle: widget.selectionWidthStyle,
backgroundCursorColor: new Color(0xFF8E8E93),// TODO: CupertinoColors.inactiveGray,
backgroundCursorColor: CupertinoColors.inactiveGray,
scrollController: widget.scrollController,
scrollPhysics: widget.scrollPhysics
)
);

decoration: _getEffectiveDecoration(),
baseStyle: widget.style,
textAlign: widget.textAlign,
textAlignVertical: widget.textAlignVertical,
isHovering: _isHovering,
isFocused: focusNode.hasFocus,
isEmpty: controller.value.text.isEmpty(),
expands: widget.expands,

);
}
void onEnter(PointerEnterEvent pEvent) {
_handleHover(true);
}
void onExit(PointerExitEvent pEvent) {
_handleHover(false);
}
ignoring: !(widget.enabled ?? widget.decoration?.enabled ?? true),
child: new TextSelectionGestureDetector(
onTapDown: _handleTapDown,
// onForcePressStart: forcePressEnabled ? this._handleForcePressStarted : null, // TODO: Remove this when force press is added
onSingleTapUp: _handleSingleTapUp,
onSingleTapCancel: _handleSingleTapCancel,
onSingleLongTapStart: _handleSingleLongTapStart,
onSingleLongTapMoveUpdate: _handleSingleLongTapMoveUpdate,
onSingleLongTapEnd: _handleSingleLongTapEnd,
onDoubleTapDown: _handleDoubleTapDown,
onDragSelectionStart: _handleMouseDragSelectionStart,
onDragSelectionUpdate: _handleMouseDragSelectionUpdate,
behavior: HitTestBehavior.translucent,
child: child
ignoring: !_isEnabled,
child: new MouseRegion(
onEnter: onEnter,
onExit: onExit,
child: new AnimatedBuilder(
animation: controller,
builder: (BuildContext context, Widget child) => { return child; },
child: _selectionGestureDetectorBuilder.buildGestureDetector(
behavior: HitTestBehavior.translucent,
child: child
)
)
)
);
}

32
com.unity.uiwidgets/Runtime/material/text_form_field.cs


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

StrutStyle strutStyle = null,
TextDirection? textDirection = null,
TextAlign textAlign = TextAlign.left,
TextAlignVertical textAlignVertical = null,
bool readOnly = false,
ToolbarOptions toolbarOptions = null,
bool? showCursor = null,
SmartDashesType? smartDashesType = null,
SmartQuotesType? smartQuotesType = null,
bool enableSuggestions = true,
bool autovalidate = false,
bool maxLengthEnforced = true,
int? maxLines = 1,

ValueChanged<string> onChanged = null,
GestureTapCallback onTap = null,
VoidCallback onEditingComplete = null,
ValueChanged<string> onFieldSubmitted = null,
FormFieldSetter<string> onSaved = null,

Brightness? keyboardAppearance = null,
EdgeInsets scrollPadding = null,
bool enableInteractiveSelection = true,
InputCounterWidgetBuilder buildCounter = null
InputCounterWidgetBuilder buildCounter = null,
ScrollPhysics scrollPhysics = null
) : base(
key: key,
initialValue: controller != null ? controller.text : (initialValue ?? ""),

_TextFormFieldState state = (_TextFormFieldState) field;
InputDecoration effectiveDecoration = (decoration ?? new InputDecoration())
.applyDefaults(Theme.of(field.context).inputDecorationTheme);
void onChangedHandler(string value) {
if (onChanged != null) {
onChanged(value);
}
field.didChange(value);
}
return new TextField(
controller: state._effectiveController,
focusNode: focusNode,

style: style,
strutStyle: strutStyle,
textAlign: textAlign,
textAlignVertical: textAlignVertical,
toolbarOptions: toolbarOptions,
readOnly: readOnly,
showCursor: showCursor,
smartDashesType: smartDashesType ?? (obscureText ? SmartDashesType.disabled : SmartDashesType.enabled),
smartQuotesType: smartQuotesType ?? (obscureText ? SmartQuotesType.disabled : SmartQuotesType.enabled),
enableSuggestions: enableSuggestions,
onChanged: field.didChange,
onChanged: onChangedHandler,
onTap: onTap,
onEditingComplete: onEditingComplete,
onSubmitted: onFieldSubmitted,
inputFormatters: inputFormatters,

cursorColor: cursorColor,
scrollPadding: scrollPadding ?? EdgeInsets.all(20.0f),
scrollPhysics: scrollPhysics,
keyboardAppearance: keyboardAppearance,
enableInteractiveSelection: enableInteractiveSelection,
buildCounter: buildCounter

() => "minLines can't be greater than maxLines");
D.assert(!expands || (maxLines == null && minLines == null),
() => "minLines and maxLines must be null when expands is true.");
D.assert(!obscureText || maxLines == 1, () => "Obscured fields cannot be multiline.");
D.assert(maxLength == null || maxLength > 0);
this.controller = controller;
}

615
com.unity.uiwidgets/Runtime/material/text_selection.cs


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

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;
class _TextSelectionToolbar : StatelessWidget {
public class _TextSelectionToolbar : StatefulWidget {
VoidCallback handleCopy = null, VoidCallback handlePaste = null, VoidCallback handleSelectAll = null) : base(key: key) {
VoidCallback handleCopy = null, VoidCallback handlePaste = null, VoidCallback handleSelectAll = null, bool isAbove = false) : base(key: key) {
this.isAbove = isAbove;
}
public readonly VoidCallback handleCut;

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);
}
MaterialLocalizations localizations = MaterialLocalizations.of(context);
MaterialLocalizations localizations = MaterialLocalizations.of(context);
if (handleCut != null) {
items.Add(new FlatButton(child: new Text(localizations.cutButtonLabel), onPressed: handleCut));
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
)
);
if (handleCopy != null) {
items.Add(new FlatButton(child: new Text(localizations.copyButtonLabel), onPressed: handleCopy));
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;
if (handlePaste != null) {
items.Add(new FlatButton(child: new Text(localizations.pasteButtonLabel), onPressed: handlePaste));
protected override void performLayout() {
child.layout(constraints.loosen(), parentUsesSize: true);
if (!overflowOpen && _closedWidth == null) {
_closedWidth = child.size.width;
if (handleSelectAll != null) {
items.Add(new FlatButton(child: new Text(localizations.selectAllButtonLabel),
onPressed: handleSelectAll));
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);
);
}
return new Material(
elevation: 1.0f,
child: new Container(
color: new Color(0xFFEFEFEF),
height: 44.0f, child: new Row(mainAxisSize: MainAxisSize.min, children: items))
);
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() => new _TextSelectionToolbarItemsElement(this);
}
class _ToolbarParentData : ContainerBoxParentData<RenderBox> {
public bool shouldPaint;
public override string ToString() => $"{base.ToString()}; shouldPaint={shouldPaint}";
class _TextSelectionToolbarLayout : SingleChildLayoutDelegate {
internal _TextSelectionToolbarLayout(Size screenSize = null, Rect globalEditableRegion = null,
Offset position = null) {
this.screenSize = screenSize;
this.globalEditableRegion = globalEditableRegion;
this.position = position;
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 children.Where(_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++;
public readonly Size screenSize;
public readonly Rect globalEditableRegion;
public readonly Offset position;
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;
public override BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
return constraints.loosen();
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 Offset getPositionForChild(Size size, Size childSize) {
Offset globalPosition = globalEditableRegion.topLeft + position;
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;
}
float x = globalPosition.dx - childSize.width / 2.0f;
float y = globalPosition.dy - childSize.height;
context.paintChild(child, childParentData.offset + offset);
});
}
if (x < MaterialUtils._kToolbarScreenPadding) {
x = MaterialUtils._kToolbarScreenPadding;
public override void setupParentData(RenderObject child) {
if (!(child.parentData is _ToolbarParentData)) {
child.parentData = new _ToolbarParentData();
else if (x + childSize.width > screenSize.width - MaterialUtils._kToolbarScreenPadding) {
x = screenSize.width - childSize.width - MaterialUtils._kToolbarScreenPadding;
}
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;
}
}
if (y < MaterialUtils._kToolbarScreenPadding) {
y = MaterialUtils._kToolbarScreenPadding;
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;
else if (y + childSize.height > screenSize.height - MaterialUtils._kToolbarScreenPadding) {
y = screenSize.height - childSize.height - MaterialUtils._kToolbarScreenPadding;
if (position + width / 2.0f > max) {
return max - width;
return position - width / 2.0f;
}
return new Offset(x, y);
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
);
return position != ((_TextSelectionToolbarLayout) oldDelegate).position;
return anchor != ((_TextSelectionToolbarLayout) oldDelegate).anchor;
}
}

Paint paint = new Paint();
paint.color = color;
float radius = size.width / 2.0f;
canvas.drawCircle(new Offset(radius, radius), radius, paint);
canvas.drawRect(Rect.fromLTWH(0.0f, 0.0f, radius, radius), paint);
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 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);
}
}
Offset position, List<TextSelectionPoint> endpoints, 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: 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
)
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
);
),
}
);
Widget handle = new Padding(
padding: EdgeInsets.only(right: 26.0f, bottom: 26.0f),
child: new SizedBox(
width: MaterialUtils._kHandleSize,
height: MaterialUtils._kHandleSize,
child: new CustomPaint(
painter: new _TextSelectionHandlePainter(
color: Theme.of(context).textSelectionHandleColor
)
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(

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

8
com.unity.uiwidgets/Runtime/widgets/text_selection.cs


);
}
}
public abstract class TextSelectionGestureDetectorBuilderDelegate {
public interface TextSelectionGestureDetectorBuilderDelegate {
public GlobalKey<EditableTextState> editableTextKey { get; }
public bool forcePressEnabled {
GlobalKey<EditableTextState> editableTextKey { get; }
bool forcePressEnabled {
public bool selectionEnabled { get; }
bool selectionEnabled { get; }
}
public class TextSelectionGestureDetector : StatefulWidget {

正在加载...
取消
保存