Skip to content

Commit

Permalink
support stack's Positioned directional
Browse files Browse the repository at this point in the history
  • Loading branch information
Milad-Akarie authored and DavBfr committed Jul 24, 2023
1 parent 81ba6e8 commit 4f6ec25
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 54 deletions.
21 changes: 10 additions & 11 deletions pdf/lib/src/widgets/basic.dart
Original file line number Diff line number Diff line change
Expand Up @@ -74,32 +74,31 @@ class Padding extends SingleChildWidget {

@override
void layout(Context context, BoxConstraints constraints, {bool parentUsesSize = false}) {
final effectivePadding = padding.resolve(Directionality.of(context));
final resolvedPadding = padding.resolve(Directionality.of(context));
if (child != null) {
final childConstraints = constraints.deflate(effectivePadding);
final childConstraints = constraints.deflate(resolvedPadding);
child!.layout(context, childConstraints, parentUsesSize: parentUsesSize);
assert(child!.box != null);
box = constraints.constrainRect(
width: child!.box!.width + effectivePadding.horizontal,
height: child!.box!.height + effectivePadding.vertical);
width: child!.box!.width + resolvedPadding.horizontal, height: child!.box!.height + resolvedPadding.vertical);
} else {
box = constraints.constrainRect(width: effectivePadding.horizontal, height: effectivePadding.vertical);
box = constraints.constrainRect(width: resolvedPadding.horizontal, height: resolvedPadding.vertical);
}
}

@override
void debugPaint(Context context) {
final effectivePadding = padding.resolve(Directionality.of(context));
final resolvedPadding = padding.resolve(Directionality.of(context));
context.canvas
..setFillColor(PdfColors.lime)
..moveTo(box!.x, box!.y)
..lineTo(box!.right, box!.y)
..lineTo(box!.right, box!.top)
..lineTo(box!.x, box!.top)
..moveTo(box!.x + effectivePadding.left, box!.y + effectivePadding.bottom)
..lineTo(box!.x + effectivePadding.left, box!.top - effectivePadding.top)
..lineTo(box!.right - effectivePadding.right, box!.top - effectivePadding.top)
..lineTo(box!.right - effectivePadding.right, box!.y + effectivePadding.bottom)
..moveTo(box!.x + resolvedPadding.left, box!.y + resolvedPadding.bottom)
..lineTo(box!.x + resolvedPadding.left, box!.top - resolvedPadding.top)
..lineTo(box!.right - resolvedPadding.right, box!.top - resolvedPadding.top)
..lineTo(box!.right - resolvedPadding.right, box!.y + resolvedPadding.bottom)
..fillPath();
}

