浏览代码

Merge branch 'yczhang' into match_flutter12

/main
Yuncong Zhang 6 年前
当前提交
cac6a69b
共有 36 个文件被更改,包括 2332 次插入220 次删除
  1. 20
      Runtime/foundation/change_notifier.cs
  2. 9
      Runtime/gestures/team.cs
  3. 3
      Runtime/material/animated_icons/animated_icons.cs
  4. 4
      Runtime/material/app.cs
  5. 10
      Runtime/material/chip.cs
  6. 1
      Runtime/material/chip_theme.cs
  7. 2
      Runtime/material/float_action_button.cs
  8. 20
      Runtime/material/theme_data.cs
  9. 4
      Runtime/painting/notched_shapes.cs
  10. 4
      Runtime/painting/text_painter.cs
  11. 3
      Runtime/rendering/binding.cs
  12. 353
      Runtime/rendering/editable.cs
  13. 8
      Runtime/rendering/layer.cs
  14. 2
      Runtime/rendering/object.cs
  15. 11
      Runtime/rendering/proxy_box.cs
  16. 6
      Runtime/rendering/sliver.cs
  17. 2
      Runtime/rendering/sliver_multi_box_adaptor.cs
  18. 10
      Runtime/rendering/sliver_persistent_header.cs
  19. 3
      Runtime/rendering/table.cs
  20. 51
      Runtime/rendering/viewport.cs
  21. 79
      Runtime/service/text_input.cs
  22. 10
      Runtime/ui/painting/canvas.cs
  23. 41
      Runtime/ui/painting/path.cs
  24. 2
      Runtime/widgets/binding.cs
  25. 148
      Runtime/widgets/editable_text.cs
  26. 22
      Runtime/widgets/focus_scope.cs
  27. 24
      Runtime/widgets/text_selection.cs
  28. 1
      Samples/ReduxSample/ObjectFinder/ObjectFinderApp.cs
  29. 39
      Samples/UIWidgetSample/MaterialSample.cs
  30. 16
      Samples/UIWidgetSample/TextInputSample.cs
  31. 7
      Runtime/foundation/constants.cs
  32. 3
      Runtime/foundation/constants.cs.meta
  33. 805
      Runtime/material/slider.cs
  34. 11
      Runtime/material/slider.cs.meta
  35. 807
      Runtime/material/slider_theme.cs
  36. 11
      Runtime/material/slider_theme.cs.meta

