Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/src/builtins/image_builtin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ class ImageBuiltIn extends HtmlExtension {
}

return WidgetSpan(
alignment: context.style!.verticalAlign
.toPlaceholderAlignment(context.style!.display),
baseline: TextBaseline.alphabetic,
child: CssBoxWidget(
style: imageStyle,
childIsReplaced: true,
Expand Down
10 changes: 9 additions & 1 deletion lib/src/builtins/interactive_element_builtin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,20 @@ class InteractiveElementBuiltIn extends HtmlExtension {
children: childSpan.children
?.map((e) => _processInteractableChild(context, e))
.toList(),
recognizer: TapGestureRecognizer()..onTap = onTap,
style: childSpan.style,
semanticsLabel: childSpan.semanticsLabel,
recognizer: TapGestureRecognizer()..onTap = onTap,
locale: childSpan.locale,
mouseCursor: childSpan.mouseCursor,
onEnter: childSpan.onEnter,
onExit: childSpan.onExit,
spellOut: childSpan.spellOut,
);
} else {
return WidgetSpan(
alignment: context.style!.verticalAlign
.toPlaceholderAlignment(context.style!.display),
baseline: TextBaseline.alphabetic,
child: MultipleTapGestureDetector(
onTap: onTap,
child: GestureDetector(
Expand Down
20 changes: 10 additions & 10 deletions lib/src/builtins/styled_element_builtin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -427,27 +427,27 @@ class StyledElementBuiltIn extends HtmlExtension {

@override
InlineSpan build(ExtensionContext context) {
if (context.styledElement!.style.display == Display.listItem ||
((context.styledElement!.style.display == Display.block ||
context.styledElement!.style.display == Display.inlineBlock) &&
final style = context.styledElement!.style;
final display = style.display ?? Display.inline;
if (display.displayListItem ||
Copy link
Owner Author

Choose a reason for hiding this comment

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

Can remove this

((display.isBlock || display == Display.inlineBlock) &&
(context.styledElement!.children.isNotEmpty ||
context.elementName == "hr"))) {
return WidgetSpan(
alignment: PlaceholderAlignment.baseline,
alignment: style.verticalAlign.toPlaceholderAlignment(display),
baseline: TextBaseline.alphabetic,
child: CssBoxWidget.withInlineSpanChildren(
key: AnchorKey.of(context.parser.key, context.styledElement),
style: context.styledElement!.style,
style: context.style!,
shrinkWrap: context.parser.shrinkWrap,
childIsReplaced: ["iframe", "img", "video", "audio"]
.contains(context.styledElement!.name),
childIsReplaced:
["iframe", "img", "video", "audio"].contains(context.elementName),
children: context.builtChildrenMap!.entries
.expandIndexed((i, child) => [
child.value,
if (context.parser.shrinkWrap &&
i != context.styledElement!.children.length - 1 &&
(child.key.style.display == Display.block ||
child.key.style.display == Display.listItem) &&
(child.key.style.display?.isBlock ?? false) &&
child.key.element?.localName != "html" &&
child.key.element?.localName != "body")
const TextSpan(text: "\n", style: TextStyle(fontSize: 0)),
Expand All @@ -463,7 +463,7 @@ class StyledElementBuiltIn extends HtmlExtension {
.expandIndexed((index, child) => [
child.value,
if (context.parser.shrinkWrap &&
child.key.style.display == Display.block &&
(child.key.style.display?.isBlock ?? false) &&
index != context.styledElement!.children.length - 1 &&
child.key.element?.parent?.localName != "th" &&
child.key.element?.parent?.localName != "td" &&
Expand Down
142 changes: 85 additions & 57 deletions lib/src/css_box_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,7 @@ class CssBoxWidget extends StatelessWidget {
/// width available to it or if it should just let its inner content
/// determine the content-box's width.
bool _shouldExpandToFillBlock() {
return (style.display == Display.block ||
style.display == Display.listItem) &&
!childIsReplaced &&
!shrinkWrap;
return (style.display?.isBlock ?? false) && !childIsReplaced && !shrinkWrap;
}

TextDirection _checkTextDirection(
Expand Down Expand Up @@ -424,42 +421,62 @@ class RenderCSSBox extends RenderBox
}
}

static double getIntrinsicDimension(RenderBox? firstChild,
double Function(RenderBox child) mainChildSizeGetter) {
static double getIntrinsicDimension(
RenderBox? firstChild,
double Function(RenderBox child) mainChildSizeGetter,
double marginSpaceNeeded) {
double extent = 0.0;
RenderBox? child = firstChild;
while (child != null) {
final CSSBoxParentData childParentData =
child.parentData! as CSSBoxParentData;
extent = math.max(extent, mainChildSizeGetter(child));
try {
extent = math.max(extent, mainChildSizeGetter(child));
} catch (_) {
// See https://github.com/flutter/flutter/issues/65895
debugPrint(
"Due to Flutter layout restrictions (see https://github.com/flutter/flutter/issues/65895), contents set to `vertical-align: baseline` within an intrinsically-sized layout may not display as expected. If content is cut off or displaying incorrectly, please try setting vertical-align to 'bottom' on the problematic elements");
}
assert(child.parentData == childParentData);
child = childParentData.nextSibling;
}
return extent;
return extent + marginSpaceNeeded;
}

@override
double computeMinIntrinsicWidth(double height) {
return getIntrinsicDimension(
firstChild, (RenderBox child) => child.getMinIntrinsicWidth(height));
firstChild,
(RenderBox child) => child.getMinIntrinsicWidth(height),
_calculateIntrinsicMargins().horizontal,
);
}

@override
double computeMaxIntrinsicWidth(double height) {
return getIntrinsicDimension(
firstChild, (RenderBox child) => child.getMaxIntrinsicWidth(height));
firstChild,
(RenderBox child) => child.getMaxIntrinsicWidth(height),
_calculateIntrinsicMargins().horizontal,
);
}

@override
double computeMinIntrinsicHeight(double width) {
return getIntrinsicDimension(
firstChild, (RenderBox child) => child.getMinIntrinsicHeight(width));
firstChild,
(RenderBox child) => child.getMinIntrinsicHeight(width),
_calculateIntrinsicMargins().vertical,
);
}

@override
double computeMaxIntrinsicHeight(double width) {
return getIntrinsicDimension(
firstChild, (RenderBox child) => child.getMaxIntrinsicHeight(width));
firstChild,
(RenderBox child) => child.getMaxIntrinsicHeight(width),
_calculateIntrinsicMargins().vertical,
);
}

@override
Expand Down Expand Up @@ -521,31 +538,20 @@ class RenderCSSBox extends RenderBox

//Calculate Width and Height of CSS Box
height = childSize.height;
switch (display) {
case Display.block:
width = (shrinkWrap || childIsReplaced)
? childSize.width + horizontalMargins
: containingBlockSize.width;
height = childSize.height + verticalMargins;
break;
case Display.inline:
width = childSize.width + horizontalMargins;
height = childSize.height;
break;
case Display.inlineBlock:
width = childSize.width + horizontalMargins;
height = childSize.height + verticalMargins;
break;
case Display.listItem:
width = shrinkWrap
? childSize.width + horizontalMargins
: containingBlockSize.width;
height = childSize.height + verticalMargins;
break;
case Display.none:
width = 0;
height = 0;
break;
if (display.displayBox == DisplayBox.none) {
width = 0;
height = 0;
} else if (display == Display.inlineBlock) {
width = childSize.width + horizontalMargins;
height = childSize.height + verticalMargins;
} else if (display.isBlock) {
width = (shrinkWrap || childIsReplaced)
? childSize.width + horizontalMargins
: containingBlockSize.width;
height = childSize.height + verticalMargins;
} else {
width = childSize.width + horizontalMargins;
height = childSize.height;
}

return _Sizes(constraints.constrain(Size(width, height)), childSize);
Expand Down Expand Up @@ -575,26 +581,14 @@ class RenderCSSBox extends RenderBox

double leftOffset = 0;
double topOffset = 0;
switch (display) {
case Display.block:
leftOffset = leftMargin;
topOffset = topMargin;
break;
case Display.inline:
leftOffset = leftMargin;
break;
case Display.inlineBlock:
leftOffset = leftMargin;
topOffset = topMargin;
break;
case Display.listItem:
leftOffset = leftMargin;
topOffset = topMargin;
break;
case Display.none:
//No offset
break;

if (display.isBlock || display == Display.inlineBlock) {
leftOffset = leftMargin;
topOffset = topMargin;
} else if (display.displayOutside == DisplayOutside.inline) {
leftOffset = leftMargin;
}

childParentData.offset = Offset(leftOffset, topOffset);
assert(child.parentData == childParentData);

Expand Down Expand Up @@ -628,7 +622,7 @@ class RenderCSSBox extends RenderBox

Margins _calculateUsedMargins(Size childSize, Size containingBlockSize) {
//We assume that margins have already been preprocessed
// (i.e. they are non-null and either px units or auto.
// (i.e. they are non-null and either px units or auto).
assert(margins.left != null && margins.right != null);
assert(margins.left!.unit == Unit.px || margins.left!.unit == Unit.auto);
assert(margins.right!.unit == Unit.px || margins.right!.unit == Unit.auto);
Expand Down Expand Up @@ -737,6 +731,40 @@ class RenderCSSBox extends RenderBox
);
}

Margins _calculateIntrinsicMargins() {
//We assume that margins have already been preprocessed
// (i.e. they are non-null and either px units or auto).
assert(margins.left != null && margins.right != null);
assert(margins.left!.unit == Unit.px || margins.left!.unit == Unit.auto);
assert(margins.right!.unit == Unit.px || margins.right!.unit == Unit.auto);

Margin marginLeft = margins.left!;
Margin marginRight = margins.right!;

bool marginLeftIsAuto = marginLeft.unit == Unit.auto;
bool marginRightIsAuto = marginRight.unit == Unit.auto;

if (display.isBlock) {
if (marginLeftIsAuto) {
marginLeft = Margin(0);
}

if (marginRightIsAuto) {
marginRight = Margin(0);
}
} else {
marginLeft = Margin(0);
marginRight = Margin(0);
}

return Margins(
left: marginLeft,
right: marginRight,
top: margins.top,
bottom: margins.bottom,
);
}

@override
bool hitTestChildren(BoxHitTestResult result, {required Offset position}) {
return defaultHitTestChildren(result, position: position);
Expand Down
9 changes: 6 additions & 3 deletions lib/src/processing/margins.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ class MarginProcessing {
//Collapsing should be depth-first.
tree.children.forEach(_collapseMargins);

//The root boxes do not collapse.
if (tree.name == '[Tree Root]' || tree.name == 'html') {
//The root boxes and table/ruby elements do not collapse.
if (tree.name == '[Tree Root]' ||
tree.name == 'html' ||
tree.style.display?.displayInternal != null) {
return tree;
}

Expand Down Expand Up @@ -67,7 +69,8 @@ class MarginProcessing {

// Handle case (3) from above.
// Bottom margins cannot collapse if the element has padding
if ((tree.style.padding?.bottom ?? tree.style.padding?.blockEnd ?? 0) ==
if ((tree.style.padding?.bottom?.value ??
tree.style.padding?.blockEnd?.value) ==
0) {
final parentBottom = tree.style.margin?.bottom?.value ??
tree.style.margin?.blockEnd?.value ??
Expand Down
43 changes: 31 additions & 12 deletions lib/src/style.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:flutter_html/flutter_html.dart';
import 'package:flutter_html/src/css_parser.dart';

//Export Style value-unit APIs
export 'package:flutter_html/src/style/display.dart';
export 'package:flutter_html/src/style/margin.dart';
export 'package:flutter_html/src/style/padding.dart';
export 'package:flutter_html/src/style/length.dart';
Expand Down Expand Up @@ -49,7 +50,7 @@ class Style {
/// CSS attribute "`display`"
///
/// Inherited: no,
/// Default: unspecified,
/// Default: inline,
Display? display;

/// CSS attribute "`font-family`"
Expand Down Expand Up @@ -271,8 +272,7 @@ class Style {
this.textOverflow,
this.textTransform = TextTransform.none,
}) {
if (alignment == null &&
(display == Display.block || display == Display.listItem)) {
if (alignment == null && (display?.isBlock ?? false)) {
alignment = Alignment.centerLeft;
}
}
Expand Down Expand Up @@ -591,14 +591,6 @@ extension MergeBorders on Border? {
}
}

enum Display {
block,
inline,
inlineBlock,
listItem,
none,
}

enum ListStyleType {
arabicIndic('arabic-indic'),
armenian('armenian'),
Expand Down Expand Up @@ -692,7 +684,34 @@ enum VerticalAlign {
sup,
top,
bottom,
middle,
middle;

/// Converts this [VerticalAlign] to a [PlaceholderAlignment] given the
/// [Display] type of the current context
PlaceholderAlignment toPlaceholderAlignment(Display? display) {
// vertical-align only applies to inline context elements.
// If we aren't in such a context, use the default 'bottom' alignment.
// Also note that the default display, if it is not set, is inline, so we
// treat null `display` values as if they were inline by default.
if (display != Display.inline &&
display != Display.inlineBlock &&
display != null) {
return PlaceholderAlignment.bottom;
}

switch (this) {
case VerticalAlign.baseline:
case VerticalAlign.sub:
case VerticalAlign.sup:
return PlaceholderAlignment.baseline;
case VerticalAlign.top:
return PlaceholderAlignment.top;
case VerticalAlign.bottom:
return PlaceholderAlignment.bottom;
case VerticalAlign.middle:
return PlaceholderAlignment.middle;
}
}
}

enum WhiteSpace {
Expand Down
Loading