Unverified Commit fc96326b authored by Greg Spencer's avatar Greg Spencer Committed by GitHub

Add neck stretch (#15445)

During the review, we determined that it would be good for the value indicator to stretch when the text scale was smaller, and the shrink when it was larger, to keep the value visible over the finger at small sizes, and closer to the finger at large sizes. This implements that change.
parent dc03398b
......@@ -55,10 +55,10 @@ class SliderTheme extends InheritedWidget {
/// @override
/// State createState() => new LaunchState();
/// }
///
///
/// class LaunchState extends State<Launch> {
/// double _rocketThrust;
///
///
/// @override
/// Widget build(BuildContext context) {
/// return new SliderTheme(
......@@ -621,6 +621,11 @@ class PaddleSliderValueIndicatorShape extends SliderComponentShape {
static const double _thirtyDegrees = math.pi / 6.0;
static const Size _preferredSize =
const Size.fromHeight(_distanceBetweenTopBottomCenters + _topLobeRadius + _bottomLobeRadius);
// Set to true if you want a rectangle to be drawn around the label bubble.
// This helps with building tests that check that the label draws in the right
// place (because it prints the rect in the failed test output). It should not
// be checked in while set to "true".
static const bool _debuggingLabelLocation = false;
static Path _bottomLobePath; // Initialized by _generateBottomLobe
static Offset _bottomLobeEnd; // Initialized by _generateBottomLobe
......@@ -767,6 +772,7 @@ class PaddleSliderValueIndicatorShape extends SliderComponentShape {
final Path path = new Path();
final Offset bottomLobeEnd = _addBottomLobe(path);
// The base of the triangle between the top lobe center and the centers of
// the two top neck arcs.
final double neckTriangleBase = _topNeckRadius - bottomLobeEnd.dx;
......@@ -789,31 +795,54 @@ class PaddleSliderValueIndicatorShape extends SliderComponentShape {
);
final double leftNeckArcAngle = _ninetyDegrees - leftTheta;
final double rightNeckArcAngle = math.pi + _ninetyDegrees - rightTheta;
// The distance between the end of the bottom neck arc and the beginning of
// the top neck arc. We use this to shrink/expand it based on the scale
// factor of the value indicator.
final double neckStretchBaseline = bottomLobeEnd.dy - math.max(neckLeftCenter.dy, neckRightCenter.dy);
final double t = math.pow(inverseTextScale, 3.0);
final double stretch = (neckStretchBaseline * t).clamp(0.0, 10.0 * neckStretchBaseline);
final Offset neckStretch = new Offset(0.0, neckStretchBaseline - stretch);
assert(!_debuggingLabelLocation || () {
final Offset leftCenter = _topLobeCenter - new Offset(leftWidthNeeded, 0.0) + neckStretch;
final Offset rightCenter = _topLobeCenter + new Offset(rightWidthNeeded, 0.0) + neckStretch;
final Rect valueRect = new Rect.fromLTRB(
leftCenter.dx - _topLobeRadius,
leftCenter.dy - _topLobeRadius,
rightCenter.dx + _topLobeRadius,
rightCenter.dy + _topLobeRadius,
);
final Paint outlinePaint = new Paint()
..color = const Color(0xffff0000)
..style = PaintingStyle.stroke..strokeWidth = 1.0;
canvas.drawRect(valueRect, outlinePaint);
return true;
}());
_addArc(
path,
neckLeftCenter,
neckLeftCenter + neckStretch,
_topNeckRadius,
0.0,
-leftNeckArcAngle,
);
_addArc(
path,
_topLobeCenter - new Offset(leftWidthNeeded, 0.0),
_topLobeCenter - new Offset(leftWidthNeeded, 0.0) + neckStretch,
_topLobeRadius,
_ninetyDegrees + leftTheta,
_twoSeventyDegrees,
);
_addArc(
path,
_topLobeCenter + new Offset(rightWidthNeeded, 0.0),
_topLobeCenter + new Offset(rightWidthNeeded, 0.0) + neckStretch,
_topLobeRadius,
_twoSeventyDegrees,
_twoSeventyDegrees + math.pi - rightTheta,
);
_addArc(
path,
neckRightCenter,
neckRightCenter + neckStretch,
_topNeckRadius,
rightNeckArcAngle,
math.pi,
......@@ -822,7 +851,7 @@ class PaddleSliderValueIndicatorShape extends SliderComponentShape {
// Draw the label.
canvas.save();
canvas.translate(shift, -_distanceBetweenTopBottomCenters);
canvas.translate(shift, -_distanceBetweenTopBottomCenters + neckStretch.dy);
canvas.scale(inverseTextScale, inverseTextScale);
labelPainter.paint(canvas, Offset.zero - new Offset(labelHalfWidth, labelPainter.height / 2.0));
canvas.restore();
......
......@@ -195,11 +195,11 @@ void main() {
expect(sliderBox, paints..circle(color: sliderTheme.thumbColor, radius: 6.0));
await tester.pumpWidget(buildApp(enabled: false));
await tester.pump(const Duration(milliseconds: 500)); // wait for disable animation
await tester.pumpAndSettle(); // wait for disable animation
expect(sliderBox, paints..circle(color: sliderTheme.disabledThumbColor, radius: 4.0));
await tester.pumpWidget(buildApp(divisions: 3));
await tester.pump(const Duration(milliseconds: 500)); // wait for disable animation
await tester.pumpAndSettle(); // wait for disable animation
expect(
sliderBox,
paints
......@@ -210,7 +210,7 @@ void main() {
..circle(color: sliderTheme.thumbColor, radius: 6.0));
await tester.pumpWidget(buildApp(divisions: 3, enabled: false));
await tester.pump(const Duration(milliseconds: 500)); // wait for disable animation
await tester.pumpAndSettle(); // wait for disable animation
expect(
sliderBox,
paints
......@@ -226,11 +226,11 @@ void main() {
primarySwatch: Colors.blue,
);
final SliderThemeData sliderTheme = theme.sliderTheme.copyWith(thumbColor: Colors.red.shade500, showValueIndicator: ShowValueIndicator.always);
Widget buildApp(String value, {double sliderValue = 0.5}) {
Widget buildApp(String value, {double sliderValue = 0.5, double textScale = 1.0}) {
return new Directionality(
textDirection: TextDirection.ltr,
child: new MediaQuery(
data: new MediaQueryData.fromWindow(window),
data: new MediaQueryData.fromWindow(window).copyWith(textScaleFactor: textScale),
child: new Material(
child: new Row(
children: <Widget>[
......@@ -258,9 +258,8 @@ void main() {
Offset center = tester.getCenter(find.byType(Slider));
TestGesture gesture = await tester.startGesture(center);
await tester.pump();
// Wait for value indicator animation to finish.
await tester.pump(const Duration(milliseconds: 500));
await tester.pumpAndSettle();
expect(
sliderBox,
paints
......@@ -280,9 +279,8 @@ void main() {
await tester.pumpWidget(buildApp('1000'));
center = tester.getCenter(find.byType(Slider));
gesture = await tester.startGesture(center);
await tester.pump();
// Wait for value indicator animation to finish.
await tester.pump(const Duration(milliseconds: 500));
await tester.pumpAndSettle();
expect(
sliderBox,
paints
......@@ -301,9 +299,8 @@ void main() {
await tester.pumpWidget(buildApp('1000000', sliderValue: 0.0));
center = tester.getCenter(find.byType(Slider));
gesture = await tester.startGesture(center);
await tester.pump();
// Wait for value indicator animation to finish.
await tester.pump(const Duration(milliseconds: 500));
await tester.pumpAndSettle();
expect(
sliderBox,
paints
......@@ -322,9 +319,8 @@ void main() {
await tester.pumpWidget(buildApp('1000000', sliderValue: 1.0));
center = tester.getCenter(find.byType(Slider));
gesture = await tester.startGesture(center);
await tester.pump();
// Wait for value indicator animation to finish.
await tester.pump(const Duration(milliseconds: 500));
await tester.pumpAndSettle();
expect(
sliderBox,
paints
......@@ -338,5 +334,55 @@ void main() {
excludes: <Offset>[const Offset(16.1, -40.0), const Offset(-98.1, -40.0)],
));
await gesture.up();
// Test that the neck stretches when the text scale gets smaller.
await tester.pumpWidget(buildApp('1000000', sliderValue: 0.0, textScale: 0.5));
center = tester.getCenter(find.byType(Slider));
gesture = await tester.startGesture(center);
// Wait for value indicator animation to finish.
await tester.pumpAndSettle();
expect(
sliderBox,
paints
..path(
color: sliderTheme.valueIndicatorColor,
includes: <Offset>[
const Offset(0.0, -49.0),
const Offset(90.0, -49.0),
const Offset(-24.0, -49.0),
],
excludes: <Offset>[
const Offset(98.0, -32.0), // inside full size, outside small
const Offset(-16.0, -32.0), // inside full size, outside small
const Offset(90.1, -49.0),
const Offset(-24.1, -49.0),
],
));
await gesture.up();
// Test that the neck shrinks when the text scale gets larger.
await tester.pumpWidget(buildApp('1000000', sliderValue: 0.0, textScale: 2.5));
center = tester.getCenter(find.byType(Slider));
gesture = await tester.startGesture(center);
// Wait for value indicator animation to finish.
await tester.pumpAndSettle();
expect(
sliderBox,
paints
..path(
color: sliderTheme.valueIndicatorColor,
includes: <Offset>[
const Offset(0.0, -38.8),
const Offset(98.0, -38.8),
const Offset(-16.0, -38.8),
const Offset(10.0, -23.0), // Inside large, outside scale=1.0
const Offset(-4.0, -23.0), // Inside large, outside scale=1.0
],
excludes: <Offset>[
const Offset(98.5, -38.8),
const Offset(-16.1, -38.8),
],
));
await gesture.up();
});
}
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