@@ -12,7 +12,11 @@ ChartPainter _painterOf(WidgetTester tester) {
1212}
1313
1414/// A chart too short to fill its box at the default spacing.
15- Widget _chart ({required bool fitContent, int count = 10 }) {
15+ Widget _chart ({
16+ required bool fitContent,
17+ int count = 10 ,
18+ List <Indicator > indicators = const [],
19+ }) {
1620 final data = candles (rampThenFall (count));
1721 DataUtil .calculate (data);
1822
@@ -30,13 +34,143 @@ Widget _chart({required bool fitContent, int count = 10}) {
3034 volHidden: true ,
3135 showNowPrice: false ,
3236 chartStyle: ChartStyle (fitContent: fitContent),
37+ indicators: indicators,
3338 ),
3439 ),
3540 ),
3641 );
3742}
3843
44+ /// The widths of the rects an indicator pane draws across its candles.
45+ List <double > _paneBarWidths (WidgetTester tester, {required double scaleX}) {
46+ final painter = _painterOf (tester);
47+ final canvas = TestRecordingCanvas ();
48+ painter.mIndicatorPaneList.single.drawSeries (
49+ canvas,
50+ painter.candles! ,
51+ start: 0 ,
52+ stop: painter.candles! .length - 1 ,
53+ xOf: (i) => i * 10.0 ,
54+ scaleX: scaleX,
55+ );
56+ final widths = [
57+ for (final call in canvas.invocations)
58+ if (call.invocation.memberName == #drawRect)
59+ (call.invocation.positionalArguments.first as Rect ).width,
60+ ];
61+ // everyElement passes on an empty list, which would hide a pane drawing none.
62+ expect (widths, isNotEmpty);
63+ return widths;
64+ }
65+
66+ /// Bars drawn over the candles, one per candle, from zero.
67+ class _OverlayBars extends Indicator {
68+ @override
69+ String get name => 'OVERLAYBARS' ;
70+
71+ @override
72+ String get label => name;
73+
74+ @override
75+ IndicatorPlacement get placement => IndicatorPlacement .overlay;
76+
77+ @override
78+ List <IndicatorLine > get lines => const [
79+ IndicatorLine ('V' , shape: IndicatorShape .histogram),
80+ ];
81+
82+ @override
83+ List <Object ?> get settings => [name];
84+
85+ @override
86+ IndicatorSeries compute (List <KLineEntity > candles) => IndicatorSeries ([
87+ [for (final c in candles) c.close],
88+ ]);
89+
90+ @override
91+ Color defaultColor (int line, ChartColors theme, int ordinal) =>
92+ theme.ma5Color;
93+ }
94+
95+ Future <void > _pinchOut (WidgetTester tester) async {
96+ final centre = tester.getCenter (find.byType (KChartWidget ));
97+ final left = await tester.startGesture (centre - const Offset (40 , 0 ));
98+ final right = await tester.startGesture (centre + const Offset (40 , 0 ));
99+ await left.moveBy (const Offset (- 90 , 0 ));
100+ await right.moveBy (const Offset (90 , 0 ));
101+ await tester.pump ();
102+ await left.up ();
103+ await right.up ();
104+ await tester.pumpAndSettle ();
105+ }
106+
39107void main () {
108+ group ('indicator pane bars' , () {
109+ testWidgets ('over the candles, widen with the zoom too' , (tester) async {
110+ await tester.pumpWidget (
111+ _chart (fitContent: false , count: 200 , indicators: [_OverlayBars ()]),
112+ );
113+ await _pinchOut (tester);
114+ final painter = _painterOf (tester);
115+ expect (painter.scaleX, greaterThan (1.2 ));
116+
117+ final canvas = TestRecordingCanvas ();
118+ painter.paint (canvas, tester.getSize (find.byType (KChartWidget )));
119+
120+ final widths = [
121+ for (final call in canvas.invocations)
122+ if (call.invocation.memberName == #drawRect)
123+ (call.invocation.positionalArguments.first as Rect ).width,
124+ ];
125+ expect (
126+ widths,
127+ contains (
128+ closeTo (const ChartStyle ().candleWidth * painter.scaleX, 0.001 ),
129+ ),
130+ );
131+ });
132+
133+ testWidgets ('the chart hands its zoom to the panes' , (tester) async {
134+ await tester.pumpWidget (
135+ _chart (fitContent: false , count: 200 , indicators: [MacdIndicator ()]),
136+ );
137+ await _pinchOut (tester);
138+ final painter = _painterOf (tester);
139+ expect (painter.scaleX, greaterThan (1.2 ));
140+
141+ final canvas = TestRecordingCanvas ();
142+ painter.paint (canvas, tester.getSize (find.byType (KChartWidget )));
143+
144+ final widths = [
145+ for (final call in canvas.invocations)
146+ if (call.invocation.memberName == #drawRect)
147+ (call.invocation.positionalArguments.first as Rect ).width,
148+ ];
149+ expect (
150+ widths,
151+ contains (closeTo (const ChartStyle ().macdWidth * painter.scaleX, 0.001 )),
152+ );
153+ });
154+
155+ testWidgets ('widen with the zoom, as the candles do' , (tester) async {
156+ await tester.pumpWidget (
157+ _chart (fitContent: false , indicators: [MacdIndicator ()]),
158+ );
159+ final width = const ChartStyle ().macdWidth;
160+
161+ expect (_paneBarWidths (tester, scaleX: 1 ), everyElement (width));
162+ expect (_paneBarWidths (tester, scaleX: 3 ), everyElement (width * 3 ));
163+ });
164+
165+ testWidgets ('never shrink past a hairline when zoomed out' , (tester) async {
166+ await tester.pumpWidget (
167+ _chart (fitContent: false , indicators: [MacdIndicator ()]),
168+ );
169+
170+ expect (_paneBarWidths (tester, scaleX: 0.1 ), everyElement (1.0 ));
171+ });
172+ });
173+
40174 group ('ChartStyle.fitContent' , () {
41175 testWidgets ('off, a short series keeps the fixed spacing' , (tester) async {
42176 await tester.pumpWidget (_chart (fitContent: false ));
@@ -67,6 +201,35 @@ void main() {
67201 );
68202 });
69203
204+ testWidgets ('on, an indicator pane\' s bars widen with the spacing' , (
205+ tester,
206+ ) async {
207+ await tester.pumpWidget (
208+ _chart (fitContent: true , indicators: [MacdIndicator ()]),
209+ );
210+ final painter = _painterOf (tester);
211+
212+ const style = ChartStyle ();
213+ final spread = painter.mPointWidth / style.pointWidth;
214+ expect (
215+ painter.mIndicatorPaneList.single.chartStyle.macdWidth,
216+ closeTo (style.macdWidth * spread, 0.001 ),
217+ );
218+ });
219+
220+ testWidgets ('off, an indicator pane\' s bars keep their width' , (
221+ tester,
222+ ) async {
223+ await tester.pumpWidget (
224+ _chart (fitContent: false , indicators: [MacdIndicator ()]),
225+ );
226+
227+ expect (
228+ _painterOf (tester).mIndicatorPaneList.single.chartStyle.macdWidth,
229+ const ChartStyle ().macdWidth,
230+ );
231+ });
232+
70233 testWidgets ('on, a series that already fills the plot is left alone' , (
71234 tester,
72235 ) async {
0 commit comments