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

432 行
16 KiB

using System;
using System.Collections.Generic;
using uiwidgets;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.scheduler;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using Canvas = Unity.UIWidgets.ui.Canvas;
using Color = Unity.UIWidgets.ui.Color;
using Rect = Unity.UIWidgets.ui.Rect;
namespace UIWidgetsSample.RaycastableScene {
class CheckboxUtils {
public const float _kEdgeSize = Checkbox.width;
public static readonly Radius _kEdgeRadius = Radius.circular(1.0f);
public const float _kStrokeWidth = 2.0f;
public static Color checkColor = Colors.blue;
public static float kRadialReactionRadius = 12f;
}
public class Checkbox : StatefulWidget {
public Checkbox(
Key key = null,
bool? value = false,
bool tristate = false,
ValueChanged<bool?> onChanged = null,
Color activeColor = null,
Color checkColor = null,
Color focusColor = null,
Color hoverColor = null,
MaterialTapTargetSize? materialTapTargetSize = null,
VisualDensity visualDensity = null,
FocusNode focusNode = null,
bool autofocus = false
) : base(key: key) {
D.assert(tristate || value != null);
this.value = value;
this.onChanged = onChanged;
this.activeColor = activeColor;
this.checkColor = checkColor;
this.focusColor = focusColor;
this.hoverColor = hoverColor;
this.tristate = tristate;
this.materialTapTargetSize = materialTapTargetSize;
this.visualDensity = visualDensity;
this.focusNode = focusNode;
this.autofocus = autofocus;
}
public readonly bool? value;
public readonly ValueChanged<bool?> onChanged;
public readonly Color activeColor;
public readonly Color checkColor;
public readonly bool tristate;
public readonly MaterialTapTargetSize? materialTapTargetSize;
public readonly VisualDensity visualDensity;
public readonly Color focusColor;
public readonly Color hoverColor;
public readonly FocusNode focusNode;
public readonly bool autofocus;
public const float width = 10.0f;
public override State createState() {
return new _CheckboxState();
}
}
class _CheckboxState : TickerProviderStateMixin<Checkbox> {
bool enabled {
get { return widget.onChanged != null; }
}
Dictionary<LocalKey, ActionFactory> _actionMap;
public override void initState() {
base.initState();
_actionMap = new Dictionary<LocalKey, ActionFactory> {
{ActivateAction.key, _createAction}
};
}
void _actionHandler(FocusNode node, Intent intent) {
if (widget.onChanged != null) {
switch (widget.value) {
case false:
widget.onChanged(true);
break;
case true:
widget.onChanged(widget.tristate);
break;
default: // case null:
widget.onChanged(false);
break;
}
}
RenderObject renderObject = node.context.findRenderObject();
// renderObject.sendSemanticsEvent(const TapSemanticEvent());
}
UiWidgetAction _createAction() {
return new CallbackAction(
ActivateAction.key,
onInvoke: _actionHandler
);
}
bool _focused = false;
void _handleFocusHighlightChanged(bool focused) {
if (focused != _focused) {
setState(() => { _focused = focused; });
}
}
bool _hovering = false;
void _handleHoverChanged(bool hovering) {
if (hovering != _hovering) {
setState(() => { _hovering = hovering; });
}
}
public override Widget build(BuildContext context) {
Size size;
switch (widget.materialTapTargetSize ?? MaterialTapTargetSize.padded) {
case MaterialTapTargetSize.padded:
size = new Size(2 * CheckboxUtils.kRadialReactionRadius,
2 * CheckboxUtils.kRadialReactionRadius);
break;
case MaterialTapTargetSize.shrinkWrap:
size = new Size(2 * CheckboxUtils.kRadialReactionRadius, 2 * CheckboxUtils.kRadialReactionRadius);
break;
default:
throw new Exception("Unknown target size: " + widget.materialTapTargetSize);
}
size += (widget.visualDensity ?? VisualDensity.standard).baseSizeAdjustment;
BoxConstraints additionalConstraints = BoxConstraints.tight(size);
return new FocusableActionDetector(
actions: _actionMap,
focusNode: widget.focusNode,
autofocus: widget.autofocus,
enabled: enabled,
onShowFocusHighlight: _handleFocusHighlightChanged,
onShowHoverHighlight: _handleHoverChanged,
child: new Builder(
builder: (BuildContext _context) => {
return new _CheckboxRenderObjectWidget(
value: widget.value,
tristate: widget.tristate,
activeColor: widget.activeColor ?? CheckboxUtils.checkColor,
checkColor: widget.checkColor ?? new Color(0xFFFFFFFF),
inactiveColor: enabled ? CheckboxUtils.checkColor : Colors.grey,
focusColor: widget.focusColor ?? CheckboxUtils.checkColor,
hoverColor: widget.hoverColor ?? CheckboxUtils.checkColor,
onChanged: widget.onChanged,
additionalConstraints: additionalConstraints,
vsync: this,
hasFocus: _focused,
hovering: _hovering
);
}
)
);
}
}
class _CheckboxRenderObjectWidget : LeafRenderObjectWidget {
public _CheckboxRenderObjectWidget(
Key key = null,
bool? value = null,
bool tristate = false,
Color activeColor = null,
Color checkColor = null,
Color inactiveColor = null,
Color focusColor = null,
Color hoverColor = null,
ValueChanged<bool?> onChanged = null,
TickerProvider vsync = null,
BoxConstraints additionalConstraints = null,
bool? hasFocus = null,
bool? hovering = null
) : base(key: key) {
D.assert(tristate || value != null);
D.assert(activeColor != null);
D.assert(inactiveColor != null);
D.assert(vsync != null);
this.value = value;
this.tristate = tristate;
this.activeColor = activeColor;
this.checkColor = checkColor;
this.inactiveColor = inactiveColor;
this.focusColor = focusColor;
this.hoverColor = hoverColor;
this.onChanged = onChanged;
this.vsync = vsync;
this.additionalConstraints = additionalConstraints;
this.hasFocus = hasFocus;
this.hovering = hovering;
}
public readonly bool? value;
public readonly bool tristate;
public readonly bool? hasFocus;
public readonly bool? hovering;
public readonly Color activeColor;
public readonly Color checkColor;
public readonly Color inactiveColor;
public readonly Color focusColor;
public readonly Color hoverColor;
public readonly ValueChanged<bool?> onChanged;
public readonly TickerProvider vsync;
public readonly BoxConstraints additionalConstraints;
public override RenderObject createRenderObject(BuildContext context) {
return new _RenderCheckbox(
value: value,
tristate: tristate,
activeColor: activeColor,
checkColor: checkColor,
inactiveColor: inactiveColor,
focusColor: focusColor,
hoverColor: hoverColor,
onChanged: onChanged,
vsync: vsync,
additionalConstraints: additionalConstraints,
hasFocus: hasFocus,
hovering: hovering
);
}
public override void updateRenderObject(BuildContext context, RenderObject _renderObject) {
_RenderCheckbox renderObject = _renderObject as _RenderCheckbox;
renderObject.value = value;
renderObject.tristate = tristate;
renderObject.activeColor = activeColor;
renderObject.checkColor = checkColor;
renderObject.focusColor = focusColor;
renderObject.hoverColor = hoverColor;
renderObject.inactiveColor = inactiveColor;
renderObject.onChanged = onChanged;
renderObject.additionalConstraints = additionalConstraints;
renderObject.vsync = vsync;
renderObject.hasFocus = hasFocus ?? false;
renderObject.hovering = hovering ?? false;
}
}
class _RenderCheckbox : RenderToggleable {
public _RenderCheckbox(
bool? value = null,
bool tristate = false,
Color activeColor = null,
Color checkColor = null,
Color inactiveColor = null,
Color focusColor = null,
Color hoverColor = null,
BoxConstraints additionalConstraints = null,
ValueChanged<bool?> onChanged = null,
TickerProvider vsync = null,
bool? hasFocus = null,
bool? hovering = null
) : base(
value: value,
tristate: tristate,
activeColor: activeColor,
inactiveColor: inactiveColor,
focusColor: focusColor,
hoverColor: hoverColor,
onChanged: onChanged,
additionalConstraints: additionalConstraints,
vsync: vsync,
hasFocus: hasFocus ?? false,
hovering: hovering ?? false
) {
_oldValue = value;
this.checkColor = checkColor;
}
bool? _oldValue;
public Color checkColor;
public override bool? value {
set {
if (value == this.value) {
return;
}
_oldValue = this.value;
base.value = value;
}
}
RRect _outerRectAt(Offset origin, float t) {
float inset = 1.0f - (t - 0.5f).abs() * 2.0f;
float size = CheckboxUtils._kEdgeSize - inset * CheckboxUtils._kStrokeWidth;
Rect rect = Rect.fromLTWH(origin.dx + inset, origin.dy + inset, size, size);
return RRect.fromRectAndRadius(rect, CheckboxUtils._kEdgeRadius);
}
Color _colorAt(float t) {
return onChanged == null
? inactiveColor
: (t >= 0.25f ? activeColor : Color.lerp(inactiveColor, activeColor, t * 4.0f));
}
Paint _createStrokePaint() {
var paint = new Paint();
paint.color = checkColor;
paint.style = PaintingStyle.stroke;
paint.strokeWidth = CheckboxUtils._kStrokeWidth;
return paint;
}
void _drawBorder(Canvas canvas, RRect outer, float t, Paint paint) {
D.assert(t >= 0.0f && t <= 0.5f);
float size = outer.width;
RRect inner = outer.deflate(Mathf.Min(size / 2.0f, CheckboxUtils._kStrokeWidth + size * t));
canvas.drawDRRect(outer, inner, paint);
}
void _drawCheck(Canvas canvas, Offset origin, float t, Paint paint) {
D.assert(t >= 0.0f && t <= 1.0f);
Path path = new Path();
Offset start = new Offset(CheckboxUtils._kEdgeSize * 0.15f, CheckboxUtils._kEdgeSize * 0.45f);
Offset mid = new Offset(CheckboxUtils._kEdgeSize * 0.4f, CheckboxUtils._kEdgeSize * 0.7f);
Offset end = new Offset(CheckboxUtils._kEdgeSize * 0.85f, CheckboxUtils._kEdgeSize * 0.25f);
if (t < 0.5f) {
float strokeT = t * 2.0f;
Offset drawMid = Offset.lerp(start, mid, strokeT);
path.moveTo(origin.dx + start.dx, origin.dy + start.dy);
path.lineTo(origin.dx + drawMid.dx, origin.dy + drawMid.dy);
}
else {
float strokeT = (t - 0.5f) * 2.0f;
Offset drawEnd = Offset.lerp(mid, end, strokeT);
path.moveTo(origin.dx + start.dx, origin.dy + start.dy);
path.lineTo(origin.dx + mid.dx, origin.dy + mid.dy);
path.lineTo(origin.dx + drawEnd.dx, origin.dy + drawEnd.dy);
}
canvas.drawPath(path, paint);
}
void _drawDash(Canvas canvas, Offset origin, float t, Paint paint) {
D.assert(t >= 0.0f && t <= 1.0f);
Offset start = new Offset(CheckboxUtils._kEdgeSize * 0.2f, CheckboxUtils._kEdgeSize * 0.5f);
Offset mid = new Offset(CheckboxUtils._kEdgeSize * 0.5f, CheckboxUtils._kEdgeSize * 0.5f);
Offset end = new Offset(CheckboxUtils._kEdgeSize * 0.8f, CheckboxUtils._kEdgeSize * 0.5f);
Offset drawStart = Offset.lerp(start, mid, 1.0f - t);
Offset drawEnd = Offset.lerp(mid, end, t);
canvas.drawLine(origin + drawStart, origin + drawEnd, paint);
}
public override void paint(PaintingContext context, Offset offset) {
Canvas canvas = context.canvas;
paintRadialReaction(canvas, offset, size.center(Offset.zero));
Paint strokePaint = _createStrokePaint();
Offset origin = offset + (size / 2.0f - Size.square(CheckboxUtils._kEdgeSize) / 2.0f);
AnimationStatus status = position.status;
float tNormalized = status == AnimationStatus.forward || status == AnimationStatus.completed
? position.value
: 1.0f - position.value;
if (_oldValue == false || value == false) {
float t = value == false ? 1.0f - tNormalized : tNormalized;
RRect outer = _outerRectAt(origin, t);
Paint paint = new Paint();
paint.color = _colorAt(t);
if (t <= 0.5f) {
_drawBorder(canvas, outer, t, paint);
}
else {
canvas.drawRRect(outer, paint);
float tShrink = (t - 0.5f) * 2.0f;
if (_oldValue == null || value == null) {
_drawDash(canvas, origin, tShrink, strokePaint);
}
else {
_drawCheck(canvas, origin, tShrink, strokePaint);
}
}
}
else {
// Two cases: null to true, true to null
RRect outer = _outerRectAt(origin, 1.0f);
Paint paint = new Paint();
paint.color = _colorAt(1.0f);
canvas.drawRRect(outer, paint);
if (tNormalized <= 0.5f) {
float tShrink = 1.0f - tNormalized * 2.0f;
if (_oldValue == true) {
_drawCheck(canvas, origin, tShrink, strokePaint);
}
else {
_drawDash(canvas, origin, tShrink, strokePaint);
}
}
else {
float tExpand = (tNormalized - 0.5f) * 2.0f;
if (value == true) {
_drawCheck(canvas, origin, tExpand, strokePaint);
}
else {
_drawDash(canvas, origin, tExpand, strokePaint);
}
}
}
}
}
}