20
Runtime/foundation/change_notifier.cs


}
}
class _MergingListenable : ChangeNotifier {
class _MergingListenable : Listenable {
foreach (Listenable child in _children) {
if (child != null) {
child.addListener(this.notifyListeners);
}
}
public override void dispose() {
public void addListener(VoidCallback listener) {
if (child != null) {
child.removeListener(this.notifyListeners);
}
child?.addListener(listener);
}
base.dispose();
public void removeListener(VoidCallback listener) {
foreach (Listenable child in this._children) {
child?.removeListener(listener);
}
}
public override string ToString() {

9
Runtime/gestures/team.cs


D.assert(this._pointer == pointer);
this._members.Add(member);
this._entry = this._entry ?? this._owner._gestureArena.add(pointer, this);
this._entry = this._entry ?? GestureBinding.instance.gestureArena.add(pointer, this);
return new _CombiningGestureArenaEntry(this, member);
}

}
}
else {
D.assert(disposition == GestureDisposition.accepted);
this._winner = this._winner ?? this._owner.captain ?? member;
this._entry.resolve(disposition);
}

public class GestureArenaTeam {
public GestureArenaTeam(GestureArenaManager gestureArena) {
this._gestureArena = gestureArena;
}
internal readonly GestureArenaManager _gestureArena;
internal readonly Dictionary<int, _CombiningGestureArenaMember> _combiners =
new Dictionary<int, _CombiningGestureArenaMember>();

3
Runtime/material/animated_icons/animated_icons.cs


Offset controlPoint1 = AnimatedIconUtils._interpolate<Offset>(this.controlPoints1, progress, Offset.lerp);
Offset controlPoint2 = AnimatedIconUtils._interpolate<Offset>(this.controlPoints2, progress, Offset.lerp);
Offset targetPoint = AnimatedIconUtils._interpolate<Offset>(this.targetPoints, progress, Offset.lerp);
// TODO: replace with cubicTo
path.bezierTo(
path.cubicTo(
controlPoint1.dx, controlPoint1.dy,
controlPoint2.dx, controlPoint2.dy,
targetPoint.dx, targetPoint.dy

4
Runtime/material/app.cs


this.title = title;
this.color = color;
this.theme = theme;
this.dartTheme = darkTheme;
this.darkTheme = darkTheme;
this.locale = locale;
this.localizationsDelegates = localizationsDelegates;
this.localeListResolutionCallback = localeListResolutionCallback;

public readonly ThemeData theme;
public readonly ThemeData dartTheme;
public readonly ThemeData darkTheme;
public readonly Color color;

10
Runtime/material/chip.cs


Color selectedShadowColor = null,
ShapeBorder avatarBorder = null
) : base(key: key) {
D.assert(selected != null);
D.assert(isEnabled != null);
D.assert(clipBehavior != null);
D.assert(pressElevation == null || pressElevation >= 0.0f);
D.assert(elevation == null || elevation >= 0.0f);
this._avatarBorder = avatarBorder ?? new CircleBorder();

float? _elevation;
public Color selectedShadowColor {
get { return this._selectedShadowColor; }
}
Color _selectedShadowColor;
public ShapeBorder avatarBorder {
get { return this._avatarBorder; }
}

) : base(key: key) {
D.assert(selected != null);
D.assert(label != null);
D.assert(clipBehavior != null);
D.assert(pressElevation == null || pressElevation >= 0.0f);
D.assert(elevation == null || elevation >= 0.0f);
this._avatarBorder = avatarBorder ?? new CircleBorder();

1
Runtime/material/chip_theme.cs


}
public static ChipThemeData lerp(ChipThemeData a, ChipThemeData b, float t) {
D.assert(t != null);
if (a == null && b == null) {
return null;
}

2
Runtime/material/float_action_button.cs


) {
D.assert(elevation >= 0.0f);
D.assert(highlightElevation >= 0.0f);
D.assert(disabledElevation != null && disabledElevation >= 0.0f);
D.assert(disabledElevation == null || disabledElevation >= 0.0f);
D.assert(icon != null);
D.assert(label != null);
heroTag = heroTag ?? new _DefaultHeroTag();

20
Runtime/material/theme_data.cs


IconThemeData iconTheme = null,
IconThemeData primaryIconTheme = null,
IconThemeData accentIconTheme = null,
SliderThemeData sliderTheme = null,
TabBarTheme tabBarTheme = null,
CardTheme cardTheme = null,
ChipThemeData chipTheme = null,

? ThemeDataUtils._kDarkThemeSplashColor
: ThemeDataUtils._kLightThemeSplashColor);
sliderTheme = sliderTheme ?? SliderThemeData.fromPrimaryColors(
primaryColor: primaryColor,
primaryColorLight: primaryColorLight,
primaryColorDark: primaryColorDark,
valueIndicatorTextStyle: accentTextTheme.body2);
tabBarTheme = tabBarTheme ?? new TabBarTheme();
cardTheme = cardTheme ?? new CardTheme();
chipTheme = chipTheme ?? ChipThemeData.fromDefaults(

D.assert(textTheme != null);
D.assert(primaryTextTheme != null);
D.assert(accentTextTheme != null);
D.assert(sliderTheme != null);
D.assert(iconTheme != null);
D.assert(primaryIconTheme != null);
D.assert(accentIconTheme != null);

this.iconTheme = iconTheme;
this.primaryIconTheme = primaryIconTheme;
this.accentIconTheme = accentIconTheme;
this.sliderTheme = sliderTheme;
this.tabBarTheme = tabBarTheme;
this.cardTheme = cardTheme;
this.chipTheme = chipTheme;

TextTheme textTheme = null,
TextTheme primaryTextTheme = null,
TextTheme accentTextTheme = null,
SliderThemeData sliderTheme = null,
InputDecorationTheme inputDecorationTheme = null,
IconThemeData iconTheme = null,
IconThemeData primaryIconTheme = null,

D.assert(textTheme != null);
D.assert(primaryTextTheme != null);
D.assert(accentTextTheme != null);
D.assert(sliderTheme != null);
D.assert(inputDecorationTheme != null);
D.assert(iconTheme != null);
D.assert(primaryIconTheme != null);

iconTheme: iconTheme,
primaryIconTheme: primaryIconTheme,
accentIconTheme: accentIconTheme,
sliderTheme: sliderTheme,
tabBarTheme: tabBarTheme,
cardTheme: cardTheme,
chipTheme: chipTheme,

public readonly TextTheme accentTextTheme;
public readonly SliderThemeData sliderTheme;
public readonly InputDecorationTheme inputDecorationTheme;
public readonly IconThemeData iconTheme;

TextTheme textTheme = null,
TextTheme primaryTextTheme = null,
TextTheme accentTextTheme = null,
SliderThemeData sliderTheme = null,
InputDecorationTheme inputDecorationTheme = null,
IconThemeData iconTheme = null,
IconThemeData primaryIconTheme = null,

textTheme: textTheme ?? this.textTheme,
primaryTextTheme: primaryTextTheme ?? this.primaryTextTheme,
accentTextTheme: accentTextTheme ?? this.accentTextTheme,
sliderTheme: sliderTheme ?? this.sliderTheme,
inputDecorationTheme: this.inputDecorationTheme ?? this.inputDecorationTheme,
iconTheme: iconTheme ?? this.iconTheme,
primaryIconTheme: primaryIconTheme ?? this.primaryIconTheme,

iconTheme: IconThemeData.lerp(a.iconTheme, b.iconTheme, t),
primaryIconTheme: IconThemeData.lerp(a.primaryIconTheme, b.primaryIconTheme, t),
accentIconTheme: IconThemeData.lerp(a.accentIconTheme, b.accentIconTheme, t),
sliderTheme: SliderThemeData.lerp(a.sliderTheme, b.sliderTheme, t),
tabBarTheme: TabBarTheme.lerp(a.tabBarTheme, b.tabBarTheme, t),
cardTheme: CardTheme.lerp(a.cardTheme, b.cardTheme, t),
chipTheme: ChipThemeData.lerp(a.chipTheme, b.chipTheme, t),

other.textTheme == this.textTheme &&
other.primaryTextTheme == this.primaryTextTheme &&
other.accentTextTheme == this.accentTextTheme &&
other.sliderTheme == this.sliderTheme &&
other.inputDecorationTheme == this.inputDecorationTheme &&
other.toggleableActiveColor == this.toggleableActiveColor &&
other.iconTheme == this.iconTheme &&

hashCode = (hashCode * 397) ^ this.iconTheme.GetHashCode();
hashCode = (hashCode * 397) ^ this.primaryIconTheme.GetHashCode();
hashCode = (hashCode * 397) ^ this.accentIconTheme.GetHashCode();
hashCode = (hashCode * 397) ^ this.sliderTheme.GetHashCode();
hashCode = (hashCode * 397) ^ this.tabBarTheme.GetHashCode();
hashCode = (hashCode * 397) ^ this.cardTheme.GetHashCode();
hashCode = (hashCode * 397) ^ this.chipTheme.GetHashCode();

properties.add(new DiagnosticsProperty<IconThemeData>("iconTheme", this.iconTheme));
properties.add(new DiagnosticsProperty<IconThemeData>("primaryIconTheme", this.primaryIconTheme));
properties.add(new DiagnosticsProperty<IconThemeData>("accentIconTheme", this.accentIconTheme));
properties.add(new DiagnosticsProperty<SliderThemeData>("sliderTheme", this.sliderTheme));
properties.add(new DiagnosticsProperty<TabBarTheme>("tabBarTheme", this.tabBarTheme));
properties.add(new DiagnosticsProperty<CardTheme>("cardTheme", this.cardTheme));
properties.add(new DiagnosticsProperty<ChipThemeData>("chipTheme", this.chipTheme));

4
Runtime/painting/notched_shapes.cs


Path ret = new Path();
ret.moveTo(host.left, host.top);
ret.lineTo(p[0].dx, p[0].dy);
ret.quadTo(p[1].dx, p[1].dy, p[2].dx, p[2].dy);
ret.quadraticBezierTo(p[1].dx, p[1].dy, p[2].dx, p[2].dy);
ret.quadTo(p[4].dx, p[4].dy, p[5].dx, p[5].dy);
ret.quadraticBezierTo(p[4].dx, p[4].dy, p[5].dx, p[5].dy);
ret.lineTo(host.right, host.top);
ret.lineTo(host.right, host.bottom);
ret.lineTo(host.left, host.bottom);

4
Runtime/painting/text_painter.cs


float _lastMinWidth;
float _lastMaxWidth;
public TextPainter(TextSpan text,
public TextPainter(TextSpan text = null,
TextAlign textAlign = TextAlign.left,
TextDirection textDirection = TextDirection.ltr,
float textScaleFactor = 1.0f,

public TextSpan text {
get { return this._text; }
set {
if (this.text.Equals(value)) {
if ((this._text == null && value == null) || (this._text != null && this.text.Equals(value))) {
return;
}

3
Runtime/rendering/binding.cs


protected virtual void handleTextScaleFactorChanged() {
}
protected virtual void handlePlatformBrightnessChanged() {
}
protected virtual ViewConfiguration createViewConfiguration() {
var devicePixelRatio = Window.instance.devicePixelRatio;
return new ViewConfiguration(

353
Runtime/rendering/editable.cs


tap,
doubleTap,
longPress,
forcePress,
keyboard,
drag
}

public static readonly char obscuringCharacter = '•';
static readonly float _kCaretGap = 1.0f;
static readonly float _kCaretHeightOffset = 2.0f;
static readonly Offset _kFloatingCaretSizeIncrease = new Offset(0.5f, 1.0f);
static readonly float _kFloatingCaretRadius = 1.0f;
TextPainter _textPainter;
Color _cursorColor;

bool _hasVisualOverflow = false;
Offset _lastTapDownPosition;
public RenderEditable(TextSpan text, TextDirection textDirection, ViewportOffset offset,
public RenderEditable(
TextSpan text,
TextDirection textDirection,
ViewportOffset offset,
TextAlign textAlign = TextAlign.left, float textScaleFactor = 1.0f, Color cursorColor = null,
bool? hasFocus = null, int? maxLines = 1, Color selectionColor = null,
TextSelection selection = null, bool obscureText = false, SelectionChangedHandler onSelectionChanged = null,
CaretChangedHandler onCaretChanged = null, bool ignorePointer = false,
TextAlign textAlign = TextAlign.left,
float textScaleFactor = 1.0f,
Color cursorColor = null,
Color backgroundCursorColor = null,
bool? hasFocus = null,
int? maxLines = 1,
Color selectionColor = null,
TextSelection selection = null,
bool obscureText = false,
SelectionChangedHandler onSelectionChanged = null,
CaretChangedHandler onCaretChanged = null,
bool ignorePointer = false,
bool enableInteractiveSelection = true,
bool? enableInteractiveSelection = null,
bool paintCursorAboveText = false,
Offset cursorOffset = null,
float devicePixelRatio = 1.0f,
EdgeInsets floatingCursorAddedMargin = null,
floatingCursorAddedMargin = floatingCursorAddedMargin ?? EdgeInsets.fromLTRB(4, 4, 4, 5);
this._showCursor = showCursor ?? new ValueNotifier<bool>(false);
this._hasFocus = hasFocus ?? false;
this._maxLines = maxLines;

this._doubleTap.onDoubleTap = this._handleDoubleTap;
this._longPress = new LongPressGestureRecognizer(debugOwner: this);
this._longPress.onLongPress = this._handleLongPress;
this._backgroundCursorColor = backgroundCursorColor;
this._paintCursorOnTop = paintCursorAboveText;
this._cursorOffset = cursorOffset;
this._floatingCursorAddedMargin = floatingCursorAddedMargin;
this._devicePixelRatio = devicePixelRatio;
}
public float devicePixelRatio {
get { return this._devicePixelRatio; }
set {
if (this.devicePixelRatio == value) {
return;
}
this._devicePixelRatio = value;
this.markNeedsTextLayout();
}
}
float _devicePixelRatio;
public Color backgroundCursorColor {
get { return this._backgroundCursorColor; }
set {
if (this.backgroundCursorColor == value) {
return;
}
this._backgroundCursorColor = value;
this.markNeedsPaint();
}
}
Color _backgroundCursorColor;
public bool paintCursorAboveText {
get { return this._paintCursorOnTop; }
set {
if (this._paintCursorOnTop == value) {
return;
}
this._paintCursorOnTop = value;
this.markNeedsLayout();
}
}
bool _paintCursorOnTop;
public Offset cursorOffset {
get { return this._cursorOffset; }
set {
if (this._cursorOffset == value) {
return;
}
this._cursorOffset = value;
this.markNeedsLayout();
}
}
Offset _cursorOffset;
public EdgeInsets floatingCursorAddedMargin {
get { return this._floatingCursorAddedMargin; }
set {
if (this._floatingCursorAddedMargin == value) {
return;
}
this._floatingCursorAddedMargin = value;
this.markNeedsPaint();
}
}
EdgeInsets _floatingCursorAddedMargin;
bool _floatingCursorOn = false;
Offset _floatingCursorOffset;
TextPosition _floatingCursorTextPosition;
public bool selectionEnabled {
get { return this.enableInteractiveSelection ?? !this.obscureText; }
}
public bool obscureText {

}
}
bool _enableInteractiveSelection;
bool? _enableInteractiveSelection;
public bool enableInteractiveSelection {
public bool? enableInteractiveSelection {
get { return this._enableInteractiveSelection; }
set {
if (this._enableInteractiveSelection == value) {

public Rect getLocalRectForCaret(TextPosition caretPosition) {
this._layoutText(this.constraints.maxWidth);
var caretOffset = this._textPainter.getOffsetForCaret(caretPosition, this._caretPrototype);
return Rect.fromLTWH(0.0f, 0.0f, this.cursorWidth, this.preferredLineHeight)
Rect rect = Rect.fromLTWH(0.0f, 0.0f, this.cursorWidth, this.preferredLineHeight)
if (this._cursorOffset != null) {
rect = rect.shift(this._cursorOffset);
}
return rect.shift(this._getPixelPerfectCursorOffset(rect));
}
public TextPosition getPositionDown(TextPosition position) {

protected override float computeMaxIntrinsicWidth(float height) {
this._layoutText(float.PositiveInfinity);
return this._textPainter.maxIntrinsicWidth;
return this._textPainter.maxIntrinsicWidth + this.cursorWidth;
}
protected override float computeMinIntrinsicHeight(float width) {

}
public void handleTapDown(TapDownDetails details) {
this._lastTapDownPosition = details.globalPosition + -this._paintOffset;
this._lastTapDownPosition = details.globalPosition - this._paintOffset;
if (!Application.isMobilePlatform) {
this.selectPosition(SelectionChangedCause.tap);
}

}
void selectPosition(SelectionChangedCause? cause = null) {
D.assert(cause != null);
this._layoutText(this.constraints.maxWidth);
D.assert(this._lastTapDownPosition != null);
if (this.onSelectionChanged != null) {
TextPosition position =
this._textPainter.getPositionForOffset(this.globalToLocal(this._lastTapDownPosition));
this.onSelectionChanged(TextSelection.fromPosition(position), this, cause.Value);
}
this.selectPositionAt(from: this._lastTapDownPosition, cause: cause);
this.selectWordsInRange(from: this._lastTapDownPosition, cause: cause);
}
void selectWordsInRange(Offset from = null, Offset to = null, SelectionChangedCause? cause = null) {
D.assert(cause != null);
D.assert(from != null);
D.assert(this._lastTapDownPosition != null);
TextPosition position =
this._textPainter.getPositionForOffset(this.globalToLocal(this._lastTapDownPosition));
this.onSelectionChanged(this._selectWordAtOffset(position), this, cause.Value);
TextPosition firstPosition =
this._textPainter.getPositionForOffset(this.globalToLocal(from - this._paintOffset));
TextSelection firstWord = this._selectWordAtOffset(firstPosition);
TextSelection lastWord = to == null
? firstWord
: this._selectWordAtOffset(
this._textPainter.getPositionForOffset(this.globalToLocal(to - this._paintOffset)));
this.onSelectionChanged(
new TextSelection(
baseOffset: firstWord.baseOffset,
extentOffset: lastWord.extentOffset,
affinity: firstWord.affinity),
this,
cause.Value);
}
}

this._textLayoutLastWidth = constraintWidth;
}
Rect _getCaretPrototype {
get {
switch (Application.platform) {
case RuntimePlatform.IPhonePlayer:
return Rect.fromLTWH(0.0f, -_kCaretHeightOffset + 0.5f, this.cursorWidth,
this.preferredLineHeight + 2.0f);
default:
return Rect.fromLTWH(0.0f, _kCaretHeightOffset, this.cursorWidth,
this.preferredLineHeight - 2.0f * _kCaretHeightOffset);
}
}
}
this._caretPrototype = Rect.fromLTWH(0.0f, _kCaretHeightOffset, this.cursorWidth,
this.preferredLineHeight - 2.0f * _kCaretHeightOffset);
this._caretPrototype = this._getCaretPrototype;
this._selectionRects = null;
var textPainterSize = this._textPainter.size;

// describeSemanticsConfiguration todo
void _paintCaret(Canvas canvas, Offset effectiveOffset) {
Offset _getPixelPerfectCursorOffset(Rect caretRect) {
Offset caretPosition = this.localToGlobal(caretRect.topLeft);
float pixelMultiple = 1.0f / this._devicePixelRatio;
int quotientX = (caretPosition.dx / pixelMultiple).round();
int quotientY = (caretPosition.dy / pixelMultiple).round();
float pixelPerfectOffsetX = quotientX * pixelMultiple - caretPosition.dx;
float pixelPerfectOffsetY = quotientY * pixelMultiple - caretPosition.dy;
return new Offset(pixelPerfectOffsetX, pixelPerfectOffsetY);
}
void _paintCaret(Canvas canvas, Offset effectiveOffset, TextPosition textPosition) {
var caretOffset = this._textPainter.getOffsetForCaret(this._selection.extendPos, this._caretPrototype);
var paint = new Paint() {color = this._cursorColor};
var caretRect = this._caretPrototype.shift(caretOffset + effectiveOffset);
var caretOffset = this._textPainter.getOffsetForCaret(textPosition, this._caretPrototype);
var paint = new Paint() {color = this._floatingCursorOn ? this.backgroundCursorColor : this._cursorColor};
Rect caretRect = this._caretPrototype.shift(caretOffset + effectiveOffset);
if (this._cursorOffset != null) {
caretRect = caretRect.shift(this._cursorOffset);
}
if (this.cursorRadius == null) {
canvas.drawRect(caretRect, paint);

}
}
public void setFloatingCursor(FloatingCursorDragState? state, Offset boundedOffset, TextPosition lastTextPosition,
float? resetLerpValue = null) {
D.assert(boundedOffset != null);
D.assert(lastTextPosition != null);
if (state == FloatingCursorDragState.Start) {
this._relativeOrigin = new Offset(0, 0);
this._previousOffset = null;
this._resetOriginOnBottom = false;
this._resetOriginOnTop = false;
this._resetOriginOnRight = false;
this._resetOriginOnBottom = false;
}
this._floatingCursorOn = state != FloatingCursorDragState.End;
this._resetFloatingCursorAnimationValue = resetLerpValue;
if (this._floatingCursorOn) {
this._floatingCursorOffset = boundedOffset;
this._floatingCursorTextPosition = lastTextPosition;
}
this.markNeedsPaint();
}
void _paintFloatingCaret(Canvas canvas, Offset effectiveOffset) {
D.assert(this._textLayoutLastWidth == this.constraints.maxWidth);
D.assert(this._floatingCursorOn);
Paint paint = new Paint() {color = this._cursorColor.withOpacity(0.75f)};
float sizeAdjustmentX = _kFloatingCaretSizeIncrease.dx;
float sizeAdjustmentY = _kFloatingCaretSizeIncrease.dy;
if (this._resetFloatingCursorAnimationValue != null) {
sizeAdjustmentX =
MathUtils.lerpFloat(sizeAdjustmentX, 0f, this._resetFloatingCursorAnimationValue.Value);
sizeAdjustmentY =
MathUtils.lerpFloat(sizeAdjustmentY, 0f, this._resetFloatingCursorAnimationValue.Value);
}
Rect floatingCaretPrototype = Rect.fromLTRB(
this._caretPrototype.left - sizeAdjustmentX,
this._caretPrototype.top - sizeAdjustmentY,
this._caretPrototype.right + sizeAdjustmentX,
this._caretPrototype.bottom + sizeAdjustmentY
);
Rect caretRect = floatingCaretPrototype.shift(effectiveOffset);
Radius floatingCursorRadius = Radius.circular(_kFloatingCaretRadius);
RRect caretRRect = RRect.fromRectAndRadius(caretRect, floatingCursorRadius);
canvas.drawRRect(caretRRect, paint);
}
Offset _relativeOrigin = new Offset(0f, 0f);
Offset _previousOffset;
bool _resetOriginOnLeft = false;
bool _resetOriginOnRight = false;
bool _resetOriginOnTop = false;
bool _resetOriginOnBottom = false;
float? _resetFloatingCursorAnimationValue;
public Offset calculateBoundedFloatingCursorOffset(Offset rawCursorOffset) {
Offset deltaPosition = new Offset(0f, 0f);
float topBound = -this.floatingCursorAddedMargin.top;
float bottomBound = this._textPainter.height - this.preferredLineHeight +
this.floatingCursorAddedMargin.bottom;
float leftBound = -this.floatingCursorAddedMargin.left;
float rightBound = this._textPainter.width + this.floatingCursorAddedMargin.right;
if (this._previousOffset != null) {
deltaPosition = rawCursorOffset - this._previousOffset;
}
if (this._resetOriginOnLeft && deltaPosition.dx > 0) {
this._relativeOrigin = new Offset(rawCursorOffset.dx - leftBound, this._relativeOrigin.dy);
this._resetOriginOnLeft = false;
}
else if (this._resetOriginOnRight && deltaPosition.dx < 0) {
this._relativeOrigin = new Offset(rawCursorOffset.dx - rightBound, this._relativeOrigin.dy);
this._resetOriginOnRight = false;
}
if (this._resetOriginOnTop && deltaPosition.dy > 0) {
this._relativeOrigin = new Offset(this._relativeOrigin.dx, rawCursorOffset.dy - topBound);
this._resetOriginOnTop = false;
}
else if (this._resetOriginOnBottom && deltaPosition.dy < 0) {
this._relativeOrigin = new Offset(this._relativeOrigin.dx, rawCursorOffset.dy - bottomBound);
this._resetOriginOnBottom = false;
}
float currentX = rawCursorOffset.dx - this._relativeOrigin.dx;
float currentY = rawCursorOffset.dy - this._relativeOrigin.dy;
float adjustedX = Mathf.Min(Mathf.Max(currentX, leftBound), rightBound);
float adjustedY = Mathf.Min(Mathf.Max(currentY, topBound), bottomBound);
Offset adjustedOffset = new Offset(adjustedX, adjustedY);
if (currentX < leftBound && deltaPosition.dx < 0) {
this._resetOriginOnLeft = true;
}
else if (currentX > rightBound && deltaPosition.dx > 0) {
this._resetOriginOnRight = true;
}
if (currentY < topBound && deltaPosition.dy < 0) {
this._resetOriginOnTop = true;
}
else if (currentY > bottomBound && deltaPosition.dy > 0) {
this._resetOriginOnBottom = true;
}
this._previousOffset = rawCursorOffset;
return adjustedOffset;
}
void _paintSelection(Canvas canvas, Offset effectiveOffset) {
D.assert(this._textLayoutLastWidth == this.constraints.maxWidth);
D.assert(this._selectionRects != null);

D.assert(this._textLayoutLastWidth == this.constraints.maxWidth);
var effectiveOffset = offset + this._paintOffset;
if (this._selection != null && this._selection.isValid) {
bool showSelection = false;
bool showCaret = false;
if (this._selection != null && !this._floatingCursorOn) {
this._paintCaret(context.canvas, effectiveOffset);
showCaret = true;
this._selectionRects =
this._selectionRects ?? this._textPainter.getBoxesForSelection(this._selection);
this._paintSelection(context.canvas, effectiveOffset);
showSelection = true;
this._textPainter.paint(context.canvas, effectiveOffset);
if (showSelection) {
this._selectionRects = this._selectionRects ?? this._textPainter.getBoxesForSelection(this._selection);
this._paintSelection(context.canvas, effectiveOffset);
}
if (this.paintCursorAboveText) {
this._textPainter.paint(context.canvas, effectiveOffset);
}
if (showCaret) {
this._paintCaret(context.canvas, effectiveOffset, this._selection.extendPos);
}
if (!this.paintCursorAboveText) {
this._textPainter.paint(context.canvas, effectiveOffset);
}
if (this._floatingCursorOn) {
if (this._resetFloatingCursorAnimationValue == null) {
this._paintCaret(context.canvas, effectiveOffset, this._floatingCursorTextPosition);
}
this._paintFloatingCaret(context.canvas, this._floatingCursorOffset);
}
}
void markNeedsSemanticsUpdate() {

8
Runtime/rendering/layer.cs


where T : class {
public AnnotatedRegionLayer(
T value = null,
Size size = null) {
Size size = null,
Offset offset = null) {
offset = offset ?? Offset.zero;
this.offset = offset;
}
public readonly T value;

public readonly Offset offset;
properties.add(new DiagnosticsProperty<Offset>("offset", this.offset, defaultValue: null));
}
}

2
Runtime/rendering/object.cs


D.assert(this._debugCanPerformMutations);
D.assert(child != null);
this.setupParentData(child);
base.adoptChild(child);
base.adoptChild(child);
}
protected override void dropChild(AbstractNodeMixinDiagnosticableTree childNode) {

11
Runtime/rendering/proxy_box.cs


float? stepHeight = null,
RenderBox child = null
) : base(child) {
D.assert(stepWidth == null || stepWidth > 0.0f);
D.assert(stepHeight == null || stepHeight > 0.0f);
this._stepWidth = stepWidth;
this._stepHeight = stepHeight;
}

public float? stepWidth {
get { return this._stepWidth; }
set {
D.assert(value == null || value > 0.0f);
if (value == this._stepWidth) {
return;
}

public float? stepHeight {
get { return this._stepHeight; }
set {
D.assert(value == null || value > 0.0f);
if (value == this._stepHeight) {
return;
}

this._updateClip();
context.pushClipPath(this.needsCompositing, offset, Offset.zero & this.size,
this._clip, base.paint, clipBehavior: this.clipBehavior);
base.paint(context, offset);
}
}

Clip clipBehavior = Clip.none,
CustomClipper<T> clipper = null
) : base(child: child, clipBehavior: clipBehavior, clipper: clipper) {
D.assert(elevation != null);
D.assert(elevation != null && elevation >= 0.0f);
D.assert(color != null);
D.assert(shadowColor != null);
this._elevation = elevation ?? 0.0f;

public float elevation {
get { return this._elevation; }
set {
D.assert(value >= 0.0f);
if (this.elevation == value) {
return;
}

BoxShape shape = BoxShape.rectangle,
Clip clipBehavior = Clip.none,
BorderRadius borderRadius = null,
float elevation = 0.0f,
float? elevation = 0.0f,
D.assert(elevation != null && elevation >= 0.0f);
this._shape = shape;
this._borderRadius = borderRadius;
}

6
Runtime/rendering/sliver.cs


GrowthDirection growthDirection,
ScrollDirection userScrollDirection,
float scrollOffset,
float precedingScrollExtent,
float overlap,
float remainingPaintExtent,
float crossAxisExtent,

this.growthDirection = growthDirection;
this.userScrollDirection = userScrollDirection;
this.scrollOffset = scrollOffset;
this.precedingScrollExtent = precedingScrollExtent;
this.overlap = overlap;
this.remainingPaintExtent = remainingPaintExtent;
this.crossAxisExtent = crossAxisExtent;

GrowthDirection? growthDirection = null,
ScrollDirection? userScrollDirection = null,
float? scrollOffset = null,
float? precedingScrollExtent = null,
float? overlap = null,
float? remainingPaintExtent = null,
float? crossAxisExtent = null,

growthDirection: growthDirection ?? this.growthDirection,
userScrollDirection: userScrollDirection ?? this.userScrollDirection,
scrollOffset: scrollOffset ?? this.scrollOffset,
precedingScrollExtent: precedingScrollExtent ?? this.precedingScrollExtent,
overlap: overlap ?? this.overlap,
remainingPaintExtent: remainingPaintExtent ?? this.remainingPaintExtent,
crossAxisExtent: crossAxisExtent ?? this.crossAxisExtent,

public readonly ScrollDirection userScrollDirection;
public readonly float scrollOffset;
public readonly float precedingScrollExtent;
public readonly float overlap;

2
Runtime/rendering/sliver_multi_box_adaptor.cs


foreach (int index in indices) {
children.Add(this._keepAliveBucket[index].toDiagnosticsNode(
name: "child with index " + index + " (kept alive offstage)",
name: "child with index " + index + " (kept alive but not laid out)",
style: DiagnosticsTreeStyle.offstage
));
}

10
Runtime/rendering/sliver_persistent_header.cs


if (value == null) {
this._controller?.dispose();
this._controller = null;
}
else {
if (this._snapConfiguration != null && value.vsync != this._snapConfiguration.vsync) {

protected override float updateGeometry() {
float? minExtent = this.minExtent;
float? minAllowedExtent = this.constraints.remainingPaintExtent > minExtent
? minExtent
: this.constraints.remainingPaintExtent;
float? clampedPaintExtent =
paintExtent?.clamp(minAllowedExtent ?? 0.0f, this.constraints.remainingPaintExtent);
paintExtent: paintExtent?.clamp(minExtent ?? 0.0f, this.constraints.remainingPaintExtent) ?? 0.0f,
layoutExtent: layoutExtent?.clamp(0.0f, this.constraints.remainingPaintExtent - minExtent ?? 0.0f),
paintExtent: clampedPaintExtent ?? 0.0f,
layoutExtent: layoutExtent?.clamp(0.0f, clampedPaintExtent ?? 0.0f),
maxPaintExtent: maxExtent ?? 0.0f,
maxScrollObstructionExtent: maxExtent ?? 0.0f,
hasVisualOverflow: true

3
Runtime/rendering/table.cs


float deficit = tableWidth - maxWidthConstraint;
int availableColumns = this.columns;
while (deficit > 0.0f && totalFlex > 0.0f) {
float minimumDeficit = 0.00000001f;
while (deficit > 0.0f && totalFlex > minimumDeficit) {
float newTotalFlex = 0.0f;
for (int x = 0; x < this.columns; x++) {
if (flexes[x] != null) {

51
Runtime/rendering/viewport.cs


GrowthDirectionUtils.applyGrowthDirectionToScrollDirection(
this.offset.userScrollDirection, growthDirection);
float maxPaintOffset = layoutOffset + overlap;
float precedingScrollExtent = 0.0f;
while (child != null) {
float sliverScrollOffset = scrollOffset <= 0.0 ? 0.0f : scrollOffset;

growthDirection: growthDirection,
userScrollDirection: adjustedUserScrollDirection,
scrollOffset: sliverScrollOffset,
precedingScrollExtent: precedingScrollExtent,
overlap: maxPaintOffset - layoutOffset,
remainingPaintExtent: Mathf.Max(0.0f,
remainingPaintExtent - layoutOffset + initialLayoutOffset),

maxPaintOffset = Mathf.Max(effectiveLayoutOffset + childLayoutGeometry.paintExtent,
maxPaintOffset);
scrollOffset -= childLayoutGeometry.scrollExtent;
precedingScrollExtent += childLayoutGeometry.scrollExtent;
layoutOffset += childLayoutGeometry.layoutExtent;
if (childLayoutGeometry.cacheExtent != 0.0) {

public RevealedOffset getOffsetToReveal(RenderObject target, float alignment, Rect rect = null) {
float leadingScrollOffset = 0.0f;
float targetMainAxisExtent = 0.0f;
RenderObject descendant;
if (target is RenderBox) {
RenderBox targetBox = (RenderBox) target;
RenderObject child = target;
RenderBox pivot = null;
bool onlySlivers = target is RenderSliver;
while (child.parent != this) {
D.assert(child.parent != null, $"target must be a descendant of ${this}");
if (child is RenderBox) {
pivot = (RenderBox) child;
}
RenderBox pivot = targetBox;
while (pivot.parent is RenderBox) {
pivot = (RenderBox) pivot.parent;
if (child.parent is RenderSliver) {
RenderSliver parent = (RenderSliver) child.parent;
leadingScrollOffset += parent.childScrollOffset(child);
else {
onlySlivers = false;
leadingScrollOffset = 0.0f;
}
child = (RenderObject) child.parent;
}
if (pivot != null) {
D.assert(pivot.parent != null);
D.assert(pivot.parent != this);
D.assert(pivot != this);

transform = targetBox.getTransformTo(pivot);
transform = target.getTransformTo(pivot);
Rect bounds = transform.mapRect(rect);
float offset = 0.0f;

break;
}
leadingScrollOffset = pivot.size.height - offset;
leadingScrollOffset += pivot.size.height - offset;
leadingScrollOffset = bounds.left;
leadingScrollOffset += bounds.left;
leadingScrollOffset = bounds.top;
leadingScrollOffset += bounds.top;
targetMainAxisExtent = bounds.height;
break;
case AxisDirection.left:

break;
}
leadingScrollOffset = pivot.size.width - offset;
leadingScrollOffset += pivot.size.width - offset;
descendant = pivot;
else if (target is RenderSliver) {
else if (onlySlivers) {
leadingScrollOffset = 0.0f;
descendant = targetSliver;
}
RenderObject child = descendant;
while (child.parent is RenderSliver) {
var parent = (RenderSliver) child.parent;
leadingScrollOffset += parent.childScrollOffset(child);
child = parent;
}
D.assert(child.parent == this);

79
Runtime/service/text_input.cs


using UnityEngine;
namespace Unity.UIWidgets.service {
public enum FloatingCursorDragState {
Start,
Update,
End
}
public class RawFloatingCursorPoint {
public RawFloatingCursorPoint(
Offset offset = null,
FloatingCursorDragState? state = null
) {
D.assert(state != null);
D.assert(state == FloatingCursorDragState.Update ? offset != null : true);
this.offset = offset;
this.state = state;
}
public readonly Offset offset;
public readonly FloatingCursorDragState? state;
}
public class TextInputType : IEquatable<TextInputType> {
public readonly int index;
public readonly bool? signed;

case "TextAffinity.upstream":
return TextAffinity.upstream;
}
return null;
}

case "TextInputAction.newline":
return TextInputAction.newline;
}
public static FloatingCursorDragState _toTextCursorAction(string state) {
switch (state) {
case "FloatingCursorDragState.start":
return FloatingCursorDragState.Start;
case "FloatingCursorDragState.update":
return FloatingCursorDragState.Update;
case "FloatingCursorDragState.end":
return FloatingCursorDragState.End;
}
throw new UIWidgetsError("Unknown text cursor action: $state");
}
public static RawFloatingCursorPoint _toTextPoint(FloatingCursorDragState state,
Dictionary<string, float?> encoded) {
D.assert(encoded.getOrDefault("X") != null,
"You must provide a value for the horizontal location of the floating cursor.");
D.assert(encoded.getOrDefault("Y") != null,
"You must provide a value for the vertical location of the floating cursor.");
Offset offset = state == FloatingCursorDragState.Update
? new Offset(encoded["X"] ?? 0.0f, encoded["Y"] ?? 0.0f)
: new Offset(0, 0);
return new RawFloatingCursorPoint(offset: offset, state: state);
}
public TextEditingValue(string text = "", TextSelection selection = null, TextRange composing = null) {
this.text = text;
this.selection = selection ?? TextSelection.collapsed(-1);

)
);
}
public TextEditingValue copyWith(string text = null, TextSelection selection = null,
TextRange composing = null) {
return new TextEditingValue(

if (selection.start < 0) {
selection = TextSelection.collapsed(0, this.selection.affinity);
}
newText = selection.textBefore(this.text) + text + selection.textAfter(this.text);
newSelection = TextSelection.collapsed(selection.start + text.Length);
return new TextEditingValue(

}
return this.copyWith(text: this.text.Substring(0, this.selection.start) +
this.text.Substring(this.selection.start + 1),
this.text.Substring(this.selection.start + 1),
return this.copyWith(text: newText, selection: TextSelection.collapsed(this.selection.start),
return this.copyWith(text: newText, selection: TextSelection.collapsed(this.selection.start),
composing: TextRange.empty);
}
}

void updateEditingValue(TextEditingValue value);
void performAction(TextInputAction action);
void updateFloatingCursor(RawFloatingCursorPoint point);
}
public enum TextInputAction {

class TextInputConfiguration {
public TextInputConfiguration(TextInputType inputType = null,
bool obscureText = false, bool autocorrect = true, TextInputAction inputAction = TextInputAction.done,
Brightness keyboardAppearance = Brightness.light, TextCapitalization textCapitalization = TextCapitalization.none,
Brightness keyboardAppearance = Brightness.light,
TextCapitalization textCapitalization = TextCapitalization.none,
bool unityTouchKeyboard = false) {
this.inputType = inputType ?? TextInputType.text;
this.inputAction = inputAction;

D.assert(this.imeRequired());
TextInput.keyboardDelegate.setIMEPos(imeGlobalPos);
}
public bool imeRequired() {
return TextInput.keyboardDelegate != null && TextInput.keyboardDelegate.imeRequired();
}

if (Application.isEditor) {
keyboardDelegate = new DefaultKeyboardDelegate();
} else {
}
else {
#if UNITY_IOS || UNITY_ANDROID
if (configuration.unityTouchKeyboard) {
keyboardDelegate = new UnityTouchScreenKeyboardDelegate();

keyboardDelegate = new DefaultKeyboardDelegate();
#endif
}
keyboardDelegate.setClient(connection._id, configuration);
return connection;
}

(keyboardDelegate as TextInputUpdateListener)?.Update();
}
}
internal static void _updateEditingState(int client, TextEditingValue value) {
if (_currentConnection == null) {
return;

_currentConnection._client.updateEditingValue(value);
}
internal static void _performAction(int client, TextInputAction action) {
internal static void _performAction(int client, TextInputAction action) {
if (_currentConnection == null) {
return;
}

keyboardDelegate.hide();
}
});
}
}
}
}

10
Runtime/ui/painting/canvas.cs


bool forceMoveTo = !useCenter;
while (sweepAngle <= -Mathf.PI * 2) {
path.addArc(rect, startAngle, -Mathf.PI, forceMoveTo);
path.arcTo(rect, startAngle, -Mathf.PI, forceMoveTo);
path.addArc(rect, startAngle, -Mathf.PI, false);
path.arcTo(rect, startAngle, -Mathf.PI, false);
startAngle -= Mathf.PI;
forceMoveTo = false;
sweepAngle += Mathf.PI * 2;

path.addArc(rect, startAngle, Mathf.PI, forceMoveTo);
path.arcTo(rect, startAngle, Mathf.PI, forceMoveTo);
path.addArc(rect, startAngle, Mathf.PI, false);
path.arcTo(rect, startAngle, Mathf.PI, false);
path.addArc(rect, startAngle, sweepAngle, forceMoveTo);
path.arcTo(rect, startAngle, sweepAngle, forceMoveTo);
if (useCenter) {
path.close();
}

41
Runtime/ui/painting/path.cs


x + x0, y + y0,
});
}
}
}
public void relativeLineTo(float x, float y) {
var x0 = this._commandx;
var y0 = this._commandy;
this._appendCommands(new[] {
(float) PathCommand.lineTo,
x + x0, y + y0,
});
}
public void lineTo(float x, float y) {
this._appendCommands(new[] {
(float) PathCommand.lineTo,

public void bezierTo(float c1x, float c1y, float c2x, float c2y, float x, float y) {
public void cubicTo(float c1x, float c1y, float c2x, float c2y, float x, float y) {
public void relativeCubicTo(float c1x, float c1y, float c2x, float c2y, float x, float y) {
var x0 = this._commandx;
var y0 = this._commandy;
this.cubicTo(x0 + c1x, y0 + c1y, x0 + c2x, y0 + c2y, x0 + x, y0 + y);
}
public void quadTo(float cx, float cy, float x, float y) {
public void quadraticBezierTo(float cx, float cy, float x, float y) {
var x0 = this._commandx;
var y0 = this._commandy;

(x + 2.0f / 3.0f * (cx - x)), (y + 2.0f / 3.0f * (cy - y)),
x, y,
});
}
public void relativeQuadraticBezierTo(float cx, float cy, float x, float y) {
var x0 = this._commandx;
var y0 = this._commandy;
this.quadraticBezierTo(x0 + cx, y0 + cy, x0 + x, y0 + y);
}
public void close() {

this.addArc(cx, cy, radius, a0, a1, dir);
}
public void addArc(Rect rect, float startAngle, float sweepAngle, bool forceMoveTo = true) {
public void arcTo(Rect rect, float startAngle, float sweepAngle, bool forceMoveTo = true) {
var mat = Matrix3.makeScale(rect.width / 2, rect.height / 2);
var center = rect.center;
mat.postTranslate(center.dx, center.dy);

this._transformCommands(vals, mat);
this._appendCommands(vals.ToArray());
}
public void addArc(Rect rect, float startAngle, float sweepAngle) {
this.arcTo(rect, startAngle, sweepAngle, true);
}
public Path transform(Matrix3 mat) {

var res1 = mat.mapXY(this._commands[i + 1], this._commands[i + 2]);
var res2 = mat.mapXY(this._commands[i + 3], this._commands[i + 4]);
var res3 = mat.mapXY(this._commands[i + 5], this._commands[i + 6]);
ret.bezierTo(res1.dx, res1.dy, res2.dx, res2.dy, res3.dx, res3.dy);
ret.cubicTo(res1.dx, res1.dy, res2.dx, res2.dy, res3.dx, res3.dy);
i += 7;
break;
case PathCommand.close:

2
Runtime/widgets/binding.cs


Application.Quit();
return;
}
this._observers[idx].didPopRoute().Then(_handlePopRouteSub);
this._observers[idx].didPopRoute().Then((Action<bool>) _handlePopRouteSub);
}
}

148
Runtime/widgets/editable_text.cs


this._cursorBlinkOpacityController = new AnimationController(vsync: this, duration: _fadeDuration);
this._cursorBlinkOpacityController.addListener(this._onCursorColorTick);
this._floatingCursorResetController = new AnimationController(vsync: this);
// this._floatingCursorResetController.addListener(_onFloatingCursorResetTick); // TODO: remove comment when _onFLoatingCursorResetTick is ready
this._floatingCursorResetController.addListener(this._onFloatingCursorResetTick);
}
public override void didChangeDependencies() {

public override void dispose() {
this.widget.controller.removeListener(this._didChangeTextEditingValue);
this._cursorBlinkOpacityController.removeListener(this._onCursorColorTick);
// this._floatingCursorResetController.removeListener(this._onFloatingCursorResetTick); // TODO: remove comment when _onFloatingCursorResetTick is ready
this._floatingCursorResetController.removeListener(this._onFloatingCursorResetTick);
this._closeInputConnectionIfNeeded();
D.assert(!this._hasInputConnection);
this._stopCursorTimer();

get { return new Offset(0, this.renderEditable.preferredLineHeight / 2); }
}
// TODO: remove comment when renderEditable is updated and FloatingCursorDragState is ready
// void updateFloatingCursor(RawFloatingCursorPoint point) {
// switch(point.state){
// case FloatingCursorDragState.Start:
// TextPosition currentTextPosition = new TextPosition(offset: this.renderEditable.selection.baseOffset);
// this._startCaretRect = this.renderEditable.getLocalRectForCaret(currentTextPosition);
// this.renderEditable.setFloatingCursor(point.state, this._startCaretRect.center - this._floatingCursorOffset, currentTextPosition);
// break;
// case FloatingCursorDragState.Update:
// // We want to send in points that are centered around a (0,0) origin, so we cache the
// // position on the first update call.
// if (this._pointOffsetOrigin != null) {
// Offset centeredPoint = point.offset - this._pointOffsetOrigin;
// Offset rawCursorOffset = this._startCaretRect.center + centeredPoint - this._floatingCursorOffset;
// this._lastBoundedOffset = this.renderEditable.calculateBoundedFloatingCursorOffset(rawCursorOffset);
// this._lastTextPosition = this.renderEditable.getPositionForPoint(this.renderEditable.localToGlobal(this._lastBoundedOffset + this._floatingCursorOffset));
// this.renderEditable.setFloatingCursor(point.state, this._lastBoundedOffset, this._lastTextPosition);
// } else {
// this._pointOffsetOrigin = point.offset;
// }
// break;
// case FloatingCursorDragState.End:
// this._floatingCursorResetController.setValue(0.0f);
// this._floatingCursorResetController.animateTo(1.0f, duration: _floatingCursorResetTime, curve: Curves.decelerate);
// break;
// }
// }
public void updateFloatingCursor(RawFloatingCursorPoint point) {
switch (point.state) {
case FloatingCursorDragState.Start:
TextPosition currentTextPosition =
new TextPosition(offset: this.renderEditable.selection.baseOffset);
this._startCaretRect = this.renderEditable.getLocalRectForCaret(currentTextPosition);
this.renderEditable.setFloatingCursor(point.state,
this._startCaretRect.center - this._floatingCursorOffset, currentTextPosition);
break;
case FloatingCursorDragState.Update:
// We want to send in points that are centered around a (0,0) origin, so we cache the
// position on the first update call.
if (this._pointOffsetOrigin != null) {
Offset centeredPoint = point.offset - this._pointOffsetOrigin;
Offset rawCursorOffset =
this._startCaretRect.center + centeredPoint - this._floatingCursorOffset;
this._lastBoundedOffset =
this.renderEditable.calculateBoundedFloatingCursorOffset(rawCursorOffset);
this._lastTextPosition = this.renderEditable.getPositionForPoint(
this.renderEditable.localToGlobal(this._lastBoundedOffset + this._floatingCursorOffset));
this.renderEditable.setFloatingCursor(point.state, this._lastBoundedOffset,
this._lastTextPosition);
}
else {
this._pointOffsetOrigin = point.offset;
}
// TODO: remove comment when RenderEditable.setFloatingCursor, Floating CursorDragState, and force press is ready
// void _onFloatingCursorResetTick() {
// Offset finalPosition = this.renderEditable.getLocalRectForCaret(this._lastTextPosition).centerLeft - this._floatingCursorOffset;
// if (this._floatingCursorResetController.isCompleted) {
// this.renderEditable.setFloatingCursor(FloatingCursorDragState.End, finalPosition, this._lastTextPosition);
// if (this._lastTextPosition.offset != this.renderEditable.selection.baseOffset)
// this._handleSelectionChanged(TextSelection.collapsed(offset: this._lastTextPosition.offset), this.renderEditable, SelectionChangedCause.forcePress);
// this._startCaretRect = null;
// this._lastTextPosition = null;
// this._pointOffsetOrigin = null;
// this._lastBoundedOffset = null;
// } else {
// float lerpValue = this._floatingCursorResetController.value;
// float lerpX = MathUtils.lerpFloat(this._lastBoundedOffset.dx, finalPosition.dx, lerpValue);
// float lerpY = MathUtils.lerpFloat(this._lastBoundedOffset.dy, finalPosition.dy, lerpValue);
//
// this.renderEditable.setFloatingCursor(FloatingCursorDragState.Update, new Offset(lerpX, lerpY), this._lastTextPosition, resetLerpValue: lerpValue);
// }
// }
break;
case FloatingCursorDragState.End:
this._floatingCursorResetController.setValue(0.0f);
this._floatingCursorResetController.animateTo(1.0f, duration: _floatingCursorResetTime,
curve: Curves.decelerate);
break;
}
}
void _onFloatingCursorResetTick() {
Offset finalPosition = this.renderEditable.getLocalRectForCaret(this._lastTextPosition).centerLeft - this._floatingCursorOffset;
if (this._floatingCursorResetController.isCompleted) {
this.renderEditable.setFloatingCursor(FloatingCursorDragState.End, finalPosition, this._lastTextPosition);
if (this._lastTextPosition.offset != this.renderEditable.selection.baseOffset)
this._handleSelectionChanged(TextSelection.collapsed(offset: this._lastTextPosition.offset), this.renderEditable, SelectionChangedCause.forcePress);
this._startCaretRect = null;
this._lastTextPosition = null;
this._pointOffsetOrigin = null;
this._lastBoundedOffset = null;
} else {
float lerpValue = this._floatingCursorResetController.value;
float lerpX = MathUtils.lerpFloat(this._lastBoundedOffset.dx, finalPosition.dx, lerpValue);
float lerpY = MathUtils.lerpFloat(this._lastBoundedOffset.dy, finalPosition.dy, lerpValue);
this.renderEditable.setFloatingCursor(FloatingCursorDragState.Update, new Offset(lerpX, lerpY), this._lastTextPosition, resetLerpValue: lerpValue);
}
}
void _finalizeEditing(bool shouldUnfocus) {
if (this.widget.onEditingComplete != null) {

this._openInputConnection();
}
else {
// TODO: remove comment when FocusScope.ancestorsOf is ready
// List<FocusScopeNode> ancestorScopes = FocusScope.ancestorsOf(this.context);
// for (int i = ancestorScopes.Count - 1; i >= 1; i -= 1)
// ancestorScopes[i].setFirstFocus(ancestorScopes[i - 1]);
List<FocusScopeNode> ancestorScopes = FocusScope.ancestorsOf(this.context);
for (int i = ancestorScopes.Count - 1; i >= 1; i -= 1)
ancestorScopes[i].setFirstFocus(ancestorScopes[i - 1]);
FocusScope.of(this.context).requestFocus(this.widget.focusNode);
}
}

layerLink: this._layerLink,
renderObject: renderObject,
selectionControls: this.widget.selectionControls,
selectionDelegate: this
// dragStartBehavior: this.widget.dragStartBehavior // TODO: remove comment when TextSelectionOverlay is updated
selectionDelegate: this,
dragStartBehavior: this.widget.dragStartBehavior
);
bool longPress = cause == SelectionChangedCause.longPress;
if (cause != SelectionChangedCause.keyboard && (this._value.text.isNotEmpty() || longPress)) {

textSpan: this.buildTextSpan(),
value: this._value,
cursorColor: this._cursorColor,
// backgroundCursorColor: this.widget.backgroundCursorColor, // TODO
backgroundCursorColor: this.widget.backgroundCursorColor,
showCursor: EditableText.debugDeterministicCursor
? new ValueNotifier<bool>(true)
: this._cursorVisibilityNotifier,

rendererIgnoresPointer: this.widget.rendererIgnoresPointer,
cursorWidth: this.widget.cursorWidth,
cursorRadius: this.widget.cursorRadius,
// cursorOffset: this.widget.cursorOffset, // TODO
// paintCursorAboveText: this.widget.paintCursorAboveText, // TODO
cursorOffset: this.widget.cursorOffset,
paintCursorAboveText: this.widget.paintCursorAboveText,
textSelectionDelegate: this
// devicePixelRatio: _devicePixelRatio // TODO
textSelectionDelegate: this,
devicePixelRatio: this._devicePixelRatio
)
)
);

public readonly TextSpan textSpan;
public readonly TextEditingValue value;
public readonly Color cursorColor;
public readonly Color backgroundColor;
public readonly Color backgroundCursorColor;
public readonly ValueNotifier<bool> showCursor;
public readonly bool hasFocus;
public readonly int? maxLines;

public _Editable(TextSpan textSpan = null, TextEditingValue value = null,
Color cursorColor = null, Color backgroundColor = null, ValueNotifier<bool> showCursor = null,
Color cursorColor = null, Color backgroundCursorColor = null, ValueNotifier<bool> showCursor = null,
bool hasFocus = false,
int? maxLines = null, Color selectionColor = null, float textScaleFactor = 1.0f,
TextDirection? textDirection = null, bool obscureText = false, TextAlign textAlign = TextAlign.left,

this.textSpan = textSpan;
this.value = value;
this.cursorColor = cursorColor;
this.backgroundColor = backgroundColor;
this.backgroundCursorColor = backgroundCursorColor;
this.showCursor = showCursor;
this.hasFocus = hasFocus;
this.maxLines = maxLines;

offset: this.offset,
showCursor: this.showCursor,
cursorColor: this.cursorColor,
// backgroundColor: this.backgroundColor, // TODO
backgroundCursorColor: this.backgroundCursorColor,
hasFocus: this.hasFocus,
maxLines: this.maxLines,
selectionColor: this.selectionColor,

ignorePointer: this.rendererIgnoresPointer,
cursorWidth: this.cursorWidth ?? 1.0f,
cursorRadius: this.cursorRadius,
// cursorOffset: this.cursorOffset, // TODO
cursorOffset: this.cursorOffset,
textSelectionDelegate: this.textSelectionDelegate
// paintCursorAboveText: this.paintCursorAboveText, // TODO
// devicePixelRatio: this.devicePixelRatio // TODO
textSelectionDelegate: this.textSelectionDelegate,
paintCursorAboveText: this.paintCursorAboveText == true,
devicePixelRatio: this.devicePixelRatio ?? 1.0f
);
}

edit.cursorColor = this.cursorColor;
// edit.backgroundColor = this.backgroundColor; // TODO
edit.backgroundCursorColor = this.backgroundCursorColor;
edit.showCursor = this.showCursor;
edit.hasFocus = this.hasFocus;
edit.maxLines = this.maxLines;

edit.textSelectionDelegate = this.textSelectionDelegate;
edit.cursorWidth = this.cursorWidth ?? 1.0f;
edit.cursorRadius = this.cursorRadius;
// edit.cursorOffset = this.cursorOffset; // TODO
edit.cursorOffset = this.cursorOffset;
// edit.paintCursorAboveText = this.paintCursorAboveText; // TODO
// edit.devicePixelRatio = this.devicePixelRatio; // TODO
edit.paintCursorAboveText = this.paintCursorAboveText == true;
edit.devicePixelRatio = this.devicePixelRatio ?? 1.0f;
}
}
}

22
Runtime/widgets/focus_scope.cs


using Unity.UIWidgets.foundation;
using System.Collections.Generic;
using Unity.UIWidgets.foundation;
namespace Unity.UIWidgets.widgets {
class _FocusScopeMarker : InheritedWidget {

public readonly Widget child;
public static FocusScopeNode of(BuildContext context) {
D.assert(context != null);
var scope = (_FocusScopeMarker) context.inheritFromWidgetOfExactType(typeof(_FocusScopeMarker));
if (scope != null && scope.node != null) {
return scope.node;

}
public static List<FocusScopeNode> ancestorsOf(BuildContext context) {
D.assert(context != null);
List<FocusScopeNode> ancestors = new List<FocusScopeNode> { };
while (true) {
context = context.ancestorInheritedElementForWidgetOfExactType(typeof(_FocusScopeMarker));
if (context == null) {
return ancestors;
}
_FocusScopeMarker scope = (_FocusScopeMarker) context.widget;
ancestors.Add(scope.node);
context.visitAncestorElements((Element parent) => {
context = parent;
return false;
});
}
}
public override State createState() {

24
Runtime/widgets/text_selection.cs


LayerLink layerLink = null,
RenderEditable renderObject = null,
TextSelectionControls selectionControls = null,
TextSelectionDelegate selectionDelegate = null) {
TextSelectionDelegate selectionDelegate = null,
DragStartBehavior? dragStartBehavior = null) {
D.assert(value != null);
D.assert(context != null);
this.context = context;

D.assert(overlay != null);
this._handleController = new AnimationController(duration: _fadeDuration, vsync: overlay);
this._toolbarController = new AnimationController(duration: _fadeDuration, vsync: overlay);
this.dragStartBehavior = dragStartBehavior;
}
public readonly BuildContext context;

public readonly TextSelectionControls selectionControls;
public readonly TextSelectionDelegate selectionDelegate;
public readonly DragStartBehavior? dragStartBehavior;
public static TimeSpan _fadeDuration = TimeSpan.FromMilliseconds(150);
AnimationController _handleController;

renderObject: this.renderObject,
selection: this._selection,
selectionControls: this.selectionControls,
position: position
position: position,
dragStartBehavior: this.dragStartBehavior ?? DragStartBehavior.down
)
);
}

RenderEditable renderObject = null,
ValueChanged<TextSelection> onSelectionHandleChanged = null,
VoidCallback onSelectionHandleTapped = null,
TextSelectionControls selectionControls = null
TextSelectionControls selectionControls = null,
DragStartBehavior dragStartBehavior = DragStartBehavior.down
) : base(key: key) {
this.selection = selection;
this.position = position;

this.onSelectionHandleTapped = onSelectionHandleTapped;
this.selectionControls = selectionControls;
this.dragStartBehavior = dragStartBehavior;
}
public readonly TextSelection selection;

public readonly ValueChanged<TextSelection> onSelectionHandleChanged;
public readonly VoidCallback onSelectionHandleTapped;
public readonly TextSelectionControls selectionControls;
public readonly DragStartBehavior dragStartBehavior;
public override State createState() {
return new _TextSelectionHandleOverlayState();

link: this.widget.layerLink,
showWhenUnlinked: false,
child: new GestureDetector(
dragStartBehavior: this.widget.dragStartBehavior,
onPanStart: this._handleDragStart,
onPanUpdate: this._handleDragUpdate,
onTap: this._handleTap,

);
if (this.widget.onSingleLongTapStart != null) {
gestures[typeof(LongPressGestureRecognizer)] = new GestureRecognizerFactoryWithHandlers<LongPressGestureRecognizer>(
() => new LongPressGestureRecognizer(debugOwner: this, kind: PointerDeviceKind.touch),
instance => {
instance.onLongPress = this._handleLongPressStart;
});
gestures[typeof(LongPressGestureRecognizer)] =
new GestureRecognizerFactoryWithHandlers<LongPressGestureRecognizer>(
() => new LongPressGestureRecognizer(debugOwner: this, kind: PointerDeviceKind.touch),
instance => { instance.onLongPress = this._handleLongPressStart; });
}
if (this.widget.onDragSelectionStart != null ||

1
Samples/ReduxSample/ObjectFinder/ObjectFinderApp.cs


padding: EdgeInsets.only(left: 8, right: 8),
child: new EditableText(
selectionControls: MaterialUtils.materialTextSelectionControls,
backgroundCursorColor: Colors.transparent,
controller: this._controller,
focusNode: this._focusNode,
style: new TextStyle(

39
Samples/UIWidgetSample/MaterialSample.cs


namespace UIWidgetsSample {
public class MaterialSample : UIWidgetsSamplePanel {
int testCaseId = 2;
const int testCaseId = 6;
List<Widget> testCases = new List<Widget> {
readonly List<Widget> testCases = new List<Widget> {
new BottomAppBarWidget()
new BottomAppBarWidget(),
new MaterialSliderWidget()
home: this.testCases[this.testCaseId]);
home: this.testCases[testCaseId]);
}
protected override void OnEnable() {

),
new PerformanceOverlay()
}
);
}
}
public class MaterialSliderWidget : StatefulWidget {
public override State createState() {
return new MaterialSliderState();
}
}
public class MaterialSliderState : State<MaterialSliderWidget> {
float _value = 0.0f;
void onChanged(float value) {
this.setState(() => { this._value = value; });
}
public override Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Slider and Indicators")),
body: new Column(
children : new List<Widget> {
new Container(
child: new Slider(
value: this._value,
onChanged: this.onChanged))
}
)
);
}
}

16
Samples/UIWidgetSample/TextInputSample.cs


child: new EditableText(maxLines: 1,
controller: this.titleController,
selectionControls: MaterialUtils.materialTextSelectionControls,
backgroundCursorColor: Colors.transparent,
autofocus: true,
focusNode: new FocusNode(),
style: new TextStyle(

padding: EdgeInsets.fromLTRB(8, 0, 8, 0),
child: new EditableText(maxLines: 200,
controller: this.descController,
backgroundCursorColor: Colors.transparent,
selectionControls: MaterialUtils.materialTextSelectionControls,
focusNode: new FocusNode(),
style: new TextStyle(

var selectionColor = new Color(0xFF6F6F6F);
widgets.Add(this.rowWidgets("Default", new EditStateProvider(builder: ((buildContext, controller, node) =>
new EditableText(controller, node, style, cursorColor, selectionColor: selectionColor, onSubmitted: this.textSubmitted
new EditableText(controller, node, style, cursorColor, Colors.transparent, selectionColor: selectionColor, onSubmitted: this.textSubmitted
new EditableText(controller, node, style, cursorColor, selectionColor: selectionColor, maxLines: 4,
new EditableText(controller, node, style, cursorColor, Colors.transparent, selectionColor: selectionColor, maxLines: 4,
new EditableText(controller, node, style, cursorColor, selectionColor: selectionColor, obscureText: true,
new EditableText(controller, node, style, cursorColor, Colors.transparent, selectionColor: selectionColor, obscureText: true,
new EditableText(controller, node, style, cursorColor, selectionColor: selectionColor, keyboardType: TextInputType.number,
new EditableText(controller, node, style, cursorColor, Colors.transparent, selectionColor: selectionColor, keyboardType: TextInputType.number,
new EditableText(controller, node, style, cursorColor, selectionColor: selectionColor, keyboardType: TextInputType.phone,
new EditableText(controller, node, style, cursorColor, Colors.transparent, selectionColor: selectionColor, keyboardType: TextInputType.phone,
new EditableText(controller, node, style, cursorColor, selectionColor: selectionColor, keyboardType: TextInputType.emailAddress,
new EditableText(controller, node, style, cursorColor, Colors.transparent, selectionColor: selectionColor, keyboardType: TextInputType.emailAddress,
new EditableText(controller, node, style, cursorColor, selectionColor: selectionColor, keyboardType: TextInputType.url,
new EditableText(controller, node, style, cursorColor, Colors.transparent, selectionColor: selectionColor, keyboardType: TextInputType.url,
onSubmitted: this.textSubmitted, unityTouchKeyboard: unityKeyboard,
selectionControls: MaterialUtils.materialTextSelectionControls)))));
return widgets;

7
Runtime/foundation/constants.cs


using UnityEngine;
namespace Unity.UIWidgets.foundation {
public class FoundationConstants {
public bool kReleaseMode = !Debug.isDebugBuild;
}
}

3
Runtime/foundation/constants.cs.meta


fileFormatVersion: 2
guid: 36d19e17f3d947f98c7accec2cd5771b
timeCreated: 1556097117

805
Runtime/material/slider.cs


using System;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.async;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.scheduler;
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;
namespace Unity.UIWidgets.material {
public class Slider : StatefulWidget {
public Slider(
Key key = null,
float? value = null,
ValueChanged<float> onChanged = null,
ValueChanged<float> onChangeStart = null,
ValueChanged<float> onChangeEnd = null,
float min = 0.0f,
float max = 1.0f,
int? divisions = null,
string label = null,
Color activeColor = null,
Color inactiveColor = null
) : base(key: key) {
D.assert(value != null);
D.assert(min <= max);
D.assert(value >= min && value <= max);
D.assert(divisions == null || divisions > 0);
this.value = value.Value;
this.onChanged = onChanged;
this.onChangeStart = onChangeStart;
this.onChangeEnd = onChangeEnd;
this.min = min;
this.max = max;
this.divisions = divisions;
this.label = label;
this.activeColor = activeColor;
this.inactiveColor = inactiveColor;
}
public readonly float value;
public readonly ValueChanged<float> onChanged;
public readonly ValueChanged<float> onChangeStart;
public readonly ValueChanged<float> onChangeEnd;
public readonly float min;
public readonly float max;
public readonly int? divisions;
public readonly string label;
public readonly Color activeColor;
public readonly Color inactiveColor;
public override State createState() {
return new _SliderState();
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new FloatProperty("value", this.value));
properties.add(new FloatProperty("min", this.min));
properties.add(new FloatProperty("max", this.max));
}
}
class _SliderState : TickerProviderStateMixin<Slider> {
static TimeSpan enableAnimationDuration = new TimeSpan(0, 0, 0, 0, 75);
static TimeSpan valueIndicatorAnimationDuration = new TimeSpan(0, 0, 0, 0, 100);
public AnimationController overlayController;
public AnimationController valueIndicatorController;
public AnimationController enableController;
public AnimationController positionController;
public Timer interactionTimer;
public override void initState() {
base.initState();
this.overlayController = new AnimationController(
duration: Constants.kRadialReactionDuration,
vsync: this
);
this.valueIndicatorController = new AnimationController(
duration: valueIndicatorAnimationDuration,
vsync: this
);
this.enableController = new AnimationController(
duration: enableAnimationDuration,
vsync: this
);
this.positionController = new AnimationController(
duration: TimeSpan.Zero,
vsync: this
);
this.enableController.setValue(this.widget.onChanged != null ? 1.0f : 0.0f);
this.positionController.setValue(this._unlerp(this.widget.value));
}
public override void dispose() {
this.interactionTimer?.cancel();
this.overlayController.dispose();
this.valueIndicatorController.dispose();
this.enableController.dispose();
this.positionController.dispose();
base.dispose();
}
void _handleChanged(float value) {
D.assert(this.widget.onChanged != null);
float lerpValue = this._lerp(value);
if (lerpValue != this.widget.value) {
this.widget.onChanged(lerpValue);
}
}
void _handleDragStart(float value) {
D.assert(this.widget.onChangeStart != null);
this.widget.onChangeStart(this._lerp(value));
}
void _handleDragEnd(float value) {
D.assert(this.widget.onChangeEnd != null);
this.widget.onChangeEnd(this._lerp(value));
}
float _lerp(float value) {
D.assert(value >= 0.0f);
D.assert(value <= 1.0f);
return value * (this.widget.max - this.widget.min) + this.widget.min;
}
float _unlerp(float value) {
D.assert(value <= this.widget.max);
D.assert(value >= this.widget.min);
return this.widget.max > this.widget.min
? (value - this.widget.min) / (this.widget.max - this.widget.min)
: 0.0f;
}
public override Widget build(BuildContext context) {
D.assert(MaterialD.debugCheckHasMaterial(context));
D.assert(WidgetsD.debugCheckHasMediaQuery(context));
SliderThemeData sliderTheme = SliderTheme.of(context);
if (this.widget.activeColor != null || this.widget.inactiveColor != null) {
sliderTheme = sliderTheme.copyWith(
activeTrackColor: this.widget.activeColor,
inactiveTrackColor: this.widget.inactiveColor,
activeTickMarkColor: this.widget.inactiveColor,
inactiveTickMarkColor: this.widget.activeColor,
thumbColor: this.widget.activeColor,
valueIndicatorColor: this.widget.activeColor,
overlayColor: this.widget.activeColor?.withAlpha(0x29)
);
}
return new _SliderRenderObjectWidget(
value: this._unlerp(this.widget.value),
divisions: this.widget.divisions,
label: this.widget.label,
sliderTheme: sliderTheme,
mediaQueryData: MediaQuery.of(context),
onChanged: (this.widget.onChanged != null) && (this.widget.max > this.widget.min)
? this._handleChanged
: (ValueChanged<float>) null,
onChangeStart: this.widget.onChangeStart != null ? this._handleDragStart : (ValueChanged<float>) null,
onChangeEnd: this.widget.onChangeEnd != null ? this._handleDragEnd : (ValueChanged<float>) null,
state: this
);
}
}
class _SliderRenderObjectWidget : LeafRenderObjectWidget {
public _SliderRenderObjectWidget(
Key key = null,
float? value = null,
int? divisions = null,
string label = null,
SliderThemeData sliderTheme = null,
MediaQueryData mediaQueryData = null,
ValueChanged<float> onChanged = null,
ValueChanged<float> onChangeStart = null,
ValueChanged<float> onChangeEnd = null,
_SliderState state = null
) : base(key: key) {
this.value = value.Value;
this.divisions = divisions;
this.label = label;
this.sliderTheme = sliderTheme;
this.mediaQueryData = mediaQueryData;
this.onChanged = onChanged;
this.onChangeStart = onChangeStart;
this.onChangeEnd = onChangeEnd;
this.state = state;
}
public readonly float value;
public readonly int? divisions;
public readonly string label;
public readonly SliderThemeData sliderTheme;
public readonly MediaQueryData mediaQueryData;
public readonly ValueChanged<float> onChanged;
public readonly ValueChanged<float> onChangeStart;
public readonly ValueChanged<float> onChangeEnd;
public readonly _SliderState state;
public override RenderObject createRenderObject(BuildContext context) {
return new _RenderSlider(
value: this.value,
divisions: this.divisions,
label: this.label,
sliderTheme: this.sliderTheme,
theme: Theme.of(context),
mediaQueryData: this.mediaQueryData,
onChanged: this.onChanged,
onChangeStart: this.onChangeStart,
onChangeEnd: this.onChangeEnd,
state: this.state,
platform: Theme.of(context).platform
);
}
public override void updateRenderObject(BuildContext context, RenderObject renderObject) {
_RenderSlider _renderObject = (_RenderSlider) renderObject;
_renderObject.value = this.value;
_renderObject.divisions = this.divisions;
_renderObject.label = this.label;
_renderObject.sliderTheme = this.sliderTheme;
_renderObject.theme = Theme.of(context);
_renderObject.mediaQueryData = this.mediaQueryData;
_renderObject.onChanged = this.onChanged;
_renderObject.onChangeStart = this.onChangeStart;
_renderObject.onChangeEnd = this.onChangeEnd;
_renderObject.platform = Theme.of(context).platform;
}
}
class _RenderSlider : RenderBox {
public _RenderSlider(
float? value = null,
int? divisions = null,
string label = null,
SliderThemeData sliderTheme = null,
ThemeData theme = null,
MediaQueryData mediaQueryData = null,
RuntimePlatform? platform = null,
ValueChanged<float> onChanged = null,
ValueChanged<float> onChangeStart = null,
ValueChanged<float> onChangeEnd = null,
_SliderState state = null
) {
D.assert(value != null && value >= 0.0 && value <= 1.0);
D.assert(state != null);
this.onChangeStart = onChangeStart;
this.onChangeEnd = onChangeEnd;
this._platform = platform;
this._label = label;
this._value = value.Value;
this._divisions = divisions;
this._sliderTheme = sliderTheme;
this._theme = theme;
this._mediaQueryData = mediaQueryData;
this._onChanged = onChanged;
this._state = state;
this._updateLabelPainter();
GestureArenaTeam team = new GestureArenaTeam();
this._drag = new HorizontalDragGestureRecognizer {
team = team,
onStart = this._handleDragStart,
onUpdate = this._handleDragUpdate,
onEnd = this._handleDragEnd,
onCancel = this._endInteraction
};
this._tap = new TapGestureRecognizer {
team = team,
onTapDown = this._handleTapDown,
onTapUp = this._handleTapUp,
onTapCancel = this._endInteraction
};
this._overlayAnimation = new CurvedAnimation(
parent: this._state.overlayController,
curve: Curves.fastOutSlowIn);
this._valueIndicatorAnimation = new CurvedAnimation(
parent: this._state.valueIndicatorController,
curve: Curves.fastOutSlowIn);
this._enableAnimation = new CurvedAnimation(
parent: this._state.enableController,
curve: Curves.easeInOut);
}
const float _positionAnimationDurationMilliSeconds = 75;
const float _overlayRadius = 16.0f;
const float _overlayDiameter = _overlayRadius * 2.0f;
const float _trackHeight = 2.0f;
const float _preferredTrackWidth = 144.0f;
const float _preferredTotalWidth = _preferredTrackWidth + _overlayDiameter;
const float _minimumInteractionTimeMilliSeconds = 500;
static readonly Animatable<float> _overlayRadiusTween = new FloatTween(begin: 0.0f, end: _overlayRadius);
_SliderState _state;
Animation<float> _overlayAnimation;
Animation<float> _valueIndicatorAnimation;
Animation<float> _enableAnimation;
TextPainter _labelPainter = new TextPainter();
HorizontalDragGestureRecognizer _drag;
TapGestureRecognizer _tap;
bool _active = false;
float _currentDragValue = 0.0f;
float _trackLength {
get { return this.size.width - _overlayDiameter; }
}
bool isInteractive {
get { return this.onChanged != null; }
}
bool isDiscrete {
get { return this.divisions != null && this.divisions.Value > 0; }
}
public float value {
get { return this._value; }
set {
D.assert(value != null && value >= 0.0f && value <= 1.0f);
float convertedValue = this.isDiscrete ? this._discretize(value) : value;
if (convertedValue == this._value) {
return;
}
this._value = convertedValue;
if (this.isDiscrete) {
float distance = (this._value - this._state.positionController.value).abs();
this._state.positionController.duration = distance != 0.0f
? new TimeSpan(0, 0, 0, 0, (int) (_positionAnimationDurationMilliSeconds * (1.0f / distance)))
: TimeSpan.Zero;
this._state.positionController.animateTo(convertedValue, curve: Curves.easeInOut);
}
else {
this._state.positionController.setValue(convertedValue);
}
}
}
float _value;
public RuntimePlatform? platform {
get { return this._platform; }
set {
if (this._platform == value) {
return;
}
this._platform = value;
}
}
RuntimePlatform? _platform;
public int? divisions {
get { return this._divisions; }
set {
if (value == this._divisions) {
return;
}
this._divisions = value;
this.markNeedsPaint();
}
}
int? _divisions;
public string label {
get { return this._label; }
set {
if (value == this._label) {
return;
}
this._label = value;
this._updateLabelPainter();
}
}
string _label;
public SliderThemeData sliderTheme {
get { return this._sliderTheme; }
set {
if (value == this._sliderTheme) {
return;
}
this._sliderTheme = value;
this.markNeedsPaint();
}
}
SliderThemeData _sliderTheme;
public ThemeData theme {
get { return this._theme; }
set {
if (value == this._theme) {
return;
}
this._theme = value;
this.markNeedsPaint();
}
}
ThemeData _theme;
public MediaQueryData mediaQueryData {
get { return this._mediaQueryData; }
set {
if (value == this._mediaQueryData) {
return;
}
this._mediaQueryData = value;
this._updateLabelPainter();
}
}
MediaQueryData _mediaQueryData;
public ValueChanged<float> onChanged {
get { return this._onChanged; }
set {
if (value == this._onChanged) {
return;
}
bool wasInteractive = this.isInteractive;
this._onChanged = value;
if (wasInteractive != this.isInteractive) {
if (this.isInteractive) {
this._state.enableController.forward();
}
else {
this._state.enableController.reverse();
}
this.markNeedsPaint();
}
}
}
ValueChanged<float> _onChanged;
public ValueChanged<float> onChangeStart;
public ValueChanged<float> onChangeEnd;
public bool showValueIndicator {
get {
bool showValueIndicator = false;
switch (this._sliderTheme.showValueIndicator) {
case ShowValueIndicator.onlyForDiscrete:
showValueIndicator = this.isDiscrete;
break;
case ShowValueIndicator.onlyForContinuous:
showValueIndicator = !this.isDiscrete;
break;
case ShowValueIndicator.always:
showValueIndicator = true;
break;
case ShowValueIndicator.never:
showValueIndicator = false;
break;
}
return showValueIndicator;
}
}
float _adjustmentUnit {
get {
switch (this._platform) {
case RuntimePlatform.IPhonePlayer:
return 0.1f;
default:
return 0.05f;
}
}
}
void _updateLabelPainter() {
if (this.label != null) {
this._labelPainter.text = new TextSpan(
style: this._sliderTheme.valueIndicatorTextStyle,
text: this.label
);
this._labelPainter.textScaleFactor = this._mediaQueryData.textScaleFactor;
this._labelPainter.layout();
}
else {
this._labelPainter.text = null;
}
this.markNeedsLayout();
}
public override void attach(object owner) {
base.attach(owner);
this._overlayAnimation.addListener(this.markNeedsPaint);
this._valueIndicatorAnimation.addListener(this.markNeedsPaint);
this._enableAnimation.addListener(this.markNeedsPaint);
this._state.positionController.addListener(this.markNeedsPaint);
}
public override void detach() {
this._overlayAnimation.removeListener(this.markNeedsPaint);
this._valueIndicatorAnimation.removeListener(this.markNeedsPaint);
this._enableAnimation.removeListener(this.markNeedsPaint);
this._state.positionController.removeListener(this.markNeedsPaint);
base.detach();
}
float _getValueFromVisualPosition(float visualPosition) {
return visualPosition;
}
float _getValueFromGlobalPosition(Offset globalPosition) {
float visualPosition =
(this.globalToLocal(globalPosition).dx - _overlayRadius) / this._trackLength;
return this._getValueFromVisualPosition(visualPosition);
}
float _discretize(float value) {
float result = value.clamp(0.0f, 1.0f);
if (this.isDiscrete) {
result = (result * this.divisions.Value).round() / this.divisions.Value;
}
return result;
}
void _startInteraction(Offset globalPosition) {
if (this.isInteractive) {
this._active = true;
if (this.onChangeStart != null) {
this.onChangeStart(this._discretize(this.value));
}
this._currentDragValue = this._getValueFromGlobalPosition(globalPosition);
this.onChanged(this._discretize(this._currentDragValue));
this._state.overlayController.forward();
if (this.showValueIndicator) {
this._state.valueIndicatorController.forward();
this._state.interactionTimer?.cancel();
this._state.interactionTimer = Window.instance.run(
new TimeSpan(0, 0, 0, 0,
(int) (_minimumInteractionTimeMilliSeconds * SchedulerBinding.instance.timeDilation)),
() => {
this._state.interactionTimer = null;
if (!this._active &&
this._state.valueIndicatorController.status == AnimationStatus.completed) {
this._state.valueIndicatorController.reverse();
}
});
}
}
}
void _endInteraction() {
if (this._active && this._state.mounted) {
if (this.onChangeEnd != null) {
this.onChangeEnd(this._discretize(this._currentDragValue));
}
this._active = false;
this._currentDragValue = 0.0f;
this._state.overlayController.reverse();
if (this.showValueIndicator && this._state.interactionTimer == null) {
this._state.valueIndicatorController.reverse();
}
}
}
void _handleDragStart(DragStartDetails details) {
this._startInteraction(details.globalPosition);
}
void _handleDragUpdate(DragUpdateDetails details) {
if (this.isInteractive) {
float valueDelta = details.primaryDelta.Value / this._trackLength;
this._currentDragValue += valueDelta;
this.onChanged(this._discretize(this._currentDragValue));
}
}
void _handleDragEnd(DragEndDetails details) {
this._endInteraction();
}
void _handleTapDown(TapDownDetails details) {
this._startInteraction(details.globalPosition);
}
void _handleTapUp(TapUpDetails details) {
this._endInteraction();
}
protected override bool hitTestSelf(Offset position) {
return true;
}
public override void handleEvent(PointerEvent evt, HitTestEntry entry) {
D.assert(this.debugHandleEvent(evt, entry));
if (evt is PointerDownEvent && this.isInteractive) {
this._drag.addPointer((PointerDownEvent) evt);
this._tap.addPointer((PointerDownEvent) evt);
}
}
protected override float computeMinIntrinsicWidth(float height) {
return Mathf.Max(_overlayDiameter,
this._sliderTheme.thumbShape.getPreferredSize(this.isInteractive, this.isDiscrete).width);
}
protected override float computeMaxIntrinsicWidth(float height) {
return _preferredTotalWidth;
}
protected override float computeMinIntrinsicHeight(float width) {
return _overlayDiameter;
}
protected override float computeMaxIntrinsicHeight(float width) {
return _overlayDiameter;
}
protected override bool sizedByParent {
get { return true; }
}
protected override void performResize() {
this.size = new Size(
this.constraints.hasBoundedWidth
? this.constraints.maxWidth
: _preferredTotalWidth,
this.constraints.hasBoundedHeight
? this.constraints.maxHeight
: _overlayDiameter
);
}
void _paintTickMarks(
Canvas canvas,
Rect trackLeft,
Rect trackRight,
Paint leftPaint,
Paint rightPaint) {
if (this.isDiscrete) {
const float tickRadius = _trackHeight / 2.0f;
float trackWidth = trackRight.right - trackLeft.left;
float dx = (trackWidth - _trackHeight) / this.divisions.Value;
if (dx >= 3.0 * _trackHeight) {
for (int i = 0; i <= this.divisions.Value; i += 1) {
float left = trackLeft.left + i * dx;
Offset center = new Offset(left + tickRadius, trackLeft.top + tickRadius);
if (trackLeft.contains(center)) {
canvas.drawCircle(center, tickRadius, leftPaint);
}
else if (trackRight.contains(center)) {
canvas.drawCircle(center, tickRadius, rightPaint);
}
}
}
}
}
void _paintOverlay(
Canvas canvas,
Offset center) {
Paint overlayPaint = new Paint {color = this._sliderTheme.overlayColor};
float radius = _overlayRadiusTween.evaluate(this._overlayAnimation);
canvas.drawCircle(center, radius, overlayPaint);
}
public override void paint(PaintingContext context, Offset offset) {
Canvas canvas = context.canvas;
float trackLength = this.size.width - 2 * _overlayRadius;
float value = this._state.positionController.value;
ColorTween activeTrackEnableColor = new ColorTween(begin: this._sliderTheme.disabledActiveTrackColor,
end: this._sliderTheme.activeTrackColor);
ColorTween inactiveTrackEnableColor = new ColorTween(begin: this._sliderTheme.disabledInactiveTrackColor,
end: this._sliderTheme.inactiveTrackColor);
ColorTween activeTickMarkEnableColor = new ColorTween(begin: this._sliderTheme.disabledActiveTickMarkColor,
end: this._sliderTheme.activeTickMarkColor);
ColorTween inactiveTickMarkEnableColor =
new ColorTween(begin: this._sliderTheme.disabledInactiveTickMarkColor,
end: this._sliderTheme.inactiveTickMarkColor);
Paint activeTrackPaint = new Paint {color = activeTrackEnableColor.evaluate(this._enableAnimation)};
Paint inactiveTrackPaint = new Paint {color = inactiveTrackEnableColor.evaluate(this._enableAnimation)};
Paint activeTickMarkPaint = new Paint {color = activeTickMarkEnableColor.evaluate(this._enableAnimation)};
Paint inactiveTickMarkPaint = new Paint
{color = inactiveTickMarkEnableColor.evaluate(this._enableAnimation)};
float visualPosition = value;
Paint leftTrackPaint = activeTrackPaint;
Paint rightTrackPaint = inactiveTrackPaint;
Paint leftTickMarkPaint = activeTickMarkPaint;
Paint rightTickMarkPaint = inactiveTickMarkPaint;
float trackRadius = _trackHeight / 2.0f;
float thumbGap = 2.0f;
float trackVerticalCenter = offset.dy + (this.size.height) / 2.0f;
float trackLeft = offset.dx + _overlayRadius;
float trackTop = trackVerticalCenter - trackRadius;
float trackBottom = trackVerticalCenter + trackRadius;
float trackRight = trackLeft + trackLength;
float trackActive = trackLeft + trackLength * visualPosition;
float thumbRadius =
this._sliderTheme.thumbShape.getPreferredSize(this.isInteractive, this.isDiscrete).width / 2.0f;
float trackActiveLeft = Mathf.Max(0.0f,
trackActive - thumbRadius - thumbGap * (1.0f - this._enableAnimation.value));
float trackActiveRight =
Mathf.Min(trackActive + thumbRadius + thumbGap * (1.0f - this._enableAnimation.value), trackRight);
Rect trackLeftRect = Rect.fromLTRB(trackLeft, trackTop, trackActiveLeft, trackBottom);
Rect trackRightRect = Rect.fromLTRB(trackActiveRight, trackTop, trackRight, trackBottom);
Offset thumbCenter = new Offset(trackActive, trackVerticalCenter);
if (visualPosition > 0.0) {
canvas.drawRect(trackLeftRect, leftTrackPaint);
}
if (visualPosition < 1.0) {
canvas.drawRect(trackRightRect, rightTrackPaint);
}
this._paintOverlay(canvas, thumbCenter);
this._paintTickMarks(
canvas,
trackLeftRect,
trackRightRect,
leftTickMarkPaint,
rightTickMarkPaint
);
if (this.isInteractive && this.label != null &&
this._valueIndicatorAnimation.status != AnimationStatus.dismissed) {
if (this.showValueIndicator) {
this._sliderTheme.valueIndicatorShape.paint(
context,
thumbCenter,
activationAnimation: this._valueIndicatorAnimation,
enableAnimation: this._enableAnimation,
isDiscrete: this.isDiscrete,
labelPainter: this._labelPainter,
parentBox: this,
sliderTheme: this._sliderTheme,
value: this._value
);
}
}
this._sliderTheme.thumbShape.paint(
context,
thumbCenter,
activationAnimation: this._valueIndicatorAnimation,
enableAnimation: this._enableAnimation,
isDiscrete: this.isDiscrete,
labelPainter: this._labelPainter,
parentBox: this,
sliderTheme: this._sliderTheme,
value: this._value
);
}
}
}

11
Runtime/material/slider.cs.meta


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

807
Runtime/material/slider_theme.cs


using Unity.UIWidgets.animation;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
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 TextStyle = Unity.UIWidgets.painting.TextStyle;
namespace Unity.UIWidgets.material {
public class SliderTheme : InheritedWidget {
public SliderTheme(
Key key = null,
SliderThemeData data = null,
Widget child = null)
: base(key: key, child: child) {
D.assert(child != null);
D.assert(data != null);
this.data = data;
}
public readonly SliderThemeData data;
public static SliderThemeData of(BuildContext context) {
SliderTheme inheritedTheme = (SliderTheme) context.inheritFromWidgetOfExactType(typeof(SliderTheme));
return inheritedTheme != null ? inheritedTheme.data : Theme.of(context).sliderTheme;
}
public override bool updateShouldNotify(InheritedWidget oldWidget) {
SliderTheme _oldWidget = (SliderTheme) oldWidget;
return this.data != _oldWidget.data;
}
}
public enum ShowValueIndicator {
onlyForDiscrete,
onlyForContinuous,
always,
never
}
public class SliderThemeData : Diagnosticable {
public SliderThemeData(
Color activeTrackColor = null,
Color inactiveTrackColor = null,
Color disabledActiveTrackColor = null,
Color disabledInactiveTrackColor = null,
Color activeTickMarkColor = null,
Color inactiveTickMarkColor = null,
Color disabledActiveTickMarkColor = null,
Color disabledInactiveTickMarkColor = null,
Color thumbColor = null,
Color disabledThumbColor = null,
Color overlayColor = null,
Color valueIndicatorColor = null,
SliderComponentShape thumbShape = null,
SliderComponentShape valueIndicatorShape = null,
ShowValueIndicator? showValueIndicator = null,
TextStyle valueIndicatorTextStyle = null
) {
D.assert(activeTrackColor != null);
D.assert(inactiveTrackColor != null);
D.assert(disabledActiveTrackColor != null);
D.assert(disabledInactiveTrackColor != null);
D.assert(activeTickMarkColor != null);
D.assert(inactiveTickMarkColor != null);
D.assert(disabledActiveTickMarkColor != null);
D.assert(disabledInactiveTickMarkColor != null);
D.assert(thumbColor != null);
D.assert(disabledThumbColor != null);
D.assert(overlayColor != null);
D.assert(valueIndicatorColor != null);
D.assert(thumbShape != null);
D.assert(valueIndicatorShape != null);
D.assert(valueIndicatorTextStyle != null);
D.assert(showValueIndicator != null);
this.activeTrackColor = activeTrackColor;
this.inactiveTrackColor = inactiveTrackColor;
this.disabledActiveTrackColor = disabledActiveTrackColor;
this.disabledInactiveTrackColor = disabledInactiveTrackColor;
this.activeTickMarkColor = activeTickMarkColor;
this.inactiveTickMarkColor = inactiveTickMarkColor;
this.disabledActiveTickMarkColor = disabledActiveTickMarkColor;
this.disabledInactiveTickMarkColor = disabledInactiveTickMarkColor;
this.thumbColor = thumbColor;
this.disabledThumbColor = disabledThumbColor;
this.overlayColor = overlayColor;
this.valueIndicatorColor = valueIndicatorColor;
this.thumbShape = thumbShape;
this.valueIndicatorShape = valueIndicatorShape;
this.showValueIndicator = showValueIndicator.Value;
this.valueIndicatorTextStyle = valueIndicatorTextStyle;
}
public static SliderThemeData fromPrimaryColors(
Color primaryColor = null,
Color primaryColorDark = null,
Color primaryColorLight = null,
TextStyle valueIndicatorTextStyle = null) {
D.assert(primaryColor != null);
D.assert(primaryColorDark != null);
D.assert(primaryColorLight != null);
D.assert(valueIndicatorTextStyle != null);
const int activeTrackAlpha = 0xff;
const int inactiveTrackAlpha = 0x3d; // 24% opacity
const int disabledActiveTrackAlpha = 0x52; // 32% opacity
const int disabledInactiveTrackAlpha = 0x1f; // 12% opacity
const int activeTickMarkAlpha = 0x8a; // 54% opacity
const int inactiveTickMarkAlpha = 0x8a; // 54% opacity
const int disabledActiveTickMarkAlpha = 0x1f; // 12% opacity
const int disabledInactiveTickMarkAlpha = 0x1f; // 12% opacity
const int thumbAlpha = 0xff;
const int disabledThumbAlpha = 0x52; // 32% opacity
const int valueIndicatorAlpha = 0xff;
const int overlayLightAlpha = 0x29;
return new SliderThemeData(
activeTrackColor: primaryColor.withAlpha(activeTrackAlpha),
inactiveTrackColor: primaryColor.withAlpha(inactiveTrackAlpha),
disabledActiveTrackColor: primaryColorDark.withAlpha(disabledActiveTrackAlpha),
disabledInactiveTrackColor: primaryColorDark.withAlpha(disabledInactiveTrackAlpha),
activeTickMarkColor: primaryColorLight.withAlpha(activeTickMarkAlpha),
inactiveTickMarkColor: primaryColor.withAlpha(inactiveTickMarkAlpha),
disabledActiveTickMarkColor: primaryColorLight.withAlpha(disabledActiveTickMarkAlpha),
disabledInactiveTickMarkColor: primaryColorDark.withAlpha(disabledInactiveTickMarkAlpha),
thumbColor: primaryColor.withAlpha(thumbAlpha),
disabledThumbColor: primaryColorDark.withAlpha(disabledThumbAlpha),
overlayColor: primaryColor.withAlpha(overlayLightAlpha),
valueIndicatorColor: primaryColor.withAlpha(valueIndicatorAlpha),
thumbShape: new RoundSliderThumbShape(),
valueIndicatorShape: new PaddleSliderValueIndicatorShape(),
valueIndicatorTextStyle: valueIndicatorTextStyle,
showValueIndicator: ShowValueIndicator.onlyForDiscrete
);
}
public readonly Color activeTrackColor;
public readonly Color inactiveTrackColor;
public readonly Color disabledActiveTrackColor;
public readonly Color disabledInactiveTrackColor;
public readonly Color activeTickMarkColor;
public readonly Color inactiveTickMarkColor;
public readonly Color disabledActiveTickMarkColor;
public readonly Color disabledInactiveTickMarkColor;
public readonly Color thumbColor;
public readonly Color disabledThumbColor;
public readonly Color overlayColor;
public readonly Color valueIndicatorColor;
public readonly SliderComponentShape thumbShape;
public readonly SliderComponentShape valueIndicatorShape;
public readonly ShowValueIndicator showValueIndicator;
public readonly TextStyle valueIndicatorTextStyle;
public SliderThemeData copyWith(
float? trackHeight = null,
Color activeTrackColor = null,
Color inactiveTrackColor = null,
Color disabledActiveTrackColor = null,
Color disabledInactiveTrackColor = null,
Color activeTickMarkColor = null,
Color inactiveTickMarkColor = null,
Color disabledActiveTickMarkColor = null,
Color disabledInactiveTickMarkColor = null,
Color thumbColor = null,
Color disabledThumbColor = null,
Color overlayColor = null,
Color valueIndicatorColor = null,
SliderTrackShape trackShape = null,
SliderTickMarkShape tickMarkShape = null,
SliderComponentShape thumbShape = null,
SliderComponentShape overlayShape = null,
SliderComponentShape valueIndicatorShape = null,
ShowValueIndicator? showValueIndicator = null,
TextStyle valueIndicatorTextStyle = null
) {
return new SliderThemeData(
activeTrackColor: activeTrackColor ?? this.activeTrackColor,
inactiveTrackColor: inactiveTrackColor ?? this.inactiveTrackColor,
disabledActiveTrackColor: disabledActiveTrackColor ?? this.disabledActiveTrackColor,
disabledInactiveTrackColor: disabledInactiveTrackColor ?? this.disabledInactiveTrackColor,
activeTickMarkColor: activeTickMarkColor ?? this.activeTickMarkColor,
inactiveTickMarkColor: inactiveTickMarkColor ?? this.inactiveTickMarkColor,
disabledActiveTickMarkColor: disabledActiveTickMarkColor ?? this.disabledActiveTickMarkColor,
disabledInactiveTickMarkColor: disabledInactiveTickMarkColor ?? this.disabledInactiveTickMarkColor,
thumbColor: thumbColor ?? this.thumbColor,
disabledThumbColor: disabledThumbColor ?? this.disabledThumbColor,
overlayColor: overlayColor ?? this.overlayColor,
valueIndicatorColor: valueIndicatorColor ?? this.valueIndicatorColor,
thumbShape: thumbShape ?? this.thumbShape,
valueIndicatorShape: valueIndicatorShape ?? this.valueIndicatorShape,
showValueIndicator: showValueIndicator ?? this.showValueIndicator,
valueIndicatorTextStyle: valueIndicatorTextStyle ?? this.valueIndicatorTextStyle
);
}
public static SliderThemeData lerp(SliderThemeData a, SliderThemeData b, float t) {
D.assert(a != null);
D.assert(b != null);
return new SliderThemeData(
activeTrackColor: Color.lerp(a.activeTrackColor, b.activeTrackColor, t),
inactiveTrackColor: Color.lerp(a.inactiveTrackColor, b.inactiveTrackColor, t),
disabledActiveTrackColor: Color.lerp(a.disabledActiveTrackColor, b.disabledActiveTrackColor, t),
disabledInactiveTrackColor: Color.lerp(a.disabledInactiveTrackColor, b.disabledInactiveTrackColor, t),
activeTickMarkColor: Color.lerp(a.activeTickMarkColor, b.activeTickMarkColor, t),
inactiveTickMarkColor: Color.lerp(a.inactiveTickMarkColor, b.inactiveTickMarkColor, t),
disabledActiveTickMarkColor: Color.lerp(a.disabledActiveTickMarkColor, b.disabledActiveTickMarkColor,
t),
disabledInactiveTickMarkColor: Color.lerp(a.disabledInactiveTickMarkColor,
b.disabledInactiveTickMarkColor, t),
thumbColor: Color.lerp(a.thumbColor, b.thumbColor, t),
disabledThumbColor: Color.lerp(a.disabledThumbColor, b.disabledThumbColor, t),
overlayColor: Color.lerp(a.overlayColor, b.overlayColor, t),
valueIndicatorColor: Color.lerp(a.valueIndicatorColor, b.valueIndicatorColor, t),
thumbShape: t < 0.5 ? a.thumbShape : b.thumbShape,
valueIndicatorShape: t < 0.5 ? a.valueIndicatorShape : b.valueIndicatorShape,
showValueIndicator: t < 0.5 ? a.showValueIndicator : b.showValueIndicator,
valueIndicatorTextStyle: TextStyle.lerp(a.valueIndicatorTextStyle, b.valueIndicatorTextStyle, t)
);
}
public bool Equals(SliderThemeData other) {
if (ReferenceEquals(null, other)) {
return false;
}
if (ReferenceEquals(this, other)) {
return true;
}
return other.activeTrackColor == this.activeTrackColor
&& other.inactiveTrackColor == this.inactiveTrackColor
&& other.disabledActiveTrackColor == this.disabledActiveTrackColor
&& other.disabledInactiveTrackColor == this.disabledInactiveTrackColor
&& other.activeTickMarkColor == this.activeTickMarkColor
&& other.inactiveTickMarkColor == this.inactiveTickMarkColor
&& other.disabledActiveTickMarkColor == this.disabledActiveTickMarkColor
&& other.disabledInactiveTickMarkColor == this.disabledInactiveTickMarkColor
&& other.thumbColor == this.thumbColor
&& other.disabledThumbColor == this.disabledThumbColor
&& other.overlayColor == this.overlayColor
&& other.valueIndicatorColor == this.valueIndicatorColor
&& other.thumbShape == this.thumbShape
&& other.valueIndicatorShape == this.valueIndicatorShape
&& other.showValueIndicator == this.showValueIndicator
&& other.valueIndicatorTextStyle == this.valueIndicatorTextStyle;
}
public override bool Equals(object obj) {
if (ReferenceEquals(null, obj)) {
return false;
}
if (ReferenceEquals(this, obj)) {
return true;
}
if (obj.GetType() != this.GetType()) {
return false;
}
return this.Equals((SliderThemeData) obj);
}
public static bool operator ==(SliderThemeData left, SliderThemeData right) {
return Equals(left, right);
}
public static bool operator !=(SliderThemeData left, SliderThemeData right) {
return !Equals(left, right);
}
int? _cachedHashCode = null;
public override int GetHashCode() {
if (this._cachedHashCode != null) {
return this._cachedHashCode.Value;
}
unchecked {
var hashCode = this.activeTrackColor.GetHashCode();
hashCode = (hashCode * 397) ^ this.inactiveTrackColor.GetHashCode();
hashCode = (hashCode * 397) ^ this.disabledActiveTrackColor.GetHashCode();
hashCode = (hashCode * 397) ^ this.disabledInactiveTrackColor.GetHashCode();
hashCode = (hashCode * 397) ^ this.activeTickMarkColor.GetHashCode();
hashCode = (hashCode * 397) ^ this.inactiveTickMarkColor.GetHashCode();
hashCode = (hashCode * 397) ^ this.disabledActiveTickMarkColor.GetHashCode();
hashCode = (hashCode * 397) ^ this.disabledInactiveTickMarkColor.GetHashCode();
hashCode = (hashCode * 397) ^ this.thumbColor.GetHashCode();
hashCode = (hashCode * 397) ^ this.disabledThumbColor.GetHashCode();
hashCode = (hashCode * 397) ^ this.overlayColor.GetHashCode();
hashCode = (hashCode * 397) ^ this.valueIndicatorColor.GetHashCode();
hashCode = (hashCode * 397) ^ this.thumbShape.GetHashCode();
hashCode = (hashCode * 397) ^ this.valueIndicatorShape.GetHashCode();
hashCode = (hashCode * 397) ^ this.showValueIndicator.GetHashCode();
hashCode = (hashCode * 397) ^ this.valueIndicatorTextStyle.GetHashCode();
this._cachedHashCode = hashCode;
return hashCode;
}
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
ThemeData defaultTheme = ThemeData.fallback();
SliderThemeData defaultData = fromPrimaryColors(
primaryColor: defaultTheme.primaryColor,
primaryColorDark: defaultTheme.primaryColorDark,
primaryColorLight: defaultTheme.primaryColorLight,
valueIndicatorTextStyle: defaultTheme.accentTextTheme.body2
);
properties.add(new DiagnosticsProperty<Color>("activeTrackColor", this.activeTrackColor,
defaultValue: defaultData.activeTrackColor));
properties.add(new DiagnosticsProperty<Color>("activeTrackColor", this.activeTrackColor,
defaultValue: defaultData.activeTrackColor));
properties.add(new DiagnosticsProperty<Color>("inactiveTrackColor", this.inactiveTrackColor,
defaultValue: defaultData.inactiveTrackColor));
properties.add(new DiagnosticsProperty<Color>("disabledActiveTrackColor", this.disabledActiveTrackColor,
defaultValue: defaultData.disabledActiveTrackColor, level: DiagnosticLevel.debug));
properties.add(new DiagnosticsProperty<Color>("disabledInactiveTrackColor", this.disabledInactiveTrackColor,
defaultValue: defaultData.disabledInactiveTrackColor, level: DiagnosticLevel.debug));
properties.add(new DiagnosticsProperty<Color>("activeTickMarkColor", this.activeTickMarkColor,
defaultValue: defaultData.activeTickMarkColor, level: DiagnosticLevel.debug));
properties.add(new DiagnosticsProperty<Color>("inactiveTickMarkColor", this.inactiveTickMarkColor,
defaultValue: defaultData.inactiveTickMarkColor, level: DiagnosticLevel.debug));
properties.add(new DiagnosticsProperty<Color>("disabledActiveTickMarkColor",
this.disabledActiveTickMarkColor, defaultValue: defaultData.disabledActiveTickMarkColor,
level: DiagnosticLevel.debug));
properties.add(new DiagnosticsProperty<Color>("disabledInactiveTickMarkColor",
this.disabledInactiveTickMarkColor, defaultValue: defaultData.disabledInactiveTickMarkColor,
level: DiagnosticLevel.debug));
properties.add(new DiagnosticsProperty<Color>("thumbColor", this.thumbColor,
defaultValue: defaultData.thumbColor));
properties.add(new DiagnosticsProperty<Color>("disabledThumbColor", this.disabledThumbColor,
defaultValue: defaultData.disabledThumbColor, level: DiagnosticLevel.debug));
properties.add(new DiagnosticsProperty<Color>("overlayColor", this.overlayColor,
defaultValue: defaultData.overlayColor, level: DiagnosticLevel.debug));
properties.add(new DiagnosticsProperty<Color>("valueIndicatorColor", this.valueIndicatorColor,
defaultValue: defaultData.valueIndicatorColor));
properties.add(new DiagnosticsProperty<SliderComponentShape>("thumbShape", this.thumbShape,
defaultValue: defaultData.thumbShape, level: DiagnosticLevel.debug));
properties.add(new DiagnosticsProperty<SliderComponentShape>("valueIndicatorShape",
this.valueIndicatorShape, defaultValue: defaultData.valueIndicatorShape, level: DiagnosticLevel.debug));
properties.add(new EnumProperty<ShowValueIndicator>("showValueIndicator", this.showValueIndicator,
defaultValue: defaultData.showValueIndicator));
properties.add(new DiagnosticsProperty<TextStyle>("valueIndicatorTextStyle", this.valueIndicatorTextStyle,
defaultValue: defaultData.valueIndicatorTextStyle));
}
}
public abstract class SliderTrackShape {
public SliderTrackShape() {
}
public abstract Rect getPreferredRect(
RenderBox parentBox = null,
Offset offset = null,
SliderThemeData sliderTheme = null,
bool? isEnabled = null,
bool? isDiscrete = null);
public abstract void paint(
PaintingContext context,
Offset offset,
RenderBox parentBox = null,
SliderThemeData sliderTheme = null,
Animation<float> enableAnimation = null,
Offset thumbCenter = null,
bool? isEnabled = null,
bool? isDiscrete = null
);
}
public abstract class SliderTickMarkShape {
public SliderTickMarkShape() {
}
public abstract Size getPreferredSize(
SliderThemeData sliderTheme = null,
bool? isEnabled = null);
public abstract void paint(
PaintingContext context,
Offset offset,
RenderBox parentBox = null,
SliderThemeData sliderTheme = null,
Animation<float> enableAnimation = null,
Offset thumbCenter = null,
bool? isEnabled = null);
public static readonly SliderTickMarkShape noTickMark = new _EmptySliderTickMarkShape();
}
class _EmptySliderTickMarkShape : SliderTickMarkShape {
public override Size getPreferredSize(
SliderThemeData sliderTheme = null,
bool? isEnabled = null) {
return Size.zero;
}
public override void paint(
PaintingContext context,
Offset offset,
RenderBox parentBox = null,
SliderThemeData sliderTheme = null,
Animation<float> enableAnimation = null,
Offset thumbCenter = null,
bool? isEnabled = null) {
}
}
public abstract class SliderComponentShape {
public SliderComponentShape() {
}
public abstract Size getPreferredSize(
bool? isEnabled,
bool? isDiscrete);
public abstract void paint(
PaintingContext context,
Offset thumbCenter,
Animation<float> activationAnimation = null,
Animation<float> enableAnimation = null,
bool? isDiscrete = null,
TextPainter labelPainter = null,
RenderBox parentBox = null,
SliderThemeData sliderTheme = null,
float? value = null);
public static readonly SliderComponentShape noThumb = new _EmptySliderComponentShape();
public static readonly SliderComponentShape noOverlay = new _EmptySliderComponentShape();
}
class _EmptySliderComponentShape : SliderComponentShape {
public override Size getPreferredSize(
bool? isEnabled,
bool? isDiscrete) {
return Size.zero;
}
public override void paint(
PaintingContext context,
Offset thumbCenter,
Animation<float> activationAnimation = null,
Animation<float> enableAnimation = null,
bool? isDiscrete = null,
TextPainter labelPainter = null,
RenderBox parentBox = null,
SliderThemeData sliderTheme = null,
float? value = null) {
}
}
public class RoundSliderThumbShape : SliderComponentShape {
public RoundSliderThumbShape(
float enabledThumbRadius = 6.0f,
float? disabledThumbRadius = null
) {
this.enabledThumbRadius = enabledThumbRadius;
this.disabledThumbRadius = disabledThumbRadius;
}
public readonly float enabledThumbRadius;
public readonly float? disabledThumbRadius;
float _disabledThumbRadius {
get { return this.disabledThumbRadius ?? this.enabledThumbRadius * 2f / 3f; }
}
public override Size getPreferredSize(bool? isEnabled, bool? isDiscrete) {
return Size.fromRadius(isEnabled.Value ? this.enabledThumbRadius : this._disabledThumbRadius);
}
public override void paint(
PaintingContext context,
Offset center,
Animation<float> activationAnimation = null,
Animation<float> enableAnimation = null,
bool? isDiscrete = null,
TextPainter labelPainter = null,
RenderBox parentBox = null,
SliderThemeData sliderTheme = null,
float? value = null
) {
Canvas canvas = context.canvas;
FloatTween radiusTween = new FloatTween(
begin: this._disabledThumbRadius,
end: this.enabledThumbRadius
);
ColorTween colorTween = new ColorTween(
begin: sliderTheme.disabledThumbColor,
end: sliderTheme.thumbColor
);
canvas.drawCircle(
center,
radiusTween.evaluate(enableAnimation),
new Paint {color = colorTween.evaluate(enableAnimation)}
);
}
}
public class PaddleSliderValueIndicatorShape : SliderComponentShape {
public PaddleSliderValueIndicatorShape() {
}
const float _topLobeRadius = 16.0f;
const float _labelTextDesignSize = 14.0f;
const float _bottomLobeRadius = 6.0f;
const float _bottomLobeStartAngle = -1.1f * Mathf.PI / 4.0f;
const float _bottomLobeEndAngle = 1.1f * 5 * Mathf.PI / 4.0f;
const float _labelPadding = 8.0f;
const float _distanceBetweenTopBottomCenters = 40.0f;
static readonly Offset _topLobeCenter = new Offset(0.0f, -_distanceBetweenTopBottomCenters);
const float _topNeckRadius = 14.0f;
const float _neckTriangleHypotenuse = _topLobeRadius + _topNeckRadius;
const float _twoSeventyDegrees = 3.0f * Mathf.PI / 2.0f;
const float _ninetyDegrees = Mathf.PI / 2.0f;
const float _thirtyDegrees = Mathf.PI / 6.0f;
static readonly Size _preferredSize =
Size.fromHeight(_distanceBetweenTopBottomCenters + _topLobeRadius + _bottomLobeRadius);
const bool _debuggingLabelLocation = false;
static Path _bottomLobePath;
static Offset _bottomLobeEnd;
public override Size getPreferredSize(
bool? isEnabled,
bool? isDiscrete) {
return _preferredSize;
}
static void _addArc(Path path, Offset center, float radius, float startAngle, float endAngle) {
Rect arcRect = Rect.fromCircle(center: center, radius: radius);
path.arcTo(arcRect, startAngle, endAngle - startAngle, false);
}
static void _generateBottomLobe() {
const float bottomNeckRadius = 4.5f;
const float bottomNeckStartAngle = _bottomLobeEndAngle - Mathf.PI;
const float bottomNeckEndAngle = 0.0f;
Path path = new Path();
Offset bottomKnobStart = new Offset(
_bottomLobeRadius * Mathf.Cos(_bottomLobeStartAngle),
_bottomLobeRadius * Mathf.Sin(_bottomLobeStartAngle)
);
Offset bottomNeckRightCenter = bottomKnobStart +
new Offset(
bottomNeckRadius * Mathf.Cos(bottomNeckStartAngle),
-bottomNeckRadius * Mathf.Sin(bottomNeckStartAngle)
);
Offset bottomNeckLeftCenter = new Offset(
-bottomNeckRightCenter.dx,
bottomNeckRightCenter.dy
);
Offset bottomNeckStartRight = new Offset(
bottomNeckRightCenter.dx - bottomNeckRadius,
bottomNeckRightCenter.dy
);
path.moveTo(bottomNeckStartRight.dx, bottomNeckStartRight.dy);
_addArc(
path,
bottomNeckRightCenter,
bottomNeckRadius,
Mathf.PI - bottomNeckEndAngle,
Mathf.PI - bottomNeckStartAngle
);
_addArc(
path,
Offset.zero,
_bottomLobeRadius,
_bottomLobeStartAngle,
_bottomLobeEndAngle
);
_addArc(
path,
bottomNeckLeftCenter,
bottomNeckRadius,
bottomNeckStartAngle,
bottomNeckEndAngle
);
_bottomLobeEnd = new Offset(
-bottomNeckStartRight.dx,
bottomNeckStartRight.dy
);
_bottomLobePath = path;
}
Offset _addBottomLobe(Path path) {
if (_bottomLobePath == null || _bottomLobeEnd == null) {
_generateBottomLobe();
}
path.addPath(_bottomLobePath, Offset.zero);
return _bottomLobeEnd;
}
float _getIdealOffset(
RenderBox parentBox,
float halfWidthNeeded,
float scale,
Offset center
) {
const float edgeMargin = 4.0f;
Rect topLobeRect = Rect.fromLTWH(
-_topLobeRadius - halfWidthNeeded,
-_topLobeRadius - _distanceBetweenTopBottomCenters,
2.0f * (_topLobeRadius + halfWidthNeeded),
2.0f * _topLobeRadius
);
Offset topLeft = (topLobeRect.topLeft * scale) + center;
Offset bottomRight = (topLobeRect.bottomRight * scale) + center;
float shift = 0.0f;
if (topLeft.dx < edgeMargin) {
shift = edgeMargin - topLeft.dx;
}
if (bottomRight.dx > parentBox.size.width - edgeMargin) {
shift = parentBox.size.width - bottomRight.dx - edgeMargin;
}
shift = scale == 0.0f ? 0.0f : shift / scale;
return shift;
}
void _drawValueIndicator(
RenderBox parentBox,
Canvas canvas,
Offset center,
Paint paint,
float scale,
TextPainter labelPainter
) {
canvas.save();
canvas.translate(center.dx, center.dy);
float textScaleFactor = labelPainter.height / _labelTextDesignSize;
float overallScale = scale * textScaleFactor;
canvas.scale(overallScale, overallScale);
float inverseTextScale = textScaleFactor != 0 ? 1.0f / textScaleFactor : 0.0f;
float labelHalfWidth = labelPainter.width / 2.0f;
float halfWidthNeeded = Mathf.Max(
0.0f,
inverseTextScale * labelHalfWidth - (_topLobeRadius - _labelPadding)
);
float shift = this._getIdealOffset(parentBox, halfWidthNeeded, overallScale, center);
float leftWidthNeeded;
float rightWidthNeeded;
if (shift < 0.0) {
shift = Mathf.Max(shift, -halfWidthNeeded);
}
else {
shift = Mathf.Min(shift, halfWidthNeeded);
}
rightWidthNeeded = halfWidthNeeded + shift;
leftWidthNeeded = halfWidthNeeded - shift;
Path path = new Path();
Offset bottomLobeEnd = this._addBottomLobe(path);
float neckTriangleBase = _topNeckRadius - bottomLobeEnd.dx;
float leftAmount = Mathf.Max(0.0f, Mathf.Min(1.0f, leftWidthNeeded / neckTriangleBase));
float rightAmount = Mathf.Max(0.0f, Mathf.Min(1.0f, rightWidthNeeded / neckTriangleBase));
float leftTheta = (1.0f - leftAmount) * _thirtyDegrees;
float rightTheta = (1.0f - rightAmount) * _thirtyDegrees;
Offset neckLeftCenter = new Offset(
-neckTriangleBase,
_topLobeCenter.dy + Mathf.Cos(leftTheta) * _neckTriangleHypotenuse
);
Offset neckRightCenter = new Offset(
neckTriangleBase,
_topLobeCenter.dy + Mathf.Cos(rightTheta) * _neckTriangleHypotenuse
);
float leftNeckArcAngle = _ninetyDegrees - leftTheta;
float rightNeckArcAngle = Mathf.PI + _ninetyDegrees - rightTheta;
float neckStretchBaseline = bottomLobeEnd.dy - Mathf.Max(neckLeftCenter.dy, neckRightCenter.dy);
float t = Mathf.Pow(inverseTextScale, 3.0f);
float stretch = (neckStretchBaseline * t).clamp(0.0f, 10.0f * neckStretchBaseline);
Offset neckStretch = new Offset(0.0f, neckStretchBaseline - stretch);
D.assert(() => {
if (!_debuggingLabelLocation) {
return true;
}
Offset leftCenter = _topLobeCenter - new Offset(leftWidthNeeded, 0.0f) + neckStretch;
Offset rightCenter = _topLobeCenter + new Offset(rightWidthNeeded, 0.0f) + neckStretch;
Rect valueRect = Rect.fromLTRB(
leftCenter.dx - _topLobeRadius,
leftCenter.dy - _topLobeRadius,
rightCenter.dx + _topLobeRadius,
rightCenter.dy + _topLobeRadius
);
Paint outlinePaint = new Paint();
outlinePaint.color = new Color(0xffff0000);
outlinePaint.style = PaintingStyle.stroke;
outlinePaint.strokeWidth = 1.0f;
canvas.drawRect(valueRect, outlinePaint);
return true;
});
_addArc(
path,
neckLeftCenter + neckStretch,
_topNeckRadius,
0.0f,
-leftNeckArcAngle
);
_addArc(
path,
_topLobeCenter - new Offset(leftWidthNeeded, 0.0f) + neckStretch,
_topLobeRadius,
_ninetyDegrees + leftTheta,
_twoSeventyDegrees
);
_addArc(
path,
_topLobeCenter + new Offset(rightWidthNeeded, 0.0f) + neckStretch,
_topLobeRadius,
_twoSeventyDegrees,
_twoSeventyDegrees + Mathf.PI - rightTheta
);
_addArc(
path,
neckRightCenter + neckStretch,
_topNeckRadius,
rightNeckArcAngle,
Mathf.PI
);
canvas.drawPath(path, paint);
canvas.save();
canvas.translate(shift, -_distanceBetweenTopBottomCenters + neckStretch.dy);
canvas.scale(inverseTextScale, inverseTextScale);
labelPainter.paint(canvas, Offset.zero - new Offset(labelHalfWidth, labelPainter.height / 2.0f));
canvas.restore();
canvas.restore();
}
public override void paint(
PaintingContext context,
Offset center,
Animation<float> activationAnimation = null,
Animation<float> enableAnimation = null,
bool? isDiscrete = null,
TextPainter labelPainter = null,
RenderBox parentBox = null,
SliderThemeData sliderTheme = null,
float? value = null) {
ColorTween enableColor = new ColorTween(
begin: sliderTheme.disabledThumbColor,
end: sliderTheme.valueIndicatorColor
);
this._drawValueIndicator(
parentBox,
context.canvas,
center,
new Paint {color = enableColor.evaluate(enableAnimation)},
activationAnimation.value,
labelPainter
);
}
}
}

11
Runtime/material/slider_theme.cs.meta


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