您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 
 

1164 行
45 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.async2;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.scheduler2;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using Color = Unity.UIWidgets.ui.Color;
using Rect = Unity.UIWidgets.ui.Rect;
namespace Unity.UIWidgets.material {
public class RangeSlider : StatefulWidget {
public RangeSlider(
Key key = null,
RangeValues values = null,
ValueChanged<RangeValues> onChanged = null,
ValueChanged<RangeValues> onChangeStart = null,
ValueChanged<RangeValues> onChangeEnd = null,
float min = 0.0f,
float max = 1.0f,
int? divisions = null,
RangeLabels labels = null,
Color activeColor = null,
Color inactiveColor = null
) : base(key: key) {
D.assert(values != null);
D.assert(min <= max);
D.assert(values.start <= values.end);
D.assert(values.start >= min && values.start <= max);
D.assert(values.end >= min && values.end <= max);
D.assert(divisions == null || divisions > 0);
this.values = values;
this.onChanged = onChanged;
this.onChangeStart = onChangeStart;
this.onChangeEnd = onChangeEnd;
this.min = min;
this.max = max;
this.divisions = divisions;
this.labels = labels;
this.activeColor = activeColor;
this.inactiveColor = inactiveColor;
}
public readonly RangeValues values;
public readonly ValueChanged<RangeValues> onChanged;
public readonly ValueChanged<RangeValues> onChangeStart;
public readonly ValueChanged<RangeValues> onChangeEnd;
public readonly float min;
public readonly float max;
public readonly int? divisions;
public readonly RangeLabels labels;
public readonly Color activeColor;
public readonly Color inactiveColor;
public const float _minTouchTargetWidth = material_.kMinInteractiveDimension;
public override State createState() {
return new _RangeSliderState();
}
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) {
base.debugFillProperties(properties);
properties.add(new FloatProperty("valueStart", values.start));
properties.add(new FloatProperty("valueEnd", values.end));
properties.add(
new ObjectFlagProperty<ValueChanged<RangeValues>>("onChanged", onChanged, ifNull: "disabled"));
properties.add(ObjectFlagProperty<ValueChanged<RangeValues>>.has("onChangeStart", onChangeStart));
properties.add(ObjectFlagProperty<ValueChanged<RangeValues>>.has("onChangeEnd", onChangeEnd));
properties.add(new FloatProperty("min", min));
properties.add(new FloatProperty("max", max));
properties.add(new IntProperty("divisions", divisions));
properties.add(new StringProperty("labelStart", labels?.start));
properties.add(new StringProperty("labelEnd", labels?.end));
properties.add(new ColorProperty("activeColor", activeColor));
properties.add(new ColorProperty("inactiveColor", inactiveColor));
}
}
class _RangeSliderState : TickerProviderStateMixin<RangeSlider> {
static readonly TimeSpan enableAnimationDuration = new TimeSpan(0, 0, 0, 0, 75);
static readonly TimeSpan valueIndicatorAnimationDuration = new TimeSpan(0, 0, 0, 0, 100);
internal AnimationController overlayController;
internal AnimationController valueIndicatorController;
internal AnimationController enableController;
internal AnimationController startPositionController;
internal AnimationController endPositionController;
internal Timer interactionTimer;
public override void initState() {
base.initState();
overlayController = new AnimationController(
duration: material_.kRadialReactionDuration,
vsync: this
);
valueIndicatorController = new AnimationController(
duration: valueIndicatorAnimationDuration,
vsync: this
);
enableController = new AnimationController(
duration: enableAnimationDuration,
vsync: this,
value: widget.onChanged != null ? 1.0f : 0.0f
);
startPositionController = new AnimationController(
duration: TimeSpan.Zero,
vsync: this,
value: _unlerp(widget.values.start)
);
endPositionController = new AnimationController(
duration: TimeSpan.Zero,
vsync: this,
value: _unlerp(widget.values.end)
);
}
public override void didUpdateWidget(StatefulWidget oldWidget) {
var _oldWidget = (RangeSlider) oldWidget;
base.didUpdateWidget(oldWidget);
if (_oldWidget.onChanged == widget.onChanged) {
return;
}
bool wasEnabled = _oldWidget.onChanged != null;
bool isEnabled = widget.onChanged != null;
if (wasEnabled != isEnabled) {
if (isEnabled) {
enableController.forward();
}
else {
enableController.reverse();
}
}
}
public override void dispose() {
interactionTimer?.cancel();
overlayController.dispose();
valueIndicatorController.dispose();
enableController.dispose();
startPositionController.dispose();
endPositionController.dispose();
base.dispose();
}
void _handleChanged(RangeValues values) {
D.assert(widget.onChanged != null);
RangeValues lerpValues = _lerpRangeValues(values);
if (lerpValues != widget.values) {
widget.onChanged(lerpValues);
}
}
void _handleDragStart(RangeValues values) {
D.assert(widget.onChangeStart != null);
widget.onChangeStart(_lerpRangeValues(values));
}
void _handleDragEnd(RangeValues values) {
D.assert(widget.onChangeEnd != null);
widget.onChangeEnd(_lerpRangeValues(values));
}
float _lerp(float value) {
return MathUtils.lerpNullableFloat(widget.min, widget.max, value);
}
RangeValues _lerpRangeValues(RangeValues values) {
return new RangeValues(_lerp(values.start), _lerp(values.end));
}
float _unlerp(float value) {
D.assert(value <= widget.max);
D.assert(value >= widget.min);
return widget.max > widget.min ? (value - widget.min) / (widget.max - widget.min) : 0.0f;
}
RangeValues _unlerpRangeValues(RangeValues values) {
return new RangeValues(_unlerp(values.start), _unlerp(values.end));
}
static Thumb? _defaultRangeThumbSelector(
TextDirection textDirection,
RangeValues values,
float tapValue,
Size thumbSize,
Size trackSize,
float dx
) {
float touchRadius = Mathf.Max(thumbSize.width, RangeSlider._minTouchTargetWidth) / 2f;
bool inStartTouchTarget = (tapValue - values.start).abs() * trackSize.width < touchRadius;
bool inEndTouchTarget = (tapValue - values.end).abs() * trackSize.width < touchRadius;
if (inStartTouchTarget && inEndTouchTarget) {
bool towardsStart = false;
bool towardsEnd = false;
switch (textDirection) {
case TextDirection.ltr:
towardsStart = dx < 0;
towardsEnd = dx > 0;
break;
case TextDirection.rtl:
towardsStart = dx > 0;
towardsEnd = dx < 0;
break;
}
if (towardsStart) {
return Thumb.start;
}
if (towardsEnd) {
return Thumb.end;
}
}
else {
if (tapValue < values.start || inStartTouchTarget) {
return Thumb.start;
}
if (tapValue > values.end || inEndTouchTarget) {
return Thumb.end;
}
}
return null;
}
const float _defaultTrackHeight = 2;
static readonly RangeSliderTrackShape _defaultTrackShape = new RoundedRectRangeSliderTrackShape();
static readonly RangeSliderTickMarkShape _defaultTickMarkShape = new RoundRangeSliderTickMarkShape();
static readonly SliderComponentShape _defaultOverlayShape = new RoundSliderOverlayShape();
static readonly RangeSliderThumbShape _defaultThumbShape = new RoundRangeSliderThumbShape();
static readonly RangeSliderValueIndicatorShape _defaultValueIndicatorShape =
new PaddleRangeSliderValueIndicatorShape();
const ShowValueIndicator _defaultShowValueIndicator = ShowValueIndicator.onlyForDiscrete;
const float _defaultMinThumbSeparation = 8;
public override Widget build(BuildContext context) {
D.assert(material_.debugCheckHasMaterial(context));
D.assert(WidgetsD.debugCheckHasMediaQuery(context));
ThemeData theme = Theme.of(context);
SliderThemeData sliderTheme = SliderTheme.of(context);
sliderTheme = sliderTheme.copyWith(
trackHeight: sliderTheme.trackHeight ?? _defaultTrackHeight,
activeTrackColor: widget.activeColor ?? sliderTheme.activeTrackColor ?? theme.colorScheme.primary,
inactiveTrackColor: widget.inactiveColor ??
sliderTheme.inactiveTrackColor ?? theme.colorScheme.primary.withOpacity(0.24f),
disabledActiveTrackColor: sliderTheme.disabledActiveTrackColor ??
theme.colorScheme.onSurface.withOpacity(0.32f),
disabledInactiveTrackColor: sliderTheme.disabledInactiveTrackColor ??
theme.colorScheme.onSurface.withOpacity(0.12f),
activeTickMarkColor: widget.inactiveColor ??
sliderTheme.activeTickMarkColor ?? theme.colorScheme.onPrimary.withOpacity(0.54f),
inactiveTickMarkColor: widget.activeColor ??
sliderTheme.inactiveTickMarkColor ??
theme.colorScheme.primary.withOpacity(0.54f),
disabledActiveTickMarkColor: sliderTheme.disabledActiveTickMarkColor ??
theme.colorScheme.onPrimary.withOpacity(0.12f),
disabledInactiveTickMarkColor: sliderTheme.disabledInactiveTickMarkColor ??
theme.colorScheme.onSurface.withOpacity(0.12f),
thumbColor: widget.activeColor ?? sliderTheme.thumbColor ?? theme.colorScheme.primary,
overlappingShapeStrokeColor: sliderTheme.overlappingShapeStrokeColor ?? theme.colorScheme.surface,
disabledThumbColor: sliderTheme.disabledThumbColor ?? theme.colorScheme.onSurface.withOpacity(0.38f),
overlayColor: widget.activeColor?.withOpacity(0.12f) ??
sliderTheme.overlayColor ?? theme.colorScheme.primary.withOpacity(0.12f),
valueIndicatorColor: widget.activeColor ?? sliderTheme.valueIndicatorColor ?? theme.colorScheme.primary,
rangeTrackShape: sliderTheme.rangeTrackShape ?? _defaultTrackShape,
rangeTickMarkShape: sliderTheme.rangeTickMarkShape ?? _defaultTickMarkShape,
rangeThumbShape: sliderTheme.rangeThumbShape ?? _defaultThumbShape,
overlayShape: sliderTheme.overlayShape ?? _defaultOverlayShape,
rangeValueIndicatorShape: sliderTheme.rangeValueIndicatorShape ?? _defaultValueIndicatorShape,
showValueIndicator: sliderTheme.showValueIndicator ?? _defaultShowValueIndicator,
valueIndicatorTextStyle: sliderTheme.valueIndicatorTextStyle ?? theme.textTheme.bodyText1.copyWith(
color: theme.colorScheme.onPrimary
),
minThumbSeparation: sliderTheme.minThumbSeparation ?? _defaultMinThumbSeparation,
thumbSelector: sliderTheme.thumbSelector ?? _defaultRangeThumbSelector
);
return new _RangeSliderRenderObjectWidget(
values: _unlerpRangeValues(widget.values),
divisions: widget.divisions,
labels: widget.labels,
sliderTheme: sliderTheme,
textScaleFactor: MediaQuery.of(context).textScaleFactor,
onChanged: (widget.onChanged != null) && (widget.max > widget.min)
? _handleChanged
: (ValueChanged<RangeValues>) null,
onChangeStart: widget.onChangeStart != null ? _handleDragStart : (ValueChanged<RangeValues>) null,
onChangeEnd: widget.onChangeEnd != null ? _handleDragEnd : (ValueChanged<RangeValues>) null,
state: this
);
}
}
class _RangeSliderRenderObjectWidget : LeafRenderObjectWidget {
public _RangeSliderRenderObjectWidget(
Key key = null,
RangeValues values = null,
int? divisions = null,
RangeLabels labels = null,
SliderThemeData sliderTheme = null,
float textScaleFactor = 0f,
ValueChanged<RangeValues> onChanged = null,
ValueChanged<RangeValues> onChangeStart = null,
ValueChanged<RangeValues> onChangeEnd = null,
_RangeSliderState state = null) : base(key: key) {
this.values = values;
this.divisions = divisions;
this.labels = labels;
this.sliderTheme = sliderTheme;
this.textScaleFactor = textScaleFactor;
this.onChanged = onChanged;
this.onChangeStart = onChangeStart;
this.onChangeEnd = onChangeEnd;
this.state = state;
}
public readonly RangeValues values;
public readonly int? divisions;
public readonly RangeLabels labels;
public readonly SliderThemeData sliderTheme;
public readonly float textScaleFactor;
public readonly ValueChanged<RangeValues> onChanged;
public readonly ValueChanged<RangeValues> onChangeStart;
public readonly ValueChanged<RangeValues> onChangeEnd;
public readonly _RangeSliderState state;
public override RenderObject createRenderObject(BuildContext context) {
return new _RenderRangeSlider(
values: values,
divisions: divisions,
labels: labels,
sliderTheme: sliderTheme,
theme: Theme.of(context),
textScaleFactor: textScaleFactor,
onChanged: onChanged,
onChangeStart: onChangeStart,
onChangeEnd: onChangeEnd,
state: state,
textDirection: Directionality.of(context),
platform: Theme.of(context).platform
);
}
public override void updateRenderObject(BuildContext context, RenderObject renderObject) {
var _renderObject = (_RenderRangeSlider) renderObject;
_renderObject.values = values;
_renderObject.divisions = divisions;
_renderObject.labels = labels;
_renderObject.sliderTheme = sliderTheme;
_renderObject.theme = Theme.of(context);
_renderObject.textScaleFactor = textScaleFactor;
_renderObject.onChanged = onChanged;
_renderObject.onChangeStart = onChangeStart;
_renderObject.onChangeEnd = onChangeEnd;
_renderObject.textDirection = Directionality.of(context);
_renderObject.platform = Theme.of(context).platform;
}
}
class _RenderRangeSlider : RelayoutWhenSystemFontsChangeMixinRenderBox {
public _RenderRangeSlider(
RangeValues values,
int? divisions,
RangeLabels labels,
SliderThemeData sliderTheme,
ThemeData theme,
float textScaleFactor,
RuntimePlatform platform,
ValueChanged<RangeValues> onChanged,
ValueChanged<RangeValues> onChangeStart,
ValueChanged<RangeValues> onChangeEnd,
_RangeSliderState state,
TextDirection? textDirection) {
D.assert(values != null);
D.assert(values.start >= 0.0 && values.start <= 1.0);
D.assert(values.end >= 0.0 && values.end <= 1.0);
D.assert(state != null);
D.assert(textDirection != null);
this.onChangeStart = onChangeStart;
this.onChangeEnd = onChangeEnd;
_platform = platform;
_labels = labels;
_values = values;
_divisions = divisions;
_sliderTheme = sliderTheme;
_theme = theme;
_textScaleFactor = textScaleFactor;
_onChanged = onChanged;
_state = state;
_textDirection = textDirection;
_updateLabelPainters();
GestureArenaTeam team = new GestureArenaTeam();
_drag = new HorizontalDragGestureRecognizer();
_drag.team = team;
_drag.onStart = _handleDragStart;
_drag.onUpdate = _handleDragUpdate;
_drag.onEnd = _handleDragEnd;
_drag.onCancel = _handleDragCancel;
_tap = new TapGestureRecognizer();
_tap.team = team;
_tap.onTapDown = _handleTapDown;
_tap.onTapUp = _handleTapUp;
_tap.onTapCancel = _handleTapCancel;
_overlayAnimation = new CurvedAnimation(
parent: _state.overlayController,
curve: Curves.fastOutSlowIn
);
_valueIndicatorAnimation = new CurvedAnimation(
parent: _state.valueIndicatorController,
curve: Curves.fastOutSlowIn
);
_enableAnimation = new CurvedAnimation(
parent: _state.enableController,
curve: Curves.easeInOut
);
}
Thumb? _lastThumbSelection;
static readonly TimeSpan _positionAnimationDuration = new TimeSpan(0, 0, 0, 0, 75);
const float _minPreferredTrackWidth = 144.0f;
float _maxSliderPartWidth {
get { return Mathf.Max(_sliderPartSizes.Select((Size size) => size.width).ToArray()); }
}
float _maxSliderPartHeight {
get { return Mathf.Max(_sliderPartSizes.Select((Size size) => size.height).ToArray()); }
}
List<Size> _sliderPartSizes {
get {
return new List<Size> {
_sliderTheme.overlayShape.getPreferredSize(isEnabled, isDiscrete),
_sliderTheme.rangeThumbShape.getPreferredSize(isEnabled, isDiscrete),
_sliderTheme.rangeTickMarkShape.getPreferredSize(isEnabled: isEnabled, sliderTheme: sliderTheme),
};
}
}
float _minPreferredTrackHeight {
get { return _sliderTheme.trackHeight.Value; }
}
Rect _trackRect {
get {
return _sliderTheme.rangeTrackShape.getPreferredRect(
parentBox: this,
offset: Offset.zero,
sliderTheme: _sliderTheme,
isDiscrete: false
);
}
}
static readonly TimeSpan _minimumInteractionTime = new TimeSpan(0, 0, 0, 0, 500);
readonly _RangeSliderState _state;
Animation<float> _overlayAnimation;
Animation<float> _valueIndicatorAnimation;
Animation<float> _enableAnimation;
TextPainter _startLabelPainter = new TextPainter();
TextPainter _endLabelPainter = new TextPainter();
HorizontalDragGestureRecognizer _drag;
TapGestureRecognizer _tap;
bool _active = false;
RangeValues _newValues;
bool isEnabled {
get { return onChanged != null; }
}
bool isDiscrete {
get { return divisions != null && divisions > 0; }
}
RangeValues _values;
public RangeValues values {
get { return _values; }
set {
D.assert(value != null);
D.assert(value.start != null && value.start >= 0.0 && value.start <= 1.0f);
D.assert(value.end != null && value.end >= 0.0 && value.end <= 1.0f);
D.assert(value.start <= value.end);
RangeValues convertedValues = isDiscrete ? _discretizeRangeValues(value) : value;
if (convertedValues == _values) {
return;
}
_values = convertedValues;
if (isDiscrete) {
float startDistance = (_values.start - _state.startPositionController.value).abs();
_state.startPositionController.duration = startDistance != 0.0f
? new TimeSpan(0, 0, 0, 0,
(int) (_positionAnimationDuration.TotalMilliseconds * (1.0f / startDistance)))
: TimeSpan.Zero;
_state.startPositionController.animateTo(_values.start, curve: Curves.easeInOut);
float endDistance = (_values.end - _state.endPositionController.value).abs();
_state.endPositionController.duration = endDistance != 0.0f
? new TimeSpan(0, 0, 0, 0,
(int) (_positionAnimationDuration.TotalMilliseconds * (1.0f / endDistance)))
: TimeSpan.Zero;
_state.endPositionController.animateTo(_values.end, curve: Curves.easeInOut);
}
else {
_state.startPositionController.setValue(convertedValues.start);
_state.endPositionController.setValue(convertedValues.end);
}
}
}
RuntimePlatform _platform;
public RuntimePlatform platform {
get { return _platform; }
set {
if (_platform == value) {
return;
}
_platform = value;
}
}
int? _divisions;
public int? divisions {
get { return _divisions; }
set {
if (value == _divisions) {
return;
}
_divisions = value;
markNeedsPaint();
}
}
RangeLabels _labels;
public RangeLabels labels {
get { return _labels; }
set {
if (labels == _labels) {
return;
}
_labels = labels;
_updateLabelPainters();
}
}
SliderThemeData _sliderTheme;
public SliderThemeData sliderTheme {
get { return _sliderTheme; }
set {
if (value == _sliderTheme) {
return;
}
_sliderTheme = value;
markNeedsPaint();
}
}
ThemeData _theme;
public ThemeData theme {
get { return _theme; }
set {
if (value == _theme) {
return;
}
_theme = value;
markNeedsPaint();
}
}
float _textScaleFactor;
public float textScaleFactor {
get { return _textScaleFactor; }
set {
if (value == _textScaleFactor) {
return;
}
_textScaleFactor = value;
_updateLabelPainters();
}
}
ValueChanged<RangeValues> _onChanged;
public ValueChanged<RangeValues> onChanged {
get { return _onChanged; }
set {
if (value == _onChanged) {
return;
}
bool wasEnabled = isEnabled;
_onChanged = value;
if (wasEnabled != isEnabled) {
markNeedsPaint();
}
}
}
public ValueChanged<RangeValues> onChangeStart;
public ValueChanged<RangeValues> onChangeEnd;
TextDirection? _textDirection;
public TextDirection? textDirection {
get { return _textDirection; }
set {
D.assert(value != null);
if (value == _textDirection) {
return;
}
_textDirection = value;
_updateLabelPainters();
}
}
bool showValueIndicator {
get {
bool showValueIndicator = false;
switch (_sliderTheme.showValueIndicator) {
case ShowValueIndicator.onlyForDiscrete:
showValueIndicator = isDiscrete;
break;
case ShowValueIndicator.onlyForContinuous:
showValueIndicator = !isDiscrete;
break;
case ShowValueIndicator.always:
showValueIndicator = true;
break;
case ShowValueIndicator.never:
showValueIndicator = false;
break;
}
return showValueIndicator;
}
}
Size _thumbSize {
get { return _sliderTheme.rangeThumbShape.getPreferredSize(isEnabled, isDiscrete); }
}
float _adjustmentUnit {
get {
switch (_platform) {
case RuntimePlatform.IPhonePlayer:
return 0.1f;
default:
return 0.05f;
}
}
}
void _updateLabelPainters() {
_updateLabelPainter(Thumb.start);
_updateLabelPainter(Thumb.end);
}
void _updateLabelPainter(Thumb thumb) {
if (labels == null) {
return;
}
string text = null;
TextPainter labelPainter = null;
switch (thumb) {
case Thumb.start:
text = labels.start;
labelPainter = _startLabelPainter;
break;
case Thumb.end:
text = labels.end;
labelPainter = _endLabelPainter;
break;
}
if (labels != null) {
labelPainter.text = new TextSpan(
style: _sliderTheme.valueIndicatorTextStyle,
text: text
);
labelPainter.textDirection = textDirection;
labelPainter.textScaleFactor = textScaleFactor;
labelPainter.layout();
}
else {
labelPainter.text = null;
}
markNeedsLayout();
}
public override void systemFontsDidChange() {
base.systemFontsDidChange();
_startLabelPainter.markNeedsLayout();
_endLabelPainter.markNeedsLayout();
_updateLabelPainters();
}
public override void attach(object owner) {
base.attach(owner);
_overlayAnimation.addListener(markNeedsPaint);
_valueIndicatorAnimation.addListener(markNeedsPaint);
_enableAnimation.addListener(markNeedsPaint);
_state.startPositionController.addListener(markNeedsPaint);
_state.endPositionController.addListener(markNeedsPaint);
}
public override void detach() {
_overlayAnimation.removeListener(markNeedsPaint);
_valueIndicatorAnimation.removeListener(markNeedsPaint);
_enableAnimation.removeListener(markNeedsPaint);
_state.startPositionController.removeListener(markNeedsPaint);
_state.endPositionController.removeListener(markNeedsPaint);
base.detach();
}
float _getValueFromVisualPosition(float visualPosition) {
switch (textDirection) {
case TextDirection.rtl:
return 1.0f - visualPosition;
case TextDirection.ltr:
return visualPosition;
}
D.assert(false);
return 0;
}
float _getValueFromGlobalPosition(Offset globalPosition) {
float visualPosition = (globalToLocal(globalPosition).dx - _trackRect.left) / _trackRect.width;
return _getValueFromVisualPosition(visualPosition);
}
float _discretize(float value) {
float result = value.clamp(0.0f, 1.0f);
if (isDiscrete) {
result = (result * divisions.Value).round() * 1.0f / divisions.Value;
}
return result;
}
RangeValues _discretizeRangeValues(RangeValues values) {
return new RangeValues(_discretize(values.start), _discretize(values.end));
}
void _startInteraction(Offset globalPosition) {
float tapValue = _getValueFromGlobalPosition(globalPosition).clamp(0.0f, 1.0f);
_lastThumbSelection = sliderTheme.thumbSelector(textDirection.Value, values, tapValue, _thumbSize, size, 0);
if (_lastThumbSelection != null) {
_active = true;
RangeValues currentValues = _discretizeRangeValues(values);
if (_lastThumbSelection == Thumb.start) {
_newValues = new RangeValues(tapValue, currentValues.end);
}
else if (_lastThumbSelection == Thumb.end) {
_newValues = new RangeValues(currentValues.start, tapValue);
}
_updateLabelPainter(_lastThumbSelection.Value);
if (onChangeStart != null) {
onChangeStart(currentValues);
}
onChanged(_discretizeRangeValues(_newValues));
_state.overlayController.forward();
if (showValueIndicator) {
_state.valueIndicatorController.forward();
_state.interactionTimer?.cancel();
_state.interactionTimer =
Timer.create(
new TimeSpan(0, 0, 0, 0,
(int) (_minimumInteractionTime.TotalMilliseconds * scheduler_.timeDilation)), () => {
_state.interactionTimer = null;
if (!_active && _state.valueIndicatorController.status == AnimationStatus.completed) {
_state.valueIndicatorController.reverse();
}
});
}
}
}
void _handleDragUpdate(DragUpdateDetails details) {
float dragValue = _getValueFromGlobalPosition(details.globalPosition);
bool shouldCallOnChangeStart = false;
if (_lastThumbSelection == null) {
_lastThumbSelection = sliderTheme.thumbSelector(textDirection.Value, values, dragValue, _thumbSize,
size, details.delta.dx);
if (_lastThumbSelection != null) {
shouldCallOnChangeStart = true;
_active = true;
_state.overlayController.forward();
if (showValueIndicator) {
_state.valueIndicatorController.forward();
}
}
}
if (isEnabled && _lastThumbSelection != null) {
RangeValues currentValues = _discretizeRangeValues(values);
if (onChangeStart != null && shouldCallOnChangeStart) {
onChangeStart(currentValues);
}
float currentDragValue = _discretize(dragValue);
float minThumbSeparationValue =
isDiscrete ? 0 : sliderTheme.minThumbSeparation.Value / _trackRect.width;
if (_lastThumbSelection == Thumb.start) {
_newValues =
new RangeValues(Mathf.Min(currentDragValue, currentValues.end - minThumbSeparationValue),
currentValues.end);
}
else if (_lastThumbSelection == Thumb.end) {
_newValues = new RangeValues(currentValues.start,
Mathf.Max(currentDragValue, currentValues.start + minThumbSeparationValue));
}
onChanged(_newValues);
}
}
void _endInteraction() {
_state.overlayController.reverse();
if (showValueIndicator && _state.interactionTimer == null) {
_state.valueIndicatorController.reverse();
}
if (_active && _state.mounted && _lastThumbSelection != null) {
RangeValues discreteValues = _discretizeRangeValues(_newValues);
if (onChangeEnd != null) {
onChangeEnd(discreteValues);
}
_active = false;
}
}
void _handleDragStart(DragStartDetails details) {
_startInteraction(details.globalPosition);
}
void _handleDragEnd(DragEndDetails details) {
_endInteraction();
}
void _handleDragCancel() {
_endInteraction();
}
void _handleTapDown(TapDownDetails details) {
_startInteraction(details.globalPosition);
}
void _handleTapUp(TapUpDetails details) {
_endInteraction();
}
void _handleTapCancel() {
_endInteraction();
}
protected override bool hitTestSelf(Offset position) {
return true;
}
public override void handleEvent(PointerEvent evt, HitTestEntry entry) {
D.assert(debugHandleEvent(evt, entry));
if (evt is PointerDownEvent && isEnabled) {
_drag.addPointer((PointerDownEvent) evt);
_tap.addPointer((PointerDownEvent) evt);
}
}
protected internal override float computeMinIntrinsicWidth(float height) {
return _minPreferredTrackWidth + _maxSliderPartWidth;
}
protected internal override float computeMaxIntrinsicWidth(float height) {
return _minPreferredTrackWidth + _maxSliderPartWidth;
}
protected internal override float computeMinIntrinsicHeight(float width) {
return Mathf.Max(_minPreferredTrackHeight, _maxSliderPartHeight);
}
protected internal override float computeMaxIntrinsicHeight(float width) {
return Mathf.Max(_minPreferredTrackHeight, _maxSliderPartHeight);
}
protected override bool sizedByParent {
get { return true; }
}
protected override void performResize() {
size = new Size(
constraints.hasBoundedWidth ? constraints.maxWidth : _minPreferredTrackWidth + _maxSliderPartWidth,
constraints.hasBoundedHeight
? constraints.maxHeight
: Mathf.Max(_minPreferredTrackHeight, _maxSliderPartHeight)
);
}
public override void paint(PaintingContext context, Offset offset) {
float startValue = _state.startPositionController.value;
float endValue = _state.endPositionController.value;
float startVisualPosition = 0f;
float endVisualPosition = 0f;
switch (textDirection) {
case TextDirection.rtl:
startVisualPosition = 1.0f - startValue;
endVisualPosition = 1.0f - endValue;
break;
case TextDirection.ltr:
startVisualPosition = startValue;
endVisualPosition = endValue;
break;
}
Rect trackRect = _sliderTheme.rangeTrackShape.getPreferredRect(
parentBox: this,
offset: offset,
sliderTheme: _sliderTheme,
isDiscrete: isDiscrete
);
Offset startThumbCenter =
new Offset(trackRect.left + startVisualPosition * trackRect.width, trackRect.center.dy);
Offset endThumbCenter =
new Offset(trackRect.left + endVisualPosition * trackRect.width, trackRect.center.dy);
_sliderTheme.rangeTrackShape.paint(
context,
offset,
parentBox: this,
sliderTheme: _sliderTheme,
enableAnimation: _enableAnimation,
textDirection: _textDirection,
startThumbCenter: startThumbCenter,
endThumbCenter: endThumbCenter,
isDiscrete: isDiscrete,
isEnabled: isEnabled
);
if (!_overlayAnimation.isDismissed) {
if (_lastThumbSelection == Thumb.start) {
_sliderTheme.overlayShape.paint(
context,
startThumbCenter,
activationAnimation: _overlayAnimation,
enableAnimation: _enableAnimation,
isDiscrete: isDiscrete,
labelPainter: _startLabelPainter,
parentBox: this,
sliderTheme: _sliderTheme,
textDirection: _textDirection,
value: startValue
);
}
if (_lastThumbSelection == Thumb.end) {
_sliderTheme.overlayShape.paint(
context,
endThumbCenter,
activationAnimation: _overlayAnimation,
enableAnimation: _enableAnimation,
isDiscrete: isDiscrete,
labelPainter: _endLabelPainter,
parentBox: this,
sliderTheme: _sliderTheme,
textDirection: _textDirection,
value: endValue
);
}
}
if (isDiscrete) {
float tickMarkWidth = _sliderTheme.rangeTickMarkShape.getPreferredSize(
isEnabled: isEnabled,
sliderTheme: _sliderTheme
).width;
float adjustedTrackWidth = trackRect.width - tickMarkWidth;
if (adjustedTrackWidth / divisions >= 3.0f * tickMarkWidth) {
float dy = trackRect.center.dy;
for (int i = 0; i <= divisions; i++) {
float value = i / divisions.Value;
float dx = trackRect.left + value * adjustedTrackWidth + tickMarkWidth / 2;
Offset tickMarkOffset = new Offset(dx, dy);
_sliderTheme.rangeTickMarkShape.paint(
context,
tickMarkOffset,
parentBox: this,
sliderTheme: _sliderTheme,
enableAnimation: _enableAnimation,
textDirection: _textDirection,
startThumbCenter: startThumbCenter,
endThumbCenter: endThumbCenter,
isEnabled: isEnabled
);
}
}
}
float thumbDelta = (endThumbCenter.dx - startThumbCenter.dx).abs();
bool isLastThumbStart = _lastThumbSelection == Thumb.start;
Thumb bottomThumb = isLastThumbStart ? Thumb.end : Thumb.start;
Thumb topThumb = isLastThumbStart ? Thumb.start : Thumb.end;
Offset bottomThumbCenter = isLastThumbStart ? endThumbCenter : startThumbCenter;
Offset topThumbCenter = isLastThumbStart ? startThumbCenter : endThumbCenter;
TextPainter bottomLabelPainter = isLastThumbStart ? _endLabelPainter : _startLabelPainter;
TextPainter topLabelPainter = isLastThumbStart ? _startLabelPainter : _endLabelPainter;
float bottomValue = isLastThumbStart ? endValue : startValue;
float topValue = isLastThumbStart ? startValue : endValue;
bool shouldPaintValueIndicators = isEnabled && labels != null && !_valueIndicatorAnimation.isDismissed &&
showValueIndicator;
if (shouldPaintValueIndicators) {
_sliderTheme.rangeValueIndicatorShape.paint(
context,
bottomThumbCenter,
activationAnimation: _valueIndicatorAnimation,
enableAnimation: _enableAnimation,
isDiscrete: isDiscrete,
isOnTop: false,
labelPainter: bottomLabelPainter,
parentBox: this,
sliderTheme: _sliderTheme,
textDirection: _textDirection,
thumb: bottomThumb,
value: bottomValue
);
}
_sliderTheme.rangeThumbShape.paint(
context,
bottomThumbCenter,
activationAnimation: _valueIndicatorAnimation,
enableAnimation: _enableAnimation,
isDiscrete: isDiscrete,
isOnTop: false,
textDirection: textDirection,
sliderTheme: _sliderTheme,
thumb: bottomThumb
);
if (shouldPaintValueIndicators) {
float startOffset = sliderTheme.rangeValueIndicatorShape.getHorizontalShift(
parentBox: this,
center: startThumbCenter,
labelPainter: _startLabelPainter,
activationAnimation: _valueIndicatorAnimation
);
float endOffset = sliderTheme.rangeValueIndicatorShape.getHorizontalShift(
parentBox: this,
center: endThumbCenter,
labelPainter: _endLabelPainter,
activationAnimation: _valueIndicatorAnimation
);
float startHalfWidth = sliderTheme.rangeValueIndicatorShape
.getPreferredSize(isEnabled, isDiscrete, labelPainter: _startLabelPainter)
.width / 2;
float endHalfWidth = sliderTheme.rangeValueIndicatorShape
.getPreferredSize(isEnabled, isDiscrete, labelPainter: _endLabelPainter)
.width / 2;
float innerOverflow = startHalfWidth + endHalfWidth;
switch (textDirection) {
case TextDirection.ltr:
innerOverflow += startOffset;
innerOverflow -= endOffset;
break;
case TextDirection.rtl:
innerOverflow -= startOffset;
innerOverflow += endOffset;
break;
}
_sliderTheme.rangeValueIndicatorShape.paint(
context,
topThumbCenter,
activationAnimation: _valueIndicatorAnimation,
enableAnimation: _enableAnimation,
isDiscrete: isDiscrete,
isOnTop: thumbDelta < innerOverflow,
labelPainter: topLabelPainter,
parentBox: this,
sliderTheme: _sliderTheme,
textDirection: _textDirection,
thumb: topThumb,
value: topValue
);
}
_sliderTheme.rangeThumbShape.paint(
context,
topThumbCenter,
activationAnimation: _valueIndicatorAnimation,
enableAnimation: _enableAnimation,
isDiscrete: isDiscrete,
isOnTop: thumbDelta < sliderTheme.rangeThumbShape.getPreferredSize(isEnabled, isDiscrete).width,
textDirection: textDirection,
sliderTheme: _sliderTheme,
thumb: topThumb
);
}
}
}