Showing
3 changed files
with
75 additions
and
19 deletions
@@ -55,9 +55,11 @@ class MultiPage extends Page { | @@ -55,9 +55,11 @@ class MultiPage extends Page { | ||
55 | this.header, | 55 | this.header, |
56 | this.footer, | 56 | this.footer, |
57 | Theme theme, | 57 | Theme theme, |
58 | + this.maxPages = 20, | ||
58 | PageOrientation orientation = PageOrientation.natural, | 59 | PageOrientation orientation = PageOrientation.natural, |
59 | EdgeInsets margin}) | 60 | EdgeInsets margin}) |
60 | : _buildList = build, | 61 | : _buildList = build, |
62 | + assert(maxPages != null && maxPages > 0), | ||
61 | super( | 63 | super( |
62 | pageFormat: pageFormat, | 64 | pageFormat: pageFormat, |
63 | margin: margin, | 65 | margin: margin, |
@@ -74,6 +76,8 @@ class MultiPage extends Page { | @@ -74,6 +76,8 @@ class MultiPage extends Page { | ||
74 | 76 | ||
75 | final List<_MultiPageInstance> _pages = <_MultiPageInstance>[]; | 77 | final List<_MultiPageInstance> _pages = <_MultiPageInstance>[]; |
76 | 78 | ||
79 | + final int maxPages; | ||
80 | + | ||
77 | void _paintChild( | 81 | void _paintChild( |
78 | Context context, Widget child, double x, double y, double pageHeight) { | 82 | Context context, Widget child, double x, double y, double pageHeight) { |
79 | if (mustRotate) { | 83 | if (mustRotate) { |
@@ -126,9 +130,9 @@ class MultiPage extends Page { | @@ -126,9 +130,9 @@ class MultiPage extends Page { | ||
126 | 130 | ||
127 | assert(() { | 131 | assert(() { |
128 | // Detect too big widgets | 132 | // Detect too big widgets |
129 | - if (sameCount++ > 20) { | 133 | + if (sameCount++ > maxPages) { |
130 | throw Exception( | 134 | throw Exception( |
131 | - 'This widget created more than 20 pages. This may be an issue in the widget or the document.'); | 135 | + 'This widget created more than $maxPages pages. This may be an issue in the widget or the document.'); |
132 | } | 136 | } |
133 | return true; | 137 | return true; |
134 | }()); | 138 | }()); |
@@ -188,7 +192,7 @@ class MultiPage extends Page { | @@ -188,7 +192,7 @@ class MultiPage extends Page { | ||
188 | 192 | ||
189 | // What to do if the widget is too big for the page? | 193 | // What to do if the widget is too big for the page? |
190 | if (offsetStart - child.box.height < offsetEnd) { | 194 | if (offsetStart - child.box.height < offsetEnd) { |
191 | - // If it is not a multi=page widget and its height | 195 | + // If it is not a multi-page widget and its height |
192 | // is smaller than a full new page, we schedule a new page creation | 196 | // is smaller than a full new page, we schedule a new page creation |
193 | if (child.box.height <= pageHeight - pageHeightMargin && | 197 | if (child.box.height <= pageHeight - pageHeightMargin && |
194 | !(child is SpanningWidget)) { | 198 | !(child is SpanningWidget)) { |
@@ -37,8 +37,16 @@ class _RunMetrics { | @@ -37,8 +37,16 @@ class _RunMetrics { | ||
37 | final int childCount; | 37 | final int childCount; |
38 | } | 38 | } |
39 | 39 | ||
40 | +class _WrapContext extends WidgetContext { | ||
41 | + int firstChild = 0; | ||
42 | + int lastChild = 0; | ||
43 | + | ||
44 | + @override | ||
45 | + String toString() => 'WrapContext first:$firstChild last:$lastChild'; | ||
46 | +} | ||
47 | + | ||
40 | /// A widget that displays its children in multiple horizontal or vertical runs. | 48 | /// A widget that displays its children in multiple horizontal or vertical runs. |
41 | -class Wrap extends MultiChildWidget { | 49 | +class Wrap extends MultiChildWidget implements SpanningWidget { |
42 | /// Creates a wrap layout. | 50 | /// Creates a wrap layout. |
43 | 51 | ||
44 | Wrap({ | 52 | Wrap({ |
@@ -83,7 +91,10 @@ class Wrap extends MultiChildWidget { | @@ -83,7 +91,10 @@ class Wrap extends MultiChildWidget { | ||
83 | 91 | ||
84 | bool get textDirection => false; | 92 | bool get textDirection => false; |
85 | 93 | ||
86 | - bool _hasVisualOverflow = false; | 94 | + @override |
95 | + bool get canSpan => _context.lastChild < children.length; | ||
96 | + | ||
97 | + final _WrapContext _context = _WrapContext(); | ||
87 | 98 | ||
88 | bool get _debugHasNecessaryDirections { | 99 | bool get _debugHasNecessaryDirections { |
89 | assert(direction != null); | 100 | assert(direction != null); |
@@ -192,9 +203,8 @@ class Wrap extends MultiChildWidget { | @@ -192,9 +203,8 @@ class Wrap extends MultiChildWidget { | ||
192 | void layout(Context context, BoxConstraints constraints, | 203 | void layout(Context context, BoxConstraints constraints, |
193 | {bool parentUsesSize = false}) { | 204 | {bool parentUsesSize = false}) { |
194 | assert(_debugHasNecessaryDirections); | 205 | assert(_debugHasNecessaryDirections); |
195 | - _hasVisualOverflow = false; | ||
196 | 206 | ||
197 | - if (children.isEmpty) { | 207 | + if (children.isEmpty || _context.firstChild >= children.length) { |
198 | box = PdfRect.fromPoints(PdfPoint.zero, constraints.smallest); | 208 | box = PdfRect.fromPoints(PdfPoint.zero, constraints.smallest); |
199 | return; | 209 | return; |
200 | } | 210 | } |
@@ -234,7 +244,7 @@ class Wrap extends MultiChildWidget { | @@ -234,7 +244,7 @@ class Wrap extends MultiChildWidget { | ||
234 | double runCrossAxisExtent = 0.0; | 244 | double runCrossAxisExtent = 0.0; |
235 | int childCount = 0; | 245 | int childCount = 0; |
236 | 246 | ||
237 | - for (Widget child in children) { | 247 | + for (Widget child in children.sublist(_context.firstChild)) { |
238 | child.layout(context, childConstraints, parentUsesSize: true); | 248 | child.layout(context, childConstraints, parentUsesSize: true); |
239 | 249 | ||
240 | final double childMainAxisExtent = _getMainAxisExtent(child); | 250 | final double childMainAxisExtent = _getMainAxisExtent(child); |
@@ -297,9 +307,6 @@ class Wrap extends MultiChildWidget { | @@ -297,9 +307,6 @@ class Wrap extends MultiChildWidget { | ||
297 | break; | 307 | break; |
298 | } | 308 | } |
299 | 309 | ||
300 | - _hasVisualOverflow = containerMainAxisExtent < mainAxisExtent || | ||
301 | - containerCrossAxisExtent < crossAxisExtent; | ||
302 | - | ||
303 | final double crossAxisFreeSpace = | 310 | final double crossAxisFreeSpace = |
304 | math.max(0.0, containerCrossAxisExtent - crossAxisExtent); | 311 | math.max(0.0, containerCrossAxisExtent - crossAxisExtent); |
305 | double runLeadingSpace = 0.0; | 312 | double runLeadingSpace = 0.0; |
@@ -333,7 +340,7 @@ class Wrap extends MultiChildWidget { | @@ -333,7 +340,7 @@ class Wrap extends MultiChildWidget { | ||
333 | ? containerCrossAxisExtent - runLeadingSpace | 340 | ? containerCrossAxisExtent - runLeadingSpace |
334 | : runLeadingSpace; | 341 | : runLeadingSpace; |
335 | 342 | ||
336 | - int currentWidget = 0; | 343 | + _context.lastChild = _context.firstChild; |
337 | for (int i = 0; i < runCount; ++i) { | 344 | for (int i = 0; i < runCount; ++i) { |
338 | final _RunMetrics metrics = runMetrics[i]; | 345 | final _RunMetrics metrics = runMetrics[i]; |
339 | final double runMainAxisExtent = metrics.mainAxisExtent; | 346 | final double runMainAxisExtent = metrics.mainAxisExtent; |
@@ -377,6 +384,12 @@ class Wrap extends MultiChildWidget { | @@ -377,6 +384,12 @@ class Wrap extends MultiChildWidget { | ||
377 | crossAxisOffset -= runCrossAxisExtent; | 384 | crossAxisOffset -= runCrossAxisExtent; |
378 | } | 385 | } |
379 | 386 | ||
387 | + if (crossAxisOffset < -.01 || | ||
388 | + crossAxisOffset + runCrossAxisExtent > containerCrossAxisExtent) { | ||
389 | + break; | ||
390 | + } | ||
391 | + | ||
392 | + int currentWidget = _context.lastChild; | ||
380 | for (Widget child in children.sublist(currentWidget)) { | 393 | for (Widget child in children.sublist(currentWidget)) { |
381 | final int runIndex = childRunMetrics[child]; | 394 | final int runIndex = childRunMetrics[child]; |
382 | if (runIndex != i) { | 395 | if (runIndex != i) { |
@@ -407,6 +420,8 @@ class Wrap extends MultiChildWidget { | @@ -407,6 +420,8 @@ class Wrap extends MultiChildWidget { | ||
407 | } else { | 420 | } else { |
408 | crossAxisOffset += runCrossAxisExtent + runBetweenSpace; | 421 | crossAxisOffset += runCrossAxisExtent + runBetweenSpace; |
409 | } | 422 | } |
423 | + | ||
424 | + _context.lastChild = currentWidget; | ||
410 | } | 425 | } |
411 | } | 426 | } |
412 | 427 | ||
@@ -416,19 +431,26 @@ class Wrap extends MultiChildWidget { | @@ -416,19 +431,26 @@ class Wrap extends MultiChildWidget { | ||
416 | 431 | ||
417 | context.canvas.saveContext(); | 432 | context.canvas.saveContext(); |
418 | 433 | ||
419 | - if (_hasVisualOverflow) { | ||
420 | - context.canvas | ||
421 | - ..drawRect(box.left, box.bottom, box.width, box.height) | ||
422 | - ..clipPath(); | ||
423 | - } | ||
424 | - | ||
425 | final Matrix4 mat = Matrix4.identity(); | 434 | final Matrix4 mat = Matrix4.identity(); |
426 | mat.translate(box.x, box.y); | 435 | mat.translate(box.x, box.y); |
427 | context.canvas.setTransform(mat); | 436 | context.canvas.setTransform(mat); |
428 | - for (Widget child in children) { | 437 | + for (Widget child |
438 | + in children.sublist(_context.firstChild, _context.lastChild)) { | ||
429 | child.paint(context); | 439 | child.paint(context); |
430 | } | 440 | } |
431 | 441 | ||
432 | context.canvas.restoreContext(); | 442 | context.canvas.restoreContext(); |
433 | } | 443 | } |
444 | + | ||
445 | + @override | ||
446 | + void restoreContext(WidgetContext context) { | ||
447 | + if (context is _WrapContext) { | ||
448 | + _context.firstChild = context.lastChild; | ||
449 | + } | ||
450 | + } | ||
451 | + | ||
452 | + @override | ||
453 | + WidgetContext saveContext() { | ||
454 | + return _context; | ||
455 | + } | ||
434 | } | 456 | } |
@@ -254,6 +254,36 @@ void main() { | @@ -254,6 +254,36 @@ void main() { | ||
254 | ); | 254 | ); |
255 | }); | 255 | }); |
256 | 256 | ||
257 | + test('Wrap Widget Multipage', () { | ||
258 | + final math.Random rnd = math.Random(42); | ||
259 | + pdf.addPage( | ||
260 | + MultiPage( | ||
261 | + pageFormat: const PdfPageFormat(200, 200), | ||
262 | + margin: const EdgeInsets.all(10), | ||
263 | + build: (Context context) => <Widget>[ | ||
264 | + Wrap( | ||
265 | + direction: Axis.vertical, | ||
266 | + verticalDirection: VerticalDirection.up, | ||
267 | + alignment: WrapAlignment.center, | ||
268 | + runAlignment: WrapAlignment.center, | ||
269 | + crossAxisAlignment: WrapCrossAlignment.center, | ||
270 | + spacing: 10, | ||
271 | + runSpacing: 10, | ||
272 | + children: List<Widget>.generate( | ||
273 | + 17, | ||
274 | + (int n) => Container( | ||
275 | + width: rnd.nextDouble() * 100, | ||
276 | + height: rnd.nextDouble() * 100, | ||
277 | + alignment: Alignment.center, | ||
278 | + color: PdfColors.blue800, | ||
279 | + child: Text('$n'), | ||
280 | + )), | ||
281 | + ) | ||
282 | + ], | ||
283 | + ), | ||
284 | + ); | ||
285 | + }); | ||
286 | + | ||
257 | test('Wrap Widget Empty', () { | 287 | test('Wrap Widget Empty', () { |
258 | pdf.addPage(Page(build: (Context context) => Wrap())); | 288 | pdf.addPage(Page(build: (Context context) => Wrap())); |
259 | }); | 289 | }); |
-
Please register or login to post a comment