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