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
...@@ -621,6 +621,11 @@ class PaddleSliderValueIndicatorShape extends SliderComponentShape { ...@@ -621,6 +621,11 @@ class PaddleSliderValueIndicatorShape extends SliderComponentShape {
static const double _thirtyDegrees = math.pi / 6.0; static const double _thirtyDegrees = math.pi / 6.0;
static const Size _preferredSize = static const Size _preferredSize =
const Size.fromHeight(_distanceBetweenTopBottomCenters + _topLobeRadius + _bottomLobeRadius); 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 Path _bottomLobePath; // Initialized by _generateBottomLobe
static Offset _bottomLobeEnd; // Initialized by _generateBottomLobe static Offset _bottomLobeEnd; // Initialized by _generateBottomLobe
...@@ -767,6 +772,7 @@ class PaddleSliderValueIndicatorShape extends SliderComponentShape { ...@@ -767,6 +772,7 @@ class PaddleSliderValueIndicatorShape extends SliderComponentShape {
final Path path = new Path(); final Path path = new Path();
final Offset bottomLobeEnd = _addBottomLobe(path); final Offset bottomLobeEnd = _addBottomLobe(path);
// The base of the triangle between the top lobe center and the centers of // The base of the triangle between the top lobe center and the centers of
// the two top neck arcs. // the two top neck arcs.
final double neckTriangleBase = _topNeckRadius - bottomLobeEnd.dx; final double neckTriangleBase = _topNeckRadius - bottomLobeEnd.dx;
...@@ -789,31 +795,54 @@ class PaddleSliderValueIndicatorShape extends SliderComponentShape { ...@@ -789,31 +795,54 @@ class PaddleSliderValueIndicatorShape extends SliderComponentShape {
); );
final double leftNeckArcAngle = _ninetyDegrees - leftTheta; final double leftNeckArcAngle = _ninetyDegrees - leftTheta;
final double rightNeckArcAngle = math.pi + _ninetyDegrees - rightTheta; 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( _addArc(
path, path,
neckLeftCenter, neckLeftCenter + neckStretch,
_topNeckRadius, _topNeckRadius,
0.0, 0.0,
-leftNeckArcAngle, -leftNeckArcAngle,
); );
_addArc( _addArc(
path, path,
_topLobeCenter - new Offset(leftWidthNeeded, 0.0), _topLobeCenter - new Offset(leftWidthNeeded, 0.0) + neckStretch,
_topLobeRadius, _topLobeRadius,
_ninetyDegrees + leftTheta, _ninetyDegrees + leftTheta,
_twoSeventyDegrees, _twoSeventyDegrees,
); );
_addArc( _addArc(
path, path,
_topLobeCenter + new Offset(rightWidthNeeded, 0.0), _topLobeCenter + new Offset(rightWidthNeeded, 0.0) + neckStretch,
_topLobeRadius, _topLobeRadius,
_twoSeventyDegrees, _twoSeventyDegrees,
_twoSeventyDegrees + math.pi - rightTheta, _twoSeventyDegrees + math.pi - rightTheta,
); );
_addArc( _addArc(
path, path,
neckRightCenter, neckRightCenter + neckStretch,
_topNeckRadius, _topNeckRadius,
rightNeckArcAngle, rightNeckArcAngle,
math.pi, math.pi,
...@@ -822,7 +851,7 @@ class PaddleSliderValueIndicatorShape extends SliderComponentShape { ...@@ -822,7 +851,7 @@ class PaddleSliderValueIndicatorShape extends SliderComponentShape {
// Draw the label. // Draw the label.
canvas.save(); canvas.save();
canvas.translate(shift, -_distanceBetweenTopBottomCenters); canvas.translate(shift, -_distanceBetweenTopBottomCenters + neckStretch.dy);
canvas.scale(inverseTextScale, inverseTextScale); canvas.scale(inverseTextScale, inverseTextScale);
labelPainter.paint(canvas, Offset.zero - new Offset(labelHalfWidth, labelPainter.height / 2.0)); labelPainter.paint(canvas, Offset.zero - new Offset(labelHalfWidth, labelPainter.height / 2.0));
canvas.restore(); canvas.restore();
......
...@@ -195,11 +195,11 @@ void main() { ...@@ -195,11 +195,11 @@ void main() {
expect(sliderBox, paints..circle(color: sliderTheme.thumbColor, radius: 6.0)); expect(sliderBox, paints..circle(color: sliderTheme.thumbColor, radius: 6.0));
await tester.pumpWidget(buildApp(enabled: false)); 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)); expect(sliderBox, paints..circle(color: sliderTheme.disabledThumbColor, radius: 4.0));
await tester.pumpWidget(buildApp(divisions: 3)); 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( expect(
sliderBox, sliderBox,
paints paints
...@@ -210,7 +210,7 @@ void main() { ...@@ -210,7 +210,7 @@ void main() {
..circle(color: sliderTheme.thumbColor, radius: 6.0)); ..circle(color: sliderTheme.thumbColor, radius: 6.0));
await tester.pumpWidget(buildApp(divisions: 3, enabled: false)); 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( expect(
sliderBox, sliderBox,
paints paints
...@@ -226,11 +226,11 @@ void main() { ...@@ -226,11 +226,11 @@ void main() {
primarySwatch: Colors.blue, primarySwatch: Colors.blue,
); );
final SliderThemeData sliderTheme = theme.sliderTheme.copyWith(thumbColor: Colors.red.shade500, showValueIndicator: ShowValueIndicator.always); 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( return new Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: new MediaQuery( child: new MediaQuery(
data: new MediaQueryData.fromWindow(window), data: new MediaQueryData.fromWindow(window).copyWith(textScaleFactor: textScale),
child: new Material( child: new Material(
child: new Row( child: new Row(
children: <Widget>[ children: <Widget>[
...@@ -258,9 +258,8 @@ void main() { ...@@ -258,9 +258,8 @@ void main() {
Offset center = tester.getCenter(find.byType(Slider)); Offset center = tester.getCenter(find.byType(Slider));
TestGesture gesture = await tester.startGesture(center); TestGesture gesture = await tester.startGesture(center);
await tester.pump();
// Wait for value indicator animation to finish. // Wait for value indicator animation to finish.
await tester.pump(const Duration(milliseconds: 500)); await tester.pumpAndSettle();
expect( expect(
sliderBox, sliderBox,
paints paints
...@@ -280,9 +279,8 @@ void main() { ...@@ -280,9 +279,8 @@ void main() {
await tester.pumpWidget(buildApp('1000')); await tester.pumpWidget(buildApp('1000'));
center = tester.getCenter(find.byType(Slider)); center = tester.getCenter(find.byType(Slider));
gesture = await tester.startGesture(center); gesture = await tester.startGesture(center);
await tester.pump();
// Wait for value indicator animation to finish. // Wait for value indicator animation to finish.
await tester.pump(const Duration(milliseconds: 500)); await tester.pumpAndSettle();
expect( expect(
sliderBox, sliderBox,
paints paints
...@@ -301,9 +299,8 @@ void main() { ...@@ -301,9 +299,8 @@ void main() {
await tester.pumpWidget(buildApp('1000000', sliderValue: 0.0)); await tester.pumpWidget(buildApp('1000000', sliderValue: 0.0));
center = tester.getCenter(find.byType(Slider)); center = tester.getCenter(find.byType(Slider));
gesture = await tester.startGesture(center); gesture = await tester.startGesture(center);
await tester.pump();
// Wait for value indicator animation to finish. // Wait for value indicator animation to finish.
await tester.pump(const Duration(milliseconds: 500)); await tester.pumpAndSettle();
expect( expect(
sliderBox, sliderBox,
paints paints
...@@ -322,9 +319,8 @@ void main() { ...@@ -322,9 +319,8 @@ void main() {
await tester.pumpWidget(buildApp('1000000', sliderValue: 1.0)); await tester.pumpWidget(buildApp('1000000', sliderValue: 1.0));
center = tester.getCenter(find.byType(Slider)); center = tester.getCenter(find.byType(Slider));
gesture = await tester.startGesture(center); gesture = await tester.startGesture(center);
await tester.pump();
// Wait for value indicator animation to finish. // Wait for value indicator animation to finish.
await tester.pump(const Duration(milliseconds: 500)); await tester.pumpAndSettle();
expect( expect(
sliderBox, sliderBox,
paints paints
...@@ -338,5 +334,55 @@ void main() { ...@@ -338,5 +334,55 @@ void main() {
excludes: <Offset>[const Offset(16.1, -40.0), const Offset(-98.1, -40.0)], excludes: <Offset>[const Offset(16.1, -40.0), const Offset(-98.1, -40.0)],
)); ));
await gesture.up(); 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