Skip to content

Commit

Permalink
Add support for auto margins (#1077)
Browse files Browse the repository at this point in the history
Also:

* BREAKING: Replace `CssLengthBox.getValueXxx` with `.getXxx`
* [core] v0.14.5
* [enhanced] v0.14.5
  • Loading branch information
daohoangson authored Oct 26, 2023
1 parent 25d7213 commit ceb41a7
Show file tree
Hide file tree
Showing 22 changed files with 417 additions and 68 deletions.
1 change: 1 addition & 0 deletions demo_app/test/goldens.json
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@
"inline/margin/1_value": "----<div style=\"margin: 30px\">Foo</div>----",
"inline/margin/2_values": "----\n<div style=\"margin: 10px 20px\">both</div>\n----\n<div style=\"margin: 10px 0\">vertical only</div>\n----\n<div style=\"margin: 0 20px\">horizontal only</div>\n----\n",
"inline/margin/4_values": "----\n<div style=\"margin: 10px 20px 30px 40px\">all</div>\n----\n<div style=\"margin: 10px 0 0 0\">top only</div>\n----\n<div style=\"margin: 0 20px 0 0; text-align: right\">right only</div>\n----\n<div style=\"margin: 0 0 30px 0\">bottom only</div>\n----\n<div style=\"margin: 0 0 0 40px\">left only</div>\n---\n",
"inline/margin/auto": "<table border=\"1\" style=\"margin: 0 auto\">\n <tbody>\n <tr>\n <td style=\"width: 100px\">1</td>\n <td style=\"width: 100px\">2</td>\n </tr>\n <tr>\n <td>3</td>\n <td>4</td>\n </tr>\n </tbody>\n</table>",
"inline/margin/margin-bottom": "----<div style=\"margin-bottom: 30px\">Foo</div>----",
"inline/margin/margin-left": "----<div style=\"margin-left: 30px\">Foo</div>----",
"inline/margin/margin-right": "----<div style=\"margin-right: 30px; text-align: right\">Foo</div>----",
Expand Down
Binary file added demo_app/test/goldens/inline/margin/auto.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 7 additions & 1 deletion packages/core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
## 0.14.5

- Add support for auto margins (#1077)
- BREAKING: Replace `CssLengthBox.getValueLeft` with `.getLeft`
- BREAKING: Replace `CssLengthBox.getValueRight` with `.getRight`

## 0.14.4+1

- Improve table support for wide columns (#1073)
- BREAKING: remove `WidgetFactory.buildDivider` (#1075)
- Make `align=center` work like `CENTER` tag (#1076)
- BREAKING: remove `WidgetFactory.buildDivider` (#1075)

## 0.14.3

Expand Down
2 changes: 1 addition & 1 deletion packages/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Add this to your app's `pubspec.yaml` file:

```yaml
dependencies:
flutter_widget_from_html_core: ^0.14.4
flutter_widget_from_html_core: ^0.14.5
```
## Usage
Expand Down
1 change: 1 addition & 0 deletions packages/core/lib/src/core_helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export 'external/csslib.dart';
export 'external/text_scale_factor.dart';
export 'modes/render_mode.dart';
export 'widgets/css_sizing.dart';
export 'widgets/horizontal_margin.dart';
export 'widgets/html_details.dart';
export 'widgets/html_list_item.dart';
export 'widgets/html_list_marker.dart';
Expand Down
16 changes: 7 additions & 9 deletions packages/core/lib/src/data/css.dart
Original file line number Diff line number Diff line change
Expand Up @@ -361,15 +361,13 @@ class CssLengthBox {
_inlineStart?.isPositive == true ||
_right?.isPositive == true;

/// Calculates the left value taking text direction into account.
double? getValueLeft(InheritedProperties resolved) =>
(_left ?? (resolved.isRtl ? _inlineEnd : _inlineStart))
?.getValue(resolved);

/// Calculates the right value taking text direction into account.
double? getValueRight(InheritedProperties resolved) =>
(_right ?? (resolved.isRtl ? _inlineStart : _inlineEnd))
?.getValue(resolved);
/// Calculates the left length taking text direction into account.
CssLength? getLeft(InheritedProperties resolved) =>
_left ?? (resolved.isRtl ? _inlineEnd : _inlineStart);

/// Calculates the right length taking text direction into account.
CssLength? getRight(InheritedProperties resolved) =>
_right ?? (resolved.isRtl ? _inlineStart : _inlineEnd);

@override
String toString() {
Expand Down
20 changes: 12 additions & 8 deletions packages/core/lib/src/internal/ops/style_margin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@ Widget _marginHorizontalBuilder(
Widget widget,
CssLengthBox box,
InheritedProperties resolved,
) =>
Padding(
padding: EdgeInsets.only(
left: max(box.getValueLeft(resolved) ?? 0.0, 0.0),
right: max(box.getValueRight(resolved) ?? 0.0, 0.0),
),
child: widget,
);
) {
final left = box.getLeft(resolved);
final leftValue = max(left?.getValue(resolved) ?? 0.0, 0.0);
final right = box.getRight(resolved);
final rightValue = max(right?.getValue(resolved) ?? 0.0, 0.0);

return HorizontalMargin(
left: left?.unit == CssLengthUnit.auto ? double.infinity : leftValue,
right: right?.unit == CssLengthUnit.auto ? double.infinity : rightValue,
child: widget,
);
}

class StyleMargin {
final WidgetFactory wf;
Expand Down
20 changes: 12 additions & 8 deletions packages/core/lib/src/internal/ops/style_padding.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,21 @@ const kCssPadding = 'padding';

WidgetPlaceholder _paddingInlineAfter(BuildTree tree, CssLengthBox box) =>
WidgetPlaceholder(
builder: (context, _) => _paddingInlineSizedBox(
box.getValueRight(tree.inheritanceResolvers.resolve(context)),
),
builder: (context, _) {
final resolved = tree.inheritanceResolvers.resolve(context);
final right = box.getRight(resolved);
return _paddingInlineSizedBox(right?.getValue(resolved));
},
debugLabel: '${tree.element.localName}--paddingInlineAfter',
);

WidgetPlaceholder _paddingInlineBefore(BuildTree tree, CssLengthBox box) =>
WidgetPlaceholder(
builder: (context, _) => _paddingInlineSizedBox(
box.getValueLeft(tree.inheritanceResolvers.resolve(context)),
),
builder: (context, _) {
final resolved = tree.inheritanceResolvers.resolve(context);
final left = box.getLeft(resolved);
return _paddingInlineSizedBox(left?.getValue(resolved));
},
debugLabel: '${tree.element.localName}--paddingInlineBefore',
);

Expand Down Expand Up @@ -72,9 +76,9 @@ class StylePadding {
tree,
child,
EdgeInsets.fromLTRB(
max(padding.getValueLeft(resolved) ?? 0.0, 0.0),
max(padding.getLeft(resolved)?.getValue(resolved) ?? 0.0, 0.0),
max(padding.top?.getValue(resolved) ?? 0.0, 0.0),
max(padding.getValueRight(resolved) ?? 0.0, 0.0),
max(padding.getRight(resolved)?.getValue(resolved) ?? 0.0, 0.0),
max(padding.bottom?.getValue(resolved) ?? 0.0, 0.0),
),
);
Expand Down
118 changes: 118 additions & 0 deletions packages/core/lib/src/widgets/horizontal_margin.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import 'dart:math';

import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';

class HorizontalMargin extends SingleChildRenderObjectWidget {
final double left;
final double right;

const HorizontalMargin({
super.child,
super.key,
required this.left,
required this.right,
}) : assert(left >= .0),
assert(right >= .0);

@override
RenderObject createRenderObject(BuildContext context) {
return _HorizontalMarginRenderObject(left: left, right: right);
}

@override
void updateRenderObject(BuildContext context, RenderObject renderObject) {
(renderObject as _HorizontalMarginRenderObject)
..setLeft(left)
..setRight(right);
}
}

class _HorizontalMarginRenderObject extends RenderShiftedBox {
_HorizontalMarginRenderObject({
RenderBox? child,
required double left,
required double right,
}) : _left = left,
_right = right,
super(child);

double _left;
void setLeft(double value) {
if (_left != value) {
_left = value;
markNeedsLayout();
}
}

double _right;
void setRight(double value) {
if (_right != value) {
_right = value;
markNeedsLayout();
}
}

double get marginsOrZero => _left.or(0) + _right.or(0);

@override
Size computeDryLayout(BoxConstraints constraints) =>
_compute(child, constraints, ChildLayoutHelper.dryLayoutChild);

@override
double computeMaxIntrinsicWidth(double height) {
final scopedChild = child;
if (scopedChild == null) {
return marginsOrZero;
}

return scopedChild.getMaxIntrinsicWidth(height) + marginsOrZero;
}

@override
double computeMinIntrinsicWidth(double height) {
final scopedChild = child;
if (scopedChild == null) {
return marginsOrZero;
}

return scopedChild.getMinIntrinsicWidth(height) + marginsOrZero;
}

@override
void performLayout() =>
size = _compute(child, constraints, ChildLayoutHelper.layoutChild);

Size _compute(RenderBox? scopedChild, BoxConstraints bc, ChildLayouter fn) {
if (scopedChild == null) {
return bc.constrain(Size(marginsOrZero, 0));
}

final edges = EdgeInsets.only(left: _left.or(0), right: _right.or(0));
final cc = bc.deflate(edges);
final childSize = fn(scopedChild, cc);
final width = bc.maxWidth.orChildWithMargins(childSize, _left, _right);
final fullSize = bc.constrain(Size(width, childSize.height));

if (identical(fn, ChildLayoutHelper.layoutChild)) {
final delta = max(.0, fullSize.width - childSize.width);
final leftWeight = _left.or(fullSize.width);
final totalWeight = leftWeight + _right.or(fullSize.width);
final leftMax = totalWeight == .0 ? .0 : delta / totalWeight * leftWeight;

final data = scopedChild.parentData! as BoxParentData;
data.offset = Offset(min(_left, leftMax), 0);
}

return fullSize;
}
}

extension on double {
double orChildWithMargins(Size child, double left, double right) =>
(isFinite && (left.isInfinite || right.isInfinite))
? this
: child.width + left.or(0) + right.or(0);

double or(double value) => isInfinite ? value : this;
}
2 changes: 1 addition & 1 deletion packages/core/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: flutter_widget_from_html_core
version: 0.14.4+1
version: 0.14.5
description: Flutter package to render html as widgets that focuses on correctness and extensibility.
homepage: https://github.com/daohoangson/flutter_widget_from_html/tree/master/packages/core

Expand Down
17 changes: 17 additions & 0 deletions packages/core/test/_.dart
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,23 @@ class Explainer {
);
}

if (widget is SingleChildRenderObjectWidget) {
final dynamicWidget = widget as dynamic;
switch (widget.runtimeType.toString()) {
case 'HorizontalMargin':
// TODO: remove ignore when our minimum core version >= 1.0
// ignore: avoid_dynamic_calls
final left = dynamicWidget.left as double;
// ignore: avoid_dynamic_calls
final right = dynamicWidget.right as double;
attr.add(
'left=${left.isInfinite ? '∞' : left.truncate()},'
'right=${right.isInfinite ? '∞' : right.truncate()}',
);
break;
}
}

if (widget is Tooltip) {
attr.add('message=${widget.message}');
}
Expand Down
2 changes: 1 addition & 1 deletion packages/core/test/core_config_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ void main() {
expect(
e,
equals(
'[Padding:(0,40,0,40),child='
'[HorizontalMargin:left=40,right=40,child='
'[CssBlock:child=[RichText:(:Foo)]]'
']',
),
Expand Down
8 changes: 4 additions & 4 deletions packages/core/test/core_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ Future<void> main() async {
equals(
'[CssBlock:child=[Column:children='
'[CssBlock:child=[RichText:(+b:Foo)]],'
'[Padding:(0,0,0,40),child=[CssBlock:child=[RichText:(:Bar)]]]'
'[HorizontalMargin:left=40,right=0,child=[CssBlock:child=[RichText:(:Bar)]]]'
']],'
'[SizedBox:0.0x10.0]',
),
Expand Down Expand Up @@ -294,7 +294,7 @@ Future<void> main() async {
explained,
equals(
'[SizedBox:0.0x10.0],'
'[Padding:(0,40,0,40),child=[CssBlock:child=[RichText:(:Foo)]]],'
'[HorizontalMargin:left=40,right=40,child=[CssBlock:child=[RichText:(:Foo)]]],'
'[SizedBox:0.0x10.0]',
),
);
Expand All @@ -321,7 +321,7 @@ Future<void> main() async {
explained,
equals(
'[SizedBox:0.0x10.0],'
'[Padding:(0,40,0,40),child=[CssBlock:child=[Column:children='
'[HorizontalMargin:left=40,right=40,child=[CssBlock:child=[Column:children='
'[CssSizing:$imgSizingConstraints,child=[Image:image=NetworkImage("http://domain.com/image.png", scale: 1.0)]],'
'[CssBlock:child=[RichText:(:(+i:fig. 1)(: Foo))]]'
']]],'
Expand Down Expand Up @@ -626,7 +626,7 @@ Future<void> main() async {
explained,
equals(
'[SizedBox:0.0x1.0],'
'[Padding:(0,1,0,1),child='
'[HorizontalMargin:left=1,right=1,child='
'[Container:bg=#FFFF0000,child='
'[Padding:(2,2,2,2),child='
'[CssBlock:child='
Expand Down
4 changes: 2 additions & 2 deletions packages/core/test/style_border_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ void main() {
explained,
equals(
'[SizedBox:0.0x1.0],'
'[Padding:(0,1,0,1),child='
'[HorizontalMargin:left=1,right=1,child='
'[Container:border=$_border1,child='
'[CssBlock:child='
'[RichText:(:Foo)]]]'
Expand All @@ -189,7 +189,7 @@ void main() {
'[Container:border=$_border1,child='
'[Column:children='
'[SizedBox:0.0x1.0],'
'[CssBlock:child=[Padding:(0,1,0,1),child=[CssBlock:child=[RichText:(:Foo)]]]],'
'[CssBlock:child=[HorizontalMargin:left=1,right=1,child=[CssBlock:child=[RichText:(:Foo)]]]],'
'[SizedBox:0.0x1.0]'
']]',
),
Expand Down
Loading

1 comment on commit ceb41a7

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.