Commit 5ba11ed6 authored by James Robinson's avatar James Robinson

Merge pull request #376 from jamesr/render_switch

Make Switch a standalone class with a custom RenderObject
parents 6c59acbf 445f4ee8
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:sky/widgets/basic.dart';
import 'package:sky/widgets/switch.dart';
import 'package:sky/theme/colors.dart';
import 'package:vector_math/vector_math.dart';
class BigSwitchApp extends App {
bool _value = false;
void _handleOnChanged(bool value) {
setState(() {
_value = value;
});
}
Widget build() {
Matrix4 scale = new Matrix4.identity();
scale.scale(5.0, 5.0);
return new Container(
child: new Switch(value: _value, onChanged: _handleOnChanged),
padding: new EdgeDims.all(5.0),
transform: scale,
decoration: new BoxDecoration(
backgroundColor: Teal[600]
)
);
}
}
void main() {
runApp(new BigSwitchApp());
}
...@@ -24,6 +24,7 @@ export 'package:sky/rendering/flex.dart' show FlexDirection, FlexJustifyContent, ...@@ -24,6 +24,7 @@ export 'package:sky/rendering/flex.dart' show FlexDirection, FlexJustifyContent,
export 'package:sky/rendering/object.dart' show Point, Offset, Size, Rect, Color, Paint, Path; export 'package:sky/rendering/object.dart' show Point, Offset, Size, Rect, Color, Paint, Path;
export 'package:sky/widgets/widget.dart' show Key, GlobalKey, Widget, Component, StatefulComponent, App, runApp, Listener, ParentDataNode; export 'package:sky/widgets/widget.dart' show Key, GlobalKey, Widget, Component, StatefulComponent, App, runApp, Listener, ParentDataNode;
typedef void ValueChanged(bool);
// PAINTING NODES // PAINTING NODES
......
...@@ -4,14 +4,18 @@ ...@@ -4,14 +4,18 @@
import 'dart:sky' as sky; import 'dart:sky' as sky;
import 'package:sky/animation/animated_value.dart';
import 'package:sky/animation/animation_performance.dart';
import 'package:sky/animation/curves.dart';
import 'package:sky/painting/shadows.dart'; import 'package:sky/painting/shadows.dart';
import 'package:sky/rendering/box.dart'; import 'package:sky/rendering/box.dart';
import 'package:sky/rendering/object.dart';
import 'package:sky/theme/shadows.dart'; import 'package:sky/theme/shadows.dart';
import 'package:sky/widgets/basic.dart'; import 'package:sky/widgets/basic.dart';
import 'package:sky/widgets/theme.dart'; import 'package:sky/widgets/theme.dart';
import 'package:sky/widgets/toggleable.dart'; import 'package:sky/widgets/widget.dart';
export 'package:sky/widgets/toggleable.dart' show ValueChanged; export 'package:sky/widgets/basic.dart' show ValueChanged;
const sky.Color _kThumbOffColor = const sky.Color(0xFFFAFAFA); const sky.Color _kThumbOffColor = const sky.Color(0xFFFAFAFA);
const sky.Color _kTrackOffColor = const sky.Color(0x42000000); const sky.Color _kTrackOffColor = const sky.Color(0x42000000);
...@@ -20,52 +24,136 @@ const double _kThumbRadius = 10.0; ...@@ -20,52 +24,136 @@ const double _kThumbRadius = 10.0;
const double _kSwitchHeight = _kThumbRadius * 2.0; const double _kSwitchHeight = _kThumbRadius * 2.0;
const double _kTrackHeight = 14.0; const double _kTrackHeight = 14.0;
const double _kTrackRadius = _kTrackHeight / 2.0; const double _kTrackRadius = _kTrackHeight / 2.0;
const double _kTrackWidth = _kSwitchWidth - (_kThumbRadius - _kTrackRadius) * 2.0; const double _kTrackWidth =
_kSwitchWidth - (_kThumbRadius - _kTrackRadius) * 2.0;
const Duration _kCheckDuration = const Duration(milliseconds: 200);
const Size _kSwitchSize = const Size(_kSwitchWidth + 2.0, _kSwitchHeight + 2.0);
class Switch extends Toggleable { class Switch extends Component {
// TODO(jackson): Hit-test the switch so that it can respond to both taps and swipe gestures Switch({Key key, this.value, this.onChanged}) : super(key: key);
Switch({ final bool value;
Key key, final ValueChanged onChanged;
bool value,
ValueChanged onChanged
}) : super(key: key, value: value, onChanged: onChanged);
Size get size => const Size(_kSwitchWidth + 2.0, _kSwitchHeight + 2.0); Widget build() {
return new _SwitchWrapper(
value: value,
onChanged: onChanged,
thumbColor: Theme.of(this).accentColor);
}
}
// This wrapper class exists only because Switch needs to be a Component in
// order to get an accent color from a Theme but Components do not know how to
// host RenderObjects.
class _SwitchWrapper extends LeafRenderObjectWrapper {
_SwitchWrapper({Key key, this.value, this.onChanged, this.thumbColor})
: super(key: key);
final bool value;
final ValueChanged onChanged;
final Color thumbColor;
_RenderSwitch get root => super.root;
_RenderSwitch createNode() => new _RenderSwitch(
value: value, thumbColor: thumbColor, onChanged: onChanged);
void syncRenderObject(_SwitchWrapper old) {
super.syncRenderObject(old);
root.value = value;
root.onChanged = onChanged;
root.thumbColor = thumbColor;
}
}
class _RenderSwitch extends RenderConstrainedBox {
_RenderSwitch(
{bool value, Color thumbColor: _kThumbOffColor, ValueChanged onChanged})
: _value = value,
_thumbColor = thumbColor,
_onChanged = onChanged,
super(additionalConstraints: new BoxConstraints.tight(_kSwitchSize)) {
_performance = new AnimationPerformance()
..variable = _position
..duration = _kCheckDuration
..progress = _value ? 1.0 : 0.0
..addListener(markNeedsPaint);
}
void handleEvent(sky.Event event, BoxHitTestEntry entry) {
if (event is sky.GestureEvent &&
event.type == 'gesturetap') _onChanged(!_value);
}
bool _value;
bool get value => _value;
void customPaintCallback(sky.Canvas canvas, Size size) { void set value(bool value) {
if (value == _value) return;
_value = value;
// TODO(abarth): Setting the curve on the position means there's a
// discontinuity when we reverse the timeline.
if (value) {
_position.curve = easeIn;
_performance.play();
} else {
_position.curve = easeOut;
_performance.reverse();
}
}
Color _thumbColor;
Color get thumbColor => _thumbColor;
void set thumbColor(Color value) {
if (value == _thumbColor) return;
_thumbColor = value;
markNeedsPaint();
}
ValueChanged _onChanged;
ValueChanged get onChanged => _onChanged;
void set onChanged(ValueChanged onChanged) {
_onChanged = onChanged;
}
final AnimatedValue<double> _position =
new AnimatedValue<double>(0.0, end: 1.0);
AnimationPerformance _performance;
void paint(PaintingCanvas canvas, Offset offset) {
sky.Color thumbColor = _kThumbOffColor; sky.Color thumbColor = _kThumbOffColor;
sky.Color trackColor = _kTrackOffColor; sky.Color trackColor = _kTrackOffColor;
if (value) { if (_value) {
thumbColor = Theme.of(this).accentColor; thumbColor = _thumbColor;
trackColor = new sky.Color(thumbColor.value & 0x80FFFFFF); trackColor = new sky.Color(_thumbColor.value & 0x80FFFFFF);
} }
// Draw the track rrect // Draw the track rrect
sky.Paint paint = new sky.Paint()..color = trackColor; sky.Paint paint = new sky.Paint()..color = trackColor;
paint.setStyle(sky.PaintingStyle.fill); paint.setStyle(sky.PaintingStyle.fill);
sky.Rect rect = new sky.Rect.fromLTRB( sky.Rect rect = new sky.Rect.fromLTWH(offset.dx,
0.0, offset.dy + _kSwitchHeight / 2.0 - _kTrackHeight / 2.0, _kTrackWidth,
_kSwitchHeight / 2.0 - _kTrackHeight / 2.0, _kTrackHeight);
_kTrackWidth, sky.RRect rrect = new sky.RRect()
_kSwitchHeight / 2.0 + _kTrackHeight / 2.0 ..setRectXY(rect, _kTrackRadius, _kTrackRadius);
);
sky.RRect rrect = new sky.RRect()..setRectXY(rect, _kTrackRadius, _kTrackRadius);
canvas.drawRRect(rrect, paint); canvas.drawRRect(rrect, paint);
// Draw the raised thumb with a shadow // Draw the raised thumb with a shadow
paint.color = thumbColor; paint.color = thumbColor;
var builder = new ShadowDrawLooperBuilder(); var builder = new ShadowDrawLooperBuilder();
for (BoxShadow boxShadow in shadows[1]) for (BoxShadow boxShadow in shadows[1]) builder.addShadow(
builder.addShadow(boxShadow.offset, boxShadow.color, boxShadow.blur); boxShadow.offset, boxShadow.color, boxShadow.blur);
paint.setDrawLooper(builder.build()); paint.setDrawLooper(builder.build());
// The thumb contracts slightly during the animation // The thumb contracts slightly during the animation
double inset = 2.0 - (position.value - 0.5).abs() * 2.0; double inset = 2.0 - (_position.value - 0.5).abs() * 2.0;
Point thumbPos = new Point( Point thumbPos = new Point(offset.dx +
_kTrackRadius + position.value * (_kTrackWidth - _kTrackRadius * 2), _kTrackRadius +
_kSwitchHeight / 2.0 _position.value * (_kTrackWidth - _kTrackRadius * 2),
); offset.dy + _kSwitchHeight / 2.0);
canvas.drawCircle(thumbPos, _kThumbRadius - inset, paint); canvas.drawCircle(thumbPos, _kThumbRadius - inset, paint);
} }
} }
...@@ -10,7 +10,7 @@ import 'package:sky/animation/curves.dart'; ...@@ -10,7 +10,7 @@ import 'package:sky/animation/curves.dart';
import 'package:sky/widgets/animated_component.dart'; import 'package:sky/widgets/animated_component.dart';
import 'package:sky/widgets/basic.dart'; import 'package:sky/widgets/basic.dart';
typedef void ValueChanged(bool value); export 'package:sky/widgets/basic.dart' show ValueChanged;
const Duration _kCheckDuration = const Duration(milliseconds: 200); const Duration _kCheckDuration = const Duration(milliseconds: 200);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment