您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
283 行
10 KiB
283 行
10 KiB
using Unity.UIWidgets.animation;
|
|
using Unity.UIWidgets.foundation;
|
|
using Unity.UIWidgets.painting;
|
|
using Unity.UIWidgets.ui;
|
|
using UnityEngine;
|
|
using Canvas = Unity.UIWidgets.ui.Canvas;
|
|
using Color = Unity.UIWidgets.ui.Color;
|
|
using Rect = Unity.UIWidgets.ui.Rect;
|
|
|
|
namespace Unity.UIWidgets.widgets {
|
|
|
|
static class ScrollbarPainterUtils {
|
|
|
|
public const float _kMinThumbExtent = 18.0f;
|
|
public const float _kMinInteractiveSize = 48.0f;
|
|
}
|
|
public class ScrollbarPainter : ChangeNotifier, CustomPainter {
|
|
public ScrollbarPainter(
|
|
Color color,
|
|
TextDirection textDirection,
|
|
float thickness,
|
|
Animation<float> fadeoutOpacityAnimation,
|
|
EdgeInsets padding = null,
|
|
float mainAxisMargin = 0.0f,
|
|
float crossAxisMargin = 0.0f,
|
|
Radius radius = null,
|
|
float minLength = ScrollbarPainterUtils._kMinThumbExtent,
|
|
float? minOverscrollLength = null
|
|
) {
|
|
D.assert(color != null);
|
|
D.assert(fadeoutOpacityAnimation != null);
|
|
D.assert(minLength >= 0);
|
|
D.assert(minOverscrollLength == null || minOverscrollLength.Value <= minLength);
|
|
D.assert(minOverscrollLength == null || minOverscrollLength.Value >= 0);
|
|
padding = padding ?? EdgeInsets.zero;
|
|
|
|
D.assert(padding.isNonNegative);
|
|
_color = color;
|
|
_textDirection = textDirection;
|
|
_padding = padding;
|
|
this.thickness = thickness;
|
|
this.fadeoutOpacityAnimation = fadeoutOpacityAnimation;
|
|
this.mainAxisMargin = mainAxisMargin;
|
|
this.crossAxisMargin = crossAxisMargin;
|
|
this.radius = radius;
|
|
this.minLength = minLength;
|
|
this.minOverscrollLength = minOverscrollLength ?? minLength;
|
|
fadeoutOpacityAnimation.addListener(notifyListeners);
|
|
}
|
|
|
|
public Color color {
|
|
get { return _color; }
|
|
set {
|
|
D.assert(value != null);
|
|
if (color == value) {
|
|
return;
|
|
}
|
|
|
|
_color = value;
|
|
notifyListeners();
|
|
}
|
|
}
|
|
Color _color;
|
|
|
|
public TextDirection textDirection {
|
|
get { return _textDirection; }
|
|
set {
|
|
if (textDirection == value) {
|
|
return;
|
|
}
|
|
|
|
_textDirection = value;
|
|
notifyListeners();
|
|
}
|
|
}
|
|
|
|
TextDirection _textDirection;
|
|
|
|
public float thickness;
|
|
public readonly Animation<float> fadeoutOpacityAnimation;
|
|
public readonly float mainAxisMargin;
|
|
public readonly float crossAxisMargin;
|
|
public Radius radius;
|
|
|
|
public EdgeInsets padding {
|
|
get { return _padding; }
|
|
set {
|
|
D.assert(value != null);
|
|
if (padding == value) {
|
|
return;
|
|
}
|
|
|
|
_padding = value;
|
|
notifyListeners();
|
|
}
|
|
}
|
|
EdgeInsets _padding;
|
|
|
|
public readonly float minLength;
|
|
public readonly float minOverscrollLength;
|
|
|
|
ScrollMetrics _lastMetrics;
|
|
AxisDirection? _lastAxisDirection;
|
|
Rect _thumbRect;
|
|
|
|
public void update(ScrollMetrics metrics, AxisDirection axisDirection) {
|
|
_lastMetrics = metrics;
|
|
_lastAxisDirection = axisDirection;
|
|
notifyListeners();
|
|
}
|
|
|
|
public void updateThickness(float nextThickness, Radius nextRadius) {
|
|
thickness = nextThickness;
|
|
radius = nextRadius;
|
|
notifyListeners();
|
|
}
|
|
|
|
Paint _paint {
|
|
get {
|
|
var paint = new Paint();
|
|
paint.color = color.withOpacity(color.opacity * fadeoutOpacityAnimation.value);
|
|
return paint;
|
|
}
|
|
}
|
|
|
|
void _paintThumbCrossAxis(Canvas canvas, Size size, float thumbOffset, float thumbExtent, AxisDirection direction) {
|
|
float x = 0;
|
|
float y = 0;
|
|
Size thumbSize = Size.zero;
|
|
|
|
switch (direction) {
|
|
case AxisDirection.down:
|
|
thumbSize = new Size(thickness, thumbExtent);
|
|
x = textDirection == TextDirection.rtl
|
|
? crossAxisMargin + padding.left
|
|
: size.width - thickness - crossAxisMargin - padding.right;
|
|
y = thumbOffset;
|
|
break;
|
|
case AxisDirection.up:
|
|
thumbSize = new Size(thickness, thumbExtent);
|
|
x = textDirection == TextDirection.rtl
|
|
? crossAxisMargin + padding.left
|
|
: size.width - thickness - crossAxisMargin - padding.right;
|
|
y = thumbOffset;
|
|
break;
|
|
case AxisDirection.left:
|
|
thumbSize = new Size(thumbExtent, thickness);
|
|
x = thumbOffset;
|
|
y = size.height - thickness - crossAxisMargin - padding.bottom;
|
|
break;
|
|
case AxisDirection.right:
|
|
thumbSize = new Size(thumbExtent, thickness);
|
|
x = thumbOffset;
|
|
y = size.height - thickness - crossAxisMargin - padding.bottom;
|
|
break;
|
|
}
|
|
|
|
_thumbRect = new Offset(x, y) & thumbSize;
|
|
if (radius == null) {
|
|
canvas.drawRect(_thumbRect, _paint);
|
|
}
|
|
else {
|
|
canvas.drawRRect(RRect.fromRectAndRadius(_thumbRect, radius), _paint);
|
|
}
|
|
}
|
|
|
|
float _thumbExtent() {
|
|
float fractionVisible = ((_lastMetrics.extentInside() - _mainAxisPadding) / (_totalContentExtent - _mainAxisPadding))
|
|
.clamp(0.0f, 1.0f);
|
|
|
|
float thumbExtent = Mathf.Max(
|
|
Mathf.Min(_trackExtent, minOverscrollLength),
|
|
_trackExtent * fractionVisible
|
|
);
|
|
|
|
float fractionOverscrolled = 1.0f - _lastMetrics.extentInside() / _lastMetrics.viewportDimension;
|
|
float safeMinLength = Mathf.Min(minLength, _trackExtent);
|
|
float newMinLength = (_beforeExtent > 0 && _afterExtent > 0)
|
|
? safeMinLength
|
|
: safeMinLength * (1.0f - fractionOverscrolled.clamp(0.0f, 0.2f) / 0.2f);
|
|
|
|
return thumbExtent.clamp(newMinLength, _trackExtent);
|
|
}
|
|
|
|
public override void dispose() {
|
|
fadeoutOpacityAnimation.removeListener(notifyListeners);
|
|
base.dispose();
|
|
}
|
|
|
|
bool _isVertical => _lastAxisDirection == AxisDirection.down || _lastAxisDirection == AxisDirection.up;
|
|
bool _isReversed => _lastAxisDirection == AxisDirection.up || _lastAxisDirection == AxisDirection.left;
|
|
float _beforeExtent => _isReversed ? _lastMetrics.extentAfter() : _lastMetrics.extentBefore();
|
|
float _afterExtent => _isReversed ? _lastMetrics.extentBefore() : _lastMetrics.extentAfter();
|
|
float _mainAxisPadding => _isVertical ? padding.vertical : padding.horizontal;
|
|
float _trackExtent => _lastMetrics.viewportDimension - 2 * mainAxisMargin - _mainAxisPadding;
|
|
|
|
float _totalContentExtent {
|
|
get {
|
|
return _lastMetrics.maxScrollExtent
|
|
- _lastMetrics.minScrollExtent
|
|
+ _lastMetrics.viewportDimension;
|
|
}
|
|
}
|
|
|
|
public float getTrackToScroll(float thumbOffsetLocal) {
|
|
float scrollableExtent = _lastMetrics.maxScrollExtent - _lastMetrics.minScrollExtent;
|
|
float thumbMovableExtent = _trackExtent - _thumbExtent();
|
|
|
|
return scrollableExtent * thumbOffsetLocal / thumbMovableExtent;
|
|
}
|
|
|
|
float _getScrollToTrack(ScrollMetrics metrics, float thumbExtent) {
|
|
float scrollableExtent = metrics.maxScrollExtent - metrics.minScrollExtent;
|
|
|
|
float fractionPast = (scrollableExtent > 0)
|
|
? ((metrics.pixels - metrics.minScrollExtent) / scrollableExtent).clamp(0.0f, 1.0f)
|
|
: 0;
|
|
|
|
return (_isReversed ? 1 - fractionPast : fractionPast) * (_trackExtent - thumbExtent);
|
|
}
|
|
|
|
public void paint(Canvas canvas, Size size) {
|
|
if (_lastAxisDirection == null
|
|
|| _lastMetrics == null
|
|
|| fadeoutOpacityAnimation.value == 0.0f) {
|
|
return;
|
|
}
|
|
|
|
if (_lastMetrics.viewportDimension <= _mainAxisPadding || _trackExtent <= 0) {
|
|
return;
|
|
}
|
|
|
|
float beforePadding = _isVertical ? padding.top : padding.left;
|
|
float thumbExtent = _thumbExtent();
|
|
float thumbOffsetLocal = _getScrollToTrack(_lastMetrics, thumbExtent);
|
|
float thumbOffset = thumbOffsetLocal + mainAxisMargin + beforePadding;
|
|
|
|
_paintThumbCrossAxis(canvas, size, thumbOffset, thumbExtent, _lastAxisDirection.Value);
|
|
}
|
|
|
|
public bool hitTestInteractive(Offset position) {
|
|
if (_thumbRect == null) {
|
|
return false;
|
|
}
|
|
|
|
if (fadeoutOpacityAnimation.value == 0.0f) {
|
|
return false;
|
|
}
|
|
Rect interactiveThumbRect = _thumbRect.expandToInclude(
|
|
Rect.fromCircle(center: _thumbRect.center, radius: ScrollbarPainterUtils._kMinInteractiveSize / 2)
|
|
);
|
|
return interactiveThumbRect.contains(position);
|
|
}
|
|
|
|
public bool? hitTest(Offset position) {
|
|
if (_thumbRect == null) {
|
|
return null;
|
|
}
|
|
|
|
if (fadeoutOpacityAnimation.value == 0.0f) {
|
|
return false;
|
|
}
|
|
|
|
return _thumbRect.contains(position);
|
|
}
|
|
|
|
public bool shouldRepaint(CustomPainter oldRaw) {
|
|
if (oldRaw is ScrollbarPainter old) {
|
|
return color != old.color
|
|
|| textDirection != old.textDirection
|
|
|| thickness != old.thickness
|
|
|| fadeoutOpacityAnimation != old.fadeoutOpacityAnimation
|
|
|| mainAxisMargin != old.mainAxisMargin
|
|
|| crossAxisMargin != old.crossAxisMargin
|
|
|| radius != old.radius
|
|
|| minLength != old.minLength
|
|
|| padding != old.padding;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
}
|