Expand Down Expand Up @@ -188,7 +187,7 @@ class Transform extends SingleChildWidget {

final bool unconstrained;

Matrix4 _effectiveTransform(Context context) {
Matrix4 _effectiveTransform(Context context) {
final result = Matrix4.identity();
if (origin != null) {
result.translate(origin!.x, origin!.y);
Expand Down
114 changes: 85 additions & 29 deletions pdf/lib/src/widgets/stack.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ import 'dart:math' as math;
import 'package:vector_math/vector_math_64.dart';

import '../../pdf.dart';
import 'geometry.dart';
import 'text.dart';
import 'widget.dart';
import '../../widgets.dart';

/// How to size the non-positioned children of a [Stack].
enum StackFit { loose, expand, passthrough }
Expand All @@ -33,22 +31,26 @@ enum Overflow { visible, clip }
/// A widget that controls where a child of a [Stack] is positioned.
class Positioned extends SingleChildWidget {
Positioned({
this.left,
double? left,
this.top,
this.right,
double? right,
this.bottom,
required Widget child,
}) : super(child: child);
}) : _left = left,
_right = right,
super(child: child);

/// Creates a Positioned object with left, top, right, and bottom set to 0.0
/// unless a value for them is passed.
Positioned.fill({
this.left = 0.0,
double? left = 0.0,
this.top = 0.0,
this.right = 0.0,
double? right = 0.0,
this.bottom = 0.0,
required Widget child,
}) : super(child: child);
}) : _left = left,
_right = right,
super(child: child);

/// Creates a widget that controls where a child of a [Stack] is positioned.
factory Positioned.directional({
Expand Down Expand Up @@ -80,11 +82,14 @@ class Positioned extends SingleChildWidget {
);
}

final double? left;
double? get left => _left;

final double? top;
double? get right => _right;

final double? _left;
final double? _right;

final double? right;
final double? top;

final double? bottom;

Expand All @@ -99,6 +104,63 @@ class Positioned extends SingleChildWidget {
}
}

/// A widget that controls where a child of a [Stack] is positioned without
/// committing to a specific [TextDirection].
class PositionedDirectional extends Positioned {
PositionedDirectional({
this.start,
this.end,
double? top,
double? bottom,
required Widget child,
}) : super(
child: child,
top: top,
bottom: bottom,
);

PositionedDirectional.fill({
this.start = 0.0,
this.end = 0.0,
double? top = 0.0,
double? bottom = 0.0,
required Widget child,
}) : super(
child: child,
top: top,
bottom: bottom,
);

final double? start;

double? _resolvedLeft;

double? _resolvedRight;

@override
double? get left => _resolvedLeft;

@override
double? get right => _resolvedRight;

final double? end;

@override
void layout(Context context, BoxConstraints constraints, {bool parentUsesSize = false}) {
super.layout(context, constraints, parentUsesSize: parentUsesSize);
switch (Directionality.of(context)) {
case TextDirection.rtl:
_resolvedLeft = end;
_resolvedRight = start;
break;
case TextDirection.ltr:
_resolvedLeft = start;
_resolvedRight = end;
break;
}
}
}

/// A widget that positions its children relative to the edges of its box.
class Stack extends MultiChildWidget {
Stack({
Expand All @@ -110,7 +172,7 @@ class Stack extends MultiChildWidget {

/// How to align the non-positioned and partially-positioned children in the
/// stack.
final Alignment alignment;
final AlignmentGeometry alignment;

/// How to size the non-positioned children in the stack.
final StackFit fit;
Expand All @@ -119,8 +181,7 @@ class Stack extends MultiChildWidget {
final Overflow overflow;

@override
void layout(Context context, BoxConstraints constraints,
{bool parentUsesSize = false}) {
void layout(Context context, BoxConstraints constraints, {bool parentUsesSize = false}) {
final childCount = children.length;

var hasNonPositionedChildren = false;
Expand Down Expand Up @@ -150,7 +211,6 @@ class Stack extends MultiChildWidget {
for (final child in children) {
if (child is! Positioned) {
hasNonPositionedChildren = true;

child.layout(context, nonPositionedConstraints, parentUsesSize: true);
assert(child.box != null);

Expand All @@ -167,28 +227,25 @@ class Stack extends MultiChildWidget {
} else {
box = PdfRect.fromPoints(PdfPoint.zero, constraints.biggest);
}

final resolvedAlignment = alignment.resolve(Directionality.of(context));
for (final child in children) {
if (child is! Positioned) {
child.box = PdfRect.fromPoints(
alignment.inscribe(child.box!.size, box!).offset, child.box!.size);
child.box = PdfRect.fromPoints(resolvedAlignment.inscribe(child.box!.size, box!).offset, child.box!.size);
} else {
final positioned = child;

var childConstraints = const BoxConstraints();

if (positioned.left != null && positioned.right != null) {
childConstraints = childConstraints.tighten(
width: box!.width - positioned.right! - positioned.left!);
childConstraints = childConstraints.tighten(width: box!.width - positioned.right! - positioned.left!);
} else if (positioned.width != null) {
childConstraints = childConstraints.tighten(width: positioned.width);
}

if (positioned.top != null && positioned.bottom != null) {
childConstraints = childConstraints.tighten(
height: box!.height - positioned.bottom! - positioned.top!);
childConstraints = childConstraints.tighten(height: box!.height - positioned.bottom! - positioned.top!);
} else if (positioned.height != null) {
childConstraints =
childConstraints.tighten(height: positioned.height);
childConstraints = childConstraints.tighten(height: positioned.height);
}

positioned.layout(context, childConstraints, parentUsesSize: true);
Expand All @@ -200,7 +257,7 @@ class Stack extends MultiChildWidget {
} else if (positioned.right != null) {
x = box!.width - positioned.right! - positioned.width!;
} else {
x = alignment.inscribe(positioned.box!.size, box!).x;
x = resolvedAlignment.inscribe(positioned.box!.size, box!).x;
}

double? y;
Expand All @@ -209,11 +266,10 @@ class Stack extends MultiChildWidget {
} else if (positioned.top != null) {
y = box!.height - positioned.top! - positioned.height!;
} else {
y = alignment.inscribe(positioned.box!.size, box!).y;
y = resolvedAlignment.inscribe(positioned.box!.size, box!).y;
}

positioned.box =
PdfRect.fromPoints(PdfPoint(x!, y!), positioned.box!.size);
positioned.box = PdfRect.fromPoints(PdfPoint(x!, y!), positioned.box!.size);
}
}
}
Expand Down
70 changes: 56 additions & 14 deletions pdf/test/rtl_layout_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,7 @@ void main() {
pdf = Document();
});

test('Should render a blue box followed by a red box ordered RTL aligned right', () {
pdf.addPage(
Page(
textDirection: TextDirection.rtl,
pageFormat: const PdfPageFormat(150, 50),
build: (Context context) => TestAnnotation(
anno: 'RTL Row',
child: Row(
children: [_blueBox, _redBox],
),
),
),
);
});


test('RTL Text', () {
pdf.addPage(
Expand Down Expand Up @@ -147,6 +134,21 @@ void main() {
);
});

test('Should render a blue box followed by a red box ordered RTL aligned right', () {
pdf.addPage(
Page(
textDirection: TextDirection.rtl,
pageFormat: const PdfPageFormat(150, 50),
build: (Context context) => TestAnnotation(
anno: 'RTL Row',
child: Row(
children: [_blueBox, _redBox],
),
),
),
);
});

test('Should render a blue box followed by a red box ordered RTL with aligned center', () {
pdf.addPage(
Page(
Expand Down Expand Up @@ -651,6 +653,46 @@ void main() {
);
});

test('RTL Stack, should directional child to right44', () {
pdf.addPage(
Page(
textDirection: TextDirection.rtl,
pageFormat: const PdfPageFormat(150, 150),
build: (Context context) {
return TestAnnotation(
anno: 'RTL Stack PositionDirectional.start',
child: Stack(children: [
PositionedDirectional(
start: 0,
child: _blueBox,
)
]),
);
},
),
);
});

test('LTR Stack, should directional child to right44', () {
pdf.addPage(
Page(
textDirection: TextDirection.ltr,
pageFormat: const PdfPageFormat(150, 150),
build: (Context context) {
return TestAnnotation(
anno: 'LTR Stack PositionDirectional.start',
child: Stack(children: [
PositionedDirectional(
start: 0,
child: _blueBox,
)
]),
);
},
),
);
});

tearDownAll(() async {
final file = File('rtl-layout.pdf');
await file.writeAsBytes(await pdf.save());
Expand Down

0 comments on commit 4f6ec25

Please sign in to comment.