From fca573fa0b3c2654f2ca178aa6556394f52be2a8 Mon Sep 17 00:00:00 2001 From: Cameron Paul Date: Thu, 29 May 2025 13:42:42 -0500 Subject: [PATCH 1/4] Implement margin auto --- dw/fltkui.cc | 12 +-- dw/ooffloatsmgr.cc | 10 +- dw/oofposrelmgr.cc | 4 +- dw/style.cc | 13 +-- dw/style.hh | 43 +++++++-- dw/textblock.cc | 22 ++--- dw/textblock_iterator.cc | 2 +- dw/textblock_linebreaking.cc | 6 +- dw/widget.cc | 176 ++++++++++++++++++++++------------- dw/widget.hh | 47 ++++++++-- src/styleengine.cc | 28 +++--- src/table.cc | 4 +- test/html/Makefile.am | 1 - 13 files changed, 239 insertions(+), 129 deletions(-) diff --git a/dw/fltkui.cc b/dw/fltkui.cc index db7c94e2d..53749e0b9 100644 --- a/dw/fltkui.cc +++ b/dw/fltkui.cc @@ -752,10 +752,10 @@ void FltkComplexButtonResource::widgetCallback (Fl_Widget *widget, res->click_x = Fl::event_x() - widget->x(); res->click_y = Fl::event_y() - widget->y(); if (res->style) { - res->click_x -= res->style->boxOffsetX(); - res->click_y -= res->style->boxOffsetY(); - w -= res->style->boxDiffWidth(); - h -= res->style->boxDiffHeight(); + res->click_x -= res->getEmbed()->marginBoxOffsetX(); + res->click_y -= res->getEmbed()->marginBoxOffsetY(); + w -= res->getEmbed()->marginBoxDiffWidth(); + h -= res->getEmbed()->marginBoxDiffHeight(); } if (res->click_x >= 0 && res->click_y >= 0 && res->click_x < w && res->click_y < h) { @@ -768,8 +768,8 @@ void FltkComplexButtonResource::widgetCallback (Fl_Widget *widget, dw::core::EventButton event; res->click_x = res->click_y = 0; - event.xCanvas = widget->x() + res->style->boxOffsetX(); - event.yCanvas = widget->y() + res->style->boxOffsetY(); + event.xCanvas = widget->x() + res->getEmbed()->marginBoxOffsetX(); + event.yCanvas = widget->y() + res->getEmbed()->marginBoxOffsetY(); // ButtonState doesn't have mouse button values on a release. event.state = (core::ButtonState) 0; event.button = 1; diff --git a/dw/ooffloatsmgr.cc b/dw/ooffloatsmgr.cc index 08a74924f..b36e0df65 100644 --- a/dw/ooffloatsmgr.cc +++ b/dw/ooffloatsmgr.cc @@ -441,7 +441,7 @@ int OOFFloatsMgr::calcFloatX (Float *vloat) // Left floats are always aligned on the left side of the generator // (content, not allocation) ... x = generator->getGeneratorX (oofmIndex) - + generator->getStyle()->boxOffsetX(); + + generator->marginBoxOffsetX(); // ... but when the float exceeds the line break width of the container, // it is corrected (but not left of the container). This way, we save @@ -479,7 +479,7 @@ int OOFFloatsMgr::calcFloatX (Float *vloat) vloat->generator->getMaxGeneratorWidth ()); x = max (generator->getGeneratorX (oofmIndex) + effGeneratorWidth - - vloat->size.width - generator->getStyle()->boxRestWidth(), + - vloat->size.width - generator->marginBoxRestWidth(), // Do not exceed container allocation: 0); break; @@ -1064,7 +1064,7 @@ void OOFFloatsMgr::getFloatsExtremes (Extremes *cbExtr, Side side, // For the maximal width, borders must be considered. *maxWidth = max (*maxWidth, extr.maxWidth - + vloat->generator->getStyle()->boxDiffWidth(), + + vloat->generator->marginBoxDiffWidth(), + max (container->getGeneratorWidth () - vloat->generator->getGeneratorWidth (), 0)); @@ -1147,7 +1147,7 @@ int OOFFloatsMgr::getBorder (Side side, int y, int h, OOFAwareWidget *lastGB, switch (side) { case LEFT: d = vloat->generator->getGeneratorX (oofmIndex) - + vloat->generator->getStyle()->boxOffsetX (); + + vloat->generator->marginBoxOffsetX (); break; case RIGHT: @@ -1158,7 +1158,7 @@ int OOFFloatsMgr::getBorder (Side side, int y, int h, OOFAwareWidget *lastGB, d = container->getMaxGeneratorWidth () - (vloat->generator->getGeneratorX (oofmIndex) + vloat->generator->getMaxGeneratorWidth ()) - + vloat->generator->getStyle()->boxRestWidth (); + + vloat->generator->marginBoxRestWidth (); break; default: diff --git a/dw/oofposrelmgr.cc b/dw/oofposrelmgr.cc index 17d59b370..479629f73 100644 --- a/dw/oofposrelmgr.cc +++ b/dw/oofposrelmgr.cc @@ -177,7 +177,7 @@ int OOFPosRelMgr::getChildPosX (Child *child, int refWidth) child->widget->getStyle()->right, child->x, refWidth - - child->widget->getStyle()->boxDiffWidth ()); + - child->widget->marginBoxDiffWidth ()); DBG_OBJ_LEAVE_VAL ("%d + %d = %d", gx, dim, gx + dim); return gx + dim; @@ -194,7 +194,7 @@ int OOFPosRelMgr::getChildPosY (Child *child, int refHeight) child->widget->getStyle()->bottom, child->y, refHeight - - child->widget->getStyle()->boxDiffHeight ()); + - child->widget->marginBoxDiffHeight ()); DBG_OBJ_LEAVE_VAL ("%d + %d = %d", gy, dim, gy + dim); return gy + dim; diff --git a/dw/style.cc b/dw/style.cc index dfb36a8d2..f41b3f236 100644 --- a/dw/style.cc +++ b/dw/style.cc @@ -81,7 +81,7 @@ void StyleAttrs::initValues () position = POSITION_STATIC; top = bottom = left = right = LENGTH_AUTO; textIndent = 0; - margin.setVal (0); + margin.setVal (createAbsLength(0)); borderWidth.setVal (0); padding.setVal (0); borderCollapse = BORDER_MODEL_SEPARATE; @@ -123,7 +123,7 @@ void StyleAttrs::resetValues () height = LENGTH_AUTO; minWidth = maxWidth = minHeight = maxHeight = LENGTH_AUTO; - margin.setVal (0); + margin.setVal (createAbsLength(0)); borderWidth.setVal (0); padding.setVal (0); setBorderColor (NULL); @@ -1167,16 +1167,17 @@ static void drawBorderRight(View *view, Style *style, */ void drawBorder (View *view, Layout *layout, Rectangle *area, int x, int y, int width, int height, + int marginLeft, int marginRight, Style *style, bool inverse) { /** \todo a lot! */ int xb1, yb1, xb2, yb2; // top left and bottom right point of outer border boundary - xb1 = x + style->margin.left; - yb1 = y + style->margin.top; - xb2 = x + (width > 0 ? width - 1 : 0) - style->margin.right; - yb2 = y + (height > 0 ? height - 1 : 0) - style->margin.bottom; + xb1 = x + marginLeft; + yb1 = y + style->marginTop(); + xb2 = x + (width > 0 ? width - 1 : 0) - marginRight; + yb2 = y + (height > 0 ? height - 1 : 0) - style->marginBottom(); /* // top left and bottom right point of inner border boundary diff --git a/dw/style.hh b/dw/style.hh index 8e2d4ca5b..9cfd63bdd 100644 --- a/dw/style.hh +++ b/dw/style.hh @@ -589,16 +589,38 @@ public: borderStyle.top = borderStyle.right = borderStyle.bottom = borderStyle.left = val; } - inline int boxOffsetX () - { return margin.left + borderWidth.left + padding.left; } - inline int boxRestWidth () - { return margin.right + borderWidth.right + padding.right; } - inline int boxDiffWidth () { return boxOffsetX () + boxRestWidth (); } - inline int boxOffsetY () - { return margin.top + borderWidth.top + padding.top; } - inline int boxRestHeight () - { return margin.bottom + borderWidth.bottom + padding.bottom; } - inline int boxDiffHeight () { return boxOffsetY () + boxRestHeight (); } + inline int marginLeft() + { + if (style::isAbsLength (margin.left)) { + return style::absLengthVal (margin.left); + } else { + return 0; + } + } + inline int marginRight() + { + if (style::isAbsLength (margin.right)) { + return style::absLengthVal (margin.right); + } else { + return 0; + } + } + inline int marginTop() + { + if (style::isAbsLength (margin.top)) { + return style::absLengthVal (margin.top); + } else { + return 0; + } + } + inline int marginBottom() + { + if (style::isAbsLength (margin.bottom)) { + return style::absLengthVal (margin.bottom); + } else { + return 0; + } + } inline bool hasBackground () { return backgroundColor != NULL || backgroundImage != NULL; } @@ -901,6 +923,7 @@ public: void drawBorder (View *view, Layout *layout, Rectangle *area, int x, int y, int width, int height, + int marginLeft, int marginRight, Style *style, bool inverse); void drawBackground (View *view, Layout *layout, Rectangle *area, int x, int y, int width, int height, diff --git a/dw/textblock.cc b/dw/textblock.cc index a1cbf2a39..b2a78ccbd 100644 --- a/dw/textblock.cc +++ b/dw/textblock.cc @@ -359,7 +359,7 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition, int numPos, // margin of the first line box: requisition->ascent = calcVerticalBorder (getStyle()->padding.top, getStyle()->borderWidth.top, - getStyle()->margin.top + getStyle()->marginTop() + extraSpace.top, firstLine->borderAscent, firstLine->marginAscent); @@ -373,7 +373,7 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition, int numPos, // for this case is not necessary.) calcVerticalBorder (getStyle()->padding.bottom, getStyle()->borderWidth.bottom, - getStyle()->margin.bottom + extraSpace.bottom, + getStyle()->marginBottom() + extraSpace.bottom, lastLine->borderDescent, lastLine->marginDescent); } else { requisition->width = leftInnerPadding + boxDiffWidth (); @@ -777,12 +777,12 @@ int Textblock::getAvailWidthOfChild (Widget *child, bool forceValue) /* Clamp to min-width and max-width if given, taking into * account leftInnerPadding. */ int maxWidth = child->calcWidth (child->getStyle()->maxWidth, - -1, this, -1, false); + -1, this, -1, false).total; if (maxWidth != -1 && width > maxWidth - leftInnerPadding) width = maxWidth - leftInnerPadding; int minWidth = child->calcWidth (child->getStyle()->minWidth, - -1, this, -1, false); + -1, this, -1, false).total; if (minWidth != -1 && width < minWidth - leftInnerPadding) width = minWidth - leftInnerPadding; } @@ -1583,7 +1583,7 @@ int Textblock::findLineIndexWhenNotAllocated (int y) return findLineIndex (y, calcVerticalBorder (getStyle()->padding.top, getStyle()->borderWidth.top, - getStyle()->margin.top + getStyle()->marginTop() + extraSpace.top, lines->getRef(0)->borderAscent, lines->getRef(0)->marginAscent)); @@ -2337,14 +2337,14 @@ bool Textblock::calcSizeOfWidgetInFlow (int wordIndex, Widget *widget, int rightBorder = boxRestWidth (); int lastMargin, yLine = yOffsetOfLineToBeCreated (&lastMargin); - int yRel = yLine - min (lastMargin, widget->getStyle()->margin.top); + int yRel = yLine - min (lastMargin, widget->getStyle()->marginTop()); DBG_OBJ_MSGF ("resize", 1, "leftBorder = %d + %d + (%d == 0 ? %d : 0) = %d, " "rightBorder = %d, yRel = %d - min (%d, %d) = %d", boxOffsetX (), leftInnerPadding , lines->size (), line1OffsetEff, leftBorder, rightBorder, yLine, lastMargin, - widget->getStyle()->margin.top, yRel); + widget->getStyle()->marginTop(), yRel); core::SizeParams childParams; DBG_OBJ_ASSOC_CHILD (&childParams); @@ -3328,14 +3328,14 @@ int Textblock::yOffsetOfLineToBeCreated (int *lastMargin) if (lines->size () == 0) { result = calcVerticalBorder (getStyle()->padding.top, getStyle()->borderWidth.top + extraSpace.top, - getStyle()->margin.top, 0, 0); + getStyle()->marginTop(), 0, 0); if (lastMargin) - *lastMargin = getStyle()->margin.top; + *lastMargin = getStyle()->marginTop(); } else { Line *firstLine = lines->getRef (0), *lastLine = lines->getLastRef (); result = calcVerticalBorder (getStyle()->padding.top, getStyle()->borderWidth.top, - getStyle()->margin.top + extraSpace.top, + getStyle()->marginTop() + extraSpace.top, firstLine->borderAscent, firstLine->marginAscent) - firstLine->borderAscent + lastLine->top + lastLine->totalHeight (0); @@ -3365,7 +3365,7 @@ int Textblock::yOffsetOfLineCreated (Line *line) Line *firstLine = lines->getRef (0); result = calcVerticalBorder (getStyle()->padding.top, getStyle()->borderWidth.top, - getStyle()->margin.top + extraSpace.top, + getStyle()->marginTop() + extraSpace.top, firstLine->borderAscent, firstLine->marginAscent) - firstLine->borderAscent + line->top; diff --git a/dw/textblock_iterator.cc b/dw/textblock_iterator.cc index b0c20eb2c..c30022db1 100644 --- a/dw/textblock_iterator.cc +++ b/dw/textblock_iterator.cc @@ -229,7 +229,7 @@ void Textblock::TextblockIterator::getAllocation (int start, int end, Word *w = textblock->words->getRef (i); int borderAscent = w->content.type == core::Content::WIDGET_IN_FLOW ? - w->size.ascent - w->content.widget->getStyle()->margin.top : + w->size.ascent - w->content.widget->getStyle()->marginTop() : w->size.ascent; lineBorderAscent = misc::max (lineBorderAscent, borderAscent); } diff --git a/dw/textblock_linebreaking.cc b/dw/textblock_linebreaking.cc index 1a95cb709..310d4757f 100644 --- a/dw/textblock_linebreaking.cc +++ b/dw/textblock_linebreaking.cc @@ -400,7 +400,7 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, // just before. The correction in sizeAllocateImpl() is irrelevant // in this regard. Also, right floats are not regarded here, but in // OutOfFlowMgr::getSize(), - lineWidth += (line->leftOffset - getStyle()->boxOffsetX ()); + lineWidth += (line->leftOffset - marginBoxOffsetX ()); if (lines->size () == 1) { // first line @@ -1589,9 +1589,9 @@ void Textblock::accumulateWordForLine (int lineIndex, int wordIndex) marginAscent = word->size.ascent; marginDescent = word->size.descent; borderAscent = - marginAscent - word->content.widget->getStyle()->margin.top; + marginAscent - word->content.widget->getStyle()->marginTop(); borderDescent = - marginDescent - word->content.widget->getStyle()->margin.bottom; + marginDescent - word->content.widget->getStyle()->marginBottom(); word->content.widget->parentRef = makeParentRefInFlow (lineIndex); DBG_OBJ_SET_NUM_O (word->content.widget, "parentRef", diff --git a/dw/widget.cc b/dw/widget.cc index 0b8328cfd..4d048a36f 100644 --- a/dw/widget.cc +++ b/dw/widget.cc @@ -97,6 +97,7 @@ Widget::Widget () allocation.descent = 0; extraSpace.top = extraSpace.right = extraSpace.bottom = extraSpace.left = 0; + margin.top = margin.right = margin.bottom = margin.left = 0; style = NULL; bgColor = NULL; @@ -883,22 +884,22 @@ void Widget::correctExtremes (Extremes *extremes, bool useAdjustmentWidth) layout->viewportWidth - (layout->canvasHeightGreater ? layout->vScrollbarThickness : 0); - int width = calcWidth (getStyle()->width, viewportWidth, NULL, - limitMinWidth, false); - int minWidth = calcWidth (getStyle()->minWidth, viewportWidth, NULL, - limitMinWidth, false); - int maxWidth = calcWidth (getStyle()->maxWidth, viewportWidth, NULL, - limitMinWidth, false); + BoxWidth width = calcWidth (getStyle()->width, viewportWidth, NULL, + limitMinWidth, false); + BoxWidth minWidth = calcWidth (getStyle()->minWidth, viewportWidth, NULL, + limitMinWidth, false); + BoxWidth maxWidth = calcWidth (getStyle()->maxWidth, viewportWidth, NULL, + limitMinWidth, false); DBG_OBJ_MSGF ("resize", 1, "width = %d, minWidth = %d, maxWidth = %d", width, minWidth, maxWidth); - if (width != -1) - extremes->minWidth = extremes->maxWidth = width; - if (minWidth != -1) - extremes->minWidth = minWidth; - if (maxWidth != -1) - extremes->maxWidth = maxWidth; + if (width.total != -1) + extremes->minWidth = extremes->maxWidth = width.total; + if (minWidth.total != -1) + extremes->minWidth = minWidth.total; + if (maxWidth.total != -1) + extremes->maxWidth = maxWidth.total; DBG_OBJ_MSG_END (); } else if (parent) { @@ -929,43 +930,81 @@ void Widget::correctExtremes (Extremes *extremes, bool useAdjustmentWidth) * than limitMinWidth. * */ -int Widget::calcWidth (style::Length cssValue, int refWidth, Widget *refWidget, - int limitMinWidth, bool forceValue) +BoxWidth Widget::calcWidth (style::Length cssValue, int refWidth, Widget *refWidget, + int limitMinWidth, bool forceValue) { DBG_OBJ_ENTER ("resize", 0, "calcWidth", "0x%x, %d, %p, %d", cssValue, refWidth, refWidget, limitMinWidth); assert (refWidth != -1 || refWidget != NULL); - int width; + int contentWidth; + int width = 0; if (style::isAbsLength (cssValue)) { DBG_OBJ_MSGF ("resize", 1, "absolute width: %dpx", style::absLengthVal (cssValue)); - width = misc::max (style::absLengthVal (cssValue) + boxDiffWidth (), - limitMinWidth); + contentWidth = style::absLengthVal (cssValue); } else if (style::isPerLength (cssValue)) { DBG_OBJ_MSGF ("resize", 1, "percentage width: %g%%", 100 * style::perLengthVal_useThisOnlyForDebugging (cssValue)); - if (refWidth != -1) - width = misc::max (applyPerWidth (refWidth, cssValue), limitMinWidth); - else { - int availWidth = refWidget->getAvailWidth (forceValue); - if (availWidth != -1) { - int containerWidth = availWidth - refWidget->boxDiffWidth (); - width = misc::max (applyPerWidth (containerWidth, cssValue), - limitMinWidth); - } else + if (refWidth != -1) { + contentWidth = style::multiplyWithPerLength (refWidth, cssValue); + } else { + int containerWidth = refWidget->contentWidth (forceValue); + if (containerWidth != -1) { + contentWidth = style::multiplyWithPerLength (containerWidth, cssValue); + } else { width = -1; + } } } else { DBG_OBJ_MSG ("resize", 1, "not specified"); width = -1; } + int marginLeft = 0, marginRight = 0; + + if (width != -1) { + int expandableWidth = 0; + if (refWidth != -1) { + expandableWidth = refWidth; + } else { + expandableWidth = refWidget->contentWidth (forceValue); + } + + expandableWidth = misc::max (expandableWidth - contentWidth, 0); + + if (style->margin.left == style::LENGTH_AUTO && style->margin.right == style::LENGTH_AUTO) { + marginLeft = expandableWidth / 2; + marginRight = expandableWidth / 2; + } else if (style->margin.left == style::LENGTH_AUTO) { + marginLeft = expandableWidth; + marginRight = style->marginRight(); + } else if (style->margin.right == style::LENGTH_AUTO) { + marginLeft = style->marginLeft(); + marginRight = expandableWidth; + } else { + marginLeft = style->marginLeft(); + marginRight = style->marginRight(); + } + + width = misc::max (contentWidth + extraSpace.left + extraSpace.right + + getStyle()->borderWidth.left + getStyle()->padding.left + + getStyle()->borderWidth.right + getStyle()->padding.right + + marginLeft + marginRight, limitMinWidth); + } else { + marginLeft = style->marginLeft(); + marginRight = style->marginRight(); + } + DBG_OBJ_LEAVE_VAL ("%d", width); - return width; + return BoxWidth { + width, + marginLeft, + marginRight, + }; } /** @@ -996,40 +1035,52 @@ void Widget::calcFinalWidth (style::Style *style, int refWidth, refWidth, refWidget, limitMinWidth, *finalWidth); int w = *finalWidth; - int width = calcWidth (style->width, refWidth, refWidget, limitMinWidth, - forceValue); + BoxWidth width = calcWidth (style->width, refWidth, refWidget, limitMinWidth, + forceValue); DBG_OBJ_MSGF ("resize", 1, "w = %d, width = %d", w, width); - if (width != -1) - w = width; + if (width.total != -1) + w = width.total; + + int marginLeft = width.marginLeft; + int marginRight = width.marginRight; /* Only correct w if not set to auto (-1) */ if (w != -1) { - int minWidth = calcWidth (style->minWidth, refWidth, refWidget, - limitMinWidth, forceValue); - int maxWidth = calcWidth (style->maxWidth, refWidth, refWidget, - limitMinWidth, forceValue); + BoxWidth minWidth = calcWidth (style->minWidth, refWidth, refWidget, + limitMinWidth, forceValue); + BoxWidth maxWidth = calcWidth (style->maxWidth, refWidth, refWidget, + limitMinWidth, forceValue); DBG_OBJ_MSGF ("resize", 1, "minWidth = %d, maxWidth = %d", - minWidth, maxWidth); + minWidth.total, maxWidth.total); - if (minWidth != -1 && maxWidth != -1) { + if (minWidth.total != -1 && maxWidth.total != -1) { /* Prefer the maximum size for pathological cases (min > max) */ - if (maxWidth < minWidth) + if (maxWidth.total < minWidth.total) { maxWidth = minWidth; + } } - if (minWidth != -1 && w < minWidth) - w = minWidth; + if (minWidth.total != -1 && w < minWidth.total) { + w = minWidth.total; + marginLeft = minWidth.marginLeft; + marginRight = minWidth.marginRight; + } - if (maxWidth != -1 && w > maxWidth) - w = maxWidth; + if (maxWidth.total != -1 && w > maxWidth.total) { + w = maxWidth.total; + marginLeft = maxWidth.marginLeft; + marginRight = maxWidth.marginRight; + } } /* Check postcondition: *finalWidth != -1 (implies) w != -1 */ assert(!(*finalWidth != -1 && w == -1)); *finalWidth = w; + margin.left = marginLeft; + margin.right = marginRight; DBG_OBJ_LEAVE_VAL ("%d", *finalWidth); } @@ -1365,10 +1416,10 @@ void Widget::setStyle (style::Style *style) // script processing RTFL messages could transfer it to something // equivalent: - DBG_OBJ_SET_NUM ("style.margin.top", style->margin.top); - DBG_OBJ_SET_NUM ("style.margin.bottom", style->margin.bottom); - DBG_OBJ_SET_NUM ("style.margin.left", style->margin.left); - DBG_OBJ_SET_NUM ("style.margin.right", style->margin.right); + DBG_OBJ_SET_NUM ("style.margin.top", style->marginTop()); + DBG_OBJ_SET_NUM ("style.margin.bottom", style->marginBottom()); + DBG_OBJ_SET_NUM ("style.margin.left", style->marginLeft()); + DBG_OBJ_SET_NUM ("style.margin.right", style->marginRight()); DBG_OBJ_SET_NUM ("style.border-width.top", style->borderWidth.top); DBG_OBJ_SET_NUM ("style.border-width.bottom", style->borderWidth.bottom); @@ -1479,7 +1530,7 @@ void Widget::drawBox (View *view, style::Style *style, Rectangle *area, style::drawBorder (view, layout, &canvasArea, allocation.x + x, allocation.y + y, - width, height, style, inverse); + width, height, margin.left, margin.right, style, inverse); // This method is used for inline elements, where the CSS 2 specification // does not define what here is called "reference area". To make it look @@ -1493,12 +1544,12 @@ void Widget::drawBox (View *view, style::Style *style, Rectangle *area, getPaddingArea (&xPad, &yPad, &widthPad, &heightPad); style::drawBackground (view, layout, &canvasArea, - allocation.x + x + style->margin.left + style->borderWidth.left, - allocation.y + y + style->margin.top + style->borderWidth.top, - width - style->margin.left - style->borderWidth.left + allocation.x + x + margin.left + style->borderWidth.left, + allocation.y + y + style->marginTop() + style->borderWidth.top, + width - margin.left - style->borderWidth.left - style->margin.right - style->borderWidth.right, - height - style->margin.top - style->borderWidth.top - - style->margin.bottom - style->borderWidth.bottom, + height - style->marginTop() - style->borderWidth.top + - style->marginBottom() - style->borderWidth.bottom, xPad, yPad, widthPad, heightPad, style, style->backgroundColor, inverse, false); } @@ -1520,7 +1571,7 @@ void Widget::drawWidgetBox (View *view, Rectangle *area, bool inverse) int xMar, yMar, widthMar, heightMar; getMarginArea (&xMar, &yMar, &widthMar, &heightMar); style::drawBorder (view, layout, &canvasArea, xMar, yMar, widthMar, - heightMar, style, inverse); + heightMar, margin.left, margin.right, style, inverse); int xPad, yPad, widthPad, heightPad; getPaddingArea (&xPad, &yPad, &widthPad, &heightPad); @@ -1539,7 +1590,6 @@ void Widget::drawWidgetBox (View *view, Rectangle *area, bool inverse) bgColor = layout->getBgColor (); } else bgColor = style->backgroundColor; - style::drawBackground (view, layout, &canvasArea, xPad, yPad, widthPad, heightPad, xPad, yPad, widthPad, heightPad, @@ -1675,10 +1725,10 @@ void Widget::getBorderArea (int *xBor, int *yBor, int *widthBor, int *heightBor) { getMarginArea (xBor, yBor, widthBor, heightBor); - *xBor += style->margin.left; - *yBor += style->margin.top; - *widthBor -= style->margin.left + style->margin.right; - *heightBor -= style->margin.top + style->margin.bottom; + *xBor += margin.left; + *yBor += style->marginTop(); + *widthBor -= margin.left + margin.right; + *heightBor -= style->marginTop() + style->marginBottom(); } /** @@ -1797,12 +1847,12 @@ int Widget::getAvailWidthOfChild (Widget *child, bool forceValue) if (width != -1) { /* Clamp to min-width and max-width if given */ int maxWidth = child->calcWidth (child->getStyle()->maxWidth, - -1, this, -1, false); + -1, this, -1, false).total; if (maxWidth != -1 && width > maxWidth) width = maxWidth; int minWidth = child->calcWidth (child->getStyle()->minWidth, - -1, this, -1, false); + -1, this, -1, false).total; if (minWidth != -1 && width < minWidth) width = minWidth; } @@ -2143,11 +2193,11 @@ void Widget::correctExtremesOfChild (Widget *child, Extremes *extremes, int limitMinWidth = useAdjustmentWidth ? child->getMinWidth (extremes, false) : 0; int width = child->calcWidth (child->getStyle()->width, -1, this, - limitMinWidth, false); + limitMinWidth, false).total; int minWidth = child->calcWidth (child->getStyle()->minWidth, -1, this, - limitMinWidth, false); + limitMinWidth, false).total; int maxWidth = child->calcWidth (child->getStyle()->maxWidth, -1, this, - limitMinWidth, false); + limitMinWidth, false).total; DBG_OBJ_MSGF ("resize", 1, "width = %d, minWidth = %d, maxWidth = %d", width, minWidth, maxWidth); diff --git a/dw/widget.hh b/dw/widget.hh index 3149c87b3..74b944564 100644 --- a/dw/widget.hh +++ b/dw/widget.hh @@ -27,6 +27,12 @@ #include "../lout/identity.hh" +typedef struct { + int total; + int marginLeft; + int marginRight; +} BoxWidth; + /** * \brief The type for callback functions. */ @@ -225,6 +231,8 @@ protected: */ style::Box extraSpace; + style::Box margin; + /** * \brief Set iff this widget constitutes a stacking context, as defined by * CSS. @@ -488,17 +496,42 @@ public: y <= allocation.y + getHeight (); } + int marginBoxOffsetX () + { return getStyle()->borderWidth.left + getStyle()->padding.left + margin.left; } + int marginBoxRestWidth () + { return getStyle()->borderWidth.right + getStyle()->padding.right + margin.right; } + int marginBoxDiffWidth () + { return marginBoxOffsetX() + marginBoxRestWidth(); } + // TODO: replace marginTop() with margin.top once it's populated + int marginBoxOffsetY () + { return getStyle()->borderWidth.top + getStyle()->padding.top + getStyle()->marginTop(); } + // TODO: replace marginBottom() with margin.bottom once it's populated + int marginBoxRestHeight () + { return getStyle()->borderWidth.bottom + getStyle()->padding.bottom + getStyle()->marginBottom(); } + int marginBoxDiffHeight () + { return marginBoxOffsetY() + marginBoxRestHeight(); } + inline int boxOffsetX () - { return extraSpace.left + getStyle()->boxOffsetX (); } + { return extraSpace.left + marginBoxOffsetX(); } inline int boxRestWidth () - { return extraSpace.right + getStyle()->boxRestWidth (); } + { return extraSpace.right + marginBoxRestWidth(); } inline int boxDiffWidth () { return boxOffsetX () + boxRestWidth (); } inline int boxOffsetY () - { return extraSpace.top + getStyle()->boxOffsetY (); } + { return extraSpace.top + marginBoxOffsetY(); } inline int boxRestHeight () - { return extraSpace.bottom + getStyle()->boxRestHeight (); } + { return extraSpace.bottom + marginBoxRestHeight(); } inline int boxDiffHeight () { return boxOffsetY () + boxRestHeight (); } - + + inline int contentWidth(bool forceValue) + { + int width = -1; + int availWidth = getAvailWidth (forceValue); + if (availWidth != -1) { + width = availWidth - boxDiffWidth (); + } + return width; + } + /** * \brief See \ref dw-widget-sizes (or \ref dw-size-request-pos). */ @@ -534,8 +567,8 @@ public: void (*splitHeightFun) (int, int*, int*), bool allowDecreaseWidth, bool allowDecreaseHeight); void correctExtremes (Extremes *extremes, bool useAdjustmentWidth); - int calcWidth (style::Length cssValue, int refWidth, Widget *refWidget, - int limitMinWidth, bool forceValue); + BoxWidth calcWidth (style::Length cssValue, int refWidth, Widget *refWidget, + int limitMinWidth, bool forceValue); void calcFinalWidth (style::Style *style, int refWidth, Widget *refWidget, int limitMinWidth, bool forceValue, int *finalWidth); int calcHeight (style::Length cssValue, bool usePercentage, int refHeight, diff --git a/src/styleengine.cc b/src/styleengine.cc index ef8ab3c02..98559b449 100644 --- a/src/styleengine.cc +++ b/src/styleengine.cc @@ -635,24 +635,28 @@ void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props, attrs->listStyleType = (ListStyleType) p->value.intVal; break; case CSS_PROPERTY_MARGIN_BOTTOM: - computeValue (&attrs->margin.bottom, p->value.lenVal, attrs->font); - if (attrs->margin.bottom < 0) // \todo fix negative margins in dw/* - attrs->margin.bottom = 0; + computeLength (&attrs->margin.bottom, p->value.lenVal, attrs->font); + // \todo fix negative margins in dw/* + if (isAbsLength(attrs->margin.bottom) && absLengthVal(attrs->margin.bottom) < 0) + attrs->margin.bottom = createAbsLength(0); break; case CSS_PROPERTY_MARGIN_LEFT: - computeValue (&attrs->margin.left, p->value.lenVal, attrs->font); - if (attrs->margin.left < 0) // \todo fix negative margins in dw/* - attrs->margin.left = 0; + computeLength (&attrs->margin.left, p->value.lenVal, attrs->font); + // \todo fix negative margins in dw/* + if (isAbsLength(attrs->margin.left) && absLengthVal(attrs->margin.left) < 0) + attrs->margin.left = createAbsLength(0); break; case CSS_PROPERTY_MARGIN_RIGHT: - computeValue (&attrs->margin.right, p->value.lenVal, attrs->font); - if (attrs->margin.right < 0) // \todo fix negative margins in dw/* - attrs->margin.right = 0; + computeLength (&attrs->margin.right, p->value.lenVal, attrs->font); + // \todo fix negative margins in dw/* + if (isAbsLength(attrs->margin.right) && absLengthVal(attrs->margin.right) < 0) + attrs->margin.right = createAbsLength(0); break; case CSS_PROPERTY_MARGIN_TOP: - computeValue (&attrs->margin.top, p->value.lenVal, attrs->font); - if (attrs->margin.top < 0) // \todo fix negative margins in dw/* - attrs->margin.top = 0; + computeLength (&attrs->margin.top, p->value.lenVal, attrs->font); + // \todo fix negative margins in dw/* + if (isAbsLength(attrs->margin.top) && absLengthVal(attrs->margin.top) < 0) + attrs->margin.top = createAbsLength(0); break; case CSS_PROPERTY_OVERFLOW: attrs->overflow = (Overflow) p->value.intVal; diff --git a/src/table.cc b/src/table.cc index 6d1443801..e6506c287 100644 --- a/src/table.cc +++ b/src/table.cc @@ -338,7 +338,7 @@ static void Html_set_collapsing_border_model(DilloHtml *html, Widget *col_tb) marginWidth = tableStyle->margin.top; collapseCellAttrs = *(html->style ()); - collapseCellAttrs.margin.setVal (0); + collapseCellAttrs.margin.setVal (createAbsLength(0)); collapseCellAttrs.borderWidth.left = 0; collapseCellAttrs.borderWidth.top = 0; collapseCellAttrs.borderWidth.right = borderWidth; @@ -378,7 +378,7 @@ static void Html_set_separate_border_model(DilloHtml *html, Widget *col_tb) separateCellAttrs = *(html->style ()); /* CSS2 17.5: Internal table elements do not have margins */ - separateCellAttrs.margin.setVal (0); + separateCellAttrs.margin.setVal (createAbsLength(0)); separateStyle = Style::create(&separateCellAttrs); col_tb->setStyle (separateStyle); } diff --git a/test/html/Makefile.am b/test/html/Makefile.am index 044d54373..f85661941 100644 --- a/test/html/Makefile.am +++ b/test/html/Makefile.am @@ -50,7 +50,6 @@ TESTS = \ XFAIL_TESTS = \ render/div-100-percent-with-padding.html \ render/float-img-justify.html \ - render/margin-auto.html \ render/max-width-html.html \ render/min-width-html.html \ render/span-padding.html \ From c84f430b57c7a752ec74c27dce3a1ee80de2521a Mon Sep 17 00:00:00 2001 From: Cameron Paul Date: Mon, 2 Jun 2025 17:32:25 -0500 Subject: [PATCH 2/4] Add test for max-width and margin auto --- test/html/Makefile.am | 2 ++ test/html/render/margin-auto-max-width.html | 16 ++++++++++++++++ test/html/render/margin-auto-max-width.ref.html | 16 ++++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 test/html/render/margin-auto-max-width.html create mode 100644 test/html/render/margin-auto-max-width.ref.html diff --git a/test/html/Makefile.am b/test/html/Makefile.am index f85661941..06a8e88dc 100644 --- a/test/html/Makefile.am +++ b/test/html/Makefile.am @@ -25,6 +25,7 @@ TESTS = \ render/img-aspect-ratio.html \ render/main-style.html \ render/margin-auto.html \ + render/margin-auto-max-width.html \ render/max-width-body.html \ render/max-width-div-clamp.html \ render/max-width-div.html \ @@ -50,6 +51,7 @@ TESTS = \ XFAIL_TESTS = \ render/div-100-percent-with-padding.html \ render/float-img-justify.html \ + render/margin-auto-max-width.html \ render/max-width-html.html \ render/min-width-html.html \ render/span-padding.html \ diff --git a/test/html/render/margin-auto-max-width.html b/test/html/render/margin-auto-max-width.html new file mode 100644 index 000000000..b3a4d6b6f --- /dev/null +++ b/test/html/render/margin-auto-max-width.html @@ -0,0 +1,16 @@ + + + + Test CSS margin auto + + + +
+
+
+ + diff --git a/test/html/render/margin-auto-max-width.ref.html b/test/html/render/margin-auto-max-width.ref.html new file mode 100644 index 000000000..f513977f1 --- /dev/null +++ b/test/html/render/margin-auto-max-width.ref.html @@ -0,0 +1,16 @@ + + + + Test CSS margin auto + + + +
+
+
+ + From 8549e08385180133904fdae4ea7e576b357424de Mon Sep 17 00:00:00 2001 From: Cameron Paul Date: Wed, 18 Jun 2025 10:40:09 -0500 Subject: [PATCH 3/4] Fix accidental use of style->margin.right --- dw/widget.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dw/widget.cc b/dw/widget.cc index 4d048a36f..e9aa4b00f 100644 --- a/dw/widget.cc +++ b/dw/widget.cc @@ -1547,7 +1547,7 @@ void Widget::drawBox (View *view, style::Style *style, Rectangle *area, allocation.x + x + margin.left + style->borderWidth.left, allocation.y + y + style->marginTop() + style->borderWidth.top, width - margin.left - style->borderWidth.left - - style->margin.right - style->borderWidth.right, + - margin.right - style->borderWidth.right, height - style->marginTop() - style->borderWidth.top - style->marginBottom() - style->borderWidth.bottom, xPad, yPad, widthPad, heightPad, style, style->backgroundColor, From 5554ca72e8caf25409218c1265c75a86e4622736 Mon Sep 17 00:00:00 2001 From: Cameron Paul Date: Wed, 18 Jun 2025 11:19:06 -0500 Subject: [PATCH 4/4] Fix "margin: auto" for elements with max-width --- dw/widget.cc | 16 ++++++++++++---- test/html/Makefile.am | 1 - 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/dw/widget.cc b/dw/widget.cc index e9aa4b00f..49ef1d982 100644 --- a/dw/widget.cc +++ b/dw/widget.cc @@ -1039,11 +1039,19 @@ void Widget::calcFinalWidth (style::Style *style, int refWidth, forceValue); DBG_OBJ_MSGF ("resize", 1, "w = %d, width = %d", w, width); - if (width.total != -1) - w = width.total; + int marginLeft; + int marginRight; - int marginLeft = width.marginLeft; - int marginRight = width.marginRight; + if (width.total != -1) { + w = width.total; + marginLeft = width.marginLeft; + marginRight = width.marginRight; + } else { + BoxWidth maxWidth = calcWidth (style->maxWidth, refWidth, refWidget, + limitMinWidth, forceValue); + marginLeft = maxWidth.marginLeft; + marginRight = maxWidth.marginRight; + } /* Only correct w if not set to auto (-1) */ if (w != -1) { diff --git a/test/html/Makefile.am b/test/html/Makefile.am index 06a8e88dc..f5ef2c600 100644 --- a/test/html/Makefile.am +++ b/test/html/Makefile.am @@ -51,7 +51,6 @@ TESTS = \ XFAIL_TESTS = \ render/div-100-percent-with-padding.html \ render/float-img-justify.html \ - render/margin-auto-max-width.html \ render/max-width-html.html \ render/min-width-html.html \ render/span-padding.html \