From ceb41a770a43f575a25ef6fc500de21fba445111 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=90=C3=A0o=20Ho=C3=A0ng=20S=C6=A1n?= Date: Thu, 26 Oct 2023 17:38:48 +0700 Subject: [PATCH] Add support for `auto` margins (#1077) Also: * BREAKING: Replace `CssLengthBox.getValueXxx` with `.getXxx` * [core] v0.14.5 * [enhanced] v0.14.5 --- demo_app/test/goldens.json | 1 + demo_app/test/goldens/inline/margin/auto.png | Bin 0 -> 16604 bytes packages/core/CHANGELOG.md | 8 +- packages/core/README.md | 2 +- packages/core/lib/src/core_helpers.dart | 1 + packages/core/lib/src/data/css.dart | 16 +- .../lib/src/internal/ops/style_margin.dart | 20 +- .../lib/src/internal/ops/style_padding.dart | 20 +- .../lib/src/widgets/horizontal_margin.dart | 118 +++++++++ packages/core/pubspec.yaml | 2 +- packages/core/test/_.dart | 17 ++ packages/core/test/core_config_test.dart | 2 +- packages/core/test/core_test.dart | 8 +- packages/core/test/style_border_test.dart | 4 +- packages/core/test/style_margin_test.dart | 246 ++++++++++++++++-- packages/core/test/style_sizing_test.dart | 2 +- packages/core/test/style_text_align_test.dart | 2 +- packages/core/test/tag_a_test.dart | 2 +- packages/enhanced/CHANGELOG.md | 6 + packages/enhanced/README.md | 2 +- packages/enhanced/pubspec.yaml | 4 +- packages/enhanced/test/config_test.dart | 2 +- 22 files changed, 417 insertions(+), 68 deletions(-) create mode 100644 demo_app/test/goldens/inline/margin/auto.png create mode 100644 packages/core/lib/src/widgets/horizontal_margin.dart diff --git a/demo_app/test/goldens.json b/demo_app/test/goldens.json index 3189d94c6..ccc80cf4e 100644 --- a/demo_app/test/goldens.json +++ b/demo_app/test/goldens.json @@ -148,6 +148,7 @@ "inline/margin/1_value": "----
Foo
----", "inline/margin/2_values": "----\n
both
\n----\n
vertical only
\n----\n
horizontal only
\n----\n", "inline/margin/4_values": "----\n
all
\n----\n
top only
\n----\n
right only
\n----\n
bottom only
\n----\n
left only
\n---\n", + "inline/margin/auto": "\n \n \n \n \n \n \n \n \n \n \n
12
34
", "inline/margin/margin-bottom": "----
Foo
----", "inline/margin/margin-left": "----
Foo
----", "inline/margin/margin-right": "----
Foo
----", diff --git a/demo_app/test/goldens/inline/margin/auto.png b/demo_app/test/goldens/inline/margin/auto.png new file mode 100644 index 0000000000000000000000000000000000000000..211f64aa083f9f876ccc480cf5183f3cb1e9711b GIT binary patch literal 16604 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 literal 0 HcmV?d00001 diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 03132c72b..343bec03b 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -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 diff --git a/packages/core/README.md b/packages/core/README.md index d5bd5cba0..345ecff06 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -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 diff --git a/packages/core/lib/src/core_helpers.dart b/packages/core/lib/src/core_helpers.dart index c830ae2bb..8091508fc 100644 --- a/packages/core/lib/src/core_helpers.dart +++ b/packages/core/lib/src/core_helpers.dart @@ -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'; diff --git a/packages/core/lib/src/data/css.dart b/packages/core/lib/src/data/css.dart index 539bb7de5..ca93ed045 100644 --- a/packages/core/lib/src/data/css.dart +++ b/packages/core/lib/src/data/css.dart @@ -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() { diff --git a/packages/core/lib/src/internal/ops/style_margin.dart b/packages/core/lib/src/internal/ops/style_margin.dart index c7a88dbf4..a4a2267fd 100644 --- a/packages/core/lib/src/internal/ops/style_margin.dart +++ b/packages/core/lib/src/internal/ops/style_margin.dart @@ -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; diff --git a/packages/core/lib/src/internal/ops/style_padding.dart b/packages/core/lib/src/internal/ops/style_padding.dart index b28298265..faf8dde39 100644 --- a/packages/core/lib/src/internal/ops/style_padding.dart +++ b/packages/core/lib/src/internal/ops/style_padding.dart @@ -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', ); @@ -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), ), ); diff --git a/packages/core/lib/src/widgets/horizontal_margin.dart b/packages/core/lib/src/widgets/horizontal_margin.dart new file mode 100644 index 000000000..58e4fe22f --- /dev/null +++ b/packages/core/lib/src/widgets/horizontal_margin.dart @@ -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; +} diff --git a/packages/core/pubspec.yaml b/packages/core/pubspec.yaml index 5b3c91271..18b50e4d6 100644 --- a/packages/core/pubspec.yaml +++ b/packages/core/pubspec.yaml @@ -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 diff --git a/packages/core/test/_.dart b/packages/core/test/_.dart index bd572c935..bf68552cf 100644 --- a/packages/core/test/_.dart +++ b/packages/core/test/_.dart @@ -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}'); } diff --git a/packages/core/test/core_config_test.dart b/packages/core/test/core_config_test.dart index aa5b2254f..16b9a149e 100644 --- a/packages/core/test/core_config_test.dart +++ b/packages/core/test/core_config_test.dart @@ -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)]]' ']', ), diff --git a/packages/core/test/core_test.dart b/packages/core/test/core_test.dart index 4156be984..9a84d12d2 100644 --- a/packages/core/test/core_test.dart +++ b/packages/core/test/core_test.dart @@ -262,7 +262,7 @@ Future 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]', ), @@ -294,7 +294,7 @@ Future 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]', ), ); @@ -321,7 +321,7 @@ Future 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))]]' ']]],' @@ -626,7 +626,7 @@ Future 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=' diff --git a/packages/core/test/style_border_test.dart b/packages/core/test/style_border_test.dart index 5ef678196..dab605e5b 100644 --- a/packages/core/test/style_border_test.dart +++ b/packages/core/test/style_border_test.dart @@ -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)]]]' @@ -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]' ']]', ), diff --git a/packages/core/test/style_margin_test.dart b/packages/core/test/style_margin_test.dart index 9f5c41f6f..0e25610fb 100644 --- a/packages/core/test/style_margin_test.dart +++ b/packages/core/test/style_margin_test.dart @@ -1,4 +1,6 @@ +import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; import '_.dart' as helper; @@ -19,7 +21,7 @@ void main() { explained, equals( '[SizedBox:0.0x1.0],' - '[Padding:(0,2,0,4),child=[CssBlock:child=[RichText:(:Foo)]]],' + '[HorizontalMargin:left=4,right=2,child=[CssBlock:child=[RichText:(:Foo)]]],' '[SizedBox:0.0x3.0]', ), ); @@ -32,7 +34,7 @@ void main() { explained, equals( '[SizedBox:0.0x1.0],' - '[Padding:(0,4,0,2),child=' + '[HorizontalMargin:left=2,right=4,child=' '[CssBlock:child=[RichText:dir=rtl,(:Foo)]]' '],' '[SizedBox:0.0x3.0]', @@ -59,7 +61,7 @@ void main() { expect( explained, equals( - '[Padding:(0,2,0,0),child=[CssBlock:child=' + '[HorizontalMargin:left=0,right=2,child=[CssBlock:child=' '[RichText:(:Foo)]]]', ), ); @@ -70,7 +72,7 @@ void main() { expect( explained, equals( - '[Padding:(0,0,0,2),child=[CssBlock:child=' + '[HorizontalMargin:left=2,right=0,child=[CssBlock:child=' '[RichText:dir=rtl,(:Foo)]]]', ), ); @@ -96,7 +98,7 @@ void main() { expect( explained, equals( - '[Padding:(0,0,0,4),child=[CssBlock:child=' + '[HorizontalMargin:left=4,right=0,child=[CssBlock:child=' '[RichText:(:Foo)]]]', ), ); @@ -107,7 +109,7 @@ void main() { expect( explained, equals( - '[Padding:(0,4,0,0),child=[CssBlock:child=' + '[HorizontalMargin:left=0,right=4,child=[CssBlock:child=' '[RichText:dir=rtl,(:Foo)]]]', ), ); @@ -123,7 +125,7 @@ void main() { explained, equals( '[SizedBox:0.0x5.0],' - '[Padding:(0,10,0,10),child=[CssBlock:child=[RichText:(:Foo)]]],' + '[HorizontalMargin:left=10,right=10,child=[CssBlock:child=[RichText:(:Foo)]]],' '[SizedBox:0.0x5.0]', ), ); @@ -148,7 +150,7 @@ void main() { expect( explained, equals( - '[Padding:(0,10,0,10),child=[CssBlock:child=' + '[HorizontalMargin:left=10,right=10,child=[CssBlock:child=' '[RichText:(:Foo)]]]', ), ); @@ -163,7 +165,7 @@ void main() { explained, equals( '[SizedBox:0.0x20.0],' - '[Padding:(0,20,0,20),child=[CssBlock:child=[RichText:(:Foo)]]],' + '[HorizontalMargin:left=20,right=20,child=[CssBlock:child=[RichText:(:Foo)]]],' '[SizedBox:0.0x20.0]', ), ); @@ -176,7 +178,7 @@ void main() { explained, equals( '[SizedBox:0.0x13.3],' - '[Padding:(0,13,0,13),child=[CssBlock:child=[RichText:(:Foo)]]],' + '[HorizontalMargin:left=13,right=13,child=[CssBlock:child=[RichText:(:Foo)]]],' '[SizedBox:0.0x13.3]', ), ); @@ -189,13 +191,52 @@ void main() { explained, equals( '[SizedBox:0.0x10.0],' - '[Padding:(0,10,0,10),child=[CssBlock:child=[RichText:(:Foo)]]],' + '[HorizontalMargin:left=10,right=10,child=[CssBlock:child=[RichText:(:Foo)]]],' '[SizedBox:0.0x10.0]', ), ); }); }); + group('auto', () { + testWidgets('parses 4 values', (WidgetTester tester) async { + const html = '
Foo
'; + final explained = await explain(tester, html); + expect( + explained, + equals( + '[SizedBox:0.0x1.0],' + '[HorizontalMargin:left=∞,right=∞,child=[CssBlock:child=[RichText:(:Foo)]]],' + '[SizedBox:0.0x2.0]', + ), + ); + }); + + testWidgets('parses 2 values', (WidgetTester tester) async { + const html = '
Foo
'; + final explained = await explain(tester, html); + expect( + explained, + equals( + '[HorizontalMargin:left=∞,right=∞,child=[CssBlock:child=[RichText:(:Foo)]]]', + ), + ); + }); + + testWidgets('parses 1 value', (WidgetTester tester) async { + const html = '
Foo
'; + final explained = await explain(tester, html); + expect( + explained, + equals( + '[widget0],' + '[HorizontalMargin:left=∞,right=∞,child=[CssBlock:child=[RichText:(:Foo)]]],' + '[widget0]', + ), + ); + }); + }); + testWidgets('renders margin within another', (WidgetTester tester) async { const html = '
' '
Foo
'; @@ -204,8 +245,8 @@ void main() { explained, equals( '[SizedBox:0.0x2.0],' - '[Padding:(0,1,0,1),child=' - '[CssBlock:child=[Padding:(0,2,0,2),child=' + '[HorizontalMargin:left=1,right=1,child=' + '[CssBlock:child=[HorizontalMargin:left=2,right=2,child=' '[CssBlock:child=[RichText:(:Foo)]]' ']]],' '[SizedBox:0.0x2.0]', @@ -222,11 +263,11 @@ void main() { explained, equals( '[SizedBox:0.0x3.0],' - '[Padding:(0,3,0,3),child=[CssBlock:child=[RichText:(:1)]]],' + '[HorizontalMargin:left=3,right=3,child=[CssBlock:child=[RichText:(:1)]]],' '[SizedBox:0.0x3.0],' - '[Padding:(0,3,0,3),child=[CssBlock:child=[RichText:(:2)]]],' + '[HorizontalMargin:left=3,right=3,child=[CssBlock:child=[RichText:(:2)]]],' '[SizedBox:0.0x3.0],' - '[Padding:(0,3,0,3),child=[CssBlock:child=[RichText:(:3)]]],' + '[HorizontalMargin:left=3,right=3,child=[CssBlock:child=[RichText:(:3)]]],' '[SizedBox:0.0x3.0]', ), ); @@ -240,12 +281,12 @@ void main() { explained, equals( '[SizedBox:0.0x3.0],' - '[Padding:(0,3,0,3),child=[CssBlock:child=[Column:children=' + '[HorizontalMargin:left=3,right=3,child=[CssBlock:child=[Column:children=' '[CssBlock:child=[RichText:(:1a)]],' '[CssBlock:child=[RichText:(:1b)]]' ']]],' '[SizedBox:0.0x3.0],' - '[Padding:(0,3,0,3),child=[CssBlock:child=[Column:children=' + '[HorizontalMargin:left=3,right=3,child=[CssBlock:child=[Column:children=' '[CssBlock:child=[RichText:(:2a)]],' '[CssBlock:child=[RichText:(:2b)]]' ']]],' @@ -283,7 +324,7 @@ void main() { expect( explained, equals( - '[Padding:(0,3,0,0),child=[CssBlock:child=' + '[HorizontalMargin:left=0,right=3,child=[CssBlock:child=' '[RichText:(:Foo)]]]', ), ); @@ -297,7 +338,7 @@ void main() { expect( explained, equals( - '[Padding:(0,3,0,0),child=[CssBlock:child=' + '[HorizontalMargin:left=0,right=3,child=[CssBlock:child=' '[RichText:(:Foo)]]]', ), ); @@ -308,7 +349,7 @@ void main() { expect( explained, equals( - '[Padding:(0,0,0,3),child=[CssBlock:child=' + '[HorizontalMargin:left=3,right=0,child=[CssBlock:child=' '[RichText:dir=rtl,(:Foo)]]]', ), ); @@ -343,7 +384,7 @@ void main() { expect( explained, equals( - '[Padding:(0,0,0,3),child=[CssBlock:child=' + '[HorizontalMargin:left=3,right=0,child=[CssBlock:child=' '[RichText:(:Foo)]]]', ), ); @@ -357,7 +398,7 @@ void main() { expect( explained, equals( - '[Padding:(0,0,0,3),child=[CssBlock:child=' + '[HorizontalMargin:left=3,right=0,child=[CssBlock:child=' '[RichText:(:Foo)]]]', ), ); @@ -368,7 +409,7 @@ void main() { expect( explained, equals( - '[Padding:(0,3,0,0),child=[CssBlock:child=' + '[HorizontalMargin:left=0,right=3,child=[CssBlock:child=' '[RichText:dir=rtl,(:Foo)]]]', ), ); @@ -382,7 +423,7 @@ void main() { explained, equals( '[SizedBox:0.0x5.0],' - '[Padding:(0,3,0,3),child=[CssBlock:child=[RichText:(:Foo)]]],' + '[HorizontalMargin:left=3,right=3,child=[CssBlock:child=[RichText:(:Foo)]]],' '[SizedBox:0.0x3.0]', ), ); @@ -395,7 +436,7 @@ void main() { explained, equals( '[SizedBox:0.0x3.0],' - '[Padding:(0,3,0,3),child=[CssBlock:child=[RichText:(:Foo)]]]', + '[HorizontalMargin:left=3,right=3,child=[CssBlock:child=[RichText:(:Foo)]]]', ), ); }); @@ -678,4 +719,157 @@ void main() { expect(explained, equals('[CssBlock:child=[RichText:(:Foo)]]')); }); }); + + group('HorizontalMargin', () { + group('_HorizontalMarginRenderObject setters', () { + testWidgets('updates left', (t) async { + await explain(t, '
Foo
'); + final element = find.byType(HorizontalMargin).evaluate().single; + final before = element.widget as HorizontalMargin; + expect(before.left, equals(1.0)); + + await explain(t, '
Foo
'); + final after = element.widget as HorizontalMargin; + expect(after.left, equals(double.infinity)); + }); + + testWidgets('updates right', (t) async { + await explain(t, '
Foo
'); + final element = find.byType(HorizontalMargin).evaluate().single; + final before = element.widget as HorizontalMargin; + expect(before.right, equals(1.0)); + + await explain(t, '
Foo
'); + final after = element.widget as HorizontalMargin; + expect(after.right, equals(double.infinity)); + }); + }); + + testWidgets('computeDryLayout', (tester) async { + await tester.pumpSizedBox( + left: double.infinity, + right: double.infinity, + ); + + final bc = BoxConstraints.loose(const Size(50, 50)); + final drySize = tester.horizontalMargin.getDryLayout(bc); + expect(drySize, equals(const Size(50, 10))); + }); + + group('computeMaxIntrinsicWidth', () { + testWidgets('computes with child', (tester) async { + await tester.pumpSizedBox(left: 1, right: 2); + final maxWidth = tester.horizontalMargin.getMaxIntrinsicWidth(50); + expect(maxWidth, equals(13)); + }); + + testWidgets('computes without child', (tester) async { + await tester.pumpSizedBox(isNull: true, left: 1, right: 2); + final maxWidth = tester.horizontalMargin.getMaxIntrinsicWidth(50); + expect(maxWidth, equals(3)); + }); + }); + + group('computeMinIntrinsicWidth', () { + testWidgets('computes with child', (tester) async { + await tester.pumpSizedBox(left: 1, right: 2); + final maxWidth = tester.horizontalMargin.getMinIntrinsicWidth(50); + expect(maxWidth, equals(13)); + }); + + testWidgets('computes without child', (tester) async { + await tester.pumpSizedBox(isNull: true, left: 1, right: 2); + final maxWidth = tester.horizontalMargin.getMinIntrinsicWidth(50); + expect(maxWidth, equals(3)); + }); + }); + + group('performLayout', () { + testWidgets('aligns left', (tester) async { + final key = await tester.pumpSizedBox(right: double.infinity); + + final full = tester.getRect(find.byType(HorizontalMargin)); + expect(full, equals(const Rect.fromLTWH(0, 0, 100, 10))); + + final child = tester.getRect(find.byKey(key)); + expect(child, equals(const Rect.fromLTWH(0, 0, 10, 10))); + }); + + testWidgets('aligns center', (tester) async { + final key = await tester.pumpSizedBox( + left: double.infinity, + right: double.infinity, + ); + + final full = tester.getRect(find.byType(HorizontalMargin)); + expect(full, equals(const Rect.fromLTWH(0, 0, 100, 10))); + + final child = tester.getRect(find.byKey(key)); + expect(child, equals(const Rect.fromLTWH(45, 0, 10, 10))); + }); + + testWidgets('aligns right', (tester) async { + final key = await tester.pumpSizedBox(left: double.infinity); + + final full = tester.getRect(find.byType(HorizontalMargin)); + expect(full, equals(const Rect.fromLTWH(0, 0, 100, 10))); + + final child = tester.getRect(find.byKey(key)); + expect(child, equals(const Rect.fromLTWH(90, 0, 10, 10))); + }); + + testWidgets('aligns values', (tester) async { + final key = await tester.pumpSizedBox(left: 20, right: 30); + + final full = tester.getRect(find.byType(HorizontalMargin)); + expect(full, equals(const Rect.fromLTWH(0, 0, 60, 10))); + + final child = tester.getRect(find.byKey(key)); + expect(child, equals(const Rect.fromLTWH(20, 0, 10, 10))); + }); + + testWidgets('aligns big values', (tester) async { + final key = await tester.pumpSizedBox(left: 200, right: 300); + + final full = tester.getRect(find.byType(HorizontalMargin)); + expect(full, equals(const Rect.fromLTWH(0, 0, 100, 10))); + + final child = tester.getSize(find.byKey(key)); + expect(child, equals(const Size(0, 10))); + }); + }); + }); +} + +extension on WidgetTester { + RenderBox get horizontalMargin => + renderObject(find.byType(HorizontalMargin)) as RenderBox; + + Future pumpSizedBox({ + bool isNull = false, + double left = .0, + double right = .0, + }) async { + setWindowSize(const Size(100, 100)); + + final key = GlobalKey(); + await pumpWidget( + Align( + alignment: Alignment.topLeft, + child: HorizontalMargin( + left: left, + right: right, + child: isNull + ? null + : SizedBox( + height: 10, + key: key, + width: 10, + ), + ), + ), + ); + + return key; + } } diff --git a/packages/core/test/style_sizing_test.dart b/packages/core/test/style_sizing_test.dart index 9f4a99116..e5ef0c688 100644 --- a/packages/core/test/style_sizing_test.dart +++ b/packages/core/test/style_sizing_test.dart @@ -432,7 +432,7 @@ Future main() async { '[CssBlock:child=' '[Container:bg=#FF008000,child=' '[CssBlock:child=' - '[Padding:(0,15,0,15),child=' + '[HorizontalMargin:left=15,right=15,child=' '[Container:bg=#FF0000FF,child=' '[Padding:(5,5,5,5),child=' '[CssSizing:height=100.0,width=100.0,child=' diff --git a/packages/core/test/style_text_align_test.dart b/packages/core/test/style_text_align_test.dart index 1508d7577..27e08c290 100644 --- a/packages/core/test/style_text_align_test.dart +++ b/packages/core/test/style_text_align_test.dart @@ -394,7 +394,7 @@ void main() { equals( '[SizedBox:0.0x5.0],' '[CssBlock:child=' - '[Padding:(0,5,0,5),child=' + '[HorizontalMargin:left=5,right=5,child=' '[CssBlock:child=[RichText:align=center,(:Foo)]]' ']],' '[SizedBox:0.0x5.0]', diff --git a/packages/core/test/tag_a_test.dart b/packages/core/test/tag_a_test.dart index 520a357da..d5e3719fd 100644 --- a/packages/core/test/tag_a_test.dart +++ b/packages/core/test/tag_a_test.dart @@ -138,7 +138,7 @@ void main() { equals( '[SizedBox:0.0x5.0],' '[MouseRegion:child=[GestureDetector:child=' - '[Padding:(0,5,0,5),child=' + '[HorizontalMargin:left=5,right=5,child=' '[CssBlock:child=[RichText:(#FF123456+u:Foo)]]' ']]],' '[SizedBox:0.0x5.0]', diff --git a/packages/enhanced/CHANGELOG.md b/packages/enhanced/CHANGELOG.md index ade0de072..23019eacd 100644 --- a/packages/enhanced/CHANGELOG.md +++ b/packages/enhanced/CHANGELOG.md @@ -1,3 +1,9 @@ +## 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) diff --git a/packages/enhanced/README.md b/packages/enhanced/README.md index b8438b7eb..3860c13bb 100644 --- a/packages/enhanced/README.md +++ b/packages/enhanced/README.md @@ -27,7 +27,7 @@ Add this to your app's `pubspec.yaml` file: ```yaml dependencies: - flutter_widget_from_html: ^0.14.4 + flutter_widget_from_html: ^0.14.5 ``` ### Platform specific configuration diff --git a/packages/enhanced/pubspec.yaml b/packages/enhanced/pubspec.yaml index d4e459205..28d98fd89 100644 --- a/packages/enhanced/pubspec.yaml +++ b/packages/enhanced/pubspec.yaml @@ -1,5 +1,5 @@ name: flutter_widget_from_html -version: 0.14.4+1 +version: 0.14.5 description: Flutter package to render html as widgets that supports hyperlink, image, audio, video, iframe and many other tags. homepage: https://github.com/daohoangson/flutter_widget_from_html @@ -10,7 +10,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_widget_from_html_core: ^0.14.4+1 + flutter_widget_from_html_core: ^0.14.5 fwfh_cached_network_image: ^0.14.2 fwfh_chewie: ^0.14.2 fwfh_just_audio: ^0.14.2 diff --git a/packages/enhanced/test/config_test.dart b/packages/enhanced/test/config_test.dart index 3108a25f3..0dfaaf8be 100644 --- a/packages/enhanced/test/config_test.dart +++ b/packages/enhanced/test/config_test.dart @@ -251,7 +251,7 @@ void main() { expect( e, equals( - '[Padding:(0,40,0,40),child=' + '[HorizontalMargin:left=40,right=40,child=' '[CssBlock:child=[RichText:(:Foo)]]' ']', ),