浏览代码

Merge branches 'kgdev' and 'master' of github.com:UnityTech/UIWidgets into kgdev

/main
kg 6 年前
当前提交
0f456b29
共有 10 个文件被更改,包括 907 次插入156 次删除
  1. 26
      Runtime/Plugins/platform/ios/UIWidgetsViewController.mm
  2. 2
      Runtime/gestures/constants.cs
  3. 232
      Runtime/material/slider.cs
  4. 213
      Runtime/material/slider_theme.cs
  5. 80
      Runtime/widgets/gesture_detector.cs
  6. 12
      Samples/UIWidgetSample/MaterialSample.cs
  7. 412
      Runtime/gestures/scale.cs
  8. 11
      Runtime/gestures/scale.cs.meta
  9. 64
      Samples/UIWidgetSample/ScaleGestureSample.cs
  10. 11
      Samples/UIWidgetSample/ScaleGestureSample.cs.meta

26
Runtime/Plugins/platform/ios/UIWidgetsViewController.mm


CGFloat bottom = CGRectGetHeight([[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]);
CGFloat scale = [UIScreen mainScreen].scale;
// scale == 3 => screen is 1242 * 2208 => we have to perform down-sampling to obtain the real length
// 0.8696 = 1920 / 2208, the vertical down-sampling ratio
if (scale == 3) {
bottom = bottom * 0.8696;
}
viewInsets.bottom = bottom * scale;
padding.bottom = 0;

viewMetrics metrics;
viewPadding insets = [[UIWidgetsViewController sharedInstance] viewInsets];
viewPadding padding = [[UIWidgetsViewController sharedInstance] padding];
metrics.insets_bottom = insets.bottom;
metrics.insets_top = insets.top;
metrics.insets_left = insets.left;
metrics.insets_right = insets.right;
metrics.padding_bottom = padding.bottom;
metrics.padding_top = padding.top;
metrics.padding_left = padding.left;
metrics.padding_right = padding.right;
CGFloat scale = [UIScreen mainScreen].scale;
BOOL needDownsample = scale == 3;
metrics.insets_bottom = needDownsample ? insets.bottom * 0.8696 : insets.bottom;
metrics.insets_top = needDownsample ? insets.top * 0.8696 : insets.top;
metrics.insets_left = needDownsample ? insets.left * 0.8696 : insets.left;
metrics.insets_right = needDownsample ? insets.right * 0.8696 : insets.right;
metrics.padding_bottom = needDownsample ? padding.bottom * 0.8696 : padding.bottom;
metrics.padding_top = needDownsample ? padding.top * 0.8696 : padding.top;
metrics.padding_left = needDownsample ? padding.left * 0.8696 : padding.left;
metrics.padding_right = needDownsample ? padding.right * 0.8696 : padding.right;
return metrics;
}

2
Runtime/gestures/constants.cs


public const float kPanSlop = kTouchSlop * 2.0f;
public const float kScaleSlop = kTouchSlop;
public static readonly TimeSpan kPressTimeout = new TimeSpan(0, 0, 0, 0, 100);
public static readonly TimeSpan kDoubleTapTimeout = new TimeSpan(0, 0, 0, 0, 300);

232
Runtime/material/slider.cs


using System;
using System.Collections.Generic;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.async;
using Unity.UIWidgets.foundation;

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;

}
class _RenderSlider : RenderBox {
static float _positionAnimationDurationMilliSeconds = 75;
static float _minimumInteractionTimeMilliSeconds = 500;
const float _minPreferredTrackWidth = 144.0f;
public _RenderSlider(
float? value = null,
int? divisions = null,

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);
float _maxSliderPartWidth {
get {
float maxValue = 0;
foreach (Size size in this._sliderPartSizes) {
if (size.width > maxValue) {
maxValue = size.width;
}
}
return maxValue;
}
}
float _maxSliderPartHeight {
get {
float maxValue = 0;
foreach (Size size in this._sliderPartSizes) {
if (size.width > maxValue) {
maxValue = size.width;
}
}
return maxValue;
}
}
List<Size> _sliderPartSizes {
get {
return new List<Size> {
this._sliderTheme.overlayShape.getPreferredSize(this.isInteractive, this.isDiscrete),
this._sliderTheme.thumbShape.getPreferredSize(this.isInteractive, this.isDiscrete),
this._sliderTheme.tickMarkShape.getPreferredSize(isEnabled: this.isInteractive,
sliderTheme: this.sliderTheme)
};
}
}
float _minPreferredTrackHeight {
get { return this._sliderTheme.trackHeight; }
}
_SliderState _state;
Animation<float> _overlayAnimation;

bool _active = false;
float _currentDragValue = 0.0f;
float _trackLength {
get { return this.size.width - _overlayDiameter; }
Rect _trackRect {
get {
return this._sliderTheme.trackShape.getPreferredRect(
parentBox: this,
offset: Offset.zero,
sliderTheme: this._sliderTheme,
isDiscrete: false
);
}
}
bool isInteractive {

float _getValueFromGlobalPosition(Offset globalPosition) {
float visualPosition =
(this.globalToLocal(globalPosition).dx - _overlayRadius) / this._trackLength;
(this.globalToLocal(globalPosition).dx - this._trackRect.left) / this._trackRect.width;
return this._getValueFromVisualPosition(visualPosition);
}

result = (result * this.divisions.Value).round() / this.divisions.Value;
result = (result * this.divisions.Value).round() * 1.0f / this.divisions.Value;
}
return result;

this._state.valueIndicatorController.status == AnimationStatus.completed) {
this._state.valueIndicatorController.reverse();
}
});
}
);
}
}
}

