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

623 行
23 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.gestures;
using Unity.UIWidgets.widgets;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using UnityEngine;
using Color = Unity.UIWidgets.ui.Color;
using Rect = Unity.UIWidgets.ui.Rect;
using TextStyle = Unity.UIWidgets.painting.TextStyle;
namespace Unity.UIWidgets.cupertino {
class CupertinoSegmentedControlsUtils {
public static readonly EdgeInsetsGeometry _kHorizontalItemPadding = EdgeInsets.symmetric(horizontal: 16.0f);
public const float _kMinSegmentedControlHeight = 28.0f;
public static readonly TimeSpan _kFadeDuration = TimeSpan.FromMilliseconds(165);
}
public class CupertinoSegmentedControl<T> : StatefulWidget {
public CupertinoSegmentedControl(
Key key = null,
Dictionary<T, Widget> children = null,
ValueChanged<T> onValueChanged = null,
T groupValue = default,
Color unselectedColor = null,
Color selectedColor = null,
Color borderColor = null,
Color pressedColor = null,
EdgeInsetsGeometry padding = null
) :base(key: key)
{ D.assert(children != null);
D.assert(children.Count >= 2);
D.assert(onValueChanged != null);
D.assert(
groupValue == null || children.Keys.Any((T child) => child.Equals(groupValue)), () =>
"The groupValue must be either null or one of the keys in the children map."
);
this.children = children;
this.onValueChanged = onValueChanged;
this.groupValue = groupValue;
this.unselectedColor = unselectedColor;
this.selectedColor = selectedColor;
this.borderColor = borderColor;
this.pressedColor = pressedColor;
this.padding = padding;
}
public readonly Dictionary<T, Widget> children;
public readonly T groupValue;
public readonly ValueChanged<T> onValueChanged;
public readonly Color unselectedColor;
public readonly Color selectedColor;
public readonly Color borderColor;
public readonly Color pressedColor;
public readonly EdgeInsetsGeometry padding;
public override State createState() {
return new _SegmentedControlState<T>();
}
}
class _SegmentedControlState<T> : TickerProviderStateMixin<CupertinoSegmentedControl<T>> {
T _pressedKey;
public readonly List<AnimationController> _selectionControllers = new List<AnimationController>();
public readonly List<ColorTween> _childTweens = new List<ColorTween>();
ColorTween _forwardBackgroundColorTween;
ColorTween _reverseBackgroundColorTween;
ColorTween _textColorTween;
Color _selectedColor;
Color _unselectedColor;
Color _borderColor;
Color _pressedColor;
AnimationController createAnimationController() {
var controller = new AnimationController(
duration: CupertinoSegmentedControlsUtils._kFadeDuration,
vsync: this
);
controller.addListener(()=> {
setState(() =>{
// State of background/text colors has changed
});
});
return controller;
}
bool _updateColors() {
D.assert(mounted, ()=>"This should only be called after didUpdateDependencies");
bool changed = false;
Color selectedColor = widget.selectedColor ?? CupertinoTheme.of(context).primaryColor;
if (_selectedColor != selectedColor) {
changed = true;
_selectedColor = selectedColor;
}
Color unselectedColor = widget.unselectedColor ?? CupertinoTheme.of(context).primaryContrastingColor;
if (_unselectedColor != unselectedColor) {
changed = true;
_unselectedColor = unselectedColor;
}
Color borderColor = widget.borderColor ?? CupertinoTheme.of(context).primaryColor;
if (_borderColor != borderColor) {
changed = true;
_borderColor = borderColor;
}
Color pressedColor = widget.pressedColor ?? CupertinoTheme.of(context).primaryColor.withOpacity(0.2f);
if (_pressedColor != pressedColor) {
changed = true;
_pressedColor = pressedColor;
}
_forwardBackgroundColorTween = new ColorTween(
begin: _pressedColor,
end: _selectedColor
);
_reverseBackgroundColorTween = new ColorTween(
begin: _unselectedColor,
end: _selectedColor
);
_textColorTween = new ColorTween(
begin: _selectedColor,
end: _unselectedColor
);
return changed;
}
void _updateAnimationControllers() {
D.assert(mounted, ()=>"This should only be called after didUpdateDependencies");
foreach ( AnimationController controller in _selectionControllers) {
controller.dispose();
}
_selectionControllers.Clear();
_childTweens.Clear();
foreach ( T key in widget.children.Keys) {
AnimationController animationController = createAnimationController();
if (widget.groupValue.Equals(key)) {
_childTweens.Add(_reverseBackgroundColorTween);
animationController.setValue(1.0f);
} else {
_childTweens.Add(_forwardBackgroundColorTween);
}
_selectionControllers.Add(animationController);
}
}
public override void didChangeDependencies() {
base.didChangeDependencies();
if (_updateColors()) {
_updateAnimationControllers();
}
}
public override void didUpdateWidget(StatefulWidget oldWidget) {
oldWidget = (CupertinoSegmentedControl<T>) oldWidget;
base.didUpdateWidget(oldWidget);
if (_updateColors() || ((CupertinoSegmentedControl<T>) oldWidget).children.Count != widget.children.Count) {
_updateAnimationControllers();
}
if (!((CupertinoSegmentedControl<T>)oldWidget).groupValue.Equals(widget.groupValue)) {
int index = 0;
foreach ( T key in widget.children.Keys) {
if (widget.groupValue.Equals(key)) {
_childTweens[index] = _forwardBackgroundColorTween;
_selectionControllers[index].forward();
} else {
_childTweens[index] = _reverseBackgroundColorTween;
_selectionControllers[index].reverse();
}
index += 1;
}
}
}
public override void dispose() {
foreach( AnimationController animationController in _selectionControllers) {
animationController.dispose();
}
base.dispose();
}
void _onTapDown(T currentKey) {
if (_pressedKey == null && !currentKey.Equals(widget.groupValue)) {
setState(()=> {
_pressedKey = currentKey;
});
}
}
void _onTapCancel() {
setState(()=> {
_pressedKey = default(T);
});
}
void _onTap(T currentKey) {
if (!currentKey.Equals( _pressedKey))
return;
if (!currentKey.Equals(widget.groupValue)) {
widget.onValueChanged(currentKey);
}
_pressedKey = default;
}
Color getTextColor(int index, T currentKey) {
if (_selectionControllers[index].isAnimating)
return _textColorTween.evaluate(_selectionControllers[index]);
if (widget.groupValue.Equals(currentKey))
return _unselectedColor;
return _selectedColor;
}
Color getBackgroundColor(int index, T currentKey) {
if (_selectionControllers[index].isAnimating)
return _childTweens[index].evaluate(_selectionControllers[index]);
if (widget.groupValue.Equals(currentKey))
return _selectedColor;
if (_pressedKey.Equals(currentKey))
return _pressedColor;
return _unselectedColor;
}
public override Widget build(BuildContext context) {
List<Widget> _gestureChildren = new List<Widget>();
List<Color> _backgroundColors = new List<Color>();
int index = 0;
int selectedIndex = 0;
int pressedIndex = 0;
foreach ( T currentKey in widget.children.Keys) {
selectedIndex = (widget.groupValue.Equals(currentKey)) ? index : selectedIndex;
pressedIndex = (_pressedKey.Equals(currentKey)) ? index : pressedIndex;
TextStyle textStyle = DefaultTextStyle.of(context).style.copyWith(
color: getTextColor(index, currentKey)
);
IconThemeData iconTheme = new IconThemeData(
color: getTextColor(index, currentKey)
);
Widget child = new Center(
child: widget.children[currentKey]
);
child = new GestureDetector(
onTapDown: (TapDownDetails _event)=> {
_onTapDown(currentKey);
},
onTapCancel: _onTapCancel,
onTap: ()=> {
_onTap(currentKey);
},
child: new IconTheme(
data: iconTheme,
child: new DefaultTextStyle(
style: textStyle,
child: child /*Semantics(
button: true,
inMutuallyExclusiveGroup: true,
selected: widget.groupValue.Equals(currentKey),
child: child
)*/
)
)
);
_backgroundColors.Add(getBackgroundColor(index, currentKey));
_gestureChildren.Add(child);
index += 1;
}
Widget box = new _SegmentedControlRenderWidget<T>(
children: _gestureChildren,
selectedIndex: selectedIndex,
pressedIndex: pressedIndex,
backgroundColors: _backgroundColors,
borderColor: _borderColor
);
return new Padding(
padding: widget.padding ?? CupertinoSegmentedControlsUtils._kHorizontalItemPadding,
child: new UnconstrainedBox(
constrainedAxis: Axis.horizontal,
child: box
)
);
}
}
public class _SegmentedControlRenderWidget<T> : MultiChildRenderObjectWidget {
public _SegmentedControlRenderWidget(
Key key = null,
List<Widget> children = null,
int? selectedIndex = null,
int? pressedIndex = null,
List<Color> backgroundColors = null,
Color borderColor = null
) : base(
key: key,
children: children ?? new List<Widget>()
) {
this.selectedIndex = selectedIndex;
this.pressedIndex = pressedIndex;
this.backgroundColors = backgroundColors;
this.borderColor = borderColor;
}
public readonly int? selectedIndex;
public readonly int? pressedIndex;
public readonly List<Color> backgroundColors;
public readonly Color borderColor;
public override RenderObject createRenderObject(BuildContext context) {
return new _RenderSegmentedControl<T>(
textDirection: Directionality.of(context),
selectedIndex: selectedIndex,
pressedIndex: pressedIndex,
backgroundColors: backgroundColors,
borderColor: borderColor
);
}
public override void updateRenderObject(BuildContext context,RenderObject renderObject) {
renderObject = (_RenderSegmentedControl<T>) renderObject;
((_RenderSegmentedControl<T>) renderObject).textDirection = Directionality.of(context);
((_RenderSegmentedControl<T>) renderObject).selectedIndex = selectedIndex;
((_RenderSegmentedControl<T>) renderObject).pressedIndex = pressedIndex;
((_RenderSegmentedControl<T>) renderObject).backgroundColors = backgroundColors;
((_RenderSegmentedControl<T>) renderObject).borderColor = borderColor;
}
}
class _SegmentedControlContainerBoxParentData : ContainerBoxParentData<RenderBox> {
public RRect surroundingRect;
}
public delegate RenderBox _NextChild(RenderBox child);
class _RenderSegmentedControl<T> : RenderBoxContainerDefaultsMixinContainerRenderObjectMixinRenderBox<RenderBox,
ContainerBoxParentData<RenderBox>> {
public _RenderSegmentedControl(
int? selectedIndex = null,
int? pressedIndex = null,
TextDirection? textDirection = null,
List<Color> backgroundColors = null,
Color borderColor = null
) {
D.assert(textDirection != null);
_textDirection = textDirection;
_selectedIndex = selectedIndex;
_pressedIndex = pressedIndex;
_backgroundColors = backgroundColors;
_borderColor = borderColor;
}
public int? selectedIndex {
get {return _selectedIndex; }
set {
if (_selectedIndex == value) {
return;
}
_selectedIndex = value;
markNeedsPaint();
}
}
int? _selectedIndex;
public int? pressedIndex {
get {
return _pressedIndex;
}
set {
if (_pressedIndex == value) {
return;
}
_pressedIndex = value;
markNeedsPaint();
}
}
int? _pressedIndex;
public TextDirection? textDirection {
get {
return _textDirection;
}
set {
if (_textDirection == value) {
return;
}
_textDirection = value;
markNeedsLayout();
}
}
TextDirection? _textDirection;
public List<Color> backgroundColors {
get {
return _backgroundColors;
}
set {
if (_backgroundColors == value) {
return;
}
_backgroundColors = value;
markNeedsPaint();
}
}
List<Color> _backgroundColors;
public Color borderColor {
get {
return _borderColor;
}
set {
if (_borderColor == value) {
return;
}
_borderColor = value;
markNeedsPaint();
}
}
Color _borderColor;
protected internal override float computeMinIntrinsicWidth(float height) {
RenderBox child = firstChild;
float minWidth = 0.0f;
while (child != null) {
_SegmentedControlContainerBoxParentData childParentData = child.parentData as _SegmentedControlContainerBoxParentData;
float childWidth = child.getMinIntrinsicWidth(height);
minWidth = Mathf.Max(minWidth, childWidth);
child = childParentData.nextSibling;
}
return minWidth * childCount;
}
protected internal override float computeMaxIntrinsicWidth(float height) {
RenderBox child = firstChild;
float maxWidth = 0.0f;
while (child != null) {
_SegmentedControlContainerBoxParentData childParentData = child.parentData as _SegmentedControlContainerBoxParentData;
float childWidth = child.getMaxIntrinsicWidth(height);
maxWidth = Mathf.Max(maxWidth, childWidth);
child = childParentData.nextSibling;
}
return maxWidth * childCount;
}
protected internal override float computeMinIntrinsicHeight(float width) {
RenderBox child = firstChild;
float minHeight = 0.0f;
while (child != null) {
_SegmentedControlContainerBoxParentData childParentData = child.parentData as _SegmentedControlContainerBoxParentData;
float childHeight = child.getMinIntrinsicHeight(width);
minHeight = Mathf.Max(minHeight, childHeight);
child = childParentData.nextSibling;
}
return minHeight;
}
protected internal override float computeMaxIntrinsicHeight(float width) {
RenderBox child = firstChild;
float maxHeight = 0.0f;
while (child != null) {
_SegmentedControlContainerBoxParentData childParentData = child.parentData as _SegmentedControlContainerBoxParentData;
float childHeight = child.getMaxIntrinsicHeight(width);
maxHeight = Mathf.Max(maxHeight, childHeight);
child = childParentData.nextSibling;
}
return maxHeight;
}
public override float? computeDistanceToActualBaseline(TextBaseline baseline) {
return defaultComputeDistanceToHighestActualBaseline(baseline);
}
public override void setupParentData(RenderObject child) {
child = (RenderBox) child;
if (!(child.parentData is _SegmentedControlContainerBoxParentData)) {
child.parentData = new _SegmentedControlContainerBoxParentData();
}
}
void _layoutRects(_NextChild nextChild, RenderBox leftChild, RenderBox rightChild) {
RenderBox child = leftChild;
float start = 0.0f;
while (child != null) {
_SegmentedControlContainerBoxParentData childParentData = child.parentData as _SegmentedControlContainerBoxParentData;
Offset childOffset = new Offset(start, 0.0f);
childParentData.offset = childOffset;
Rect childRect = Rect.fromLTWH(start, 0.0f, child.size.width, child.size.height);
RRect rChildRect = null;
if (child == leftChild) {
rChildRect = RRect.fromRectAndCorners(childRect, topLeft: Radius.circular(3.0f),
bottomLeft: Radius.circular(3.0f));
} else if (child == rightChild) {
rChildRect = RRect.fromRectAndCorners(childRect, topRight: Radius.circular(3.0f),
bottomRight: Radius.circular(3.0f));
} else {
rChildRect = RRect.fromRectAndCorners(childRect,topRight:Radius.zero);
}
childParentData.surroundingRect = rChildRect;
start += child.size.width;
child = nextChild(child);
}
}
protected override void performLayout() {
BoxConstraints constraints = this.constraints;
float maxHeight = CupertinoSegmentedControlsUtils._kMinSegmentedControlHeight;
float childWidth = constraints.minWidth / childCount;
foreach ( RenderBox child in getChildrenAsList()) {
childWidth = Mathf.Max(childWidth, child.getMaxIntrinsicWidth(float.PositiveInfinity));
}
childWidth = Mathf.Min(childWidth, constraints.maxWidth / childCount);
RenderBox child1 = firstChild;
while (child1 != null) {
float boxHeight = child1.getMaxIntrinsicHeight(childWidth);
maxHeight = Mathf.Max(maxHeight, boxHeight);
child1 = childAfter(child1);
}
constraints.constrainHeight(maxHeight);
BoxConstraints childConstraints = BoxConstraints.tightFor(
width: childWidth,
height: maxHeight
);
child1 = firstChild;
while (child1 != null) {
child1.layout(childConstraints, parentUsesSize: true);
child1 = childAfter(child1);
}
switch (textDirection) {
case TextDirection.rtl:
_layoutRects(
childBefore,
lastChild,
firstChild
);
break;
case TextDirection.ltr:
_layoutRects(
childAfter,
firstChild,
lastChild
);
break;
}
size = constraints.constrain(new Size(childWidth * childCount, maxHeight));
}
public override void paint(PaintingContext context, Offset offset) {
RenderBox child = firstChild;
int index = 0;
while (child != null) {
_paintChild(context, offset, child, index);
child = childAfter(child);
index += 1;
}
}
void _paintChild(PaintingContext context, Offset offset, RenderBox child, int childIndex) {
D.assert(child != null);
_SegmentedControlContainerBoxParentData childParentData = child.parentData as _SegmentedControlContainerBoxParentData;
context.canvas.drawRRect(
childParentData.surroundingRect.shift(offset),
new Paint(){
color = backgroundColors[childIndex],
style = PaintingStyle.fill}
);
context.canvas.drawRRect(
childParentData.surroundingRect.shift(offset),
new Paint(){
color = borderColor,
strokeWidth = 1.0f,
style = PaintingStyle.stroke}
);
context.paintChild(child, childParentData.offset + offset);
}
protected override bool hitTestChildren(BoxHitTestResult result, Offset position = null ) {
D.assert(position != null);
RenderBox child = lastChild;
while (child != null) {
_SegmentedControlContainerBoxParentData childParentData = child.parentData as _SegmentedControlContainerBoxParentData;
if (childParentData.surroundingRect.contains(position)) {
Offset center = (Offset.zero & child.size).center;
return result.addWithRawTransform(
transform: MatrixUtils.forceToPoint(center),
position: center,
hitTest: (BoxHitTestResult result1, Offset position1) =>{
D.assert(position1 == center);
return child.hitTest(result1, position: center);
}
);
}
child = childParentData.previousSibling;
}
return false;
}
}
}