| Index: sky/sdk/lib/framework/widgets/checkbox.dart
|
| diff --git a/sky/sdk/lib/framework/widgets/checkbox.dart b/sky/sdk/lib/framework/widgets/checkbox.dart
|
| index 1d2630c48487e9f77f3d34126202361c8a9c698e..a6b341844895a0e35081f2d48a5b260386fcbab4 100644
|
| --- a/sky/sdk/lib/framework/widgets/checkbox.dart
|
| +++ b/sky/sdk/lib/framework/widgets/checkbox.dart
|
| @@ -6,22 +6,54 @@ import 'dart:sky' as sky;
|
|
|
| import 'package:sky/framework/theme2/colors.dart' as colors;
|
|
|
| +import '../animation/animated_value.dart';
|
| +import '../animation/curves.dart';
|
| import '../rendering/box.dart';
|
| -import 'button_base.dart';
|
| +import 'animated_component.dart';
|
| import 'wrappers.dart';
|
|
|
| typedef void ValueChanged(value);
|
|
|
| -class Checkbox extends ButtonBase {
|
| +const double _kMidpoint = 0.5;
|
| +const double _kCheckDuration = 200.0;
|
| +const sky.Color _kUncheckedColor = const sky.Color(0x8A000000);
|
| +// TODO(jackson): This should change colors with the theme
|
| +sky.Color _kCheckedColor = colors.Purple[500];
|
| +const double _kEdgeSize = 20.0;
|
| +const double _kEdgeRadius = 1.0;
|
|
|
| - Checkbox({ Object key, this.onChanged, this.checked }) : super(key: key);
|
| +class Checkbox extends AnimatedComponent {
|
| +
|
| + Checkbox({
|
| + Object key,
|
| + this.checked,
|
| + this.onChanged
|
| + }) : super(key: key) {
|
| + _checkedAnimation = new AnimatedValue(checked ? 1.0 : 0.0);
|
| + }
|
|
|
| bool checked;
|
| + AnimatedValue _checkedAnimation;
|
| ValueChanged onChanged;
|
|
|
| void syncFields(Checkbox source) {
|
| - checked = source.checked;
|
| onChanged = source.onChanged;
|
| + if (checked != source.checked) {
|
| + checked = source.checked;
|
| + double targetValue = checked ? 1.0 : 0.0;
|
| + double difference = (_checkedAnimation.value - targetValue).abs();
|
| + if (difference > 0) {
|
| + double duration = difference * _kCheckDuration;
|
| + _checkedAnimation.stop();
|
| + Curve curve;
|
| + if (targetValue > _checkedAnimation.value) {
|
| + curve = easeIn;
|
| + } else {
|
| + curve = easeOut;
|
| + }
|
| + _checkedAnimation.animateTo(targetValue, duration, curve: curve);
|
| + }
|
| + }
|
| super.syncFields(source);
|
| }
|
|
|
| @@ -29,37 +61,68 @@ class Checkbox extends ButtonBase {
|
| onChanged(!checked);
|
| }
|
|
|
| - UINode buildContent() {
|
| - // TODO(jackson): This should change colors with the theme
|
| - sky.Color color = highlight ? colors.Purple[500] : const sky.Color(0x8A000000);
|
| - const double kEdgeSize = 20.0;
|
| - const double kEdgeRadius = 1.0;
|
| + UINode build() {
|
| return new EventListenerNode(
|
| new Container(
|
| margin: const EdgeDims.symmetric(horizontal: 5.0),
|
| - width: kEdgeSize + 2.0,
|
| - height: kEdgeSize + 2.0,
|
| + width: _kEdgeSize + 2.0,
|
| + height: _kEdgeSize + 2.0,
|
| child: new CustomPaint(
|
| + token: _checkedAnimation.value,
|
| callback: (sky.Canvas canvas, Size size) {
|
| + // Choose a color between grey and the theme color
|
| + sky.Paint paint = new sky.Paint()..strokeWidth = 2.0
|
| + ..color = _kUncheckedColor;
|
|
|
| - sky.Paint paint = new sky.Paint()..color = color
|
| - ..strokeWidth = 2.0;
|
| + // The rrect contracts slightly during the animation
|
| + double inset = 2.0 - (_checkedAnimation.value - _kMidpoint).abs() * 2.0;
|
| + sky.Rect rect = new sky.Rect.fromLTRB(inset, inset, _kEdgeSize - inset, _kEdgeSize - inset);
|
| + sky.RRect rrect = new sky.RRect()..setRectXY(rect, _kEdgeRadius, _kEdgeRadius);
|
|
|
| - // Draw the outer rrect
|
| - paint.setStyle(checked ? sky.PaintingStyle.strokeAndFill : sky.PaintingStyle.stroke);
|
| - sky.Rect rect = new sky.Rect.fromLTRB(0.0, 0.0, kEdgeSize, kEdgeSize);
|
| - sky.RRect rrect = new sky.RRect()..setRectXY(rect, kEdgeRadius, kEdgeRadius);
|
| +
|
| + // Outline of the empty rrect
|
| + paint.setStyle(sky.PaintingStyle.stroke);
|
| canvas.drawRRect(rrect, paint);
|
|
|
| - // Draw the inner check
|
| - if (checked) {
|
| - // TODO(jackson): Use the theme color
|
| + // Radial gradient that changes size
|
| + if (_checkedAnimation.value > 0) {
|
| + paint.setStyle(sky.PaintingStyle.fill);
|
| + paint.setShader(
|
| + new sky.Gradient.radial(
|
| + new Point(_kEdgeSize / 2.0, _kEdgeSize / 2.0),
|
| + _kEdgeSize * (_kMidpoint - _checkedAnimation.value) * 8.0,
|
| + [const sky.Color(0x00000000), _kUncheckedColor],
|
| + [0.0, 1.0]
|
| + )
|
| + );
|
| + canvas.drawRRect(rrect, paint);
|
| + }
|
| +
|
| + if (_checkedAnimation.value > _kMidpoint) {
|
| + double t = (_checkedAnimation.value - _kMidpoint) / (1.0 - _kMidpoint);
|
| +
|
| + // Solid filled rrect
|
| + paint.setStyle(sky.PaintingStyle.strokeAndFill);
|
| + paint.color = new Color.fromARGB((t * 255).floor(),
|
| + _kCheckedColor.red,
|
| + _kCheckedColor.green,
|
| + _kCheckedColor.blue);
|
| + canvas.drawRRect(rrect, paint);
|
| +
|
| + // White inner check
|
| paint.color = const sky.Color(0xFFFFFFFF);
|
| paint.setStyle(sky.PaintingStyle.stroke);
|
| sky.Path path = new sky.Path();
|
| - path.moveTo(kEdgeSize * 0.2, kEdgeSize * 0.5);
|
| - path.lineTo(kEdgeSize * 0.4, kEdgeSize * 0.7);
|
| - path.lineTo(kEdgeSize * 0.8, kEdgeSize * 0.3);
|
| + sky.Point start = new sky.Point(_kEdgeSize * 0.2, _kEdgeSize * 0.5);
|
| + sky.Point mid = new sky.Point(_kEdgeSize * 0.4, _kEdgeSize * 0.7);
|
| + sky.Point end = new sky.Point(_kEdgeSize * 0.8, _kEdgeSize * 0.3);
|
| + Point lerp(Point p1, Point p2, double t)
|
| + => new Point(p1.x * (1.0 - t) + p2.x * t, p1.y * (1.0 - t) + p2.y * t);
|
| + sky.Point drawStart = lerp(start, mid, 1.0 - t);
|
| + sky.Point drawEnd = lerp(mid, end, t);
|
| + path.moveTo(drawStart.x, drawStart.y);
|
| + path.lineTo(mid.x, mid.y);
|
| + path.lineTo(drawEnd.x, drawEnd.y);
|
| canvas.drawPath(path, paint);
|
| }
|
| }
|
|
|