void _handleDragUpdate(DragUpdateDetails details) {
if (this.isInteractive) {
float valueDelta = details.primaryDelta.Value / this._trackLength;
float valueDelta = details.primaryDelta.Value / this._trackRect.width;
this._currentDragValue += valueDelta;
this.onChanged(this._discretize(this._currentDragValue));
}

protected override float computeMinIntrinsicWidth(float height) {
return Mathf.Max(_overlayDiameter,
this._sliderTheme.thumbShape.getPreferredSize(this.isInteractive, this.isDiscrete).width);
return _minPreferredTrackWidth + this._maxSliderPartWidth;
return _preferredTotalWidth;
return _minPreferredTrackWidth + this._maxSliderPartWidth;
return _overlayDiameter;
return Mathf.Max(this._minPreferredTrackHeight, this._maxSliderPartHeight);
return _overlayDiameter;
return Mathf.Max(this._minPreferredTrackHeight, this._maxSliderPartHeight);
}
protected override bool sizedByParent {

this.size = new Size(
this.constraints.hasBoundedWidth
? this.constraints.maxWidth
: _preferredTotalWidth,
: _minPreferredTrackWidth + this._maxSliderPartWidth,
: _overlayDiameter
: Mathf.Max(this._minPreferredTrackHeight, this._maxSliderPartHeight)
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);
}
Canvas canvas = context.canvas;
float trackLength = this.size.width - 2 * _overlayRadius;
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)};
Paint leftTrackPaint = activeTrackPaint;
Paint rightTrackPaint = inactiveTrackPaint;
Paint leftTickMarkPaint = activeTickMarkPaint;
Paint rightTickMarkPaint = inactiveTickMarkPaint;
float trackRadius = _trackHeight / 2.0f;
float thumbGap = 2.0f;
Rect trackRect = this._sliderTheme.trackShape.getPreferredRect(
parentBox: this,
offset: offset,
sliderTheme: this._sliderTheme,
isDiscrete: this.isDiscrete
);
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(trackRect.left + visualPosition * trackRect.width, trackRect.center.dy);
Offset thumbCenter = new Offset(trackActive, trackVerticalCenter);
if (visualPosition > 0.0) {
canvas.drawRect(trackLeftRect, leftTrackPaint);
}
this._sliderTheme.trackShape.paint(
context,
offset,
parentBox: this,
sliderTheme: this._sliderTheme,
enableAnimation: this._enableAnimation,
thumbCenter: thumbCenter,
isDiscrete: this.isDiscrete,
isEnabled: this.isInteractive
);
if (visualPosition < 1.0) {
canvas.drawRect(trackRightRect, rightTrackPaint);
if (!this._overlayAnimation.isDismissed) {
this._sliderTheme.overlayShape.paint(
context,
thumbCenter,
activationAnimation: this._overlayAnimation,
enableAnimation: this._enableAnimation,
isDiscrete: this.isDiscrete,
labelPainter: this._labelPainter,
parentBox: this,
sliderTheme: this._sliderTheme,
value: this._value
);
this._paintOverlay(canvas, thumbCenter);
if (this.isDiscrete) {
float tickMarkWidth = this._sliderTheme.tickMarkShape.getPreferredSize(
isEnabled: this.isInteractive,
sliderTheme: this._sliderTheme
).width;
this._paintTickMarks(
canvas,
trackLeftRect,
trackRightRect,
leftTickMarkPaint,
rightTickMarkPaint
);
if ((trackRect.width - tickMarkWidth) / this.divisions.Value >= 3.0f * tickMarkWidth) {
for (int i = 0; i <= this.divisions; i++) {
float tickValue = i / this.divisions.Value;
float tickX = trackRect.left +
tickValue * (trackRect.width - tickMarkWidth) + tickMarkWidth / 2;
float tickY = trackRect.center.dy;
Offset tickMarkOffset = new Offset(tickX, tickY);
this._sliderTheme.tickMarkShape.paint(
context,
tickMarkOffset,
parentBox: this,
sliderTheme: this._sliderTheme,
enableAnimation: this._enableAnimation,
thumbCenter: thumbCenter,
isEnabled: this.isInteractive
);
}
}
}
if (this.isInteractive && this.label != null &&
this._valueIndicatorAnimation.status != AnimationStatus.dismissed) {
if (this.isInteractive && this.label != null && !this._valueIndicatorAnimation.isDismissed) {
if (this.showValueIndicator) {
this._sliderTheme.valueIndicatorShape.paint(
context,

213
Runtime/material/slider_theme.cs


public class SliderThemeData : Diagnosticable {
public SliderThemeData(
float? trackHeight = null,
Color activeTrackColor = null,
Color inactiveTrackColor = null,
Color disabledActiveTrackColor = null,

Color disabledThumbColor = null,
Color overlayColor = null,
Color valueIndicatorColor = null,
SliderTrackShape trackShape = null,
SliderTickMarkShape tickMarkShape = null,
SliderComponentShape overlayShape = null,
D.assert(trackHeight != null);
D.assert(activeTrackColor != null);
D.assert(inactiveTrackColor != null);
D.assert(disabledActiveTrackColor != null);

D.assert(disabledThumbColor != null);
D.assert(overlayColor != null);
D.assert(valueIndicatorColor != null);
D.assert(trackShape != null);
D.assert(tickMarkShape != null);
D.assert(overlayShape != null);
this.trackHeight = trackHeight.Value;
this.activeTrackColor = activeTrackColor;
this.inactiveTrackColor = inactiveTrackColor;
this.disabledActiveTrackColor = disabledActiveTrackColor;

this.disabledThumbColor = disabledThumbColor;
this.overlayColor = overlayColor;
this.valueIndicatorColor = valueIndicatorColor;
this.trackShape = trackShape;
this.tickMarkShape = tickMarkShape;
this.overlayShape = overlayShape;
this.valueIndicatorShape = valueIndicatorShape;
this.showValueIndicator = showValueIndicator.Value;
this.valueIndicatorTextStyle = valueIndicatorTextStyle;

const int overlayLightAlpha = 0x29;
return new SliderThemeData(
trackHeight: 2.0f,
activeTrackColor: primaryColor.withAlpha(activeTrackAlpha),
inactiveTrackColor: primaryColor.withAlpha(inactiveTrackAlpha),
disabledActiveTrackColor: primaryColorDark.withAlpha(disabledActiveTrackAlpha),

disabledThumbColor: primaryColorDark.withAlpha(disabledThumbAlpha),
overlayColor: primaryColor.withAlpha(overlayLightAlpha),
valueIndicatorColor: primaryColor.withAlpha(valueIndicatorAlpha),
trackShape: new RectangularSliderTrackShape(),
tickMarkShape: new RoundSliderTickMarkShape(),
overlayShape: new RoundSliderOverlayShape(),
public readonly float trackHeight;
public readonly Color activeTrackColor;

public readonly Color valueIndicatorColor;
public readonly SliderTrackShape trackShape;
public readonly SliderTickMarkShape tickMarkShape;
public readonly SliderComponentShape overlayShape;
public readonly SliderComponentShape thumbShape;
public readonly SliderComponentShape valueIndicatorShape;

TextStyle valueIndicatorTextStyle = null
) {
return new SliderThemeData(
trackHeight: trackHeight ?? this.trackHeight,
activeTrackColor: activeTrackColor ?? this.activeTrackColor,
inactiveTrackColor: inactiveTrackColor ?? this.inactiveTrackColor,
disabledActiveTrackColor: disabledActiveTrackColor ?? this.disabledActiveTrackColor,

disabledThumbColor: disabledThumbColor ?? this.disabledThumbColor,
overlayColor: overlayColor ?? this.overlayColor,
valueIndicatorColor: valueIndicatorColor ?? this.valueIndicatorColor,
trackShape: trackShape ?? this.trackShape,
tickMarkShape: tickMarkShape ?? this.tickMarkShape,
overlayShape: overlayShape ?? this.overlayShape,
valueIndicatorShape: valueIndicatorShape ?? this.valueIndicatorShape,
showValueIndicator: showValueIndicator ?? this.showValueIndicator,
valueIndicatorTextStyle: valueIndicatorTextStyle ?? this.valueIndicatorTextStyle

D.assert(a != null);
D.assert(b != null);
return new SliderThemeData(
trackHeight: MathUtils.lerpFloat(a.trackHeight, b.trackHeight, t),
activeTrackColor: Color.lerp(a.activeTrackColor, b.activeTrackColor, t),
inactiveTrackColor: Color.lerp(a.inactiveTrackColor, b.inactiveTrackColor, t),
disabledActiveTrackColor: Color.lerp(a.disabledActiveTrackColor, b.disabledActiveTrackColor, 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),
trackShape: t < 0.5 ? a.trackShape : b.trackShape,
tickMarkShape: t < 0.5 ? a.tickMarkShape : b.tickMarkShape,
overlayShape: t < 0.5 ? a.overlayShape : b.overlayShape,
valueIndicatorShape: t < 0.5 ? a.valueIndicatorShape : b.valueIndicatorShape,
showValueIndicator: t < 0.5 ? a.showValueIndicator : b.showValueIndicator,
valueIndicatorTextStyle: TextStyle.lerp(a.valueIndicatorTextStyle, b.valueIndicatorTextStyle, t)

return true;
}
return other.activeTrackColor == this.activeTrackColor
return other.trackHeight == this.trackHeight
&& other.activeTrackColor == this.activeTrackColor
&& other.inactiveTrackColor == this.inactiveTrackColor
&& other.disabledActiveTrackColor == this.disabledActiveTrackColor
&& other.disabledInactiveTrackColor == this.disabledInactiveTrackColor

&& other.disabledThumbColor == this.disabledThumbColor
&& other.overlayColor == this.overlayColor
&& other.valueIndicatorColor == this.valueIndicatorColor
&& other.trackShape == this.trackShape
&& other.tickMarkShape == this.tickMarkShape
&& other.overlayShape == this.overlayShape
&& other.valueIndicatorShape == this.valueIndicatorShape
&& other.showValueIndicator == this.showValueIndicator
&& other.valueIndicatorTextStyle == this.valueIndicatorTextStyle;

}
unchecked {
var hashCode = this.activeTrackColor.GetHashCode();
var hashCode = this.trackHeight.GetHashCode();
hashCode = (hashCode * 397) ^ 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.disabledThumbColor.GetHashCode();
hashCode = (hashCode * 397) ^ this.overlayColor.GetHashCode();
hashCode = (hashCode * 397) ^ this.valueIndicatorColor.GetHashCode();
hashCode = (hashCode * 397) ^ this.trackShape.GetHashCode();
hashCode = (hashCode * 397) ^ this.tickMarkShape.GetHashCode();
hashCode = (hashCode * 397) ^ this.overlayShape.GetHashCode();
hashCode = (hashCode * 397) ^ this.valueIndicatorShape.GetHashCode();
hashCode = (hashCode * 397) ^ this.showValueIndicator.GetHashCode();
hashCode = (hashCode * 397) ^ this.valueIndicatorTextStyle.GetHashCode();

defaultValue: defaultData.overlayColor, level: DiagnosticLevel.debug));
properties.add(new DiagnosticsProperty<Color>("valueIndicatorColor", this.valueIndicatorColor,
defaultValue: defaultData.valueIndicatorColor));
properties.add(new DiagnosticsProperty<SliderTrackShape>("trackShape", this.trackShape,
defaultValue: defaultData.trackShape, level: DiagnosticLevel.debug));
properties.add(new DiagnosticsProperty<SliderTickMarkShape>("tickMarkShape", this.tickMarkShape,
defaultValue: defaultData.tickMarkShape, level: DiagnosticLevel.debug));
properties.add(new DiagnosticsProperty<SliderComponentShape>("overlayShape", this.overlayShape,
defaultValue: defaultData.overlayShape, 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,

public abstract void paint(
PaintingContext context,
Offset thumbCenter,
Offset center,
Animation<float> activationAnimation = null,
Animation<float> enableAnimation = null,
bool? isDiscrete = null,

public override void paint(
PaintingContext context,
Offset thumbCenter,
Offset center,
Animation<float> activationAnimation = null,
Animation<float> enableAnimation = null,
bool? isDiscrete = null,

}
}
public class RectangularSliderTrackShape : SliderTrackShape {
public RectangularSliderTrackShape(
float disabledThumbGapWidth = 2.0f) {
this.disabledThumbGapWidth = disabledThumbGapWidth;
}
public readonly float disabledThumbGapWidth;
public override Rect getPreferredRect(
RenderBox parentBox = null,
Offset offset = null,
SliderThemeData sliderTheme = null,
bool? isEnabled = null,
bool? isDiscrete = null) {
float overlayWidth = sliderTheme.overlayShape.getPreferredSize(isEnabled, isDiscrete).width;
float trackHeight = sliderTheme.trackHeight;
D.assert(overlayWidth >= 0);
D.assert(trackHeight >= 0);
D.assert(parentBox.size.width >= overlayWidth);
D.assert(parentBox.size.height >= trackHeight);
float trackLeft = offset.dx + overlayWidth / 2f;
float trackTop = offset.dy + (parentBox.size.height - trackHeight) / 2f;
float trackWidth = parentBox.size.width - overlayWidth;
return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight);
}
public override 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) {
if (sliderTheme.trackHeight == 0) {
return;
}
ColorTween activeTrackColorTween = new ColorTween(begin: sliderTheme.disabledActiveTrackColor,
end: sliderTheme.activeTrackColor);
ColorTween inactiveTrackColorTween = new ColorTween(begin: sliderTheme.disabledInactiveTrackColor,
end: sliderTheme.inactiveTrackColor);
Paint activePaint = new Paint {color = activeTrackColorTween.evaluate(enableAnimation)};
Paint inactivePaint = new Paint {color = inactiveTrackColorTween.evaluate(enableAnimation)};
Paint leftTrackPaint = activePaint;
Paint rightTrackPaint = inactivePaint;
float horizontalAdjustment = 0.0f;
if (!isEnabled.Value) {
float disabledThumbRadius = sliderTheme.thumbShape.getPreferredSize(false, isDiscrete).width / 2.0f;
float gap = this.disabledThumbGapWidth * (1.0f - enableAnimation.value);
horizontalAdjustment = disabledThumbRadius + gap;
}
Rect trackRect = this.getPreferredRect(
parentBox: parentBox,
offset: offset,
sliderTheme: sliderTheme,
isEnabled: isEnabled,
isDiscrete: isDiscrete
);
Rect leftTrackSegment = Rect.fromLTRB(trackRect.left, trackRect.top, thumbCenter.dx - horizontalAdjustment,
trackRect.bottom);
context.canvas.drawRect(leftTrackSegment, leftTrackPaint);
Rect rightTrackSegment = Rect.fromLTRB(thumbCenter.dx + horizontalAdjustment, trackRect.top,
trackRect.right, trackRect.bottom);
context.canvas.drawRect(rightTrackSegment, rightTrackPaint);
}
}
public class RoundSliderTickMarkShape : SliderTickMarkShape {
public RoundSliderTickMarkShape(
float? tickMarkRadius = null) {
this.tickMarkRadius = tickMarkRadius;
}
public readonly float? tickMarkRadius;
public override Size getPreferredSize(
SliderThemeData sliderTheme = null,
bool? isEnabled = null
) {
return Size.fromRadius(this.tickMarkRadius ?? sliderTheme.trackHeight / 2f);
}
public override void paint(
PaintingContext context,
Offset center,
RenderBox parentBox = null,
SliderThemeData sliderTheme = null,
Animation<float> enableAnimation = null,
Offset thumbCenter = null,
bool? isEnabled = null
) {
Color begin;
Color end;
bool isTickMarkRightOfThumb = center.dx > thumbCenter.dx;
begin = isTickMarkRightOfThumb
? sliderTheme.disabledInactiveTickMarkColor
: sliderTheme.disabledActiveTickMarkColor;
end = isTickMarkRightOfThumb ? sliderTheme.inactiveTickMarkColor : sliderTheme.activeTickMarkColor;
Paint paint = new Paint {color = new ColorTween(begin: begin, end: end).evaluate(enableAnimation)};
float tickMarkRadius = this.getPreferredSize(
isEnabled: isEnabled,
sliderTheme: sliderTheme
).width / 2f;
context.canvas.drawCircle(center, tickMarkRadius, paint);
}
}
public class RoundSliderThumbShape : SliderComponentShape {
public RoundSliderThumbShape(
float enabledThumbRadius = 6.0f,

}
}
public class RoundSliderOverlayShape : SliderComponentShape {
public RoundSliderOverlayShape(
float overlayRadius = 16.0f) {
this.overlayRadius = overlayRadius;
}
public readonly float overlayRadius;
public override Size getPreferredSize(bool? isEnabled, bool? isDiscrete) {
return Size.fromRadius(this.overlayRadius);
}
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: 0.0f,
end: this.overlayRadius
);
canvas.drawCircle(
center,
radiusTween.evaluate(activationAnimation),
new Paint {color = sliderTheme.overlayColor}
);
}
}
public class PaddleSliderValueIndicatorShape : SliderComponentShape {
public PaddleSliderValueIndicatorShape() {
}

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 _bottomLobeEndAngle = 1.1f * 5f * Mathf.PI / 4.0f;
const float _labelPadding = 8.0f;
const float _distanceBetweenTopBottomCenters = 40.0f;
static readonly Offset _topLobeCenter = new Offset(0.0f, -_distanceBetweenTopBottomCenters);

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(

80
Runtime/widgets/gesture_detector.cs


GestureDragUpdateCallback onPanUpdate = null,
GestureDragEndCallback onPanEnd = null,
GestureDragCancelCallback onPanCancel = null,
GestureScaleStartCallback onScaleStart = null,
GestureScaleUpdateCallback onScaleUpdate = null,
GestureScaleEndCallback onScaleEnd = null,
HitTestBehavior behavior = HitTestBehavior.deferToChild,
DragStartBehavior dragStartBehavior = DragStartBehavior.down
) : base(key) {

onHorizontalDragStart != null || onHorizontalDragUpdate != null ||
onHorizontalDragEnd != null;
bool haveLongPress = onLongPress != null || onLongPressUp != null;
bool haveLongPressDrag = onLongPressDragStart != null || onLongPressDragUpdate != null || onLongPressDragUp != null;
bool haveLongPressDrag = onLongPressDragStart != null || onLongPressDragUpdate != null ||
onLongPressDragUp != null;
if (havePan) {
bool haveScale = onScaleStart != null || onScaleUpdate != null || onScaleEnd != null;
if (havePan || haveScale) {
if (havePan && haveScale) {
throw new UIWidgetsError(
"Incorrect GestureDetector arguments.\n" +
"Having both a pan gesture recognizer and a scale gesture recognizer is redundant; scale is a superset of pan. Just use the scale gesture recognizer."
);
}
string recognizer = havePan ? "pan" : "scale";
"Simultaneously having a vertical drag gesture recognizer, a horizontal drag gesture recognizer, and a pan gesture recognizer " +
"will result in the pan gesture recognizer being ignored, since the other two will catch all drags."
$"Simultaneously having a vertical drag gesture recognizer, a horizontal drag gesture recognizer, and a {recognizer} gesture recognizer " +
$"will result in the {recognizer} gesture recognizer being ignored, since the other two will catch all drags."
"Incorrect GestureDetector arguments.\n" +
"Having both a long press and a long press drag recognizer is " +
"redundant as the long press drag is a superset of long press. " +
"Except long press drag allows for drags after the long press is " +
"triggered."
);
"Incorrect GestureDetector arguments.\n" +
"Having both a long press and a long press drag recognizer is " +
"redundant as the long press drag is a superset of long press. " +
"Except long press drag allows for drags after the long press is " +
"triggered."
);
}
return true;

this.onPanUpdate = onPanUpdate;
this.onPanEnd = onPanEnd;
this.onPanCancel = onPanCancel;
this.onScaleStart = onScaleStart;
this.onScaleUpdate = onScaleUpdate;
this.onScaleEnd = onScaleEnd;
this.behavior = behavior;
this.dragStartBehavior = dragStartBehavior;
}

public readonly GestureDragUpdateCallback onPanUpdate;
public readonly GestureDragEndCallback onPanEnd;
public readonly GestureDragCancelCallback onPanCancel;
public readonly GestureScaleStartCallback onScaleStart;
public readonly GestureScaleUpdateCallback onScaleUpdate;
public readonly GestureScaleEndCallback onScaleEnd;
public readonly HitTestBehavior behavior;
public readonly DragStartBehavior dragStartBehavior;

instance => { instance.onLongPress = this.onLongPress; }
);
}
if (this.onLongPressDragStart != null || this.onLongPressDragUpdate != null || this.onLongPressDragUp != null) {
gestures[typeof(LongPressDragGestureRecognizer)] = new GestureRecognizerFactoryWithHandlers<LongPressDragGestureRecognizer>(
() => new LongPressDragGestureRecognizer(debugOwner: this),
(LongPressDragGestureRecognizer instance) => {
instance.onLongPressStart = this.onLongPressDragStart;
instance.onLongPressDragUpdate = this.onLongPressDragUpdate;
instance.onLongPressUp = this.onLongPressDragUp;
}
);
if (this.onLongPressDragStart != null || this.onLongPressDragUpdate != null ||
this.onLongPressDragUp != null) {
gestures[typeof(LongPressDragGestureRecognizer)] =
new GestureRecognizerFactoryWithHandlers<LongPressDragGestureRecognizer>(
() => new LongPressDragGestureRecognizer(debugOwner: this),
(LongPressDragGestureRecognizer instance) => {
instance.onLongPressStart = this.onLongPressDragStart;
instance.onLongPressDragUpdate = this.onLongPressDragUpdate;
instance.onLongPressUp = this.onLongPressDragUp;
}
);
}
if (this.onVerticalDragDown != null ||

instance.onEnd = this.onPanEnd;
instance.onCancel = this.onPanCancel;
instance.dragStartBehavior = this.dragStartBehavior;
}
);
}
if (this.onScaleStart != null ||
this.onScaleUpdate != null ||
this.onScaleEnd != null) {
gestures[typeof(ScaleGestureRecognizer)] =
new GestureRecognizerFactoryWithHandlers<ScaleGestureRecognizer>(
() => new ScaleGestureRecognizer(debugOwner: this),
instance => {
instance.onStart = this.onScaleStart;
instance.onUpdate = this.onScaleUpdate;
instance.onEnd = this.onScaleEnd;
}
);
}

: gestures[type].constructorRaw();
D.assert(this._recognizers[type].GetType() == type,
() => "GestureRecognizerFactory of type " + type + " created a GestureRecognizer of type " +
this._recognizers[type].GetType() +
". The GestureRecognizerFactory must be specialized with the type of the class that it returns from its constructor method.");
this._recognizers[type].GetType() +
". The GestureRecognizerFactory must be specialized with the type of the class that it returns from its constructor method.");
gestures[type].initializerRaw(this._recognizers[type]);
}

12
Samples/UIWidgetSample/MaterialSample.cs


namespace UIWidgetsSample {
public class MaterialSample : UIWidgetsSamplePanel {
const int testCaseId = 7;
const int testCaseId = 6;
readonly List<Widget> testCases = new List<Widget> {
new MaterialButtonWidget(),

public class MaterialSliderState : State<MaterialSliderWidget> {
float _value = 0.0f;
float _value = 0.8f;
void onChanged(float value) {
this.setState(() => { this._value = value; });

title: new Text("Slider and Indicators")),
body: new Column(
children: new List<Widget> {
new Container(
new Padding(
padding: EdgeInsets.only(top: 100.0f),
child: new Container(
divisions: 10,
min: 0.4f,
label: "Here",
)
}
)
);

412
Runtime/gestures/scale.cs


using System.Collections.Generic;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.ui;
using UnityEngine;
namespace Unity.UIWidgets.gestures {
enum _ScaleState {
ready,
possible,
accepted,
started
}
public class ScaleStartDetails {
public ScaleStartDetails(Offset focalPoint = null) {
this.focalPoint = focalPoint ?? Offset.zero;
}
public readonly Offset focalPoint;
public override string ToString() {
return $"ScaleStartDetails(focalPoint: {this.focalPoint}";
}
}
public class ScaleUpdateDetails {
public ScaleUpdateDetails(
Offset focalPoint = null,
float scale = 1.0f,
float horizontalScale = 1.0f,
float verticalScale = 1.0f,
float rotation = 0.0f
) {
focalPoint = focalPoint ?? Offset.zero;
D.assert(scale >= 0.0f);
D.assert(horizontalScale >= 0.0f);
D.assert(verticalScale >= 0.0f);
this.focalPoint = focalPoint;
this.scale = scale;
this.horizontalScale = horizontalScale;
this.verticalScale = verticalScale;
this.rotation = rotation;
}
public readonly Offset focalPoint;
public readonly float scale;
public readonly float horizontalScale;
public readonly float verticalScale;
public readonly float rotation;
public override string ToString() {
return
$"ScaleUpdateDetails(focalPoint: {this.focalPoint}, scale: {this.scale}, horizontalScale: {this.horizontalScale}, verticalScale: {this.verticalScale}, rotation: {this.rotation}";
}
}
public class ScaleEndDetails {
public ScaleEndDetails(Velocity velocity = null) {
this.velocity = velocity ?? Velocity.zero;
}
public readonly Velocity velocity;
public override string ToString() {
return $"ScaleEndDetails(velocity: {this.velocity}";
}
}
public delegate void GestureScaleStartCallback(ScaleStartDetails details);
public delegate void GestureScaleUpdateCallback(ScaleUpdateDetails details);
public delegate void GestureScaleEndCallback(ScaleEndDetails details);
static class _ScaleGestureUtils {
public static bool _isFlingGesture(Velocity velocity) {
D.assert(velocity != null);
float speedSquared = velocity.pixelsPerSecond.distanceSquared;
return speedSquared > Constants.kMinFlingVelocity * Constants.kMinFlingVelocity;
}
}
class _LineBetweenPointers {
public _LineBetweenPointers(
Offset pointerStartLocation = null,
int pointerStartId = 0,
Offset pointerEndLocation = null,
int pointerEndId = 1) {
pointerStartLocation = pointerEndLocation ?? Offset.zero;
pointerEndLocation = pointerEndLocation ?? Offset.zero;
D.assert(pointerStartId != pointerEndId);
this.pointerStartLocation = pointerStartLocation;
this.pointerStartId = pointerStartId;
this.pointerEndLocation = pointerEndLocation;
this.pointerEndId = pointerEndId;
}
public readonly Offset pointerStartLocation;
public readonly int pointerStartId;
public readonly Offset pointerEndLocation;
public readonly int pointerEndId;
}
public class ScaleGestureRecognizer : OneSequenceGestureRecognizer {
public ScaleGestureRecognizer(object debugOwner) : base(debugOwner: debugOwner) {
}
public GestureScaleStartCallback onStart;
public GestureScaleUpdateCallback onUpdate;
public GestureScaleEndCallback onEnd;
_ScaleState _state = _ScaleState.ready;
Offset _initialFocalPoint;
Offset _currentFocalPoint;
float _initialSpan;
float _currentSpan;
float _initialHorizontalSpan;
float _currentHorizontalSpan;
float _initialVerticalSpan;
float _currentVerticalSpan;
_LineBetweenPointers _initialLine;
_LineBetweenPointers _currentLine;
Dictionary<int, Offset> _pointerLocations;
List<int> _pointerQueue;
readonly Dictionary<int, VelocityTracker> _velocityTrackers = new Dictionary<int, VelocityTracker>();
float _scaleFactor {
get { return this._initialSpan > 0.0f ? this._currentSpan / this._initialSpan : 1.0f; }
}
float _horizontalScaleFactor {
get {
return this._initialHorizontalSpan > 0.0f
? this._currentHorizontalSpan / this._initialHorizontalSpan
: 1.0f;
}
}
float _verticalScaleFactor {
get {
return this._initialVerticalSpan > 0.0f ? this._currentVerticalSpan / this._initialVerticalSpan : 1.0f;
}
}
float _computeRotationFactor() {
if (this._initialLine == null || this._currentLine == null) {
return 0.0f;
}
float fx = this._initialLine.pointerStartLocation.dx;
float fy = this._initialLine.pointerStartLocation.dy;
float sx = this._initialLine.pointerEndLocation.dx;
float sy = this._initialLine.pointerEndLocation.dy;
float nfx = this._currentLine.pointerStartLocation.dx;
float nfy = this._currentLine.pointerStartLocation.dy;
float nsx = this._currentLine.pointerEndLocation.dx;
float nsy = this._currentLine.pointerEndLocation.dy;
float angle1 = Mathf.Atan2(fy - sy, fx - sx);
float angle2 = Mathf.Atan2(nfy - nsy, nfx - nsx);
return angle2 - angle1;
}
public override void addAllowedPointer(PointerDownEvent evt) {
this.startTrackingPointer(evt.pointer);
this._velocityTrackers[evt.pointer] = new VelocityTracker();
if (this._state == _ScaleState.ready) {
this._state = _ScaleState.possible;
this._initialSpan = 0.0f;
this._currentSpan = 0.0f;
this._initialHorizontalSpan = 0.0f;
this._currentHorizontalSpan = 0.0f;
this._initialVerticalSpan = 0.0f;
this._currentVerticalSpan = 0.0f;
this._pointerLocations = new Dictionary<int, Offset>();
this._pointerQueue = new List<int>();
}
}
protected override void handleEvent(PointerEvent evt) {
D.assert(this._state != _ScaleState.ready);
bool didChangeConfiguration = false;
bool shouldStartIfAccepted = false;
if (evt is PointerMoveEvent) {
VelocityTracker tracker = this._velocityTrackers[evt.pointer];
D.assert(tracker != null);
if (!evt.synthesized) {
tracker.addPosition(evt.timeStamp, evt.position);
}
this._pointerLocations[evt.pointer] = evt.position;
shouldStartIfAccepted = true;
}
else if (evt is PointerDownEvent) {
this._pointerLocations[evt.pointer] = evt.position;
this._pointerQueue.Add(evt.pointer);
didChangeConfiguration = true;
shouldStartIfAccepted = true;
}
else if (evt is PointerUpEvent || evt is PointerCancelEvent) {
this._pointerLocations.Remove(evt.pointer);
this._pointerQueue.Remove(evt.pointer);
didChangeConfiguration = true;
}
this._updateLines();
this._update();
if (!didChangeConfiguration || this._reconfigure(evt.pointer)) {
this._advanceStateMachine(shouldStartIfAccepted);
}
this.stopTrackingIfPointerNoLongerDown(evt);
}
void _update() {
int count = this._pointerLocations.Keys.Count;
Offset focalPoint = Offset.zero;
foreach (int pointer in this._pointerLocations.Keys) {
focalPoint += this._pointerLocations[pointer];
}
this._currentFocalPoint = count > 0 ? focalPoint / count : Offset.zero;
float totalDeviation = 0.0f;
float totalHorizontalDeviation = 0.0f;
float totalVerticalDeviation = 0.0f;
foreach (int pointer in this._pointerLocations.Keys) {
totalDeviation += (this._currentFocalPoint - this._pointerLocations[pointer]).distance;
totalHorizontalDeviation += (this._currentFocalPoint.dx - this._pointerLocations[pointer].dx).abs();
totalVerticalDeviation += (this._currentFocalPoint.dy - this._pointerLocations[pointer].dy).abs();
}
this._currentSpan = count > 0 ? totalDeviation / count : 0.0f;
this._currentHorizontalSpan = count > 0 ? totalHorizontalDeviation / count : 0.0f;
this._currentVerticalSpan = count > 0 ? totalVerticalDeviation / count : 0.0f;
}
void _updateLines() {
int count = this._pointerLocations.Keys.Count;
D.assert(this._pointerQueue.Count >= count);
if (count < 2) {
this._initialLine = this._currentLine;
}
else if (this._initialLine != null &&
this._initialLine.pointerStartId == this._pointerQueue[0] &&
this._initialLine.pointerEndId == this._pointerQueue[1]) {
this._currentLine = new _LineBetweenPointers(
pointerStartId: this._pointerQueue[0],
pointerStartLocation: this._pointerLocations[this._pointerQueue[0]],
pointerEndId: this._pointerQueue[1],
pointerEndLocation: this._pointerLocations[this._pointerQueue[1]]
);
}
else {
this._initialLine = new _LineBetweenPointers(
pointerStartId: this._pointerQueue[0],
pointerStartLocation: this._pointerLocations[this._pointerQueue[0]],
pointerEndId: this._pointerQueue[1],
pointerEndLocation: this._pointerLocations[this._pointerQueue[1]]
);
this._currentLine = null;
}
}
bool _reconfigure(int pointer) {
this._initialFocalPoint = this._currentFocalPoint;
this._initialSpan = this._currentSpan;
this._initialLine = this._currentLine;
this._initialHorizontalSpan = this._currentHorizontalSpan;
this._initialVerticalSpan = this._currentVerticalSpan;
if (this._state == _ScaleState.started) {
if (this.onEnd != null) {
VelocityTracker tracker = this._velocityTrackers[pointer];
D.assert(tracker != null);
Velocity velocity = tracker.getVelocity();
if (_ScaleGestureUtils._isFlingGesture(velocity)) {
Offset pixelsPerSecond = velocity.pixelsPerSecond;
if (pixelsPerSecond.distanceSquared >
Constants.kMaxFlingVelocity * Constants.kMaxFlingVelocity) {
velocity = new Velocity(
pixelsPerSecond: (pixelsPerSecond / pixelsPerSecond.distance) *
Constants.kMaxFlingVelocity);
}
this.invokeCallback<object>("onEnd", () => {
this.onEnd(new ScaleEndDetails(velocity: velocity));
return null;
});
}
else {
this.invokeCallback<object>("onEnd", () => {
this.onEnd(new ScaleEndDetails(velocity: Velocity.zero));
return null;
});
}
}
this._state = _ScaleState.accepted;
return false;
}
return true;
}
void _advanceStateMachine(bool shouldStartIfAccepted) {
if (this._state == _ScaleState.ready) {
this._state = _ScaleState.possible;
}
if (this._state == _ScaleState.possible) {
float spanDelta = (this._currentSpan - this._initialSpan).abs();
float focalPointDelta = (this._currentFocalPoint - this._initialFocalPoint).distance;
if (spanDelta > Constants.kScaleSlop || focalPointDelta > Constants.kPanSlop) {
this.resolve(GestureDisposition.accepted);
}
}
else if (this._state >= _ScaleState.accepted) {
this.resolve(GestureDisposition.accepted);
}
if (this._state == _ScaleState.accepted && shouldStartIfAccepted) {
this._state = _ScaleState.started;
this._dispatchOnStartCallbackIfNeeded();
}
if (this._state == _ScaleState.started && this.onUpdate != null) {
this.invokeCallback<object>("onUpdate", () => {
this.onUpdate(new ScaleUpdateDetails(
scale: this._scaleFactor,
horizontalScale: this._horizontalScaleFactor,
verticalScale: this._verticalScaleFactor,
focalPoint: this._currentFocalPoint,
rotation: this._computeRotationFactor()
));
return null;
});
}
}
void _dispatchOnStartCallbackIfNeeded() {
D.assert(this._state == _ScaleState.started);
if (this.onStart != null) {
this.invokeCallback<object>("onStart", () => {
this.onStart(new ScaleStartDetails(focalPoint: this._currentFocalPoint));
return null;
});
}
}
public override void acceptGesture(int pointer) {
if (this._state == _ScaleState.possible) {
this._state = _ScaleState.started;
this._dispatchOnStartCallbackIfNeeded();
}
}
public override void rejectGesture(int pointer) {
this.stopTrackingPointer(pointer);
}
protected override void didStopTrackingLastPointer(int pointer) {
switch (this._state) {
case _ScaleState.possible:
this.resolve(GestureDisposition.rejected);
break;
case _ScaleState.ready:
D.assert(false);
break;
case _ScaleState.accepted:
break;
case _ScaleState.started:
D.assert(false);
break;
}
this._state = _ScaleState.ready;
}
public override void dispose() {
this._velocityTrackers.Clear();
base.dispose();
}
public override string debugDescription {
get { return "scale"; }
}
}
}

11
Runtime/gestures/scale.cs.meta


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

64
Samples/UIWidgetSample/ScaleGestureSample.cs


using System.Collections.Generic;
using Unity.UIWidgets.material;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
namespace UIWidgetsSample {
public class ScaleGestureSample : UIWidgetsSamplePanel {
protected override Widget createWidget() {
return new MaterialApp(
showPerformanceOverlay: false,
home: new ScaleGesturePanel()
);
}
protected override void OnEnable() {
FontManager.instance.addFont(Resources.Load<Font>(path: "MaterialIcons-Regular"), "Material Icons");
base.OnEnable();
}
}
class ScaleGesturePanel : StatefulWidget {
public override State createState() {
return new ScaleGesturePanelState();
}
}
class ScaleGesturePanelState : State<ScaleGesturePanel> {
float scaleValue = 1.0f;
public override Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Center(
child: new Text("Test Scale Gesture Widget")
)
),
body: new GestureDetector(
onScaleStart: scaleDetails => { Debug.Log("Scale Start !"); },
onScaleUpdate: scaleDetails => {
Debug.Log("Scale value = " + scaleDetails.scale);
this.setState(() => { this.scaleValue = scaleDetails.scale; });
},
onScaleEnd: scaleDetails => { Debug.Log("Scale End"); },
child: new Card(
color: Colors.white,
child: new Center(
child: new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: new List<Widget> {
new Icon(Unity.UIWidgets.material.Icons.ac_unit, size: 128.0f, color: Colors.black),
new RaisedButton(
child: new Text("Scale: " + this.scaleValue),
onPressed: () => { Debug.Log("Button Pressed"); })
}
)
))
)
);
}
}
}

11
Samples/UIWidgetSample/ScaleGestureSample.cs.meta


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