diff options
37 files changed, 2233 insertions, 1540 deletions
diff --git a/doc/dw-grows.doc b/doc/dw-grows.doc index 32800c19..8c2941dd 100644 --- a/doc/dw-grows.doc +++ b/doc/dw-grows.doc @@ -14,6 +14,103 @@ calculating widget sizes. Goals are: - *display: inline-block*; - <button>. -(...) -*/
\ No newline at end of file +A short sketch +============== + +**dw::core::Widget::sizeRequest and dw::core::Widget::getExtremes will +return final results.** The caller does not have to correct the size, +e. g. when percentages are defined. As an example, +dw::Textblock::calcWidgetSize has already become much simpler. + +**A new hierarchy, *container*:** Aside from dw::core::Widget::parent +and dw::core::Widget::generator, there is a third hierarchy +dw::core::Widget::container, which is (unlike *generator*) always a +direct ancestor, and represents what in CSS is called *containing +block*. Containers are important to define the "context size", which +is (not solely) used for percentage sizes. + +(There is another "containing block", dw::Textblock::containingBlock; +these may be consolidated some day.) + +**The process of size calculation is split between the widget itself +and its container:** + +- The container provides some abstract methods: + dw::core::Widget::getAvailWidthOfChild, + dw::core::Widget::getAvailHeightOfChild, + dw::core::Widget::correctRequisitionOfChild, and + dw::core::Widget::correctExtremesOfChild, which can be used in the + actual implementation of dw::core::Widget::sizeRequestImpl; + different containers with different ways how to arrange their + children will implement these methods in a different way. (Simple + example: the *available width* for children within a textblock is + the *available width* for the textblock itself, minus + margin/border/padding; on the other hand, it is completely different + for children of tables, for which a complex column width calculation + is used.) + +- The actual size calculation is, however, controlled by the widget + itself, which only *uses* these methods above. + +<div style="border: 2px solid #ffff00; margin-top: 0.5em; + margin-bottom: 0.5em; padding: 0.5em 1em; background-color: #ffffe0"> + <b>Update:</b> This is not fully correct; the parents are also involved + for calculating available widths and heights, at least when CSS 'width' + and 'height' are not set.</div> + +**Size hints are removed.** Instead, the container methods in the +previous paragraph are used. Changes of container sizes (especially +viewport the size) are handled in a different way. + +**Extremes are extended by intrinsic values.** In some cases (see +dw::Table::forceCalcCellSizes, case *minWidth* > *totalWidth*, for an +example) it is useful to know about minimal and maximal width of a +widget independent of CSS attributes. For this, dw::core::Extremes is +extended by: + +- dw::core::Extremes::minWidthIntrinsic and +- dw::core::Extremes::maxWidthIntrinsic. + +The rules for the calculation: + +1. If a widget has no children, it calculates *minWidthIntrinsic* and + *maxWidthIntrinsic* as those values not affected by CSS hints. + (dw::core::Widget::correctExtremes will not change these values.) +2. A widget must calculate *minWidthIntrinsic* and *maxWidthIntrinsic* + from *minWidthIntrinsic* and *maxWidthIntrinsic* of its children, + and *minWidth* and *maxWidth* from *minWidth* and *maxWidth* of its + children. +3. At the end, *minWidth* and *maxWidth* of a widget are corrected by + CSS attributes. (dw::core::Widget::correctExtremes will do this.) + +<div style="border: 2px solid #ffff00; margin-top: 0.5em; + margin-bottom: 0.5em; padding: 0.5em 1em; background-color: #ffffe0"> + <b>Notice:</b> Currently, dw::core::Widget::getExtremesImpl must + set all four members in dw::core::Extremes; this may change.</div> + +Open issues +=========== + +**Do CSS size dimensions override intrinsic sizes in all cases?** If a +textblock needs at least, say, 100 pixels width so that the text can +be read, but has a specification "width: 50px", should half of the +text be invisible? Or should the width be corrected again to 100 +pixels? + +Currently, in the CSS size specification is honoured in all cases, +with one exception: see dw::Textblock::sizeRequestImpl and see +dw::Textblock::getExtremesImpl (the time when +dw::core::Widget::correctRequisition and +dw::core::Widget::correctExtremes, respectively, is called). + +*Not* honouring the CSS size specification in all cases could improve +readability in some cases, so this could depend on a user preference. + +**Percentage values for margins and paddings, as well as negative +margins** are interesting applications, but have not been considered +yet. For negative margins, a new attribute +dw::core::Widget::extraSpace could solve the problem of widgets +sticking out of the allocation of parent. + +*/ diff --git a/doc/dw-widget-sizes.doc b/doc/dw-widget-sizes.doc index a9b6b00c..a82d3b99 100644 --- a/doc/dw-widget-sizes.doc +++ b/doc/dw-widget-sizes.doc @@ -1,5 +1,9 @@ /** \page dw-widget-sizes Sizes of Dillo Widgets +<div style="border: 2px solid #ff4040; margin-bottom: 0.5em; +padding: 0.5em 1em; background-color: #fff0f0"><b>Info:</b> +Not up to date, see \ref dw-grows.</div> + Allocation ========== @@ -87,23 +91,9 @@ the image buffer when allocated at another size.) Size Hints ========== -Some widgets do not have an inherent size, but depend on the context, -e.g. the viewport size. These widgets should adhere to <i>size hints</i>, -i.e. implement the methods dw::core::Widget::setWidth, -dw::core::Widget::setAscent and dw::core::Widget::setDescent. The values -passed to the callees are - -- the viewport size (ascent is the heigt here, while descent is 0) for - the toplevel widget, and -- determined by the parent for its child widgets. - -Generally, the values should define the available space for the -widget. - -A widget, which depends on size hints, should call -dw::core::Widget::queueResize, when apropriate. - -\todo There should be a definition of "available space". +<div style="border: 2px solid #ff4040; margin-bottom: 0.5em; +padding: 0.5em 1em; background-color: #fff0f0"><b>Info:</b> +Size hints have been removed, see \ref dw-grows.</div> Width Extremes diff --git a/dw/Makefile.am b/dw/Makefile.am index 5306c5a5..25d8bbce 100644 --- a/dw/Makefile.am +++ b/dw/Makefile.am @@ -57,6 +57,8 @@ libDw_fltk_a_SOURCES = \ libDw_fltk_a_CXXFLAGS = @LIBFLTK_CXXFLAGS@ libDw_widgets_a_SOURCES = \ + alignedtablecell.cc \ + alignedtablecell.hh \ alignedtextblock.cc \ alignedtextblock.hh \ bullet.cc \ @@ -71,10 +73,11 @@ libDw_widgets_a_SOURCES = \ outofflowmgr.hh \ ruler.cc \ ruler.hh \ + simpletablecell.cc \ + simpletablecell.hh \ table.cc \ + table_iterator.cc \ table.hh \ - tablecell.cc \ - tablecell.hh \ textblock.cc \ textblock_iterator.cc \ textblock_linebreaking.cc \ diff --git a/dw/tablecell.cc b/dw/alignedtablecell.cc index cbd5e0bf..a9f091f3 100644 --- a/dw/tablecell.cc +++ b/dw/alignedtablecell.cc @@ -19,19 +19,19 @@ -#include "tablecell.hh" +#include "alignedtablecell.hh" #include "../lout/debug.hh" #include <stdio.h> namespace dw { -int TableCell::CLASS_ID = -1; +int AlignedTableCell::CLASS_ID = -1; -TableCell::TableCell (TableCell *ref, bool limitTextWidth): +AlignedTableCell::AlignedTableCell (AlignedTableCell *ref, bool limitTextWidth): AlignedTextblock (limitTextWidth) { - DBG_OBJ_CREATE ("dw::TableCell"); - registerName ("dw::TableCell", &CLASS_ID); + DBG_OBJ_CREATE ("dw::AlignedTableCell"); + registerName ("dw::AlignedTableCell", &CLASS_ID); /** \bug ignoreLine1OffsetSometimes does not work? */ //ignoreLine1OffsetSometimes = true; @@ -40,12 +40,29 @@ TableCell::TableCell (TableCell *ref, bool limitTextWidth): setButtonSensitive(true); } -TableCell::~TableCell() +AlignedTableCell::~AlignedTableCell() { DBG_OBJ_DELETE (); } -int TableCell::wordWrap(int wordIndex, bool wrapAll) +bool AlignedTableCell::isBlockLevel () +{ + return false; +} + +int AlignedTableCell::applyPerWidth (int containerWidth, + core::style::Length perWidth) +{ + return core::style::multiplyWithPerLength (containerWidth, perWidth); +} + +int AlignedTableCell::applyPerHeight (int containerHeight, + core::style::Length perHeight) +{ + return core::style::multiplyWithPerLength (containerHeight, perHeight); +} + +int AlignedTableCell::wordWrap(int wordIndex, bool wrapAll) { Textblock::Word *word; const char *p; @@ -73,7 +90,7 @@ int TableCell::wordWrap(int wordIndex, bool wrapAll) return ret; } -int TableCell::getValue () +int AlignedTableCell::getValue () { Textblock::Word *word; int i, wordIndex; @@ -104,7 +121,7 @@ int TableCell::getValue () return w; } -void TableCell::setMaxValue (int maxValue, int value) +void AlignedTableCell::setMaxValue (int maxValue, int value) { line1Offset = maxValue - value; queueResize (OutOfFlowMgr::createRefNormalFlow (0), true); diff --git a/dw/alignedtablecell.hh b/dw/alignedtablecell.hh new file mode 100644 index 00000000..caf59307 --- /dev/null +++ b/dw/alignedtablecell.hh @@ -0,0 +1,34 @@ +#ifndef __DW_ALIGNEDTABLECELL_HH__ +#define __DW_ALIGNEDTABLECELL_HH__ + +#include "core.hh" +#include "alignedtextblock.hh" + +namespace dw { + +class AlignedTableCell: public AlignedTextblock +{ +private: + int charWordIndex, charWordPos; + +protected: + int wordWrap (int wordIndex, bool wrapAll); + + int getValue (); + void setMaxValue (int maxValue, int value); + +public: + static int CLASS_ID; + + AlignedTableCell(AlignedTableCell *ref, bool limitTextWidth); + ~AlignedTableCell(); + + int applyPerWidth (int containerWidth, core::style::Length perWidth); + int applyPerHeight (int containerHeight, core::style::Length perHeight); + + bool isBlockLevel (); +}; + +} // namespace dw + +#endif // __DW_ALIGNEDTABLECELL_HH__ diff --git a/dw/bullet.cc b/dw/bullet.cc index af7f5451..b74f836c 100644 --- a/dw/bullet.cc +++ b/dw/bullet.cc @@ -27,6 +27,12 @@ namespace dw { Bullet::Bullet () { + DBG_OBJ_CREATE ("dw::Bullet"); +} + +Bullet::~Bullet () +{ + DBG_OBJ_DELETE (); } void Bullet::sizeRequestImpl (core::Requisition *requisition) @@ -36,6 +42,11 @@ void Bullet::sizeRequestImpl (core::Requisition *requisition) requisition->descent = 0; } +void Bullet::containerSizeChangedForChildren () +{ + // Nothing to do. +} + void Bullet::draw (core::View *view, core::Rectangle *area) { int x, y, l; diff --git a/dw/bullet.hh b/dw/bullet.hh index 98854abb..97a4909d 100644 --- a/dw/bullet.hh +++ b/dw/bullet.hh @@ -15,11 +15,13 @@ class Bullet: public core::Widget { protected: void sizeRequestImpl (core::Requisition *requisition); + void containerSizeChangedForChildren (); void draw (core::View *view, core::Rectangle *area); core::Iterator *iterator (core::Content::Type mask, bool atEnd); public: Bullet (); + ~Bullet (); }; } // namespace dw diff --git a/dw/image.cc b/dw/image.cc index e71c8f2f..ae48961f 100644 --- a/dw/image.cc +++ b/dw/image.cc @@ -170,45 +170,50 @@ Image::~Image() void Image::sizeRequestImpl (core::Requisition *requisition) { + DBG_OBJ_MSG ("resize", 0, "<b>sizeRequestImpl</b> ()"); + DBG_OBJ_MSG_START (); + if (buffer) { - if (getStyle ()->height == core::style::LENGTH_AUTO && - core::style::isAbsLength (getStyle ()->width) && - buffer->getRootWidth () > 0) { - // preserve aspect ratio when only width is given - requisition->width = core::style::absLengthVal (getStyle ()->width); - requisition->ascent = buffer->getRootHeight () * - requisition->width / buffer->getRootWidth (); - } else if (getStyle ()->width == core::style::LENGTH_AUTO && - core::style::isAbsLength (getStyle ()->height) && - buffer->getRootHeight () > 0) { - // preserve aspect ratio when only height is given - requisition->ascent = core::style::absLengthVal (getStyle ()->height); - requisition->width = buffer->getRootWidth () * - requisition->ascent / buffer->getRootHeight (); - } else { - requisition->width = buffer->getRootWidth (); - requisition->ascent = buffer->getRootHeight (); - } - requisition->descent = 0; - } else { - if (altText && altText[0]) { - if (altTextWidth == -1) - altTextWidth = - layout->textWidth (getStyle()->font, altText, strlen (altText)); + requisition->width = buffer->getRootWidth (); + requisition->ascent = buffer->getRootHeight (); + } else + requisition->width = requisition->ascent = 0; + + requisition->width += boxDiffWidth (); + requisition->ascent += boxOffsetY (); - requisition->width = altTextWidth; - requisition->ascent = getStyle()->font->ascent; - requisition->descent = getStyle()->font->descent; - } else { - requisition->width = 0; - requisition->ascent = 0; - requisition->descent = 0; + requisition->descent = boxRestHeight (); + + correctRequisition (requisition, core::splitHeightPreserveDescent); + + if (buffer) { + // If one dimension is set, preserve the aspect ratio (without + // extraSpace/margin/border/padding). Notice that + // requisition->descent could have been changed in + // core::splitHeightPreserveDescent, so we do not make any + // assumtions here about it (and requisition->ascent). + + // TODO Check again possible overflows. (Aren't buffer + // dimensions limited to 2^15?) + + if (getStyle()->width == core::style::LENGTH_AUTO && + getStyle()->height != core::style::LENGTH_AUTO) { + requisition->width = + (requisition->ascent + requisition->descent - boxDiffHeight ()) + * buffer->getRootWidth () / buffer->getRootHeight () + + boxDiffWidth (); + } else if (getStyle()->width != core::style::LENGTH_AUTO && + getStyle()->height == core::style::LENGTH_AUTO) { + requisition->ascent = (requisition->width + boxDiffWidth ()) + * buffer->getRootHeight () / buffer->getRootWidth () + + boxOffsetY (); + requisition->descent = boxRestHeight (); } - } + } - requisition->width += getStyle()->boxDiffWidth (); - requisition->ascent += getStyle()->boxOffsetY (); - requisition->descent += getStyle()->boxRestHeight (); + DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)", + requisition->width, requisition->ascent, requisition->descent); + DBG_OBJ_MSG_END (); } void Image::sizeAllocateImpl (core::Allocation *allocation) @@ -247,6 +252,11 @@ void Image::sizeAllocateImpl (core::Allocation *allocation) } } +void Image::containerSizeChangedForChildren () +{ + // Nothing to do. +} + void Image::enterNotifyImpl (core::EventCrossing *event) { // BUG: this is wrong for image maps, but the cursor position is unknown. diff --git a/dw/image.hh b/dw/image.hh index a712936e..2d3cafd6 100644 --- a/dw/image.hh +++ b/dw/image.hh @@ -131,6 +131,7 @@ private: protected: void sizeRequestImpl (core::Requisition *requisition); void sizeAllocateImpl (core::Allocation *allocation); + void containerSizeChangedForChildren (); void draw (core::View *view, core::Rectangle *area); diff --git a/dw/layout.cc b/dw/layout.cc index 1609dae0..3cc13980 100644 --- a/dw/layout.cc +++ b/dw/layout.cc @@ -277,6 +277,11 @@ Layout::Layout (Platform *platform) viewportWidth = viewportHeight = 0; hScrollbarThickness = vScrollbarThickness = 0; + DBG_OBJ_SET_NUM ("viewportWidth", viewportWidth); + DBG_OBJ_SET_NUM ("viewportHeight", viewportHeight); + DBG_OBJ_SET_NUM ("hScrollbarThickness", hScrollbarThickness); + DBG_OBJ_SET_NUM ("vScrollbarThickness", vScrollbarThickness); + requestedAnchor = NULL; scrollIdleId = -1; scrollIdleNotInterrupted = false; @@ -371,13 +376,17 @@ void Layout::addWidget (Widget *widget) topLevel = widget; widget->layout = this; + widget->container = NULL; + DBG_OBJ_SET_PTR_O (widget, "container", widget->container); + queueResizeList->clear (); - widget->notifySetAsTopLevel(); + widget->notifySetAsTopLevel (); findtextState.setWidget (widget); canvasHeightGreater = false; - setSizeHints (); + DBG_OBJ_SET_SYM ("canvasHeightGreater", + canvasHeightGreater ? "true" : "false"); // Do not directly call Layout::queueResize(), but // Widget::queueResize(), so that all flags are set properly, @@ -471,6 +480,11 @@ void Layout::attachView (View *view) hScrollbarThickness = view->getHScrollbarThickness (); vScrollbarThickness = view->getVScrollbarThickness (); } + + DBG_OBJ_SET_NUM ("viewportWidth", viewportWidth); + DBG_OBJ_SET_NUM ("viewportHeight", viewportHeight); + DBG_OBJ_SET_NUM ("hScrollbarThickness", hScrollbarThickness); + DBG_OBJ_SET_NUM ("vScrollbarThickness", vScrollbarThickness); } /* @@ -908,12 +922,12 @@ void Layout::resizeIdle () int currVThickness = currVScrollbarThickness(); if (!canvasHeightGreater && - canvasAscent + canvasDescent - > viewportHeight - currHThickness) { + canvasAscent + canvasDescent > viewportHeight - currHThickness) { canvasHeightGreater = true; - setSizeHints (); - /* May queue a new resize. */ - } + DBG_OBJ_SET_SYM ("canvasHeightGreater", + canvasHeightGreater ? "true" : "false"); + containerSizeChanged (); + } // Set viewport sizes. view->setViewportSize (viewportWidth, viewportHeight, @@ -932,16 +946,6 @@ void Layout::resizeIdle () leaveResizeIdle (); } -void Layout::setSizeHints () -{ - if (topLevel) { - topLevel->setWidth (viewportWidth - - (canvasHeightGreater ? vScrollbarThickness : 0)); - topLevel->setAscent (viewportHeight - hScrollbarThickness); - topLevel->setDescent (0); - } -} - void Layout::queueDraw (int x, int y, int width, int height) { Rectangle area; @@ -1264,13 +1268,17 @@ void Layout::scrollPosChanged (View *view, int x, int y) */ void Layout::viewportSizeChanged (View *view, int width, int height) { - _MSG("Layout::viewportSizeChanged w=%d h=%d new_w=%d new_h=%d\n", - viewportWidth, viewportHeight, width, height); + DBG_OBJ_MSGF ("resize", 0, "<b>viewportSizeChanged</b> (%p, %d, %d)", + view, width, height); + DBG_OBJ_MSG_START (); /* If the width has become higher, we test again, whether the vertical * scrollbar (so to speak) can be hidden again. */ - if (usesViewport && width > viewportWidth) + if (usesViewport && width > viewportWidth) { canvasHeightGreater = false; + DBG_OBJ_SET_SYM ("canvasHeightGreater", + canvasHeightGreater ? "true" : "false"); + } /* if size changes, redraw this view. * TODO: this is a resize call (redraw/resize code needs a review). */ @@ -1285,7 +1293,25 @@ void Layout::viewportSizeChanged (View *view, int width, int height) viewportWidth = width; viewportHeight = height; - setSizeHints (); + DBG_OBJ_SET_NUM ("viewportWidth", viewportWidth); + DBG_OBJ_SET_NUM ("viewportHeight", viewportHeight); + + containerSizeChanged (); + + DBG_OBJ_MSG_END (); +} + +void Layout::containerSizeChanged () +{ + DBG_OBJ_MSG ("resize", 0, "<b>containerSizeChanged</b> ()"); + DBG_OBJ_MSG_START (); + + if (topLevel) { + topLevel->containerSizeChanged (); + queueResize (true); + } + + DBG_OBJ_MSG_END (); } } // namespace core diff --git a/dw/layout.hh b/dw/layout.hh index 2120d877..d83aeedb 100644 --- a/dw/layout.hh +++ b/dw/layout.hh @@ -157,13 +157,15 @@ private: public: Widget *widget; int ref; - bool extremesChanged; + bool extremesChanged, fast; - inline QueueResizeItem (Widget *widget, int ref, bool extremesChanged) + inline QueueResizeItem (Widget *widget, int ref, bool extremesChanged, + bool fast) { this->widget = widget; this->ref = ref; this->extremesChanged = extremesChanged; + this->fast = fast; } }; @@ -267,6 +269,8 @@ private: void enterResizeIdle () { resizeIdleCounter++; } void leaveResizeIdle () { resizeIdleCounter--; } + void containerSizeChanged (); + public: Layout (Platform *platform); ~Layout (); diff --git a/dw/listitem.cc b/dw/listitem.cc index 05344d79..65293d8d 100644 --- a/dw/listitem.cc +++ b/dw/listitem.cc @@ -69,7 +69,7 @@ int ListItem::getValue () void ListItem::setMaxValue (int maxValue, int value) { - innerPadding = maxValue; + leftInnerPadding = maxValue; line1Offset = - value; redrawY = 0; queueResize (0, true); diff --git a/dw/outofflowmgr.cc b/dw/outofflowmgr.cc index eb5bf3e8..c774d0d9 100644 --- a/dw/outofflowmgr.cc +++ b/dw/outofflowmgr.cc @@ -616,7 +616,7 @@ void OutOfFlowMgr::sizeAllocateEnd (Textblock *caller) Textblock *tb = key->getTypedValue(); tbInfo->updateAllocation (); - tbInfo->availWidth = tb->getAvailWidth (); + tbInfo->lineBreakWidth = tb->getLineBreakWidth (); } // There are cases where some allocated floats (TODO: later also @@ -641,6 +641,16 @@ void OutOfFlowMgr::sizeAllocateEnd (Textblock *caller) DBG_OBJ_MSG_END (); } +void OutOfFlowMgr::containerSizeChangedForChildren () +{ + for (int i = 0; i < leftFloatsCB->size (); i++) + leftFloatsCB->get(i)->getWidget()->containerSizeChanged (); + for (int i = 0; i < rightFloatsCB->size (); i++) + rightFloatsCB->get(i)->getWidget()->containerSizeChanged (); + for (int i = 0; i < absolutelyPositioned->size(); i++) + absolutelyPositioned->get(i)->widget->containerSizeChanged (); +} + bool OutOfFlowMgr::hasRelationChanged (TBInfo *tbInfo, int *minFloatPos, Widget **minFloat) { @@ -709,8 +719,8 @@ bool OutOfFlowMgr::hasRelationChanged (TBInfo *tbInfo, Side side, int newFlx = calcFloatX (vloat, side, - gba->x - containingBlockAllocation.x, - gba->width, vloat->generatingBlock->getAvailWidth ()); + gba->x - containingBlockAllocation.x, gba->width, + vloat->generatingBlock->getLineBreakWidth ()); int newFly = vloat->generatingBlock->getAllocation()->y - containingBlockAllocation.y + vloat->yReal; @@ -1042,11 +1052,11 @@ void OutOfFlowMgr::sizeAllocateFloats (Side side, int newLastAllocatedFloat) ensureFloatSize (vloat); Allocation *gba = getAllocation (vloat->generatingBlock); - int availWidth = vloat->generatingBlock->getAvailWidth(); + int lineBreakWidth = vloat->generatingBlock->getLineBreakWidth(); Allocation childAllocation; childAllocation.x = cba->x + - calcFloatX (vloat, side, gba->x - cba->x, gba->width, availWidth); + calcFloatX (vloat, side, gba->x - cba->x, gba->width, lineBreakWidth); childAllocation.y = gba->y + vloat->yReal; childAllocation.width = vloat->size.width; childAllocation.ascent = vloat->size.ascent; @@ -1067,14 +1077,14 @@ void OutOfFlowMgr::sizeAllocateFloats (Side side, int newLastAllocatedFloat) * gbX is given relative to the CB, as is the return value. */ int OutOfFlowMgr::calcFloatX (Float *vloat, Side side, int gbX, int gbWidth, - int gbAvailWidth) + int gbLineBreakWidth) { DBG_OBJ_MSGF ("resize.oofm", 0, "<b>calcFloatX</b> (%p, %s, %d, %d, %d)", vloat->getWidget (), side == LEFT ? "LEFT" : "RIGHT", gbX, - gbWidth, gbAvailWidth); + gbWidth, gbLineBreakWidth); DBG_OBJ_MSG_START (); - int gbActualWidth, x; + int x; switch (side) { case LEFT: @@ -1086,22 +1096,20 @@ int OutOfFlowMgr::calcFloatX (Float *vloat, Side side, int gbX, int gbWidth, break; case RIGHT: - // In some cases, the actual (allocated) width is too large; we - // use the "available" width here. - gbActualWidth = min (gbWidth, gbAvailWidth); - DBG_OBJ_MSGF ("resize.oofm", 1, - "right: gbActualWidth = min (%d, %d) = %d", - gbWidth, gbAvailWidth, gbActualWidth); - // Similar for right floats, but in this case, floats are // shifted to the right when they are too big (instead of // shifting the generator to the right). - x = max (gbX + gbActualWidth - vloat->size.width + + // Notice that not the actual width, but the line break width is + // used. (This changed for GROWS, where the width of a textblock + // is often smaller that the line break.) + + x = max (gbX + gbLineBreakWidth - vloat->size.width - vloat->generatingBlock->getStyle()->boxRestWidth(), // Do not exceed CB allocation: 0); DBG_OBJ_MSGF ("resize.oofm", 1, "x = max (%d + %d - %d - %d, 0) = %d", - gbX, gbActualWidth, vloat->size.width, + gbX, gbLineBreakWidth, vloat->size.width, vloat->generatingBlock->getStyle()->boxRestWidth(), x); break; @@ -1206,6 +1214,9 @@ void OutOfFlowMgr::addWidgetOOF (Widget *widget, Textblock *generatingBlock, case FLOAT_LEFT: leftFloatsAll->put (vloat); DBG_OBJ_SET_NUM ("leftFloatsAll.size", leftFloatsAll->size()); + DBG_OBJ_ARRATTRSET_PTR ("leftFloatsAll", leftFloatsAll->size() - 1, + "widget", vloat->getWidget ()); + widget->parentRef = createRefLeftFloat (leftFloatsAll->size() - 1); tbInfo->leftFloatsGB->put (vloat); @@ -1230,6 +1241,9 @@ void OutOfFlowMgr::addWidgetOOF (Widget *widget, Textblock *generatingBlock, case FLOAT_RIGHT: rightFloatsAll->put (vloat); DBG_OBJ_SET_NUM ("rightFloatsAll.size", rightFloatsAll->size()); + DBG_OBJ_ARRATTRSET_PTR ("rightFloatsAll", rightFloatsAll->size() - 1, + "widget", vloat->getWidget ()); + widget->parentRef = createRefRightFloat (rightFloatsAll->size() - 1); tbInfo->rightFloatsGB->put (vloat); @@ -1565,7 +1579,7 @@ bool OutOfFlowMgr::collidesH (Float *vloat, Float *other, SFVType type) if (vloat->generatingBlock == other->generatingBlock) collidesH = vloat->size.width + other->size.width + vloat->generatingBlock->getStyle()->boxDiffWidth() - > vloat->generatingBlock->getAvailWidth(); + > vloat->generatingBlock->getLineBreakWidth(); else { assert (wasAllocated (vloat->generatingBlock)); assert (wasAllocated (other->generatingBlock)); @@ -1582,7 +1596,7 @@ bool OutOfFlowMgr::collidesH (Float *vloat, Float *other, SFVType type) vloat->getWidget()->getStyle()->vloat == FLOAT_LEFT ? LEFT : RIGHT, gba->x, gba->width, - vloat->generatingBlock->getAvailWidth ()); + vloat->generatingBlock->getLineBreakWidth ()); // Generally: right border of the left float > left border of // the right float (all in canvas coordinates). @@ -1681,13 +1695,13 @@ void OutOfFlowMgr::getFloatsSize (Requisition *cbReq, Side side, int *width, if (vloat->generatingBlock == containingBlock) { x = calcFloatX (vloat, side, 0, cbReq->width, - vloat->generatingBlock->getAvailWidth ()); + vloat->generatingBlock->getLineBreakWidth ()); y = vloat->yReal; } else { Allocation *gba = getAllocation(vloat->generatingBlock); x = calcFloatX (vloat, side, gba->x - containingBlockAllocation.x, gba->width, - vloat->generatingBlock->getAvailWidth ()); + vloat->generatingBlock->getLineBreakWidth ()); y = gba->y - containingBlockAllocation.y + vloat->yReal; } @@ -1763,19 +1777,6 @@ void OutOfFlowMgr::getFloatsExtremes (Extremes *cbExtr, Side side, vloat->getWidget (), vloat->generatingBlock, extr.minWidth, extr.maxWidth); - if (isAbsLength (vloat->getWidget()->getStyle()->width)) { - int width = absLengthVal (vloat->getWidget()->getStyle()->width); - if (extr.minWidth < width) - extr.minWidth = width; - if (extr.maxWidth > width) - // maxWidth not smaller than minWidth - extr.maxWidth = max (width, extr.minWidth); - - DBG_OBJ_MSGF ("resize.oofm", 1, - "corrected by absolute width %d: %d / %d", - width, extr.minWidth, extr.maxWidth); - } - // TODO: Or zero (instead of rightDiff) for right floats? *minWidth = max (*minWidth, @@ -1931,12 +1932,12 @@ int OutOfFlowMgr::getBorder (Textblock *textblock, Side side, int y, int h, fla->x, fla->width, tba->x, thisBorder); } else { // See also calcFloatX. - int tbAvWidth = textblock->getAvailWidth (); - thisBorder = tba->x + min (tba->width, tbAvWidth) - fla->x; + thisBorder = + tba->x + textblock->getLineBreakWidth () - fla->x; DBG_OBJ_MSGF ("border", 1, - "not GB: thisBorder = %d + min (%d, %d) - %d " + "not GB: thisBorder = %d + %d - %d " "= %d", - tba->x, tba->width, tbAvWidth, fla->x, + tba->x, textblock->getLineBreakWidth (), fla->x, thisBorder); } } @@ -2070,120 +2071,21 @@ int OutOfFlowMgr::getClearPosition (Textblock *tb, Side side) void OutOfFlowMgr::ensureFloatSize (Float *vloat) { - if (vloat->dirty || - (vloat->cbAvailWidth != containingBlock->getAvailWidth () && - (// If the size of the containing block has changed (represented - // currently by the available width), a recalculation of a relative - // float width may also be necessary. - isPerLength (vloat->getWidget()->getStyle()->width) || - // Similar for "auto" widths of textblocks etc. - (vloat->getWidget()->usesHints () && - vloat->getWidget()->getStyle()->width == LENGTH_AUTO)))) { + // Historical note: relative sizes (e. g. percentages) are already + // handled by (at this time) Layout::containerSizeChanged, so + // Float::dirty will be set. + + if (vloat->dirty) { DBG_OBJ_MSGF ("resize.oofm", 0, "<b>ensureFloatSize</b> (%p): recalculation", vloat->getWidget ()); DBG_OBJ_MSG_START (); - Extremes extremes; - vloat->getWidget()->getExtremes (&extremes); - DBG_OBJ_MSGF ("resize.oofm", 1, "getExtremes => %d / %d", - extremes.minWidth, extremes.maxWidth); - - // TODO Ugly. Soon to be replaced by cleaner code? See also - // comment in Textblock::calcWidgetSize. - - if (vloat->getWidget()->usesHints ()) { - // For widths defined by CSS, similar adjustments (extremes - // etc.) like below are necessary, to prevent CPU hogging. - - Length cssWidth = vloat->getWidget()->getStyle()->width; - if (isAbsLength (cssWidth)) { - int width = absLengthVal (cssWidth); - DBG_OBJ_MSGF ("resize.oofm", 1, "about to set absolute width: %d", - width); - width = adjustFloatWidth (width, &extremes); - vloat->getWidget()->setWidth (width); - } else if (cssWidth == LENGTH_AUTO || isPerLength (cssWidth)) { - // It is important that the width of the *CB* is not - // larger than its minimal width, when the latter is set - // as size hint; otherwise we have an endless queueResize - // cycle (resulting in CPU hogging) when the CB is part of - // a narrow table column. To prevent this, the width of - // the *float* has to be limited (cf. also getExtremes). - - int availWidth, leftDiff, rightDiff; - if (getFloatDiffToCB (vloat, &leftDiff, &rightDiff)) - availWidth = containingBlock->getAvailWidth() - - (vloat->getWidget()->getStyle()->vloat == FLOAT_LEFT ? - leftDiff : rightDiff); - else - // Not allocated: next allocation will take care. - availWidth = containingBlock->getAvailWidth(); - - int width; - - if (cssWidth == LENGTH_AUTO) { - width = availWidth; - DBG_OBJ_MSGF ("resize.oofm", 1, "setting width 'auto': %d", - width); - } else { - width = multiplyWithPerLength (availWidth, cssWidth); - - // Some more corrections (nonsense percentage values): - if (width < 1) - width = 1; - if (width > availWidth) - width = availWidth; - - DBG_OBJ_MSGF ("resize.oofm", 1, - "about to set percentage width: %d * %g -> %d", - availWidth, perLengthVal (cssWidth), width); - width = adjustFloatWidth (width, &extremes); - } - - vloat->getWidget()->setWidth (width); - } else - DBG_OBJ_MSG ("resize.oofm", 1, - "setting width: <b>relative length? may be a bug</b>"); - } else - DBG_OBJ_MSG ("resize.oofm", 1, "setting no width: uses no hints"); - - // This is a bit hackish: We first request the size, then set - // the available width (also considering the one of the - // containing block, and the extremes of the float), then - // request the size again, which may of course have a different - // result. This is a fix for the bug: - // - // Text in floats, which are wider because of an image, are - // broken at a too narrow width. Reproduce: - // test/floats2.html. After the image has been loaded, the - // text "Some text in a float." should not be broken - // anymore. - // - // If the call of setWidth not is neccessary, the second call - // will read the size from the cache, so no redundant - // calculation is necessary. - // - // Furthermore, extremes are considered; especially, floats are too - // wide, sometimes. - - vloat->getWidget()->sizeRequest (&vloat->size); - DBG_OBJ_MSGF ("resize.oofm", 1, "sizeRequest (1) => %d * (%d + %d)", - vloat->size.width, vloat->size.ascent, vloat->size.descent); - - // Set width ... - int width = vloat->size.width; - DBG_OBJ_MSGF ("resize.oofm", 1, "new width: %d", width); - width = adjustFloatWidth (width, &extremes); - vloat->getWidget()->setWidth (width); vloat->getWidget()->sizeRequest (&vloat->size); - DBG_OBJ_MSGF ("resize.oofm", 1, "sizeRequest (2) => %d * (%d + %d)", - vloat->size.width, vloat->size.ascent, vloat->size.descent); - - vloat->cbAvailWidth = containingBlock->getAvailWidth (); + vloat->cbLineBreakWidth = containingBlock->getLineBreakWidth (); vloat->dirty = false; - DBG_OBJ_MSGF ("resize.oofm", 1, "final size: %d * (%d + %d)", + DBG_OBJ_MSGF ("resize.oofm", 1, "size: %d * (%d + %d)", vloat->size.width, vloat->size.ascent, vloat->size.descent); DBG_OBJ_SET_NUM_O (vloat->getWidget(), "<Float>.size.width", @@ -2199,39 +2101,6 @@ void OutOfFlowMgr::ensureFloatSize (Float *vloat) } } -int OutOfFlowMgr::adjustFloatWidth (int width, Extremes *extremes) -{ - DBG_OBJ_MSGF ("resize.oofm", 0, - "<b>adjustFloatWidth</b> (%d, (%d, %d)) [CB->availWidth = %d]", - width, extremes->minWidth, extremes->maxWidth, - containingBlock->getAvailWidth()); - DBG_OBJ_MSG_START (); - - // Consider extremes (as described above). - if (width < extremes->minWidth) { - width = extremes->minWidth; - DBG_OBJ_MSGF ("resize.oofm", 1, "adjusted to minWidth: %d", width); - } - if (width > extremes->maxWidth) { - width = extremes->maxWidth; - DBG_OBJ_MSGF ("resize.oofm", 1, "adjusted to maxWidth: %d", width); - } - // Finally, consider the available width of the containing - // block. Order is important: to prevent problems, the available - // width of the float must never be larger than the one of the - // containing block. (Somewhat hackish, will be solved cleaner with - // GROWS.) - if (width > containingBlock->getAvailWidth()) { - width = containingBlock->getAvailWidth(); - DBG_OBJ_MSGF ("resize.oofm", 1, "adjusted to CB::availWidth: %d", width); - } - - DBG_OBJ_MSGF ("resize.oofm", 1, "=> %d", width); - DBG_OBJ_MSG_END (); - - return width; -} - void OutOfFlowMgr::getAbsolutelyPositionedSize (Requisition *cbReq, int *width, int *height) { @@ -2250,69 +2119,8 @@ void OutOfFlowMgr::getAbsolutelyPositionedExtremes (Extremes *cbExtr, void OutOfFlowMgr::ensureAbsolutelyPositionedSizeAndPosition (AbsolutelyPositioned *abspos) { - // No work is done anymore on this, since widget sizes will be - // redesigned before absolute positions are finished. - - if (abspos->dirty) { - Style *style = abspos->widget->getStyle(); - int availWidth = containingBlock->getAvailWidth(); - int availHeight = - containingBlock->getAvailAscent() + containingBlock->getAvailDescent(); - - if (style->left == LENGTH_AUTO) - abspos->xCB = 0; - else - abspos->xCB = - calcValueForAbsolutelyPositioned (abspos, style->left, availWidth); - - if (style->top == LENGTH_AUTO) - abspos->yCB = 0; - else - abspos->yCB = - calcValueForAbsolutelyPositioned (abspos, style->top, availHeight); - - abspos->width = -1; // undefined - if (style->width != LENGTH_AUTO) - abspos->width = calcValueForAbsolutelyPositioned (abspos, style->width, - availWidth); - else if (style->right != LENGTH_AUTO) { - int right = calcValueForAbsolutelyPositioned (abspos, style->right, - availWidth); - abspos->width = max (0, availWidth - (abspos->xCB + right)); - } - - abspos->height = -1; // undefined - if (style->height != LENGTH_AUTO) - abspos->height = calcValueForAbsolutelyPositioned (abspos, - style->height, - availHeight); - else if (style->bottom != LENGTH_AUTO) { - int bottom = calcValueForAbsolutelyPositioned (abspos, style->bottom, - availHeight); - abspos->height = max (0, availHeight - (abspos->yCB + bottom)); - } - - if (abspos->width != -1) - abspos->widget->setWidth (abspos->width); - - if (abspos->height != -1) { - abspos->widget->setAscent (abspos->height); - abspos->widget->setDescent (0); // TODO - } - - if (abspos->width == -1 || abspos->height == -1) { - Requisition req; - abspos->widget->sizeRequest (&req); - - if (abspos->width == -1) - abspos->width = req.width; - - if (abspos->height == -1) - abspos->height = req.ascent + req.descent; - } - - abspos->dirty = false; - } + // TODO + assertNotReached (); } int OutOfFlowMgr::calcValueForAbsolutelyPositioned diff --git a/dw/outofflowmgr.hh b/dw/outofflowmgr.hh index 429482fd..19205717 100644 --- a/dw/outofflowmgr.hh +++ b/dw/outofflowmgr.hh @@ -97,9 +97,9 @@ private: respectively. -1 initially. */ int sideSpanningIndex, mark; core::Requisition size; - int cbAvailWidth; /* On which the calculation of relative sizes - is based. Height not yet used, and probably - not added before size redesign. */ + int cbLineBreakWidth; /* On which the calculation of relative sizes + is based. Height not yet used, and probably + not added before size redesign. */ bool dirty, sizeChangedSinceLastAllocation; Float (OutOfFlowMgr *oofm, core::Widget *widget, @@ -172,7 +172,7 @@ private: class TBInfo: public WidgetInfo { public: - int availWidth; + int lineBreakWidth; int index; // position within "tbInfos" TBInfo *parent; @@ -272,7 +272,7 @@ private: void moveFromGBToCB (Side side); void sizeAllocateFloats (Side side, int newLastAllocatedFloat); int calcFloatX (Float *vloat, Side side, int gbX, int gbWidth, - int gbAvailWidth); + int gbLineBreakWidth); bool hasRelationChanged (TBInfo *tbInfo,int *minFloatPos, core::Widget **minFloat); @@ -319,7 +319,6 @@ private: int getClearPosition (Textblock *tb, Side side); void ensureFloatSize (Float *vloat); - int adjustFloatWidth (int width, core::Extremes *extremes); void tellFloatPosition (core::Widget *widget, int yReq); @@ -395,6 +394,7 @@ public: void sizeAllocateStart (Textblock *caller, core::Allocation *allocation); void sizeAllocateEnd (Textblock *caller); + void containerSizeChangedForChildren (); void draw (core::View *view, core::Rectangle *area); void markSizeChange (int ref); diff --git a/dw/ruler.cc b/dw/ruler.cc index 2b5288c2..b624718d 100644 --- a/dw/ruler.cc +++ b/dw/ruler.cc @@ -29,25 +29,38 @@ namespace dw { Ruler::Ruler () { setFlags (USES_HINTS); - setFlags (BLOCK_LEVEL); unsetFlags (HAS_CONTENTS); - availWidth = 0; } void Ruler::sizeRequestImpl (core::Requisition *requisition) { requisition->width = - lout::misc::max (availWidth, getStyle()->boxDiffWidth ()); + lout::misc::max (getAvailWidth (true), getStyle()->boxDiffWidth ()); requisition->ascent = getStyle()->boxOffsetY (); requisition->descent = getStyle()->boxRestHeight (); } -void Ruler::setWidth (int width) +void Ruler::getExtremesImpl (core::Extremes *extremes) { - if (availWidth != width) { - availWidth = width; - queueResize (0, false); - } + extremes->minWidth = extremes->maxWidth = getStyle()->boxDiffWidth (); + extremes->minWidthIntrinsic = extremes->minWidth; + extremes->maxWidthIntrinsic = extremes->maxWidth; + correctExtremes (extremes); +} + +bool Ruler::isBlockLevel () +{ + return true; +} + +void Ruler::containerSizeChangedForChildren () +{ + // Nothing to do. +} + +bool Ruler::usesAvailWidth () +{ + return true; } void Ruler::draw (core::View *view, core::Rectangle *area) diff --git a/dw/ruler.hh b/dw/ruler.hh index 863792dd..1f3491bc 100644 --- a/dw/ruler.hh +++ b/dw/ruler.hh @@ -15,17 +15,18 @@ namespace dw { */ class Ruler: public core::Widget { -private: - int availWidth; - protected: void sizeRequestImpl (core::Requisition *requisition); - void setWidth (int width); + void getExtremesImpl (core::Extremes *extremes); + void containerSizeChangedForChildren (); + bool usesAvailWidth (); void draw (core::View *view, core::Rectangle *area); public: Ruler (); + bool isBlockLevel (); + core::Iterator *iterator (core::Content::Type mask, bool atEnd); }; diff --git a/dw/simpletablecell.cc b/dw/simpletablecell.cc new file mode 100644 index 00000000..083deaed --- /dev/null +++ b/dw/simpletablecell.cc @@ -0,0 +1,58 @@ +/* + * Dillo Widget + * + * Copyright 2014 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + + +#include "simpletablecell.hh" +#include "../lout/debug.hh" + +namespace dw { + +int SimpleTableCell::CLASS_ID = -1; + +SimpleTableCell::SimpleTableCell (bool limitTextWidth): + Textblock (limitTextWidth) +{ + DBG_OBJ_CREATE ("dw::SimpleTableCell"); + registerName ("dw::SimpleTableCell", &CLASS_ID); +} + +SimpleTableCell::~SimpleTableCell() +{ + DBG_OBJ_DELETE (); +} + +bool SimpleTableCell::isBlockLevel () +{ + return false; +} + +int SimpleTableCell::applyPerWidth (int containerWidth, + core::style::Length perWidth) +{ + return core::style::multiplyWithPerLength (containerWidth, perWidth); +} + +int SimpleTableCell::applyPerHeight (int containerHeight, + core::style::Length perHeight) +{ + return core::style::multiplyWithPerLength (containerHeight, perHeight); +} + +} // namespace dw diff --git a/dw/simpletablecell.hh b/dw/simpletablecell.hh new file mode 100644 index 00000000..a452add9 --- /dev/null +++ b/dw/simpletablecell.hh @@ -0,0 +1,24 @@ +#ifndef __DW_SIMPLETABLECELL_HH__ +#define __DW_SIMPLETABLECELL_HH__ + +#include "textblock.hh" + +namespace dw { + +class SimpleTableCell: public Textblock +{ +public: + static int CLASS_ID; + + SimpleTableCell (bool limitTextWidth); + ~SimpleTableCell (); + + int applyPerWidth (int containerWidth, core::style::Length perWidth); + int applyPerHeight (int containerHeight, core::style::Length perHeight); + + bool isBlockLevel (); +}; + +} // namespace dw + +#endif // __DW_SIMPLETABLECELL_HH__ diff --git a/dw/style.hh b/dw/style.hh index 2cc258bf..578e65c0 100644 --- a/dw/style.hh +++ b/dw/style.hh @@ -435,7 +435,8 @@ inline int absLengthVal(Length l) { return l >> 2; } * When possible, do not use this function directly; it may be removed * soon. Instead, use multiplyWithPerLength or multiplyWithPerLengthRounded. */ -inline double perLengthVal(Length l) { return (double)(l & ~3) / (1 << 18); } +inline double perLengthVal_useThisOnlyForDebugging(Length l) +{ return (double)(l & ~3) / (1 << 18); } /** \brief Returns the value of a relative length, as a float. * @@ -450,7 +451,7 @@ inline double relLengthVal(Length l) { return (double)(l & ~3) / (1 << 18); } * Use this instead of perLengthVal, when possible. */ inline int multiplyWithPerLength(int x, Length l) { - return x * perLengthVal(l); + return x * perLengthVal_useThisOnlyForDebugging (l); } /** @@ -459,8 +460,8 @@ inline int multiplyWithPerLength(int x, Length l) { * * (This function exists for backward compatibility.) */ -inline int multiplyWithPerLengthRounded (int x, Length l) { - return lout::misc::roundInt (x * perLengthVal(l)); +inline int multiplyWithPerLengthRounded(int x, Length l) { + return lout::misc::roundInt (x * perLengthVal_useThisOnlyForDebugging (l)); } inline int multiplyWithRelLength(int x, Length l) { @@ -564,22 +565,14 @@ public: = borderStyle.left = val; } inline int boxOffsetX () - { - return margin.left + borderWidth.left + padding.left; - } + { return margin.left + borderWidth.left + padding.left; } inline int boxRestWidth () - { - return margin.right + borderWidth.right + padding.right; - } + { return margin.right + borderWidth.right + padding.right; } inline int boxDiffWidth () { return boxOffsetX () + boxRestWidth (); } inline int boxOffsetY () - { - return margin.top + borderWidth.top + padding.top; - } + { return margin.top + borderWidth.top + padding.top; } inline int boxRestHeight () - { - return margin.bottom + borderWidth.bottom + padding.bottom; - } + { return margin.bottom + borderWidth.bottom + padding.bottom; } inline int boxDiffHeight () { return boxOffsetY () + boxRestHeight (); } inline bool hasBackground () diff --git a/dw/table.cc b/dw/table.cc index 565dfc9e..18597099 100644 --- a/dw/table.cc +++ b/dw/table.cc @@ -1,7 +1,7 @@ /* * Dillo Widget * - * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * Copyright 2005-2007, 2014 Sebastian Geerken <sgeerken@dillo.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,7 +34,6 @@ Table::Table(bool limitTextWidth) { DBG_OBJ_CREATE ("dw::Table"); registerName ("dw::Table", &CLASS_ID); - setFlags (BLOCK_LEVEL); setFlags (USES_HINTS); setButtonSensitive(false); @@ -42,33 +41,30 @@ Table::Table(bool limitTextWidth) rowClosed = false; - // random values - availWidth = 100; - availAscent = 100; - availDescent = 0; - numRows = 0; numCols = 0; curRow = -1; curCol = 0; + DBG_OBJ_SET_NUM ("numCols", numCols); + DBG_OBJ_SET_NUM ("numRows", numCols); + children = new misc::SimpleVector <Child*> (16); colExtremes = new misc::SimpleVector<core::Extremes> (8); colWidths = new misc::SimpleVector <int> (8); cumHeight = new misc::SimpleVector <int> (8); rowSpanCells = new misc::SimpleVector <int> (8); - colSpanCells = new misc::SimpleVector <int> (8); baseline = new misc::SimpleVector <int> (8); rowStyle = new misc::SimpleVector <core::style::Style*> (8); - hasColPercent = 0; - colPercents = new misc::SimpleVector <core::style::Length> (8); + colWidthsUpToDateWidthColExtremes = true; + DBG_OBJ_SET_BOOL ("colWidthsUpToDateWidthColExtremes", + colWidthsUpToDateWidthColExtremes); redrawX = 0; redrawY = 0; } - Table::~Table() { for (int i = 0; i < children->size (); i++) { @@ -94,17 +90,18 @@ Table::~Table() delete colWidths; delete cumHeight; delete rowSpanCells; - delete colSpanCells; delete baseline; delete rowStyle; - delete colPercents; DBG_OBJ_DELETE (); } void Table::sizeRequestImpl (core::Requisition *requisition) { - forceCalcCellSizes (); + DBG_OBJ_MSG ("resize", 0, "<b>sizeRequestImpl</b>"); + DBG_OBJ_MSG_START (); + + forceCalcCellSizes (true); /** * \bug Baselines are not regarded here. @@ -119,40 +116,46 @@ void Table::sizeRequestImpl (core::Requisition *requisition) + getStyle()->vBorderSpacing; requisition->descent = 0; + correctRequisition (requisition, core::splitHeightPreserveDescent); + + DBG_OBJ_MSG_END (); } void Table::getExtremesImpl (core::Extremes *extremes) { - if (numCols == 0) { - extremes->minWidth = extremes->maxWidth = 0; - return; - } + DBG_OBJ_MSG ("resize", 0, "<b>getExtremesImpl</b>"); + DBG_OBJ_MSG_START (); - forceCalcColumnExtremes (); - - extremes->minWidth = extremes->maxWidth = - (numCols + 1) * getStyle()->hBorderSpacing - + getStyle()->boxDiffWidth (); - for (int col = 0; col < numCols; col++) { - extremes->minWidth += colExtremes->getRef(col)->minWidth; - extremes->maxWidth += colExtremes->getRef(col)->maxWidth; - } - if (core::style::isAbsLength (getStyle()->width)) { - extremes->minWidth = - misc::max (extremes->minWidth, - core::style::absLengthVal(getStyle()->width)); - extremes->maxWidth = - misc::max (extremes->maxWidth, - core::style::absLengthVal(getStyle()->width)); + if (numCols == 0) + extremes->minWidth = extremes->minWidthIntrinsic = extremes->maxWidth = + extremes->maxWidthIntrinsic = boxDiffWidth (); + else { + forceCalcColumnExtremes (); + + extremes->minWidth = extremes->minWidthIntrinsic = extremes->maxWidth = + extremes->maxWidthIntrinsic = + (numCols + 1) * getStyle()->hBorderSpacing + boxDiffWidth (); + for (int col = 0; col < numCols; col++) { + extremes->minWidth += colExtremes->getRef(col)->minWidth; + extremes->minWidthIntrinsic += + colExtremes->getRef(col)->minWidthIntrinsic; + extremes->maxWidth += colExtremes->getRef(col)->maxWidth; + extremes->maxWidthIntrinsic += + colExtremes->getRef(col)->maxWidthIntrinsic; + } } - _MSG(" Table::getExtremesImpl, {%d, %d} numCols=%d\n", - extremes->minWidth, extremes->maxWidth, numCols); + correctExtremes (extremes); + + DBG_OBJ_MSG_END (); } void Table::sizeAllocateImpl (core::Allocation *allocation) { - calcCellSizes (); + DBG_OBJ_MSG ("resize", 0, "<b>sizeAllocateImpl</b>"); + DBG_OBJ_MSG_START (); + + calcCellSizes (true); /** * \bug Baselines are not regarded here. @@ -167,8 +170,7 @@ void Table::sizeAllocateImpl (core::Allocation *allocation) for (int row = 0; row < numRows; row++) { int n = row * numCols + col; if (childDefined (n)) { - int width = - (children->get(n)->cell.colspanEff - 1) + int width = (children->get(n)->cell.colspanEff - 1) * getStyle()->hBorderSpacing; for (int i = 0; i < children->get(n)->cell.colspanEff; i++) width += colWidths->get (col + i); @@ -192,6 +194,8 @@ void Table::sizeAllocateImpl (core::Allocation *allocation) x += colWidths->get (col) + getStyle()->hBorderSpacing; } + + DBG_OBJ_MSG_END (); } void Table::resizeDrawImpl () @@ -202,32 +206,84 @@ void Table::resizeDrawImpl () redrawY = getHeight (); } -void Table::setWidth (int width) +int Table::getAvailWidthOfChild (Widget *child, bool forceValue) { - // If limitTextWidth is set, a queueResize may also be necessary. - if (availWidth != width || limitTextWidth) { - _MSG(" Table::setWidth %d\n", width); - availWidth = width; - queueResize (0, false); + DBG_OBJ_MSGF ("resize", 0, "<b>getAvailWidthOfChild</b> (%p, %s)", + child, forceValue ? "true" : "false"); + DBG_OBJ_MSG_START (); + + int width = -1; + + // Unlike other containers, the table widget sometimes narrows + // columns to a width less than specified by CSS (see + // forceCalcCellSizes). For this reason, the column widths have to + // be calculated in all cases. + if (forceValue) { + calcCellSizes (false); + + // "child" is not a direct child, but a direct descendant. + // Search for the actual childs. + Widget *actualChild = child; + while (actualChild != NULL && actualChild->getParent () != this) + actualChild = actualChild->getParent (); + + assert (actualChild != NULL); + + // TODO This is inefficient. (Use parentRef?) + for (int row = numRows - 1; width == -1 && row >= 0; row--) { + for (int col = 0; width == -1 && col < numCols; col++) { + int n = row * numCols + col; + if (childDefined (n) && + children->get(n)->cell.widget == actualChild) { + DBG_OBJ_MSGF ("resize", 1, "calculated from column %d", col); + width = (children->get(n)->cell.colspanEff - 1) + * getStyle()->hBorderSpacing; + for (int i = 0; i < children->get(n)->cell.colspanEff; i++) + width += colWidths->get (col + i); + width = misc::max (width, 0); + } + } + } + + assert (width != -1); } + + DBG_OBJ_MSGF ("resize", 1, "=> %d", width); + DBG_OBJ_MSG_END (); + return width; } -void Table::setAscent (int ascent) +int Table::applyPerWidth (int containerWidth, core::style::Length perWidth) { - if (availAscent != ascent) { - availAscent = ascent; - queueResize (0, false); - } + return core::style::multiplyWithPerLength (containerWidth, perWidth); } -void Table::setDescent (int descent) +int Table::applyPerHeight (int containerHeight, core::style::Length perHeight) { - if (availDescent != descent) { - availDescent = descent; - queueResize (0, false); + return core::style::multiplyWithPerLength (containerHeight, perHeight); +} + +void Table::containerSizeChangedForChildren () +{ + for (int col = 0; col < numCols; col++) { + for (int row = 0; row < numRows; row++) { + int n = row * numCols + col; + if (childDefined (n)) + children->get(n)->cell.widget->containerSizeChanged (); + } } } +bool Table::usesAvailWidth () +{ + return true; +} + +bool Table::isBlockLevel () +{ + return true; +} + void Table::draw (core::View *view, core::Rectangle *area) { // Can be optimized, by iterating on the lines in area. @@ -272,6 +328,10 @@ core::Iterator *Table::iterator (core::Content::Type mask, bool atEnd) void Table::addCell (Widget *widget, int colspan, int rowspan) { + DBG_OBJ_MSGF ("resize", 0, "<b>addCell</b> (%p, %d, %d)", + widget, colspan, rowspan); + DBG_OBJ_MSG_START (); + const int maxspan = 100; Child *child; int colspanEff; @@ -373,6 +433,8 @@ void Table::addCell (Widget *widget, int colspan, int rowspan) } MSG("\n"); #endif + + DBG_OBJ_MSG_END (); } void Table::addRow (core::style::Style *style) @@ -393,7 +455,7 @@ void Table::addRow (core::style::Style *style) rowClosed = false; } -TableCell *Table::getCellRef () +AlignedTableCell *Table::getCellRef () { core::Widget *child; @@ -401,14 +463,93 @@ TableCell *Table::getCellRef () int n = curCol + row * numCols; if (childDefined (n)) { child = children->get(n)->cell.widget; - if (child->instanceOf (TableCell::CLASS_ID)) - return (TableCell*)child; + if (child->instanceOf (AlignedTableCell::CLASS_ID)) + return (AlignedTableCell*)child; } } return NULL; } +const char *Table::getExtrModName (ExtrMod mod) +{ + switch (mod) { + case MIN: + return "MIN"; + + case MIN_INTR: + return "MIN_INTR"; + + case MIN_MIN: + return "MIN_MIN"; + + case MAX_MIN: + return "MAX_MIN"; + + case MAX: + return "MAX"; + + case MAX_INTR: + return "MAX_INTR"; + + default: + misc::assertNotReached (); + return NULL; + } +} + +int Table::getExtreme (core::Extremes *extremes, ExtrMod mod) +{ + switch (mod) { + case MIN: + return extremes->minWidth; + + case MIN_INTR: + return extremes->minWidthIntrinsic; + + case MIN_MIN: + return misc::min (extremes->minWidth, extremes->minWidthIntrinsic); + + case MAX_MIN: + return misc::max (extremes->minWidth, extremes->minWidthIntrinsic); + + case MAX: + return extremes->maxWidth; + + case MAX_INTR: + return extremes->maxWidthIntrinsic; + + default: + misc::assertNotReached (); return 0; + } +} + +void Table::setExtreme (core::Extremes *extremes, ExtrMod mod, int value) +{ + switch (mod) { + case MIN: + extremes->minWidth = value; + break; + + case MIN_INTR: + extremes->minWidthIntrinsic = value; + break; + + // MIN_MIN and MAX_MIN not supported here. + + case MAX: + extremes->maxWidth = value; + break; + + case MAX_INTR: + extremes->maxWidthIntrinsic = value; + break; + + default: + misc::assertNotReached (); + } +} + void Table::reallocChildren (int newNumCols, int newNumRows) { assert (newNumCols >= numCols); @@ -476,107 +617,174 @@ void Table::reallocChildren (int newNumCols, int newNumRows) numCols = newNumCols; numRows = newNumRows; + + DBG_OBJ_SET_NUM ("numCols", numCols); + DBG_OBJ_SET_NUM ("numRows", numCols); } // ---------------------------------------------------------------------- -void Table::calcCellSizes () +void Table::calcCellSizes (bool calcHeights) { - if (needsResize () || resizeQueued ()) - forceCalcCellSizes (); + DBG_OBJ_MSGF ("resize", 0, "<b>calcCellSizes</b> (%s)", + calcHeights ? "true" : "false"); + DBG_OBJ_MSG_START (); + + bool sizeChanged = needsResize () || resizeQueued (); + bool extremesChanges = extremesChanged () || extremesQueued (); + + if (calcHeights ? (extremesChanges || sizeChanged) : + (extremesChanges || !colWidthsUpToDateWidthColExtremes)) + forceCalcCellSizes (calcHeights); + + DBG_OBJ_MSG_END (); } -void Table::forceCalcCellSizes () +void Table::forceCalcCellSizes (bool calcHeights) { - int totalWidth = 0, childHeight, forceTotalWidth = 1; + DBG_OBJ_MSG ("resize", 0, "<b>forceCalcCellSizes</b>"); + DBG_OBJ_MSG_START (); + + int childHeight; core::Extremes extremes; // Will also call calcColumnExtremes(), when needed. getExtremes (&extremes); - if (core::style::isAbsLength (getStyle()->width)) { - totalWidth = core::style::absLengthVal (getStyle()->width); - } else if (core::style::isPerLength (getStyle()->width)) { - /* - * If the width is > 100%, we use 100%, this prevents ugly - * results. (May be changed in future, when a more powerful - * rendering is implemented, to handle fixed positions etc., - * as defined by CSS2.) - */ - totalWidth = - misc::min (core::style::multiplyWithPerLength (availWidth, - getStyle()->width), - availWidth); - } else if (getStyle()->width == core::style::LENGTH_AUTO) { - totalWidth = availWidth; - forceTotalWidth = 0; - } - - _MSG(" availWidth = %d\n", availWidth); - _MSG(" totalWidth1 = %d\n", totalWidth); - - if (totalWidth < extremes.minWidth) - totalWidth = extremes.minWidth; - totalWidth = totalWidth - - (numCols + 1) * getStyle()->hBorderSpacing - - getStyle()->boxDiffWidth (); - - _MSG(" totalWidth2 = %d curCol=%d\n", totalWidth,curCol); + int availWidth = getAvailWidth (true); + int totalWidth = availWidth - + ((numCols + 1) * getStyle()->hBorderSpacing + boxDiffWidth ()); + DBG_OBJ_MSGF ("resize", 1, + "totalWidth = %d - ((%d - 1) * %d + %d) = <b>%d</b>", + availWidth, numCols, getStyle()->hBorderSpacing, + boxDiffWidth (), totalWidth); colWidths->setSize (numCols, 0); cumHeight->setSize (numRows + 1, 0); rowSpanCells->setSize (0); baseline->setSize (numRows); - _MSG(" extremes = %d,%d\n", extremes.minWidth, extremes.maxWidth); - _MSG(" getStyle()->boxDiffWidth() = %d\n", getStyle()->boxDiffWidth()); - _MSG(" getStyle()->hBorderSpacing = %d\n", getStyle()->hBorderSpacing); + misc::SimpleVector<int> *oldColWidths = colWidths; + colWidths = new misc::SimpleVector <int> (8); + int minWidth = 0; + for (int col = 0; col < colExtremes->size(); col++) + minWidth += getColExtreme (col, MIN); + + DBG_OBJ_MSGF ("resize", 1, "minWidth (= %d) > totalWidth (= %d)?", + minWidth, totalWidth); + + if (minWidth > totalWidth) + // The sum of all column minima is larger than the available + // width, so we narrow the columns (see also CSS2 spec, + // section 17.5, #6). We use a similar apportioning, but not + // bases on minimal and maximal widths, but on intrinsic minimal + // widths and corrected minimal widths. This way, intrinsic + // extremes are preferred (so avoiding columns too narrow for + // the actual contents), at the expenses of corrected ones + // (which means that sometimes CSS values are handled + // incorrectly). + + apportion2 (totalWidth, 0, colExtremes->size() - 1, MIN_MIN, MAX_MIN, + colWidths, 0); + else { + // Normal apportioning. + int width; + if (getStyle()->width == core::style::LENGTH_AUTO) { + // Do not force width, when maximal width is smaller. + int maxWidth = 0; + for (int col = 0; col < colExtremes->size(); col++) + maxWidth += getColExtreme (col, MAX); + width = misc::min (totalWidth, maxWidth); + DBG_OBJ_MSGF ("resize", 1, "width = min (%d, %d) = %d", + totalWidth, maxWidth, width); + } else + // CSS 'width' defined: force this width. + width = totalWidth; - apportion_percentages2 (totalWidth, forceTotalWidth); - if (!hasColPercent) - apportion2 (totalWidth, forceTotalWidth); + apportion2 (width, 0, colExtremes->size() - 1, MIN, MAX, colWidths, 0); + } - setCumHeight (0, 0); - for (int row = 0; row < numRows; row++) { - /** - * \bug dw::Table::baseline is not filled. - */ - int rowHeight = 0; + // TODO: Adapted from old inline function "setColWidth". But (i) is + // this anyway correct (col width is is not x)? And does the + // performance gain actually play a role? + for (int col = 0; col < colExtremes->size(); col++) { + if (colWidths->get (col) != oldColWidths->get (col)) + redrawX = lout::misc::min (redrawX, colWidths->get (col)); + } + + DBG_IF_RTFL { + DBG_OBJ_SET_NUM ("colWidths.size", colWidths->size ()); + for (int i = 0; i < colWidths->size (); i++) + DBG_OBJ_ARRSET_NUM ("colWidths", i, colWidths->get (i)); + } - for (int col = 0; col < numCols; col++) { - int n = row * numCols + col; - if (childDefined (n)) { - int width = (children->get(n)->cell.colspanEff - 1) - * getStyle()->hBorderSpacing; - for (int i = 0; i < children->get(n)->cell.colspanEff; i++) - width += colWidths->get (col + i); + colWidthsUpToDateWidthColExtremes = true; + DBG_OBJ_SET_BOOL ("colWidthsUpToDateWidthColExtremes", + colWidthsUpToDateWidthColExtremes); - core::Requisition childRequisition; - children->get(n)->cell.widget->setWidth (width); - children->get(n)->cell.widget->sizeRequest (&childRequisition); - childHeight = childRequisition.ascent + childRequisition.descent; - if (children->get(n)->cell.rowspan == 1) { - rowHeight = misc::max (rowHeight, childHeight); - } else { - rowSpanCells->increase(); - rowSpanCells->set(rowSpanCells->size()-1, n); - } + for (int col = 0; col < numCols; col++) { + if (col >= oldColWidths->size () || col >= colWidths->size () || + oldColWidths->get (col) != colWidths->get (col)) { + // Column width has changed, tell children about this. + for (int row = 0; row < numRows; row++) { + int n = row * numCols + col; + // TODO: Columns spanning several rows are only regarded + // when the first column is affected. + if (childDefined (n)) + children->get(n)->cell.widget->containerSizeChanged (); } - }/*for col*/ + } + } + + delete oldColWidths; - setCumHeight (row + 1, - cumHeight->get (row) + rowHeight + getStyle()->vBorderSpacing); + if (calcHeights) { + setCumHeight (0, 0); + for (int row = 0; row < numRows; row++) { + /** + * \bug dw::Table::baseline is not filled. + */ + int rowHeight = 0; + + for (int col = 0; col < numCols; col++) { + int n = row * numCols + col; + if (childDefined (n)) { + int width = (children->get(n)->cell.colspanEff - 1) + * getStyle()->hBorderSpacing; + for (int i = 0; i < children->get(n)->cell.colspanEff; i++) + width += colWidths->get (col + i); + + core::Requisition childRequisition; + //children->get(n)->cell.widget->setWidth (width); + children->get(n)->cell.widget->sizeRequest (&childRequisition); + childHeight = childRequisition.ascent + childRequisition.descent; + if (children->get(n)->cell.rowspan == 1) { + rowHeight = misc::max (rowHeight, childHeight); + } else { + rowSpanCells->increase(); + rowSpanCells->set(rowSpanCells->size()-1, n); + } + } + } // for col + + setCumHeight (row + 1, + cumHeight->get (row) + rowHeight + getStyle()->vBorderSpacing); + } // for row - }/*for row*/ + apportionRowSpan (); + } - apportionRowSpan (); + DBG_OBJ_MSG_END (); } void Table::apportionRowSpan () { + DBG_OBJ_MSG ("resize", 0, "<b>apportionRowSpan</b>"); + DBG_OBJ_MSG_START (); + int *rowHeight = NULL; for (int c = 0; c < rowSpanCells->size(); ++c) { @@ -630,6 +838,8 @@ void Table::apportionRowSpan () setCumHeight (i+1, cumHeight->get(i) + rowHeight[i]); } delete[] rowHeight; + + DBG_OBJ_MSG_END (); } @@ -640,8 +850,13 @@ void Table::apportionRowSpan () */ void Table::calcColumnExtremes () { + DBG_OBJ_MSG ("resize", 0, "<b>calcColumnExtremes</b>"); + DBG_OBJ_MSG_START (); + if (extremesChanged () || extremesQueued ()) forceCalcColumnExtremes (); + + DBG_OBJ_MSG_END (); } @@ -650,570 +865,286 @@ void Table::calcColumnExtremes () */ void Table::forceCalcColumnExtremes () { - _MSG(" Table::forceCalcColumnExtremes numCols=%d\n", numCols); + DBG_OBJ_MSG ("resize", 0, "<b>forceCalcColumnExtremes</b>"); + DBG_OBJ_MSG_START (); - if (numCols == 0) - return; - - colExtremes->setSize (numCols); - colPercents->setSize (numCols); - colSpanCells->setSize (0); - /* 1. cells with colspan = 1 */ - for (int col = 0; col < numCols; col++) { - colExtremes->getRef(col)->minWidth = 0; - colExtremes->getRef(col)->maxWidth = 0; - colPercents->set(col, core::style::LENGTH_AUTO); + if (numCols > 0) { + lout::misc::SimpleVector<int> colSpanCells (8); + colExtremes->setSize (numCols); - for (int row = 0; row < numRows; row++) { - int n = row * numCols + col; - if (!childDefined (n)) - continue; - if (children->get(n)->cell.colspanEff == 1) { - core::Extremes cellExtremes; - int cellMinW, cellMaxW, pbm; - core::style::Length width = - children->get(n)->cell.widget->getStyle()->width; - pbm = (numCols + 1) * getStyle()->hBorderSpacing - + children->get(n)->cell.widget->getStyle()->boxDiffWidth (); - children->get(n)->cell.widget->getExtremes (&cellExtremes); - if (core::style::isAbsLength (width)) { - // Fixed lengths include table padding, border and margin. - cellMinW = cellExtremes.minWidth; - cellMaxW = misc::max (cellMinW, - core::style::absLengthVal(width) - pbm); - } else { - cellMinW = cellExtremes.minWidth; - cellMaxW = cellExtremes.maxWidth; + // 1. cells with colspan = 1 + for (int col = 0; col < numCols; col++) { + DBG_OBJ_MSGF ("resize", 1, "column %d", col); + DBG_OBJ_MSG_START (); + + colExtremes->getRef(col)->minWidth = 0; + colExtremes->getRef(col)->minWidthIntrinsic = 0; + colExtremes->getRef(col)->maxWidth = 0; + colExtremes->getRef(col)->maxWidthIntrinsic = 0; + + for (int row = 0; row < numRows; row++) { + DBG_OBJ_MSGF ("resize", 1, "row %d", row); + DBG_OBJ_MSG_START (); + + int n = row * numCols + col; + + if (childDefined (n)) { + if (children->get(n)->cell.colspanEff == 1) { + core::Extremes cellExtremes; + children->get(n)->cell.widget->getExtremes (&cellExtremes); + + DBG_OBJ_MSGF ("resize", 1, "child: %d / %d", + cellExtremes.minWidth, cellExtremes.maxWidth); + + colExtremes->getRef(col)->minWidthIntrinsic = + misc::max (colExtremes->getRef(col)->minWidthIntrinsic, + cellExtremes.minWidthIntrinsic); + colExtremes->getRef(col)->maxWidthIntrinsic = + misc::max (colExtremes->getRef(col)->minWidthIntrinsic, + colExtremes->getRef(col)->maxWidthIntrinsic, + cellExtremes.maxWidthIntrinsic); + + colExtremes->getRef(col)->minWidth = + misc::max (colExtremes->getRef(col)->minWidth, + cellExtremes.minWidth); + colExtremes->getRef(col)->maxWidth = + misc::max (colExtremes->getRef(col)->minWidth, + colExtremes->getRef(col)->maxWidth, + cellExtremes.maxWidth); + + DBG_OBJ_MSGF ("resize", 1, "column: %d / %d (%d / %d)", + colExtremes->getRef(col)->minWidth, + colExtremes->getRef(col)->maxWidth, + colExtremes->getRef(col)->minWidthIntrinsic, + colExtremes->getRef(col)->maxWidthIntrinsic); + } else { + colSpanCells.increase (); + colSpanCells.setLast (n); + } } - _MSG("FCCE, col%d colMin,colMax,cellMin,cellMax = %d,%d,%d,%d\n", - col, - colExtremes->getRef(col)->minWidth, - colExtremes->getRef(col)->maxWidth, - cellMinW, cellMaxW); - - colExtremes->getRef(col)->minWidth = - misc::max (colExtremes->getRef(col)->minWidth, cellMinW); - colExtremes->getRef(col)->maxWidth = - misc::max (colExtremes->getRef(col)->minWidth, misc::max ( - colExtremes->getRef(col)->maxWidth, - cellMaxW)); - - // Also fill the colPercents array in this pass - if (core::style::isPerLength (width)) { - hasColPercent = 1; - if (colPercents->get(col) == core::style::LENGTH_AUTO) - colPercents->set(col, width); - } else if (core::style::isAbsLength (width)) { - // We treat LEN_ABS as a special case of LEN_AUTO. - /* - * if (colPercents->get(col) == LEN_AUTO) - * colPercents->set(col, LEN_ABS); - * - * (Hint: that's old code!) - */ - } - } else { - colSpanCells->increase(); - colSpanCells->set(colSpanCells->size()-1, n); + DBG_OBJ_MSG_END (); } - } - } - /* 2. cells with colspan > 1 */ - /* If needed, here we set proportionally apportioned col maximums */ - for (int c = 0; c < colSpanCells->size(); ++c) { - core::Extremes cellExtremes; - int cellMinW, cellMaxW, pbm; - int n = colSpanCells->get(c); - int col = n % numCols; - int cs = children->get(n)->cell.colspanEff; - core::style::Length width = - children->get(n)->cell.widget->getStyle()->width; - pbm = (numCols + 1) * getStyle()->hBorderSpacing - + children->get(n)->cell.widget->getStyle()->boxDiffWidth (); - children->get(n)->cell.widget->getExtremes (&cellExtremes); - if (core::style::isAbsLength (width)) { - // Fixed lengths include table padding, border and margin. - cellMinW = cellExtremes.minWidth; - cellMaxW = - misc::max (cellMinW, core::style::absLengthVal(width) - pbm); - } else { - cellMinW = cellExtremes.minWidth; - cellMaxW = cellExtremes.maxWidth; - } - int minSumCols = 0, maxSumCols = 0; - for (int i = 0; i < cs; ++i) { - minSumCols += colExtremes->getRef(col+i)->minWidth; - maxSumCols += colExtremes->getRef(col+i)->maxWidth; + DBG_OBJ_MSG_END (); } - _MSG("cs=%d spanWidth=%d,%d sumCols=%d,%d\n", - cs,cellMinW,cellMaxW,minSumCols,maxSumCols); + // 2. cells with colspan > 1 - if (minSumCols >= cellMinW && maxSumCols >= cellMaxW) - continue; + // TODO: Is this old comment still relevant? "If needed, here we + // set proportionally apportioned col maximums." - // Cell size is too small; apportion {min,max} for this colspan. - int spanMinW = misc::max (misc::max (cs, minSumCols), - cellMinW - (cs-1) * getStyle()->hBorderSpacing), - spanMaxW = misc::max (misc::max (cs, maxSumCols), - cellMaxW - (cs-1) * getStyle()->hBorderSpacing); - - if (minSumCols == 0) { - // No single cells defined for this span => pre-apportion equally - minSumCols = spanMinW; maxSumCols = spanMaxW; - int minW = spanMinW, maxW = spanMaxW; - for (int i = 0; i < cs; ++i) { - colExtremes->getRef(col+i)->minWidth = minW / (cs - i); - colExtremes->getRef(col+i)->maxWidth = maxW / (cs - i); - minW -= colExtremes->getRef(col+i)->minWidth; - maxW -= colExtremes->getRef(col+i)->maxWidth; - } - } + for (int i = 0; i < colSpanCells.size(); i++) { + int n = colSpanCells.get (i); + int col = n % numCols; + int cs = children->get(n)->cell.colspanEff; - // These values will help if the span has percents. - int spanHasColPercent = 0; - int availSpanMinW = spanMinW; - float cumSpanPercent = 0.0f; - for (int i = col; i < col + cs; ++i) { - if (core::style::isPerLength (colPercents->get(i))) { - cumSpanPercent += core::style::perLengthVal (colPercents->get(i)); - ++spanHasColPercent; - } else - availSpanMinW -= colExtremes->getRef(i)->minWidth; - } - - // Calculate weighted-apportion columns for this span. - int wMin = 0, wMax; - int cumMaxWnew = 0, cumMaxWold = 0, goalMaxW = spanMaxW; - int curAppW = maxSumCols; - int curExtraW = spanMinW - minSumCols; - for (int i = col; i < col + cs; ++i) { - - if (!spanHasColPercent) { - int d_a = colExtremes->getRef(i)->maxWidth; - int d_w = curAppW > 0 ? (int)((float)curExtraW * d_a/curAppW) : 0; - if (d_a < 0||d_w < 0) { - MSG("d_a=%d d_w=%d\n",d_a,d_w); - exit(1); - } - wMin = colExtremes->getRef(i)->minWidth + d_w; - colExtremes->getRef(i)->minWidth = wMin; - curExtraW -= d_w; - curAppW -= d_a; - } else { - if (core::style::isPerLength (colPercents->get(i))) { - // multiplyWithPerLength would cause rounding errors, - // therefore the deprecated way, using perLengthVal: - wMin = misc::max (colExtremes->getRef(i)->minWidth, - (int)(availSpanMinW * - core::style::perLengthVal - (colPercents->get (i)) - / cumSpanPercent)); - colExtremes->getRef(i)->minWidth = wMin; - } - } - - wMax = (goalMaxW-cumMaxWnew <= 0) ? 0 : - (int)((float)(goalMaxW-cumMaxWnew) - * colExtremes->getRef(i)->maxWidth - / (maxSumCols-cumMaxWold)); - wMax = misc::max (wMin, wMax); - cumMaxWnew += wMax; - cumMaxWold += colExtremes->getRef(i)->maxWidth; - colExtremes->getRef(i)->maxWidth = wMax; - - _MSG("i=%d, wMin=%d wMax=%d cumMaxWold=%d\n", - i,wMin,wMax,cumMaxWold); + core::Extremes cellExtremes; + children->get(n)->cell.widget->getExtremes (&cellExtremes); + calcExtremesSpanMulteCols (col, cs, &cellExtremes, MIN, MAX); + calcExtremesSpanMulteCols (col, cs, &cellExtremes, MIN_INTR, MAX_INTR); } -#ifdef DBG - MSG("col min,max: ["); - for (int i = 0; i < numCols; i++) - MSG("%d,%d ", - colExtremes->getRef(i)->minWidth, - colExtremes->getRef(i)->maxWidth); - MSG("]\n"); - MSG("getStyle()->hBorderSpacing = %d\n", getStyle()->hBorderSpacing); -#endif } -} -/** - * \brief Apportionment function for AUTO-length columns. - * 'extremes' comes filled, 'result' comes defined for percentage columns. - */ -void Table::apportion2 (int totalWidth, int forceTotalWidth) -{ - if (colExtremes->size() == 0) - return; -#ifdef DBG - MSG("app2, availWidth=%d, totalWidth=%d, forceTotalWidth=%d\n", - availWidth, totalWidth, forceTotalWidth); - MSG("app2, extremes: ( "); - for (int i = 0; i < colExtremes->size (); i++) - MSG("%d,%d ", - colExtremes->get(i).minWidth, colExtremes->get(i).maxWidth); - MSG(")\n"); -#endif - int minAutoWidth = 0, maxAutoWidth = 0, availAutoWidth = totalWidth; - for (int col = 0; col < numCols; col++) { - if (core::style::isAbsLength (colPercents->get(col))) { - // set absolute lengths - setColWidth (col, colExtremes->get(col).minWidth); + DBG_IF_RTFL { + DBG_OBJ_SET_NUM ("colExtremes.size", colExtremes->size ()); + for (int i = 0; i < colExtremes->size (); i++) { + DBG_OBJ_ARRATTRSET_NUM ("colExtremes", i, "minWidth", + colExtremes->get(i).minWidth); + DBG_OBJ_ARRATTRSET_NUM ("colExtremes", i, "minWidthIntrinsic", + colExtremes->get(i).minWidthIntrinsic); + DBG_OBJ_ARRATTRSET_NUM ("colExtremes", i, "maxWidth", + colExtremes->get(i).maxWidth); + DBG_OBJ_ARRATTRSET_NUM ("colExtremes", i, "maxWidthIntrinsic", + colExtremes->get(i).maxWidthIntrinsic); } - if (colPercents->get(col) == core::style::LENGTH_AUTO) { - maxAutoWidth += colExtremes->get(col).maxWidth; - minAutoWidth += colExtremes->get(col).minWidth; - } else - availAutoWidth -= colWidths->get(col); } - if (!maxAutoWidth) // no core::style::LENGTH_AUTO cols! - return; - - colWidths->setSize (colExtremes->size (), 0); + colWidthsUpToDateWidthColExtremes = false; + DBG_OBJ_SET_BOOL ("colWidthsUpToDateWidthColExtremes", + colWidthsUpToDateWidthColExtremes); - if (!forceTotalWidth && maxAutoWidth < availAutoWidth) { - // Enough space for the maximum table, don't widen past max. - availAutoWidth = maxAutoWidth; - } - - // General case. - int curTargetWidth = misc::max (availAutoWidth, minAutoWidth); - int curExtraWidth = curTargetWidth - minAutoWidth; - int curMaxWidth = maxAutoWidth; - int curNewWidth = minAutoWidth; - for (int col = 0; col < numCols; col++) { - _MSG("app2, col %d, minWidth=%d maxWidth=%d\n", - col, colExtremes->getRef(col)->minWidth, - colExtremes->get(col).maxWidth); - - if (colPercents->get(col) != core::style::LENGTH_AUTO) - continue; - - int colMinWidth = colExtremes->getRef(col)->minWidth; - int colMaxWidth = colExtremes->getRef(col)->maxWidth; - int w = (curMaxWidth <= 0) ? 0 : - (int)((float)curTargetWidth * colMaxWidth/curMaxWidth); - - _MSG("app2, curTargetWidth=%d colMaxWidth=%d curMaxWidth=%d " - "curNewWidth=%d ", - curTargetWidth, colMaxWidth,curMaxWidth,curNewWidth); - _MSG("w = %d, ", w); - - if (w <= colMinWidth) - w = colMinWidth; - else if (curNewWidth - colMinWidth + w > curTargetWidth) - w = colMinWidth + curExtraWidth; - - _MSG("w = %d\n", w); - - curNewWidth -= colMinWidth; - curMaxWidth -= colMaxWidth; - curExtraWidth -= (w - colMinWidth); - curTargetWidth -= w; - setColWidth (col, w); - } -#ifdef DBG - MSG("app2, result: ( "); - for (int i = 0; i < colWidths->size (); i++) - MSG("%d ", colWidths->get (i)); - MSG(")\n"); -#endif + DBG_OBJ_MSG_END (); } -void Table::apportion_percentages2(int totalWidth, int forceTotalWidth) +void Table::calcExtremesSpanMulteCols (int col, int cs, + core::Extremes *cellExtremes, + ExtrMod minExtrMod, ExtrMod maxExtrMod) { - int hasTablePercent = core::style::isPerLength (getStyle()->width) ? 1 : 0; - - if (colExtremes->size() == 0 || (!hasTablePercent && !hasColPercent)) - return; - - // If there's a table-wide percentage, totalWidth comes already scaled. - _MSG("APP_P, availWidth=%d, totalWidth=%d, forceTotalWidth=%d\n", - availWidth, totalWidth, forceTotalWidth); + DBG_OBJ_MSGF ("resize", 0, + "<b>calcExtremesSpanMulteCols (%d, %d, ..., %s, %s)", + col, cs, getExtrModName (minExtrMod), + getExtrModName (minExtrMod)); + DBG_OBJ_MSG_START (); + + int cellMin = getExtreme (cellExtremes, minExtrMod); + int cellMax = getExtreme (cellExtremes, maxExtrMod); + + int minSumCols = 0, maxSumCols = 0; + + for (int j = 0; j < cs; j++) { + minSumCols += getColExtreme (col + j, minExtrMod); + maxSumCols += getColExtreme (col + j, maxExtrMod); + } - if (!hasColPercent) { -#ifdef DBG - MSG("APP_P, only a table-wide percentage\n"); - MSG("APP_P, extremes = { "); - for (int col = 0; col < numCols; col++) - MSG("%d,%d ", colExtremes->getRef(col)->minWidth, - colExtremes->getRef(col)->maxWidth); - MSG("}\n"); -#endif - // It has only a table-wide percentage. Apportion non-absolute widths. - int sumMaxWidth = 0, perAvailWidth = totalWidth; - for (int col = 0; col < numCols; col++) { - if (core::style::isAbsLength (colPercents->get(col))) - perAvailWidth -= colExtremes->getRef(col)->maxWidth; - else - sumMaxWidth += colExtremes->getRef(col)->maxWidth; + DBG_OBJ_MSGF ("resize", 1, "cs = %d, cell: %d / %d, sum: %d / %d\n", + cs, cellMin, cellMax, minSumCols, maxSumCols); + + bool changeMin = cellMin > minSumCols; + bool changeMax = cellMax > maxSumCols; + if (changeMin || changeMax) { + // TODO This differs from the documentation? Should work, anyway. + misc::SimpleVector<int> newMin, newMax; + if (changeMin) + apportion2 (cellMin, col, col + cs - 1, MIN, MAX, &newMin, 0); + if (changeMax) + apportion2 (cellMax, col, col + cs - 1, MIN, MAX, &newMax, 0); + + for (int j = 0; j < cs; j++) { + if (changeMin) + setColExtreme (col + j, minExtrMod, newMin.get (j)); + if (changeMax) + setColExtreme (col + j, maxExtrMod, newMax.get (j)); + + // For cases where min and max are somewhat confused: + setColExtreme (col + j, maxExtrMod, + misc::max (getColExtreme (col + j, minExtrMod), + getColExtreme (col + j, maxExtrMod))); } + } - _MSG("APP_P, perAvailWidth=%d, sumMaxWidth=%d\n", - perAvailWidth, sumMaxWidth); - - for (int col = 0; col < numCols; col++) { - int max_wi = colExtremes->getRef(col)->maxWidth, new_wi; - if (!core::style::isAbsLength (colPercents->get(col))) { - new_wi = - misc::max (colExtremes->getRef(col)->minWidth, - (int)((float)max_wi * perAvailWidth/sumMaxWidth)); - setColWidth (col, new_wi); - perAvailWidth -= new_wi; - sumMaxWidth -= max_wi; - } - } -#ifdef DBG - MSG("APP_P, result = { "); - for (int col = 0; col < numCols; col++) - MSG("%d ", colWidths->get(col)); - MSG("}\n"); -#endif - - } else { - // we'll have to apportion... - _MSG("APP_P, we'll have to apportion...\n"); - - // Calculate cumPercent and available space - float cumPercent = 0.0f; - int hasAutoCol = 0; - int sumMinWidth = 0, sumMaxWidth = 0, sumMinNonPer = 0, sumMaxNonPer = 0; - for (int col = 0; col < numCols; col++) { - if (core::style::isPerLength (colPercents->get(col))) { - cumPercent += core::style::perLengthVal (colPercents->get(col)); - } else { - sumMinNonPer += colExtremes->getRef(col)->minWidth; - sumMaxNonPer += colExtremes->getRef(col)->maxWidth; - if (colPercents->get(col) == core::style::LENGTH_AUTO) - hasAutoCol++; - } - sumMinWidth += colExtremes->getRef(col)->minWidth; - sumMaxWidth += colExtremes->getRef(col)->maxWidth; + DBG_OBJ_MSG_END (); +} - _MSG("APP_P, col %d minWidth=%d maxWidth=%d\n", col, - colExtremes->getRef(col)->minWidth, - colExtremes->getRef(col)->maxWidth); +/** + * \brief Actual apportionment function. + */ +void Table::apportion2 (int totalWidth, int firstCol, int lastCol, + ExtrMod minExtrMod, ExtrMod maxExtrMod, + misc::SimpleVector<int> *dest, int destOffset) +{ + DBG_OBJ_MSGF ("resize", 0, + "<b>apportion2</b> (%d, %d, %d, %s, %s, ..., %d)", + totalWidth, firstCol, lastCol, getExtrModName (minExtrMod), + getExtrModName (maxExtrMod), destOffset); + DBG_OBJ_MSG_START (); + + if (lastCol >= firstCol) { + dest->setSize (destOffset + lastCol - firstCol + 1, 0); + + int totalMin = 0, totalMax = 0; + for (int col = firstCol; col <= lastCol; col++) { + totalMin += getColExtreme (col, minExtrMod); + totalMax += getColExtreme (col, maxExtrMod); } - int oldTotalWidth = totalWidth; - if (!forceTotalWidth) { - if (sumMaxNonPer == 0 || cumPercent < 0.99f) { - // only percentage columns, or cumPercent < 100% => restrict width - int totW = (int)(sumMaxNonPer / (1.0f - cumPercent)); - for (int col = 0; col < numCols; col++) { - totW = misc::max - (totW, - (int)(colExtremes->getRef(col)->maxWidth - / core::style::perLengthVal (colPercents->get(col)))); - } - totalWidth = misc::min (totW, totalWidth); + + DBG_OBJ_MSGF ("resize", 1, + "totalWidth = %d, totalMin = %d, totalMax = %d", + totalWidth, totalMin, totalMax); + + // The actual calculation is rather simple, the ith value is: + // + // + // (max[i] - min[i]) * (totalMax - totalMin) + // width[i] = totalMin + ----------------------------------------- + // (totalWidth - totalMin) + // + // (Regard "total" as "sum".) With the following general + // definitions (for both the list and sums): + // + // diffExtr = max - min + // diffWidth = width - min + // + // it is simplified to: + // + // diffExtr[i] * totalDiffWidth + // diffWidth[i] = ---------------------------- + // totalDiffExtr + // + // Of course, if totalDiffExtr is 0, this is not defined; + // instead, we apportion according to the minima: + // + // min[i] * totalWidth + // width[i] = ------------------- + // totalMin + // + // Since min[i] <= max[i] for all i, totalMin == totalMax + // implies that min[i] == max[i] for all i. + // + // Third, it totalMin == 0 (which also implies min[i] = max[i] = 0), + // the result is + // + // width[i] = totalWidth / n + + int totalDiffExtr = totalMax - totalMin; + if (totalDiffExtr != 0) { + // Normal case. The algorithm described in + // "rounding-errors.doc" is used, with: + // + // x[i] = diffExtr[i] + // y[i] = diffWidth[i] + // a = totalDiffWidth + // b = totalDiffExtr + + DBG_OBJ_MSG ("resize", 1, "normal case"); + + int totalDiffWidth = totalWidth - totalMin; + int cumDiffExtr = 0, cumDiffWidth = 0; + + for (int col = firstCol; col <= lastCol; col++) { + int min = getColExtreme (col, minExtrMod); + int max = getColExtreme (col, maxExtrMod); + int diffExtr = max - min; + + cumDiffExtr += diffExtr; + int diffWidth = + (cumDiffExtr * totalDiffWidth) / totalDiffExtr - cumDiffWidth; + cumDiffWidth += diffWidth; + + dest->set (destOffset - firstCol + col, diffWidth + min); } - } - - // make sure there's enough space - totalWidth = misc::max (totalWidth, sumMinWidth); - // extraWidth is always >= 0 - int extraWidth = totalWidth - sumMinWidth; - int sumMinWidthPer = sumMinWidth - sumMinNonPer; - int curPerWidth = sumMinWidthPer; - // percentages refer to workingWidth - int workingWidth = totalWidth - sumMinNonPer; - if (cumPercent < 0.99f) { - // In this case, use the whole table width - workingWidth = totalWidth; - curPerWidth = sumMinWidth; - } - - _MSG("APP_P, oldTotalWidth=%d totalWidth=%d" - " workingWidth=%d extraWidth=%d sumMinNonPer=%d\n", - oldTotalWidth,totalWidth,workingWidth,extraWidth,sumMinNonPer); - - for (int col = 0; col < numCols; col++) { - int colMinWidth = colExtremes->getRef(col)->minWidth; - if (core::style::isPerLength (colPercents->get(col))) { - int w = core::style::multiplyWithPerLength (workingWidth, - colPercents->get(col)); - if (w < colMinWidth) - w = colMinWidth; - else if (curPerWidth - colMinWidth + w > workingWidth) - w = colMinWidth + extraWidth; - extraWidth -= (w - colMinWidth); - curPerWidth += (w - colMinWidth); - setColWidth (col, w); - } else { - setColWidth (col, colMinWidth); + } else if (totalMin != 0) { + // Special case. Again, same algorithm, with + // + // x[i] = min[i] + // y[i] = width[i] + // a = totalWidth + // b = totalMin + + DBG_OBJ_MSG ("resize", 1, "special case 1"); + + int cumMin = 0, cumWidth = 0; + for (int col = firstCol; col <= lastCol; col++) { + int min = getColExtreme (col, minExtrMod); + cumMin += min; + int width = (cumMin * totalWidth) / totalMin - cumWidth; + cumWidth += width; + + dest->set (destOffset - firstCol + col, width); } - } - - if (cumPercent < 0.99f) { - // Will have to apportion the other columns -#ifdef DBG - MSG("APP_P, extremes: ( "); - for (int i = 0; i < colExtremes->size (); i++) - MSG("%d,%d ", - colExtremes->get(i).minWidth, colExtremes->get(i).maxWidth); - MSG(")\n"); -#endif - curPerWidth -= sumMinNonPer; - int perWidth = (int)(curPerWidth/cumPercent); - totalWidth = misc::max (totalWidth, perWidth); - totalWidth = misc::min (totalWidth, oldTotalWidth); - - _MSG("APP_P, curPerWidth=%d perWidth=%d, totalWidth=%d\n", - curPerWidth, perWidth, totalWidth); - - if (hasAutoCol == 0) { - // Special case, cumPercent < 100% and no other columns to expand. - // We'll honor totalWidth by expanding the percentage cols. - int extraWidth = totalWidth - curPerWidth - sumMinNonPer; - for (int col = 0; col < numCols; col++) { - if (core::style::isPerLength (colPercents->get(col))) { - // This could cause rounding errors: - // - // int d = - // core::dw::multiplyWithPerLength (extraWidth, - // colPercents->get(col)) - // / cumPercent; - // - // Thus the "old" way: - int d = - (int)(extraWidth * - core::style::perLengthVal (colPercents->get(col)) - / cumPercent); - setColWidth (col, colWidths->get(col) + d); - } - } + } else if (totalMin != 0) { + // Last special case. Ssame algorithm, with + // + // x[i] = 1 (so cumX = i = col - firstCol + 1) + // y[i] = width[i] + // a = totalWidth + // b = n = lastCol - firstCol + 1 + + DBG_OBJ_MSG ("resize", 1, "special case 2"); + + int cumWidth = 0, n = (lastCol - firstCol + 1); + for (int col = firstCol; col <= lastCol; col++) { + int i = (col - firstCol + 1); + int width = (i * totalWidth) / n - cumWidth; + cumWidth += width; + + dest->set (destOffset - firstCol + col, width); } } -#ifdef DBG - MSG("APP_P, result ={ "); - for (int col = 0; col < numCols; col++) - MSG("%d ", colWidths->get(col)); - MSG("}\n"); -#endif - apportion2 (totalWidth, 2); - -#ifdef DBG - MSG("APP_P, percent={"); - for (int col = 0; col < numCols; col++) - MSG("%f ", core::dw::perLengthVal (colPercents->get(col))); - MSG("}\n"); - MSG("APP_P, result ={ "); - for (int col = 0; col < numCols; col++) - MSG("%d ", colWidths->get(col)); - MSG("}\n"); -#endif } -} - -// ---------------------------------------------------------------------- - -Table::TableIterator::TableIterator (Table *table, - core::Content::Type mask, bool atEnd): - core::Iterator (table, mask, atEnd) -{ - index = atEnd ? table->children->size () : -1; - content.type = atEnd ? core::Content::END : core::Content::START; -} - -Table::TableIterator::TableIterator (Table *table, - core::Content::Type mask, int index): - core::Iterator (table, mask, false) -{ - this->index = index; - - if (index < 0) - content.type = core::Content::START; - else if (index >= table->children->size ()) - content.type = core::Content::END; - else { - content.type = core::Content::WIDGET_IN_FLOW; - content.widget = table->children->get(index)->cell.widget; - } -} - -object::Object *Table::TableIterator::clone() -{ - return new TableIterator ((Table*)getWidget(), getMask(), index); -} - -int Table::TableIterator::compareTo(object::Comparable *other) -{ - return index - ((TableIterator*)other)->index; -} - -bool Table::TableIterator::next () -{ - Table *table = (Table*)getWidget(); - - if (content.type == core::Content::END) - return false; - - // tables only contain widgets (in flow): - if ((getMask() & core::Content::WIDGET_IN_FLOW) == 0) { - content.type = core::Content::END; - return false; - } - - do { - index++; - if (index >= table->children->size ()) { - content.type = core::Content::END; - return false; - } - } while (table->children->get(index) == NULL || - table->children->get(index)->type != Child::CELL); - - content.type = core::Content::WIDGET_IN_FLOW; - content.widget = table->children->get(index)->cell.widget; - return true; -} - -bool Table::TableIterator::prev () -{ - Table *table = (Table*)getWidget(); - - if (content.type == core::Content::START) - return false; - - // tables only contain widgets (in flow): - if ((getMask() & core::Content::WIDGET_IN_FLOW) == 0) { - content.type = core::Content::START; - return false; - } - - do { - index--; - if (index < 0) { - content.type = core::Content::START; - return false; - } - } while (table->children->get(index) == NULL || - table->children->get(index)->type != Child::CELL); - - content.type = core::Content::WIDGET_IN_FLOW; - content.widget = table->children->get(index)->cell.widget; - return true; -} - -void Table::TableIterator::highlight (int start, int end, - core::HighlightLayer layer) -{ - /** todo Needs this an implementation? */ -} - -void Table::TableIterator::unhighlight (int direction, - core::HighlightLayer layer) -{ -} - -void Table::TableIterator::getAllocation (int start, int end, - core::Allocation *allocation) -{ - /** \bug Not implemented. */ + + DBG_OBJ_MSG_END (); } } // namespace dw diff --git a/dw/table.hh b/dw/table.hh index 6966a163..186f1f50 100644 --- a/dw/table.hh +++ b/dw/table.hh @@ -2,7 +2,7 @@ #define __DW_TABLE_HH__ #include "core.hh" -#include "tablecell.hh" +#include "alignedtablecell.hh" #include "../lout/misc.hh" namespace dw { @@ -10,6 +10,11 @@ namespace dw { /** * \brief A Widget for rendering tables. * + * <div style="border: 2px solid #ff0000; margin-top: 0.5em; + * margin-bottom: 0.5em; padding: 0.5em 1em; + * background-color: #ffefe0"><b>Warning:</b> Some parts of this + * description are outdated since \ref dw-grows.</div> + * * <h3>Introduction</h3> * * The dw::Table widget is used to render HTML tables. @@ -191,8 +196,9 @@ namespace dw { * * <ul> * <li> the specified absolute width of the table, when given, or - * <li> the available width (set by dw::Table::setWidth) times the specified - * percentage width of t(at max 100%), if the latter is given, or + * <li> the available width (set by dw::Table::setWidth [TODO outdated]) times + * the specified percentage width of t(at max 100%), if the latter is + * given, or * <li> otherwise the available width. * </ul> * @@ -356,7 +362,6 @@ private: friend class TableIterator; bool limitTextWidth, rowClosed; - int availWidth, availAscent, availDescent; // set by set... int numRows, numCols, curRow, curCol; lout::misc::SimpleVector<Child*> *children; @@ -383,19 +388,22 @@ private: * If a Cell has rowspan > 1, it goes into this array */ lout::misc::SimpleVector<int> *rowSpanCells; - /** - * If a Cell has colspan > 1, it goes into this array - */ - lout::misc::SimpleVector<int> *colSpanCells; lout::misc::SimpleVector<int> *baseline; lout::misc::SimpleVector<core::style::Style*> *rowStyle; - /** - * hasColPercent becomes true when any cell specifies a percentage width. - */ - int hasColPercent; - lout::misc::SimpleVector<core::style::Length> *colPercents; + bool colWidthsUpToDateWidthColExtremes; + + enum ExtrMod { MIN, MIN_INTR, MIN_MIN, MAX_MIN, MAX, MAX_INTR }; + + const char *getExtrModName (ExtrMod mod); + int getExtreme (core::Extremes *extremes, ExtrMod mod); + void setExtreme (core::Extremes *extremes, ExtrMod mod, int value); + + inline int getColExtreme (int col, ExtrMod mod) { + return getExtreme (colExtremes->getRef(col), mod); } + inline void setColExtreme (int col, ExtrMod mod, int value) { + setExtreme (colExtremes->getRef(col), mod, value); } inline bool childDefined(int n) { @@ -405,15 +413,19 @@ private: void reallocChildren (int newNumCols, int newNumRows); - void calcCellSizes (); - void forceCalcCellSizes (); + void calcCellSizes (bool calcHeights); + void forceCalcCellSizes (bool calcHeights); void apportionRowSpan (); void calcColumnExtremes (); void forceCalcColumnExtremes (); + void calcExtremesSpanMulteCols (int col, int cs, + core::Extremes *cellExtremes, + ExtrMod minExtrMod, ExtrMod maxExtrMod); - void apportion2 (int totalWidth, int forceTotalWidth); - void apportion_percentages2 (int totalWidth, int forceTotalWidth); + void apportion2 (int totalWidth, int firstCol, int lastCol, + ExtrMod minExtrMod, ExtrMod maxExtrMod, + lout::misc::SimpleVector<int> *dest, int destOffset); void setCumHeight (int row, int value) { @@ -423,23 +435,19 @@ private: } } - inline void setColWidth (int col, int value) - { - if (value != colWidths->get (col)) { - redrawX = lout::misc::min (redrawX, value); - colWidths->set (col, value); - } - } - protected: void sizeRequestImpl (core::Requisition *requisition); void getExtremesImpl (core::Extremes *extremes); void sizeAllocateImpl (core::Allocation *allocation); void resizeDrawImpl (); - void setWidth (int width); - void setAscent (int ascent); - void setDescent (int descent); + int getAvailWidthOfChild (Widget *child, bool forceValue); + + void containerSizeChangedForChildren (); + bool usesAvailWidth (); + + bool isBlockLevel (); + void draw (core::View *view, core::Rectangle *area); //bool buttonPressImpl (core::EventButton *event); @@ -454,11 +462,14 @@ public: Table(bool limitTextWidth); ~Table(); + int applyPerWidth (int containerWidth, core::style::Length perWidth); + int applyPerHeight (int containerHeight, core::style::Length perHeight); + core::Iterator *iterator (core::Content::Type mask, bool atEnd); void addCell (Widget *widget, int colspan, int rowspan); void addRow (core::style::Style *style); - TableCell *getCellRef (); + AlignedTableCell *getCellRef (); }; } // namespace dw diff --git a/dw/table_iterator.cc b/dw/table_iterator.cc new file mode 100644 index 00000000..4da0ef4f --- /dev/null +++ b/dw/table_iterator.cc @@ -0,0 +1,134 @@ +/* + * Dillo Widget + * + * Copyright 2005-2007, 2014 Sebastian Geerken <sgeerken@dillo.org> + * + * (This file was originally part of textblock.cc.) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "table.hh" + +using namespace lout; + +namespace dw { + +Table::TableIterator::TableIterator (Table *table, + core::Content::Type mask, bool atEnd): + core::Iterator (table, mask, atEnd) +{ + index = atEnd ? table->children->size () : -1; + content.type = atEnd ? core::Content::END : core::Content::START; +} + +Table::TableIterator::TableIterator (Table *table, + core::Content::Type mask, int index): + core::Iterator (table, mask, false) +{ + this->index = index; + + if (index < 0) + content.type = core::Content::START; + else if (index >= table->children->size ()) + content.type = core::Content::END; + else { + content.type = core::Content::WIDGET_IN_FLOW; + content.widget = table->children->get(index)->cell.widget; + } +} + +object::Object *Table::TableIterator::clone() +{ + return new TableIterator ((Table*)getWidget(), getMask(), index); +} + +int Table::TableIterator::compareTo(object::Comparable *other) +{ + return index - ((TableIterator*)other)->index; +} + +bool Table::TableIterator::next () +{ + Table *table = (Table*)getWidget(); + + if (content.type == core::Content::END) + return false; + + // tables only contain widgets (in flow): + if ((getMask() & core::Content::WIDGET_IN_FLOW) == 0) { + content.type = core::Content::END; + return false; + } + + do { + index++; + if (index >= table->children->size ()) { + content.type = core::Content::END; + return false; + } + } while (table->children->get(index) == NULL || + table->children->get(index)->type != Child::CELL); + + content.type = core::Content::WIDGET_IN_FLOW; + content.widget = table->children->get(index)->cell.widget; + return true; +} + +bool Table::TableIterator::prev () +{ + Table *table = (Table*)getWidget(); + + if (content.type == core::Content::START) + return false; + + // tables only contain widgets (in flow): + if ((getMask() & core::Content::WIDGET_IN_FLOW) == 0) { + content.type = core::Content::START; + return false; + } + + do { + index--; + if (index < 0) { + content.type = core::Content::START; + return false; + } + } while (table->children->get(index) == NULL || + table->children->get(index)->type != Child::CELL); + + content.type = core::Content::WIDGET_IN_FLOW; + content.widget = table->children->get(index)->cell.widget; + return true; +} + +void Table::TableIterator::highlight (int start, int end, + core::HighlightLayer layer) +{ + /** todo Needs this an implementation? */ +} + +void Table::TableIterator::unhighlight (int direction, + core::HighlightLayer layer) +{ +} + +void Table::TableIterator::getAllocation (int start, int end, + core::Allocation *allocation) +{ + /** \bug Not implemented. */ +} + +} // namespace dw diff --git a/dw/tablecell.hh b/dw/tablecell.hh deleted file mode 100644 index 1e13abf9..00000000 --- a/dw/tablecell.hh +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef __DW_TABLECELL_HH__ -#define __DW_TABLECELL_HH__ - -#include "core.hh" -#include "alignedtextblock.hh" - -namespace dw { - -class TableCell: public AlignedTextblock -{ -private: - int charWordIndex, charWordPos; - -protected: - int wordWrap (int wordIndex, bool wrapAll); - - int getValue (); - void setMaxValue (int maxValue, int value); - -public: - static int CLASS_ID; - - TableCell(TableCell *ref, bool limitTextWidth); - ~TableCell(); -}; - -} // namespace dw - -#endif // __DW_TABLECELL_HH__ diff --git a/dw/textblock.cc b/dw/textblock.cc index beb3f3f8..8cb42999 100644 --- a/dw/textblock.cc +++ b/dw/textblock.cc @@ -225,13 +225,12 @@ Textblock::Textblock (bool limitTextWidth) { DBG_OBJ_CREATE ("dw::Textblock"); registerName ("dw::Textblock", &CLASS_ID); - setFlags (BLOCK_LEVEL); setFlags (USES_HINTS); setButtonSensitive(true); containingBlock = NULL; hasListitemValue = false; - innerPadding = 0; + leftInnerPadding = 0; line1Offset = 0; ignoreLine1OffsetSometimes = false; mustQueueResize = false; @@ -265,14 +264,9 @@ Textblock::Textblock (bool limitTextWidth) hoverLink = -1; - // random values - availWidth = 100; - availAscent = 100; - availDescent = 0; - - DBG_OBJ_SET_NUM ("availWidth", availWidth); - DBG_OBJ_SET_NUM ("availAscent", availAscent); - DBG_OBJ_SET_NUM ("availDescent", availDescent); + // -1 means undefined. + lineBreakWidth = -1; + DBG_OBJ_SET_NUM ("lineBreakWidth", lineBreakWidth); verticalOffset = 0; DBG_OBJ_SET_NUM ("verticalOffset", verticalOffset); @@ -338,6 +332,14 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition) DBG_OBJ_MSG ("resize", 0, "<b>sizeRequestImpl</b> ()"); DBG_OBJ_MSG_START (); + int newLineBreakWidth = getAvailWidth (true); + if (newLineBreakWidth != lineBreakWidth) { + lineBreakWidth = newLineBreakWidth; + wrapRefLines = 0; + DBG_OBJ_SET_NUM ("lineBreakWidth", lineBreakWidth); + DBG_OBJ_SET_NUM ("wrapRefLines", wrapRefLines); + } + rewrap (); showMissingLines (); @@ -369,18 +371,33 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition) requisition->descent = 0; } - DBG_OBJ_MSGF ("resize", 1, "inner padding = %d, boxDiffWidth = %d", - innerPadding, getStyle()->boxDiffWidth ()); + DBG_OBJ_MSGF ("resize", 1, "left inner padding = %d, boxDiffWidth = %d", + leftInnerPadding, getStyle()->boxDiffWidth ()); - requisition->width += innerPadding + getStyle()->boxDiffWidth (); + requisition->width += leftInnerPadding + getStyle()->boxDiffWidth (); requisition->ascent += verticalOffset + getStyle()->boxOffsetY (); requisition->descent += getStyle()->boxRestHeight (); + DBG_OBJ_MSGF ("resize", 1, "before correction: %d * (%d + %d)", + requisition->width, requisition->ascent, requisition->descent); + + correctRequisition (requisition, core::splitHeightPreserveAscent); + // Dealing with parts out of flow, which may overlap the borders of // the text block. Base lines are ignored here: they do not play a // role (currently) and caring about them (for the future) would // cause too much problems. + // Notice that the order is not typical: correctRequisition should + // be the last call. However, calling correctRequisition after + // outOfFlowMgr->getSize may result again in a size which is too + // small for floats, so triggering again (and again) the resize + // idle function resulting in CPU hogging. See also + // getExtremesImpl. + // + // Is this really what we want? An alternative could be that + // OutOfFlowMgr::getSize honours CSS attributes an corrected sizes. + DBG_OBJ_MSGF ("resize", 1, "before considering OOF widgets: %d * (%d + %d)", requisition->width, requisition->ascent, requisition->descent); @@ -392,20 +409,8 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition) requisition->descent = oofHeight - requisition->ascent; } - DBG_OBJ_MSGF ("resize", 1, - "before considering availWidth (= %d): %d * (%d + %d)", - availWidth, requisition->width, requisition->ascent, - requisition->descent); - - if (requisition->width < availWidth) { - requisition->width = availWidth; - DBG_OBJ_MSGF ("resize", 1, "adjusting to availWidth => %d", - requisition->width); - } - - DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)", + DBG_OBJ_MSGF ("resize", 1, "final: %d * (%d + %d)", requisition->width, requisition->ascent, requisition->descent); - DBG_OBJ_MSG_END (); } @@ -414,44 +419,11 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition) */ void Textblock::getWordExtremes (Word *word, core::Extremes *extremes) { - if (word->content.type == core::Content::WIDGET_IN_FLOW) { - if (word->content.widget->usesHints ()) { - word->content.widget->getExtremes (extremes); - - if (core::style::isAbsLength (word->content.widget - ->getStyle()->width)) { - int width = - core::style::absLengthVal (word->content.widget - ->getStyle()->width); - if (extremes->minWidth < width) - extremes->minWidth = width; - if (extremes->maxWidth > width) - // maxWidth not smaller than minWidth - extremes->maxWidth = misc::max (width, extremes->minWidth); - } - } else { - if (core::style::isPerLength - (word->content.widget->getStyle()->width)) { - extremes->minWidth = 0; - if (word->content.widget->hasContents ()) - extremes->maxWidth = 1000000; - else - extremes->maxWidth = 0; - } else if (core::style::isAbsLength - (word->content.widget->getStyle()->width)) { - /* Fixed lengths are only applied to the content, so we have to - * add padding, border and margin. */ - extremes->minWidth = extremes->maxWidth = - core::style::absLengthVal (word->content.widget->getStyle() - ->width) - + word->style->boxDiffWidth (); - } else - word->content.widget->getExtremes (extremes); - } - } else { - extremes->minWidth = word->size.width; - extremes->maxWidth = word->size.width; - } + if (word->content.type == core::Content::WIDGET_IN_FLOW) + word->content.widget->getExtremes (extremes); + else + extremes->minWidth = extremes->minWidthIntrinsic = extremes->maxWidth = + extremes->maxWidthIntrinsic = word->size.width; } void Textblock::getExtremesImpl (core::Extremes *extremes) @@ -459,41 +431,83 @@ void Textblock::getExtremesImpl (core::Extremes *extremes) DBG_OBJ_MSG ("resize", 0, "<b>getExtremesImpl</b>"); DBG_OBJ_MSG_START (); + // TODO Can extremes depend on the available width? Should not; if + // they do, the following code must be reactivated, but it causes + // an endless recursion. +#if 0 + int newLineBreakWidth = getAvailWidth (true); + if (newLineBreakWidth != lineBreakWidth) { + lineBreakWidth = newLineBreakWidth; + wrapRefParagraphs = 0; + DBG_OBJ_SET_NUM ("lineBreakWidth", lineBreakWidth); + DBG_OBJ_SET_NUM ("wrapRefParagraphs", wrapRefLines); + } +#endif + fillParagraphs (); if (paragraphs->size () == 0) { /* empty page */ extremes->minWidth = 0; + extremes->minWidthIntrinsic = 0; extremes->maxWidth = 0; + extremes->maxWidthIntrinsic = 0; } else { Paragraph *lastPar = paragraphs->getLastRef (); extremes->minWidth = lastPar->maxParMin; + extremes->minWidthIntrinsic = lastPar->maxParMinIntrinsic; extremes->maxWidth = lastPar->maxParMax; + extremes->maxWidthIntrinsic = lastPar->maxParMaxIntrinsic; - DBG_OBJ_MSGF ("resize", 1, "paragraphs[%d]->maxParMin = %d", - paragraphs->size () - 1, lastPar->maxParMin); - DBG_OBJ_MSGF ("resize", 1, "paragraphs[%d]->maxParMax = %d", - paragraphs->size () - 1, lastPar->maxParMax); + DBG_OBJ_MSGF ("resize", 1, "paragraphs[%d]->maxParMin = %d (%d)", + paragraphs->size () - 1, lastPar->maxParMin, + lastPar->maxParMinIntrinsic); + DBG_OBJ_MSGF ("resize", 1, "paragraphs[%d]->maxParMax = %d (%d)", + paragraphs->size () - 1, lastPar->maxParMax, + lastPar->maxParMaxIntrinsic); } - int diff = innerPadding + getStyle()->boxDiffWidth (); + DBG_OBJ_MSGF ("resize", 0, "after considering paragraphs: %d (%d) / %d (%d)", + extremes->minWidth, extremes->minWidthIntrinsic, + extremes->maxWidth, extremes->maxWidthIntrinsic); + + int diff = leftInnerPadding + getStyle()->boxDiffWidth (); extremes->minWidth += diff; + extremes->minWidthIntrinsic += diff; extremes->maxWidth += diff; + extremes->maxWidthIntrinsic += diff; + + DBG_OBJ_MSGF ("resize", 0, "after adding diff: %d (%d) / %d (%d)", + extremes->minWidth, extremes->minWidthIntrinsic, + extremes->maxWidth, extremes->maxWidthIntrinsic); + + // For the order, see similar reasoning in sizeRequestImpl. + + correctExtremes (extremes); + + DBG_OBJ_MSGF ("resize", 0, "after correction: %d (%d) / %d (%d)", + extremes->minWidth, extremes->minWidthIntrinsic, + extremes->maxWidth, extremes->maxWidthIntrinsic); if (outOfFlowMgr) { int oofMinWidth, oofMaxWidth; outOfFlowMgr->getExtremes (extremes, &oofMinWidth, &oofMaxWidth); - DBG_OBJ_MSGF ("resize", 1, "extremes: %d / %d, corrected: %d / %d", - extremes->minWidth, extremes->maxWidth, + DBG_OBJ_MSGF ("resize", 1, "OOFM correction: %d / %d", oofMinWidth, oofMaxWidth); extremes->minWidth = misc::max (extremes->minWidth, oofMinWidth); + extremes->minWidthIntrinsic = + misc::max (extremes->minWidthIntrinsic, oofMinWidth); extremes->maxWidth = misc::max (extremes->maxWidth, oofMaxWidth); + extremes->maxWidthIntrinsic = + misc::max (extremes->maxWidthIntrinsic, oofMinWidth); } - DBG_OBJ_MSGF ("resize", 1, "=> %d / %d", - extremes->minWidth, extremes->maxWidth); + DBG_OBJ_MSGF ("resize", 0, + "finally, after considering OOFM: %d (%d) / %d (%d)", + extremes->minWidth, extremes->minWidthIntrinsic, + extremes->maxWidth, extremes->maxWidthIntrinsic); DBG_OBJ_MSG_END (); } @@ -523,8 +537,13 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) } for (lineIndex = 0; lineIndex < lines->size (); lineIndex++) { + // Especially for floats, allocation->width may be different + // from the line break width, so that for centered and right + // text, the offsets have to be recalculated again. + calcTextOffset (lineIndex, allocation->width); + line = lines->getRef (lineIndex); - xCursor = line->offsetCompleteWidget; + xCursor = line->textOffset; for (wordIndex = line->firstWord; wordIndex <= line->lastWord; wordIndex++) { @@ -632,6 +651,23 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) } } +void Textblock::containerSizeChangedForChildren () +{ + for (int i = 0; i < words->size (); i++) { + Word *word = words->getRef (i); + if (word->content.type == core::Content::WIDGET_IN_FLOW) + word->content.widget->containerSizeChanged (); + } + + if (outOfFlowMgr) + outOfFlowMgr->containerSizeChangedForChildren (); +} + +bool Textblock::usesAvailWidth () +{ + return true; +} + void Textblock::resizeDrawImpl () { DBG_OBJ_MSG ("draw", 0, "<b>resizeDrawImpl</b> ()"); @@ -773,53 +809,9 @@ void Textblock::notifySetParent () assert (containingBlock != NULL); } -void Textblock::setWidth (int width) +bool Textblock::isBlockLevel () { - /* If limitTextWidth is set to YES, a queueResize() may also be - * necessary. */ - if (availWidth != width || limitTextWidth) { - DBG_OBJ_MSGF ("resize", 0, "<b>setWidth</b> (%d)", width); - DBG_OBJ_MSG_START (); - - availWidth = width; - DBG_OBJ_SET_NUM ("availWidth", availWidth); - queueResize (OutOfFlowMgr::createRefNormalFlow (0), false); - mustQueueResize = false; - redrawY = 0; - DBG_OBJ_SET_NUM ("redrawY", redrawY); - - DBG_OBJ_MSG_END (); - } -} - -void Textblock::setAscent (int ascent) -{ - if (availAscent != ascent) { - DBG_OBJ_MSGF ("resize", 0, "<b>setAscent</b> (%d)", ascent); - DBG_OBJ_MSG_START (); - - availAscent = ascent; - DBG_OBJ_SET_NUM ("availAscent", availAscent); - queueResize (OutOfFlowMgr::createRefNormalFlow (0), false); - mustQueueResize = false; - - DBG_OBJ_MSG_END (); - } -} - -void Textblock::setDescent (int descent) -{ - if (availDescent != descent) { - DBG_OBJ_MSGF ("resize", 0, "<b>setDescent</b> (%d)", descent); - DBG_OBJ_MSG_START (); - - availDescent = descent; - DBG_OBJ_SET_NUM ("availDescent", availDescent); - queueResize (OutOfFlowMgr::createRefNormalFlow (0), false); - mustQueueResize = false; - - DBG_OBJ_MSG_END (); - } + return true; } bool Textblock::buttonPressImpl (core::EventButton *event) @@ -935,11 +927,11 @@ bool Textblock::sendSelectionEvent (core::SelectionState::EventType eventType, // Choose this break. wordIndex = line->lastWord; charPos = core::SelectionState::END_OF_WORD; - } else if (event->xWidget < line->offsetCompleteWidget) { + } else if (event->xWidget < line->textOffset) { // Left of the first word in the line. wordIndex = line->firstWord; } else { - int nextWordStartX = line->offsetCompleteWidget; + int nextWordStartX = line->textOffset; for (wordIndex = line->firstWord; wordIndex <= line->lastWord; @@ -1052,7 +1044,6 @@ core::Iterator *Textblock::iterator (core::Content::Type mask, bool atEnd) return new TextblockIterator (this, mask, atEnd); } - /** * Calculate the size of a widget within the page. * (Subject of change in the near future!) @@ -1061,78 +1052,11 @@ void Textblock::calcWidgetSize (core::Widget *widget, core::Requisition *size) { DBG_OBJ_MSGF ("resize", 0, "<b>calcWidgetSize</b> (%p, ...)", widget); - core::Requisition requisition; - int availWidth, availAscent, availDescent; - core::style::Style *wstyle = widget->getStyle(); + widget->sizeRequest (size); - /* We ignore line1_offset[_eff]. */ - availWidth = this->availWidth - getStyle()->boxDiffWidth () - innerPadding; - availAscent = this->availAscent - getStyle()->boxDiffHeight (); - availDescent = this->availDescent; - - if (widget->usesHints ()) { - // This is a simplified version of calcAvailWidth (see there for - // more details). Until recently, the *attribute* availWidth was - // used, widthout any corrections. To limit the damage, only - // includde left and right border (by floats), until the Great - // Redesign Of Widget Sizes (GROWS). - int corrAvailWidth; - // Textblocks keep track of borders themselves, so they get the - // total available width. (Should once replaced by something - // like OOFAware.) - if (widget->instanceOf (Textblock::CLASS_ID)) - corrAvailWidth = availWidth; - else - corrAvailWidth = - misc::max (availWidth - (newLineLeftBorder + newLineRightBorder), - 0); - - DBG_OBJ_MSGF ("resize", 1, "setting hints: %d, %d, %d", - corrAvailWidth, availAscent, availDescent); - widget->setWidth (corrAvailWidth); - widget->setAscent (availAscent); - widget->setDescent (availDescent); - widget->sizeRequest (size); - DBG_OBJ_MSGF ("resize", 1, "sizeRequest => %d * (%d + %d)", - size->width, size->ascent, size->descent); - } else { - if (wstyle->width == core::style::LENGTH_AUTO || - wstyle->height == core::style::LENGTH_AUTO) { - widget->sizeRequest (&requisition); - DBG_OBJ_MSGF ("resize", 1, "AUTO; sizeRequest => %d * (%d + %d)", - requisition.width, requisition.ascent, - requisition.descent); - } - - if (wstyle->width == core::style::LENGTH_AUTO) - size->width = requisition.width; - else if (core::style::isAbsLength (wstyle->width)) - /* Fixed lengths are only applied to the content, so we have to - * add padding, border and margin. */ - size->width = core::style::absLengthVal (wstyle->width) - + wstyle->boxDiffWidth (); - else - size->width = - core::style::multiplyWithPerLength (availWidth, wstyle->width); - - if (wstyle->height == core::style::LENGTH_AUTO) { - size->ascent = requisition.ascent; - size->descent = requisition.descent; - } else if (core::style::isAbsLength (wstyle->height)) { - /* Fixed lengths are only applied to the content, so we have to - * add padding, border and margin. */ - size->ascent = core::style::absLengthVal (wstyle->height) - + wstyle->boxDiffHeight (); - size->descent = 0; - } else { - size->ascent = - core::style::multiplyWithPerLength (wstyle->height, availAscent); - size->descent = - core::style::multiplyWithPerLength (wstyle->height, availDescent); - } - } - - /* ascent and descent in words do not contain margins. */ + // Ascent and descent in words do not contain margins. + // TODO: Re-evaluate (GROWS)! + core::style::Style *wstyle = widget->getStyle(); size->ascent -= wstyle->margin.top; size->descent -= wstyle->margin.bottom; @@ -1454,7 +1378,7 @@ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area) area->x, area->y, area->width, area->height); DBG_OBJ_MSG_START (); - int xWidget = line->offsetCompleteWidget; + int xWidget = line->textOffset; int yWidgetBase = lineYOffsetWidget (line) + line->boxAscent; DBG_OBJ_MSGF ("draw", 1, "line from %d to %d (%d words), at (%d, %d)", @@ -1647,7 +1571,7 @@ Textblock::Word *Textblock::findWord (int x, int y, bool *inSpace) if (yWidgetBase + line->boxDescent <= y) return NULL; - xCursor = line->offsetCompleteWidget; + xCursor = line->textOffset; for (wordIndex = line->firstWord; wordIndex <= line->lastWord;wordIndex++) { word = words->getRef (wordIndex); lastXCursor = xCursor; @@ -1720,6 +1644,11 @@ Textblock::Word *Textblock::addWord (int width, int ascent, int descent, width, ascent, descent, flags, style); DBG_OBJ_MSG_START (); + if (lineBreakWidth == -1) { + lineBreakWidth = getAvailWidth (true); + DBG_OBJ_SET_NUM ("lineBreakWidth", lineBreakWidth); + } + words->increase (); DBG_OBJ_SET_NUM ("words.size", words->size ()); int wordNo = words->size () - 1; diff --git a/dw/textblock.hh b/dw/textblock.hh index 3254b843..b112ba29 100644 --- a/dw/textblock.hh +++ b/dw/textblock.hh @@ -130,7 +130,7 @@ namespace dw { * widget: * * <ul> - * <li> The available size of the widget has changed, e.g., because the + * <li> The line break size of the widget has changed, e.g., because the * user has changed the size of the browser window. In this case, * it is necessary to rewrap all the lines. * @@ -306,14 +306,18 @@ protected: int parMin; /* The sum of all word minima (plus spaces, hyphen width etc.) since the last possible break within this paragraph. */ + int parMinIntrinsic; int parMax; /* The sum of all word maxima in this paragraph (plus spaces, hyphen width etc.). */ + int parMaxIntrinsic; int maxParMin; /* Maximum of all paragraph minima (value of "parMin"), including this paragraph. */ + int maxParMinIntrinsic; int maxParMax; /* Maximum of all paragraph maxima (value of "parMax""), including this paragraph. */ + int maxParMaxIntrinsic; }; struct Line @@ -334,8 +338,7 @@ protected: int contentAscent; /* ??? */ int contentDescent; /* ??? */ int breakSpace; /* Space between this line and the next one. */ - int leftOffset; /* ??? */ - int offsetCompleteWidget; /* ??? */ + int textOffset; /* ??? */ /* This is similar to descent, but includes the bottom margins of the * widgets within this line. */ @@ -361,6 +364,9 @@ protected: * even a following line, when positioned before (this is the * reason this attribute exists); see \ref dw-out-of-flow. */ int lastOofRefPositionedBeforeThisLine; + + int leftOffset, rightOffset; + enum { LEFT, RIGHT, CENTER } alignment; }; struct Word @@ -472,7 +478,7 @@ protected: /* These fields provide some ad-hoc-functionality, used by sub-classes. */ bool hasListitemValue; /* If true, the first word of the page is treated specially (search in source). */ - int innerPadding; /* This is an additional padding on the left side + int leftInnerPadding; /* This is an additional padding on the left side (used by ListItem). */ int line1Offset; /* This is an additional offset of the first line. May be negative (shift to left) or positive @@ -489,7 +495,7 @@ protected: * (which is used by DwTable!), and * (ii) line1_offset is ignored (line1_offset_eff is set to 0), * when line1_offset plus the width of the first word is - * greater than the the available witdh. + * greater than the the line break witdh. * * \todo Eliminate all these ad-hoc features by a new, simpler and * more elegant design. ;-) @@ -516,8 +522,8 @@ protected: int redrawY; int lastWordDrawn; - /* These values are set by set_... */ - int availWidth, availAscent, availDescent; + /* This value is (currently) set by setAscent(). */ + int lineBreakWidth; // Additional vertical offset, used for the "clear" attribute. int verticalOffset; @@ -702,8 +708,6 @@ protected: bool sendSelectionEvent (core::SelectionState::EventType eventType, core::MousePositionEvent *event); - void accumulateWordExtremes (int firstWord, int lastWord, - int *maxOfMinWidth, int *sumOfMaxWidth); void processWord (int wordIndex); virtual int wordWrap (int wordIndex, bool wrapAll); int wrapWordInFlow (int wordIndex, bool wrapAll); @@ -737,22 +741,26 @@ protected: void moveWordIndices (int wordIndex, int num, int *addIndex1 = NULL); void accumulateWordForLine (int lineIndex, int wordIndex); void accumulateWordData (int wordIndex); - int calcAvailWidth (int lineIndex); + int calcLineBreakWidth (int lineIndex); void initLine1Offset (int wordIndex); void alignLine (int lineIndex); + void calcTextOffset (int lineIndex, int totalWidth); void sizeRequestImpl (core::Requisition *requisition); void getExtremesImpl (core::Extremes *extremes); void sizeAllocateImpl (core::Allocation *allocation); + void containerSizeChangedForChildren (); + bool usesAvailWidth (); void resizeDrawImpl (); void markSizeChange (int ref); void markExtremesChange (int ref); + void notifySetAsTopLevel(); void notifySetParent(); - void setWidth (int width); - void setAscent (int ascent); - void setDescent (int descent); + + bool isBlockLevel (); + void draw (core::View *view, core::Rectangle *area); bool buttonPressImpl (core::EventButton *event); @@ -814,9 +822,7 @@ public: queueResize (-1, extremesChanged); DBG_OBJ_MSG_END (); } - inline int getAvailWidth () { return availWidth; } - inline int getAvailAscent () { return availAscent; } - inline int getAvailDescent () { return availDescent; } + inline int getLineBreakWidth () { return lineBreakWidth; } }; #define DBG_SET_WORD_PENALTY(n, i, is) \ diff --git a/dw/textblock_iterator.cc b/dw/textblock_iterator.cc index c26b7f6e..56ad6e15 100644 --- a/dw/textblock_iterator.cc +++ b/dw/textblock_iterator.cc @@ -289,7 +289,7 @@ void Textblock::TextblockIterator::getAllocation (int start, int end, Word *word = textblock->words->getRef (index); allocation->x = - textblock->allocation.x + line->offsetCompleteWidget; + textblock->allocation.x + line->textOffset; for (int i = line->firstWord; i < index; i++) { Word *w = textblock->words->getRef(i); diff --git a/dw/textblock_linebreaking.cc b/dw/textblock_linebreaking.cc index a773f24c..7787ce63 100644 --- a/dw/textblock_linebreaking.cc +++ b/dw/textblock_linebreaking.cc @@ -92,7 +92,7 @@ void Textblock::BadnessAndPenalty::calcBadness (int totalWidth, int idealWidth, badness = ratio * ratio * ratio; } } - } else { // if (totalWidth > availWidth) + } else { // if (totalWidth > idealWidth) if (totalShrinkability == 0) badnessState = TOO_TIGHT; else { @@ -378,10 +378,6 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, lineWidth); } - int maxOfMinWidth, sumOfMaxWidth; - accumulateWordExtremes (firstWord, lastWord, &maxOfMinWidth, - &sumOfMaxWidth); - lines->increase (); DBG_OBJ_SET_NUM ("lines.size", lines->size ()); @@ -403,10 +399,17 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, line->boxDescent = line->contentDescent = 0; line->marginDescent = 0; line->breakSpace = 0; - line->leftOffset = 0; line->finished = false; + + bool regardBorder = mustBorderBeRegarded (line); + line->leftOffset = misc::max (regardBorder ? newLineLeftBorder : 0, + boxOffsetX () + leftInnerPadding + + (lineIndex == 0 ? line1OffsetEff : 0)); + line->rightOffset = misc::max (regardBorder ? newLineRightBorder : 0, + boxRestWidth ()); alignLine (lineIndex); + calcTextOffset (lineIndex, lineBreakWidth); for (int i = line->firstWord; i < line->lastWord; i++) { Word *word = words->getRef (i); @@ -438,14 +441,6 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, // the height should be positive. line->boxAscent = misc::max (line->boxAscent, 1); - // Calculate offsetCompleteWidget, which includes also floats. - int leftBorder = mustBorderBeRegarded (line) ? newLineLeftBorder : 0; - line->offsetCompleteWidget = - misc::max (leftBorder, - getStyle()->boxOffsetX() + innerPadding - + (lineIndex == 0 ? line1OffsetEff : 0)) - + line->leftOffset; - DBG_OBJ_MSGF ("construct.line", 1, "top = %d\n", line->top); DBG_OBJ_MSGF ("construct.line", 1, "boxAscent = %d\n", line->boxAscent); DBG_OBJ_MSGF ("construct.line", 1, "boxDescent = %d\n", line->boxDescent); @@ -455,8 +450,7 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, line->contentDescent); DBG_OBJ_MSGF ("construct.line", 1, "maxLineWidth = %d (lineWidth = %d)\n", line->maxLineWidth, lineWidth); - DBG_OBJ_MSGF ("construct.line", 1, "offsetCompleteWidget = %d\n", - line->offsetCompleteWidget); + DBG_OBJ_MSGF ("construct.line", 1, "textOffset = %d\n", line->textOffset); mustQueueResize = true; @@ -467,7 +461,7 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, //words->getRef(line->lastWord)->badnessAndPenalty.print (); //printf ("\n"); - int xWidget = line->offsetCompleteWidget; + int xWidget = line->textOffset; for (int i = firstWord; i <= lastWord; i++) { Word *word = words->getRef (i); if (word->wordImgRenderer) @@ -489,44 +483,6 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, return line; } -void Textblock::accumulateWordExtremes (int firstWord, int lastWord, - int *maxOfMinWidth, int *sumOfMaxWidth) -{ - int parMin = 0; - *maxOfMinWidth = *sumOfMaxWidth = 0; - - for (int i = firstWord; i <= lastWord; i++) { - Word *word = words->getRef (i); - bool atLastWord = i == lastWord; - - core::Extremes extremes; - getWordExtremes (word, &extremes); - - // Minimum: between two *possible* breaks (or at the end). - // TODO This is redundant to getExtremesImpl(). - // TODO: Again, index 1 is used for lineCanBeBroken(). See getExtremes(). - if (word->badnessAndPenalty.lineCanBeBroken (1) || atLastWord) { - parMin += extremes.minWidth + word->hyphenWidth; - *maxOfMinWidth = misc::max (*maxOfMinWidth, parMin); - parMin = 0; - } else - // Shrinkability could be considered, but really does not play a - // role. - parMin += extremes.minWidth + word->origSpace; - - //printf ("[%p] after word: ", this); - //printWord (word); - //printf ("\n"); - - //printf ("[%p] (%d / %d) => parMin = %d, maxOfMinWidth = %d\n", - // this, extremes.minWidth, extremes.maxWidth, parMin, - // *maxOfMinWidth); - - *sumOfMaxWidth += (extremes.maxWidth + word->origSpace); - // Notice that the last space is added. See also: Line::parMax. - } -} - void Textblock::processWord (int wordIndex) { DBG_OBJ_MSGF ("construct.all", 0, "<b>processWord</b> (%d)", wordIndex); @@ -637,7 +593,7 @@ int Textblock::wrapWordInFlow (int wordIndex, bool wrapAll) // be left empty. // (In other cases, lines are never left empty, even if this means - // that the contents is wider than the available witdh. Leaving + // that the contents is wider than the line break width. Leaving // lines empty does not make sense without floats, since there will // be no possibility with more space anymore.) @@ -1209,12 +1165,15 @@ void Textblock::handleWordExtremes (int wordIndex) core::Extremes wordExtremes; getWordExtremes (word, &wordExtremes); - DBG_OBJ_MSGF ("construct.paragraph", 1, "extremes: %d / %d", - wordExtremes.minWidth, wordExtremes.maxWidth); + DBG_OBJ_MSGF ("construct.paragraph", 1, "extremes: %d (%d) / %d (%d", + wordExtremes.minWidth, wordExtremes.minWidthIntrinsic, + wordExtremes.maxWidth, wordExtremes.maxWidthIntrinsic); if (wordIndex == 0) { wordExtremes.minWidth += line1OffsetEff; + wordExtremes.minWidthIntrinsic += line1OffsetEff; wordExtremes.maxWidth += line1OffsetEff; + wordExtremes.maxWidthIntrinsic += line1OffsetEff; } if (paragraphs->size() == 0 || @@ -1227,13 +1186,17 @@ void Textblock::handleWordExtremes (int wordIndex) Paragraph *par = paragraphs->getLastRef(); par->firstWord = par->lastWord = wordIndex; - par->parMin = par->parMax = 0; + par->parMin = par->parMinIntrinsic = par->parMax = par->parMaxIntrinsic + = 0; if (prevPar) { par->maxParMin = prevPar->maxParMin; + par->maxParMinIntrinsic = prevPar->maxParMinIntrinsic; par->maxParMax = prevPar->maxParMax; + par->maxParMaxIntrinsic = prevPar->maxParMaxIntrinsic; } else - par->maxParMin = par->maxParMax = 0; + par->maxParMin = par->maxParMinIntrinsic = par->maxParMax = + par->maxParMaxIntrinsic = 0; DBG_OBJ_MSGF ("construct.paragraph", 1, "new par: %d", paragraphs->size() - 1); @@ -1261,22 +1224,41 @@ void Textblock::handleWordExtremes (int wordIndex) lastPar->firstWord, lastPar->lastWord, corrDiffMin, corrDiffMax); + DBG_OBJ_MSGF ("construct.paragraph", 1, + "before: parMin = %d (%d) (max = %d (%d)), " + "parMax = %d (%d) (max = %d (%d))", + lastPar->parMin, lastPar->parMinIntrinsic, + lastPar->maxParMin, lastPar->maxParMinIntrinsic, + lastPar->parMax, lastPar->parMaxIntrinsic, + lastPar->maxParMax, lastPar->maxParMaxIntrinsic); + // Minimum: between two *possible* breaks. // Shrinkability could be considered, but really does not play a role. lastPar->parMin += wordExtremes.minWidth + word->hyphenWidth + corrDiffMin; + lastPar->parMinIntrinsic += + wordExtremes.minWidthIntrinsic + word->hyphenWidth + corrDiffMin; lastPar->maxParMin = misc::max (lastPar->maxParMin, lastPar->parMin); + lastPar->maxParMinIntrinsic = + misc::max (lastPar->maxParMinIntrinsic, lastPar->parMinIntrinsic); if (word->badnessAndPenalty.lineCanBeBroken (1) && (word->flags & Word::UNBREAKABLE_FOR_MIN_WIDTH) == 0) - lastPar->parMin = 0; + lastPar->parMin = lastPar->parMinIntrinsic = 0; // Maximum: between two *necessary* breaks. lastPar->parMax += wordExtremes.maxWidth + word->hyphenWidth + corrDiffMax; + lastPar->parMaxIntrinsic += + wordExtremes.maxWidthIntrinsic + word->hyphenWidth + corrDiffMax; lastPar->maxParMax = misc::max (lastPar->maxParMax, lastPar->parMax); + lastPar->maxParMaxIntrinsic = + misc::max (lastPar->maxParMaxIntrinsic, lastPar->parMaxIntrinsic); DBG_OBJ_MSGF ("construct.paragraph", 1, - "=> parMin = %d (max = %d), parMax = %d (max = %d)", - lastPar->parMin, lastPar->maxParMin, lastPar->parMax, - lastPar->maxParMax); + "after: parMin = %d (%d) (max = %d (%d)), " + "parMax = %d (%d) (max = %d (%d))", + lastPar->parMin, lastPar->parMinIntrinsic, + lastPar->maxParMin, lastPar->maxParMinIntrinsic, + lastPar->parMax, lastPar->parMaxIntrinsic, + lastPar->maxParMax, lastPar->maxParMaxIntrinsic); lastPar->lastWord = wordIndex; DBG_OBJ_MSG_END (); @@ -1474,7 +1456,7 @@ void Textblock::accumulateWordForLine (int lineIndex, int wordIndex) word->content.widget->getStyle()->margin.bottom); if (lines->size () == 1 && - word->content.widget->blockLevel () && + word->content.widget->isBlockLevel () && getStyle ()->borderWidth.top == 0 && getStyle ()->padding.top == 0) { // collapse top margins of parent element and its first child @@ -1526,12 +1508,13 @@ void Textblock::accumulateWordData (int wordIndex) Word *word = words->getRef (wordIndex); DBG_OBJ_MSGF ("construct.word.accum", 2, "lineIndex = %d", lineIndex); - int availWidth = calcAvailWidth (lineIndex); + int lineBreakWidth = calcLineBreakWidth (lineIndex); DBG_OBJ_MSGF ("construct.word.accum", 2, - "(%s existing line %d starts with word %d; availWidth = %d)", + "(%s existing line %d starts with word %d; " + "lineBreakWidth = %d)", lineIndex < lines->size () ? "already" : "not yet", - lineIndex, firstWordOfLine, availWidth); + lineIndex, firstWordOfLine, lineBreakWidth); if (wordIndex == firstWordOfLine) { // first word of the (not neccessarily yet existing) line @@ -1582,7 +1565,7 @@ void Textblock::accumulateWordData (int wordIndex) "totalShrinkability = %d + ... = %d", word->totalSpaceShrinkability, totalShrinkability); - word->badnessAndPenalty.calcBadness (word->totalWidth, availWidth, + word->badnessAndPenalty.calcBadness (word->totalWidth, lineBreakWidth, totalStretchability, totalShrinkability); @@ -1595,22 +1578,22 @@ void Textblock::accumulateWordData (int wordIndex) DBG_OBJ_MSG_END (); } -int Textblock::calcAvailWidth (int lineIndex) +int Textblock::calcLineBreakWidth (int lineIndex) { DBG_OBJ_MSGF ("construct.word.width", 1, - "<b>calcAvailWidth</b> (%d <i>of %d</i>)", + "<b>calcLineBreakWidth</b> (%d <i>of %d</i>)", lineIndex, lines->size()); DBG_OBJ_MSG_START (); - int availWidth = this->availWidth - innerPadding; + int lineBreakWidth = this->lineBreakWidth - leftInnerPadding; if (limitTextWidth && layout->getUsesViewport () && // margin/border/padding will be subtracted later, via OOFM. - availWidth - getStyle()->boxDiffWidth() + lineBreakWidth - getStyle()->boxDiffWidth() > layout->getWidthViewport () - 10) - availWidth = layout->getWidthViewport () - 10; + lineBreakWidth = layout->getWidthViewport () - 10; if (lineIndex == 0) - availWidth -= line1OffsetEff; + lineBreakWidth -= line1OffsetEff; int leftBorder, rightBorder; if (mustBorderBeRegarded (lineIndex)) { @@ -1622,14 +1605,14 @@ int Textblock::calcAvailWidth (int lineIndex) leftBorder = misc::max (leftBorder, getStyle()->boxOffsetX()); rightBorder = misc::max (rightBorder, getStyle()->boxRestWidth()); - availWidth -= (leftBorder + rightBorder); + lineBreakWidth -= (leftBorder + rightBorder); DBG_OBJ_MSGF ("construct.word.width", 2, "=> %d - %d - (%d + %d) = %d\n", - this->availWidth, innerPadding, leftBorder, rightBorder, - availWidth); + this->lineBreakWidth, leftInnerPadding, leftBorder, + rightBorder, lineBreakWidth); DBG_OBJ_MSG_END (); - return availWidth; + return lineBreakWidth; } void Textblock::initLine1Offset (int wordIndex) @@ -1639,18 +1622,18 @@ void Textblock::initLine1Offset (int wordIndex) /* Test whether line1Offset can be used. */ if (wordIndex == 0) { if (ignoreLine1OffsetSometimes && - line1Offset + word->size.width > availWidth) { + line1Offset + word->size.width > lineBreakWidth) { line1OffsetEff = 0; } else { int indent = 0; if (word->content.type == core::Content::WIDGET_IN_FLOW && - word->content.widget->blockLevel() == true) { + word->content.widget->isBlockLevel()) { /* don't use text-indent when nesting blocks */ } else { if (core::style::isPerLength(getStyle()->textIndent)) { indent = core::style::multiplyWithPerLengthRounded - (this->availWidth, getStyle()->textIndent); + (lineBreakWidth, getStyle()->textIndent); } else { indent = core::style::absLengthVal (getStyle()->textIndent); } @@ -1671,10 +1654,12 @@ void Textblock::alignLine (int lineIndex) DBG_OBJ_MSG_START (); Line *line = lines->getRef (lineIndex); - int availWidth = calcAvailWidth (lineIndex); + if (line->firstWord <= line->lastWord) { Word *firstWord = words->getRef (line->firstWord); Word *lastWord = words->getRef (line->lastWord); + int lineBreakWidth = + this->lineBreakWidth - (line->leftOffset + line->rightOffset); for (int i = line->firstWord; i < line->lastWord; i++) words->getRef(i)->origSpace = words->getRef(i)->effSpace; @@ -1684,18 +1669,18 @@ void Textblock::alignLine (int lineIndex) case core::style::TEXT_ALIGN_LEFT: DBG_OBJ_MSG ("construct.line", 1, "first word has 'text-align: left'"); - line->leftOffset = 0; + line->alignment = Line::LEFT; break; case core::style::TEXT_ALIGN_STRING: /* handled elsewhere (in the * future)? */ DBG_OBJ_MSG ("construct.line", 1, "first word has 'text-align: string'"); - line->leftOffset = 0; + line->alignment = Line::LEFT; break; case core::style::TEXT_ALIGN_JUSTIFY: /* see some lines above */ - line->leftOffset = 0; DBG_OBJ_MSG ("construct.line", 1, "first word has 'text-align: justify'"); + line->alignment = Line::LEFT; // Do not justify the last line of a paragraph (which ends on a // BREAK or with the last word of the page). if(!(lastWord->content.type == core::Content::BREAK || @@ -1704,35 +1689,64 @@ void Textblock::alignLine (int lineIndex) // when the line would be shrunken otherwise. (This solution is // far from perfect, but a better solution would make changes in // the line breaking algorithm necessary.) - availWidth < lastWord->totalWidth) - justifyLine (line, availWidth - lastWord->totalWidth); + lineBreakWidth < lastWord->totalWidth) + justifyLine (line, lineBreakWidth - lastWord->totalWidth); break; case core::style::TEXT_ALIGN_RIGHT: DBG_OBJ_MSG ("construct.line", 1, "first word has 'text-align: right'"); - line->leftOffset = availWidth - lastWord->totalWidth; + line->alignment = Line::RIGHT; break; case core::style::TEXT_ALIGN_CENTER: DBG_OBJ_MSG ("construct.line", 1, "first word has 'text-align: center'"); - line->leftOffset = (availWidth - lastWord->totalWidth) / 2; + line->alignment = Line::CENTER; break; default: - /* compiler happiness */ - line->leftOffset = 0; + // compiler happiness + line->alignment = Line::LEFT; } - /* For large lines (images etc), which do not fit into the viewport: */ - if (line->leftOffset < 0) - line->leftOffset = 0; - } + } else + // empty line (only line break); + line->alignment = Line::LEFT; } else // empty line - line->leftOffset = 0; + line->alignment = Line::LEFT; DBG_OBJ_MSG_END (); } +void Textblock::calcTextOffset (int lineIndex, int totalWidth) +{ + Line *line = lines->getRef (lineIndex); + int lineWidth = line->firstWord <= line->lastWord ? + words->getRef(line->lastWord)->totalWidth : 0; + + switch (line->alignment) { + case Line::LEFT: + line->textOffset = line->leftOffset; + break; + + case Line::RIGHT: + line->textOffset = totalWidth - line->rightOffset - lineWidth; + break; + + case Line::CENTER: + line->textOffset = + (line->leftOffset + totalWidth - line->rightOffset - lineWidth) / 2; + break; + + default: + misc::assertNotReached (); + break; + } + + // For large lines (images etc), which do not fit into the viewport: + if (line->textOffset < line->leftOffset) + line->textOffset = line->leftOffset; +} + /** * Rewrap the page from the line from which this is necessary. * There are basically two times we'll want to do this: diff --git a/dw/types.hh b/dw/types.hh index e910d296..36d6caa1 100644 --- a/dw/types.hh +++ b/dw/types.hh @@ -180,6 +180,8 @@ struct Extremes { int minWidth; int maxWidth; + int minWidthIntrinsic; + int maxWidthIntrinsic; }; struct Content @@ -56,6 +56,7 @@ void Embed::sizeRequestImpl (Requisition *requisition) void Embed::getExtremesImpl (Extremes *extremes) { resource->getExtremes (extremes); + correctExtremes (extremes); } void Embed::sizeAllocateImpl (Allocation *allocation) @@ -63,6 +64,11 @@ void Embed::sizeAllocateImpl (Allocation *allocation) resource->sizeAllocate (allocation); } +void Embed::containerSizeChangedForChildren () +{ + // Nothing to do (as long as all resources return empty iterators). +} + void Embed::enterNotifyImpl (core::EventCrossing *event) { resource->emitEnter(); @@ -88,36 +94,6 @@ bool Embed::buttonPressImpl (core::EventButton *event) return handled; } -void Embed::setWidth (int width) -{ - DBG_OBJ_MSGF ("resize", 0, "<b>setWidth</b> (%d)", width); - DBG_OBJ_MSG_START (); - - resource->setWidth (width); - - DBG_OBJ_MSG_END (); -} - -void Embed::setAscent (int ascent) -{ - DBG_OBJ_MSGF ("resize", 0, "<b>setAscent</b> (%d)", ascent); - DBG_OBJ_MSG_START (); - - resource->setAscent (ascent); - - DBG_OBJ_MSG_END (); -} - -void Embed::setDescent (int descent) -{ - DBG_OBJ_MSGF ("resize", 0, "<b>setDescent</b> (%d)", descent); - DBG_OBJ_MSG_START (); - - resource->setDescent (descent); - - DBG_OBJ_MSG_END (); -} - void Embed::setDisplayed (bool displayed) { resource->setDisplayed (displayed); @@ -223,18 +199,6 @@ void Resource::sizeAllocate (Allocation *allocation) { } -void Resource::setWidth (int width) -{ -} - -void Resource::setAscent (int ascent) -{ -} - -void Resource::setDescent (int descent) -{ -} - void Resource::setDisplayed (bool displayed) { } @@ -368,36 +332,6 @@ void ComplexButtonResource::sizeAllocate (Allocation *allocation) { } -void ComplexButtonResource::setWidth (int width) -{ - DBG_OBJ_MSGF ("resize", 0, "<b>setWidth</b> (%d)", width); - DBG_OBJ_MSG_START (); - - childWidget->setWidth (width - 2 * reliefXThickness ()); - - DBG_OBJ_MSG_END (); -} - -void ComplexButtonResource::setAscent (int ascent) -{ - DBG_OBJ_MSGF ("resize", 0, "<b>setAscent</b> (%d)", ascent); - DBG_OBJ_MSG_START (); - - childWidget->setAscent (ascent - reliefYThickness ()); - - DBG_OBJ_MSG_END (); -} - -void ComplexButtonResource::setDescent (int descent) -{ - DBG_OBJ_MSGF ("resize", 0, "<b>setDescent</b> (%d)", descent); - DBG_OBJ_MSG_START (); - - childWidget->setDescent (descent - reliefYThickness ()); - - DBG_OBJ_MSG_END (); -} - Iterator *ComplexButtonResource::iterator (Content::Type mask, bool atEnd) { /** @@ -231,6 +231,7 @@ protected: void sizeRequestImpl (Requisition *requisition); void getExtremesImpl (Extremes *extremes); void sizeAllocateImpl (Allocation *allocation); + void containerSizeChangedForChildren (); void enterNotifyImpl (core::EventCrossing *event); void leaveNotifyImpl (core::EventCrossing *event); bool buttonPressImpl (core::EventButton *event); @@ -241,9 +242,6 @@ public: Embed(Resource *resource); ~Embed(); - void setWidth (int width); - void setAscent (int ascent); - void setDescent (int descent); void setDisplayed (bool displayed); void setEnabled (bool enabled); void draw (View *view, Rectangle *area); @@ -337,9 +335,6 @@ public: virtual void sizeRequest (Requisition *requisition) = 0; virtual void getExtremes (Extremes *extremes); virtual void sizeAllocate (Allocation *allocation); - virtual void setWidth (int width); - virtual void setAscent (int ascent); - virtual void setDescent (int descent); virtual void setDisplayed (bool displayed); virtual void draw (View *view, Rectangle *area); virtual Iterator *iterator (Content::Type mask, bool atEnd) = 0; @@ -407,9 +402,6 @@ public: void sizeRequest (Requisition *requisition); void getExtremes (Extremes *extremes); void sizeAllocate (Allocation *allocation); - void setWidth (int width); - void setAscent (int ascent); - void setDescent (int descent); Iterator *iterator (Content::Type mask, bool atEnd); int getClickX () {return click_x;}; int getClickY () {return click_y;}; diff --git a/dw/widget.cc b/dw/widget.cc index a3d85d0a..2271ee1e 100644 --- a/dw/widget.cc +++ b/dw/widget.cc @@ -70,7 +70,9 @@ Widget::Widget () registerName ("dw::core::Widget", &CLASS_ID); flags = (Flags)(NEEDS_RESIZE | EXTREMES_CHANGED | HAS_CONTENTS); - parent = generator = NULL; + parent = generator = container = NULL; + DBG_OBJ_SET_PTR ("container", container); + layout = NULL; allocation.x = -1; @@ -79,6 +81,8 @@ Widget::Widget () allocation.ascent = 1; allocation.descent = 0; + extraSpace.top = extraSpace.right = extraSpace.bottom = extraSpace.left = 0; + style = NULL; bgColor = NULL; buttonSensitive = true; @@ -153,6 +157,20 @@ void Widget::setParent (Widget *parent) //printf ("The %s %p becomes a child of the %s %p\n", // getClassName(), this, parent->getClassName(), parent); + // Determine the container. Currently rather simple; will become + // more complicated when absolute and fixed positions are + // supported. + container = NULL; + for (Widget *widget = getParent (); widget != NULL && container == NULL; + widget = widget->getParent()) + if (widget->isPossibleContainer ()) + container = widget; + // If there is no possible container widget, there is + // (surprisingly!) also no container (i. e. the viewport is + // used). Does not occur in dillo, where the toplevel widget is a + // Textblock. + DBG_OBJ_SET_PTR ("container", container); + notifySetParent(); } @@ -176,11 +194,16 @@ void Widget::queueDrawArea (int x, int y, int width, int height) /** * \brief This method should be called, when a widget changes its size. + * + * A "fast" queueResize will ignore the anchestors, and furthermore + * not trigger the idle function. Used only within + * viewportSizeChanged, and not available outside Layout and Widget. */ -void Widget::queueResize (int ref, bool extremesChanged) +void Widget::queueResize (int ref, bool extremesChanged, bool fast) { - DBG_OBJ_MSGF ("resize", 0, "<b>queueResize</b> (%d, %s)", - ref, extremesChanged ? "true" : "false"); + DBG_OBJ_MSGF ("resize", 0, "<b>queueResize</b> (%d, %s, %s)", + ref, extremesChanged ? "true" : "false", + fast ? "true" : "false"); DBG_OBJ_MSG_START (); // queueResize() can be called recursively; calls are queued, so @@ -189,15 +212,16 @@ void Widget::queueResize (int ref, bool extremesChanged) if (queueResizeEntered ()) { DBG_OBJ_MSG ("resize", 1, "put into queue"); layout->queueQueueResizeList->put (new Layout::QueueResizeItem - (this, ref, extremesChanged)); + (this, ref, extremesChanged, fast)); } else { - actualQueueResize (ref, extremesChanged); + actualQueueResize (ref, extremesChanged, fast); while (layout != NULL && layout->queueQueueResizeList->size () > 0) { Layout::QueueResizeItem *item = layout->queueQueueResizeList->get (0); DBG_OBJ_MSGF ("resize", 1, "taken out of queue queue (size = %d)", layout->queueQueueResizeList->size ()); - item->widget->actualQueueResize (item->ref, item->extremesChanged); + item->widget->actualQueueResize (item->ref, item->extremesChanged, + item->fast); layout->queueQueueResizeList->remove (0); // hopefully not too large } } @@ -205,22 +229,19 @@ void Widget::queueResize (int ref, bool extremesChanged) DBG_OBJ_MSG_END (); } -void Widget::actualQueueResize (int ref, bool extremesChanged) +void Widget::actualQueueResize (int ref, bool extremesChanged, bool fast) { assert (!queueResizeEntered ()); - DBG_OBJ_MSGF ("resize", 0, "<b>actualQueueResize</b> (%d, %s)", - ref, extremesChanged ? "true" : "false"); + DBG_OBJ_MSGF ("resize", 0, "<b>actualQueueResize</b> (%d, %s, %s)", + ref, extremesChanged ? "true" : "false", + fast ? "true" : "false"); DBG_OBJ_MSG_START (); enterQueueResize (); Widget *widget2, *child; - //printf("The %stop-level %s %p with parentRef = %d has changed its size. " - // "Layout = %p.\n", - // parent ? "non-" : "", getClassName(), this, parentRef, layout); - Flags resizeFlag, extremesFlag; if (layout) { @@ -243,36 +264,160 @@ void Widget::actualQueueResize (int ref, bool extremesChanged) setFlags (extremesFlag); markExtremesChange (ref); } - - for (widget2 = parent, child = this; widget2; - child = widget2, widget2 = widget2->parent) { - //printf (" Setting %s and ALLOCATE_QUEUED for the " - // "%stop-level %s %p with parentRef = %d\n", - // resizeFlag == RESIZE_QUEUED ? "RESIZE_QUEUED" : "NEEDS_RESIZE", - // widget2->parent ? "non-" : "", widget2->getClassName(), widget2, - // widget2->parentRef); - - if (layout && !widget2->resizeQueued ()) - layout->queueResizeList->put (widget2); - - widget2->setFlags (resizeFlag); - widget2->markSizeChange (child->parentRef); - widget2->setFlags (ALLOCATE_QUEUED); - - if (extremesChanged) { - widget2->setFlags (extremesFlag); - widget2->markExtremesChange (child->parentRef); + + if (!fast) { + for (widget2 = parent, child = this; widget2; + child = widget2, widget2 = widget2->parent) { + if (layout && !widget2->resizeQueued ()) + layout->queueResizeList->put (widget2); + + widget2->setFlags (resizeFlag); + widget2->markSizeChange (child->parentRef); + widget2->setFlags (ALLOCATE_QUEUED); + + if (extremesChanged) { + widget2->setFlags (extremesFlag); + widget2->markExtremesChange (child->parentRef); + } } - } - if (layout) - layout->queueResize (extremesChanged); + if (layout) + layout->queueResize (extremesChanged); + } leaveQueueResize (); DBG_OBJ_MSG_END (); } +void Widget::containerSizeChanged () +{ + DBG_OBJ_MSG ("resize", 0, "<b>containerSizeChanged</b> ()"); + DBG_OBJ_MSG_START (); + + // If there is a container widget (not the viewport), which has not + // changed its size (which can be determined by the respective + // flags: this method is called recursively), this widget will + // neither change its size. Also, the recursive iteration can be + // stopped, since the children of this widget will + if (container == NULL || + container->needsResize () || container->resizeQueued () || + container->extremesChanged () || container->extremesQueued ()) { + // Viewport (container == NULL) or container widget has changed + // its size. + if (affectedByContainerSizeChange ()) + queueResizeFast (0, true); + + // Even if *this* widget is not affected, children may be, so + // iterate over children. + containerSizeChangedForChildren (); + } + + DBG_OBJ_MSG_END (); +} + +bool Widget::affectedByContainerSizeChange () +{ + DBG_OBJ_MSG ("resize", 0, "<b>affectedByContainerSizeChange</b> ()"); + DBG_OBJ_MSG_START (); + + bool ret; + + // This standard implementation is suitable for all widgets which + // call correctRequisition() and correctExtremes(), even in the way + // how Textblock and Image do (see comments there). Has to be kept + // in sync. + + if (container == NULL) { + if (style::isAbsLength (getStyle()->width) && + style::isAbsLength (getStyle()->height)) + // Both absolute, i. e. fixed: no dependency. + ret = false; + else if (style::isPerLength (getStyle()->width) || + style::isPerLength (getStyle()->height)) { + // Any percentage: certainly dependenant. + ret = true; + } else + // One or both is "auto": depends ... + ret = + (getStyle()->width == style::LENGTH_AUTO ? + usesAvailWidth () : false) || + (getStyle()->height == style::LENGTH_AUTO ? + usesAvailHeight () : false); + } else + ret = this->affectsSizeChangeContainerChild (this); + + DBG_OBJ_MSGF ("resize", 1, "=> %s", ret ? "true" : "false"); + DBG_OBJ_MSG_END (); + return ret; +} + +bool Widget::affectsSizeChangeContainerChild (Widget *child) +{ + DBG_OBJ_MSGF ("resize", 0, "<b>affectsSizeChangeContainerChild</b> (%p)", + child); + DBG_OBJ_MSG_START (); + + bool ret; + + // From the point of view of the container. This standard + // implementation should be suitable for most (if not all) + // containers. + + if (style::isAbsLength (child->getStyle()->width) && + style::isAbsLength (child->getStyle()->height)) + // Both absolute, i. e. fixed: no dependency. + ret = false; + else if (style::isPerLength (child->getStyle()->width) || + style::isPerLength (child->getStyle()->height)) { + // Any percentage: certainly dependenant. + ret = true; + } else + // One or both is "auto": depends ... + ret = + (child->getStyle()->width == style::LENGTH_AUTO ? + child->usesAvailWidth () : false) || + (child->getStyle()->height == style::LENGTH_AUTO ? + child->usesAvailHeight () : false); + + DBG_OBJ_MSGF ("resize", 1, "=> %s", ret ? "true" : "false"); + DBG_OBJ_MSG_END (); + return ret; +} + +void Widget::containerSizeChangedForChildren () +{ + DBG_OBJ_MSG ("resize", 0, "<b>containerSizeChangedForChildren</b> ()"); + DBG_OBJ_MSG_START (); + + // Working, but inefficient standard implementation. + Iterator *it = iterator ((Content::Type)(Content::WIDGET_IN_FLOW | + Content::WIDGET_OOF_CONT), + false); + while (it->next ()) + it->getContent()->widget->containerSizeChanged (); + it->unref (); + + DBG_OBJ_MSG_END (); +} + +/** + * \brief Must be implemengted by a method returning true, when + * getAvailWidth() is called. + */ +bool Widget::usesAvailWidth () +{ + return false; +} + +/** + * \brief Must be implemengted by a method returning true, when + * getAvailHeight() is called. + */ +bool Widget::usesAvailHeight () +{ + return false; +} /** * \brief This method is a wrapper for Widget::sizeRequestImpl(); it calls @@ -323,6 +468,193 @@ void Widget::sizeRequest (Requisition *requisition) } /** + * Return available width including margin/border/padding + * (extraSpace?), not only the content width. + */ +int Widget::getAvailWidth (bool forceValue) +{ + // TODO Correct by extremes? + + DBG_OBJ_MSGF ("resize", 0, "<b>getAvailWidth</b> (%s)", + forceValue ? "true" : "false"); + DBG_OBJ_MSG_START (); + + int width; + + if (parent == NULL) { + DBG_OBJ_MSG ("resize", 1, "no parent, regarding viewport"); + DBG_OBJ_MSG_START (); + + // TODO Consider nested layouts (e. g. <button>). + if (style::isAbsLength (getStyle()->width)) { + DBG_OBJ_MSGF ("resize", 1, "absolute width: %dpx", + style::absLengthVal (getStyle()->width)); + width = style::absLengthVal (getStyle()->width) + boxDiffWidth (); + } else { + int viewportWidth = + layout->viewportWidth - (layout->canvasHeightGreater ? + layout->vScrollbarThickness : 0); + if (style::isPerLength (getStyle()->width)) { + DBG_OBJ_MSGF ("resize", 1, "percentage width: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (getStyle()->width)); + width = applyPerWidth (viewportWidth, getStyle()->width); + } else { + DBG_OBJ_MSG ("resize", 1, "no specification"); + width = viewportWidth; + } + + } + DBG_OBJ_MSG_END (); + } else { + DBG_OBJ_MSG ("resize", 1, "delegated to parent"); + DBG_OBJ_MSG_START (); + width = parent->getAvailWidthOfChild (this, forceValue); + DBG_OBJ_MSG_END (); + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d", width); + DBG_OBJ_MSG_END (); + + return width; +} + +/** + * Return available height including margin/border/padding + * (extraSpace?), not only the content height. + */ +int Widget::getAvailHeight (bool forceValue) +{ + // TODO Correct by ... not extremes, but ...? (Height extremes?) + + DBG_OBJ_MSGF ("resize", 0, "<b>getAvailHeight</b> (%s)", + forceValue ? "true" : "false"); + DBG_OBJ_MSG_START (); + + int height; + + if (parent == NULL) { + DBG_OBJ_MSG ("resize", 1, "no parent, regarding viewport"); + DBG_OBJ_MSG_START (); + + // TODO Consider nested layouts (e. g. <button>). + if (style::isAbsLength (getStyle()->height)) { + DBG_OBJ_MSGF ("resize", 1, "absolute height: %dpx", + style::absLengthVal (getStyle()->height)); + height = style::absLengthVal (getStyle()->height) + boxDiffHeight (); + } else if (style::isPerLength (getStyle()->height)) { + DBG_OBJ_MSGF ("resize", 1, "percentage height: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (getStyle()->height)); + // Notice that here -- unlike getAvailWidth() -- + // layout->hScrollbarThickness is not considered here; + // something like canvasWidthGreater (analogue to + // canvasHeightGreater) would be complicated and lead to + // possibly contradictory self-references. + height = applyPerHeight (layout->viewportHeight, getStyle()->height); + } else { + DBG_OBJ_MSG ("resize", 1, "no specification"); + height = layout->viewportHeight; + } + + DBG_OBJ_MSG_END (); + } else { + DBG_OBJ_MSG ("resize", 1, "delegated to parent"); + DBG_OBJ_MSG_START (); + height = container->getAvailHeightOfChild (this, forceValue); + DBG_OBJ_MSG_END (); + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d", height); + DBG_OBJ_MSG_END (); + + return height; +} + +void Widget::correctRequisition (Requisition *requisition, + void (*splitHeightFun) (int, int *, int *)) +{ + // TODO Correct by extremes? + + DBG_OBJ_MSGF ("resize", 0, "<b>correctRequisition</b> (%d * (%d + %d), ...)", + requisition->width, requisition->ascent, requisition->descent); + DBG_OBJ_MSG_START (); + + if (container == NULL) { + if (style::isAbsLength (getStyle()->width)) { + DBG_OBJ_MSGF ("resize", 1, "absolute width: %dpx", + style::absLengthVal (getStyle()->width)); + requisition->width = + style::absLengthVal (getStyle()->width) + boxDiffWidth (); + } else if (style::isPerLength (getStyle()->width)) { + DBG_OBJ_MSGF ("resize", 1, "percentage width: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (getStyle()->width)); + int viewportWidth = + layout->viewportWidth - (layout->canvasHeightGreater ? + layout->vScrollbarThickness : 0); + requisition->width = applyPerWidth (viewportWidth, getStyle()->width); + } + + // TODO Perhaps split first, then add box ascent and descent. + if (style::isAbsLength (getStyle()->height)) { + DBG_OBJ_MSGF ("resize", 1, "absolute height: %dpx", + style::absLengthVal (getStyle()->height)); + splitHeightFun (style::absLengthVal (getStyle()->height) + + boxDiffHeight (), + &requisition->ascent, &requisition->descent); + } else if (style::isPerLength (getStyle()->height)) { + DBG_OBJ_MSGF ("resize", 1, "percentage height: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (getStyle()->height)); +#if 0 + // TODO Percentage heights are somewhat more complicated. Has + // to be clarified. + + // For layout->viewportHeight, see comment in getAvailHeight(). + splitHeightFun (applyPerHeight (layout->viewportHeight, + getStyle()->height), + &requisition->ascent, &requisition->descent); +#endif + } + } else + container->correctRequisitionOfChild (this, requisition, splitHeightFun); + + DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)", + requisition->width, requisition->ascent, + requisition->descent); + DBG_OBJ_MSG_END (); +} + +void Widget::correctExtremes (Extremes *extremes) +{ + // TODO Extremes only corrected? + + DBG_OBJ_MSGF ("resize", 0, "<b>correctExtremes</b> (%d (%d) / %d (%d))", + extremes->minWidth, extremes->minWidthIntrinsic, + extremes->maxWidth, extremes->maxWidthIntrinsic); + DBG_OBJ_MSG_START (); + + if (container == NULL) { + if (style::isAbsLength (getStyle()->width)) + extremes->minWidth = extremes->maxWidth = + style::absLengthVal (getStyle()->width) + boxDiffWidth (); + else if (style::isPerLength (getStyle()->width)) { + int viewportWidth = + layout->viewportWidth - (layout->canvasHeightGreater ? + layout->vScrollbarThickness : 0); + extremes->minWidth = extremes->maxWidth = + applyPerWidth (viewportWidth, getStyle()->width); + } + } else + container->correctExtremesOfChild (this, extremes); + + DBG_OBJ_MSGF ("resize", 1, "=> %d / %d", + extremes->minWidth, extremes->maxWidth); + DBG_OBJ_MSG_END (); +} + +/** * \brief Wrapper for Widget::getExtremesImpl(). */ void Widget::getExtremes (Extremes *extremes) @@ -344,12 +676,26 @@ void Widget::getExtremes (Extremes *extremes) } if (extremesChanged ()) { + // For backward compatibility (part 1/2): + extremes->minWidthIntrinsic = extremes->maxWidthIntrinsic = -1; + getExtremesImpl (extremes); + + // For backward compatibility (part 2/2): + if (extremes->minWidthIntrinsic == -1) + extremes->minWidthIntrinsic = extremes->minWidth; + if (extremes->maxWidthIntrinsic == -1) + extremes->maxWidthIntrinsic = extremes->maxWidth; + this->extremes = *extremes; unsetFlags (EXTREMES_CHANGED); DBG_OBJ_SET_NUM ("extremes.minWidth", extremes->minWidth); + DBG_OBJ_SET_NUM ("extremes.minWidthIntrinsic", + extremes->minWidthIntrinsic); DBG_OBJ_SET_NUM ("extremes.maxWidth", extremes->maxWidth); + DBG_OBJ_SET_NUM ("extremes.maxWidthIntrinsic", + extremes->maxWidthIntrinsic); } else *extremes = this->extremes; @@ -507,6 +853,28 @@ void Widget::setStyle (style::Style *style) queueResize (0, true); else queueDraw (); + + // These should better be attributed to the style itself, and a + // 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.border-width.top", style->borderWidth.top); + DBG_OBJ_SET_NUM ("style.border-width.bottom", style->borderWidth.bottom); + DBG_OBJ_SET_NUM ("style.border-width.left", style->borderWidth.left); + DBG_OBJ_SET_NUM ("style.border-width.right", style->borderWidth.right); + + DBG_OBJ_SET_NUM ("style.padding.top", style->padding.top); + DBG_OBJ_SET_NUM ("style.padding.bottom", style->padding.bottom); + DBG_OBJ_SET_NUM ("style.padding.left", style->padding.left); + DBG_OBJ_SET_NUM ("style.padding.right", style->padding.right); + + DBG_OBJ_SET_NUM ("style.border-spacing (h)", style->hBorderSpacing); + DBG_OBJ_SET_NUM ("style.border-spacing (v)", style->vBorderSpacing); } /** @@ -787,6 +1155,7 @@ void Widget::getExtremesImpl (Extremes *extremes) Requisition requisition; sizeRequest (&requisition); extremes->minWidth = extremes->maxWidth = requisition.width; + correctExtremes (extremes); } void Widget::sizeAllocateImpl (Allocation *allocation) @@ -801,6 +1170,227 @@ void Widget::markExtremesChange (int ref) { } +int Widget::applyPerWidth (int containerWidth, style::Length perWidth) +{ + return style::multiplyWithPerLength (containerWidth, perWidth) + + boxDiffWidth (); +} + +int Widget::applyPerHeight (int containerHeight, style::Length perHeight) +{ + return style::multiplyWithPerLength (containerHeight, perHeight) + + boxDiffHeight (); +} + +int Widget::getAvailWidthOfChild (Widget *child, bool forceValue) +{ + // This is a halfway suitable implementation for all + // containers. For simplification, this will be used during the + // development; then, a differentiation could be possible. + + // TODO Correct by extremes? + + DBG_OBJ_MSGF ("resize", 0, "<b>getAvailWidthOfChild</b> (%p, %s)", + child, forceValue ? "true" : "false"); + DBG_OBJ_MSG_START (); + + int width; + + if (child->getStyle()->width == style::LENGTH_AUTO) { + DBG_OBJ_MSG ("resize", 1, "no specification"); + int availWidth = getAvailWidth (forceValue); + if (availWidth == -1) + width = -1; + else + width = misc::max (availWidth - boxDiffWidth (), 0); + } else { + // In most cases, the toplevel widget should be a container, so + // the container is non-NULL when the parent is non-NULL. Just + // in case ...: + Widget *effContainer = + child->container ? child->container : child->parent; + + if (effContainer == this) { + if (style::isAbsLength (child->getStyle()->width)) { + DBG_OBJ_MSGF ("resize", 1, "absolute width: %dpx", + style::absLengthVal (child->getStyle()->width)); + width = misc::max (style::absLengthVal (child->getStyle()->width) + + child->boxDiffWidth (), 0); + } else { + assert (style::isPerLength (child->getStyle()->width)); + DBG_OBJ_MSGF ("resize", 1, "percentage width: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (child->getStyle()->width)); + + int availWidth = getAvailWidth (forceValue); + if (availWidth == -1) + width = -1; + else + width = + misc::max (child->applyPerWidth (availWidth - boxDiffWidth (), + child->getStyle()->width), + 0); + } + } else { + DBG_OBJ_MSG ("resize", 1, "delegated to (effective) container"); + DBG_OBJ_MSG_START (); + width = effContainer->getAvailWidthOfChild (child, forceValue); + DBG_OBJ_MSG_END (); + } + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d", width); + DBG_OBJ_MSG_END (); + + return width; +} + +int Widget::getAvailHeightOfChild (Widget *child, bool forceValue) +{ + // Again, a suitable implementation for all widgets (perhaps). + + // TODO Correct by extremes? (Height extemes?) + + DBG_OBJ_MSGF ("resize", 0, "<b>getAvailHeightOfChild</b> (%p, %s)", + child, forceValue ? "true" : "false"); + DBG_OBJ_MSG_START (); + + int height; + + if (child->getStyle()->height == style::LENGTH_AUTO) { + DBG_OBJ_MSG ("resize", 1, "no specification"); + int availHeight = getAvailHeight (forceValue); + if (availHeight == -1) + height = -1; + else + height = misc::max (availHeight - boxDiffHeight (), 0); + } else { + // In most cases, the toplevel widget should be a container, so + // the container is non-NULL when the parent is non-NULL. Just + // in case ...: + Widget *effContainer = + child->container ? child->container : child->parent; + + if (effContainer == this) { + if (style::isAbsLength (child->getStyle()->height)) { + DBG_OBJ_MSGF ("resize", 1, "absolute height: %dpx", + style::absLengthVal (child->getStyle()->height)); + height = misc::max (style::absLengthVal (child->getStyle()->height) + + child->boxDiffHeight (), 0); + } else { + assert (style::isPerLength (child->getStyle()->height)); + DBG_OBJ_MSGF ("resize", 1, "percentage height: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (child->getStyle()->height)); + + int availHeight = getAvailHeight (forceValue); + if (availHeight == -1) + height = -1; + else + height = + misc::max (child->applyPerHeight (availHeight - + boxDiffHeight (), + child->getStyle()->height), + 0); + } + } else { + DBG_OBJ_MSG ("resize", 1, "delegated to (effective) container"); + DBG_OBJ_MSG_START (); + height = effContainer->getAvailHeightOfChild (child, forceValue); + DBG_OBJ_MSG_END (); + } + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d", height); + DBG_OBJ_MSG_END (); + + return height; +} + +void Widget::correctRequisitionOfChild (Widget *child, Requisition *requisition, + void (*splitHeightFun) (int, int*, + int*)) +{ + // Again, a suitable implementation for all widgets (perhaps). + + // TODO Correct by extremes? + + DBG_OBJ_MSGF ("resize", 0, + "<b>correctRequisitionOfChild</b> (%p, %d * (%d + %d), ...)", + child, requisition->width, requisition->ascent, + requisition->descent); + DBG_OBJ_MSG_START (); + + if (style::isAbsLength (child->getStyle()->width)) + requisition->width = style::absLengthVal (child->getStyle()->width) + + child->boxDiffWidth (); + else if (style::isPerLength (child->getStyle()->width)) { + int availWidth = getAvailWidth (false); + if (availWidth != -1) { + int containerWidth = availWidth - boxDiffWidth (); + requisition->width = child->applyPerWidth (containerWidth, + child->getStyle()->width); + } + } + + // TODO Perhaps split first, then add box ascent and descent. + if (style::isAbsLength (child->getStyle()->height)) + splitHeightFun (style::absLengthVal (child->getStyle()->height) + + child->boxDiffHeight (), + &requisition->ascent, &requisition->descent); + else if (style::isPerLength (child->getStyle()->height)) { + int availHeight = getAvailHeight (false); + if (availHeight != -1) { + int containerHeight = availHeight - boxDiffHeight (); + splitHeightFun (child->applyPerHeight (containerHeight, + child->getStyle()->height), + &requisition->ascent, &requisition->descent); + } + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)", + requisition->width, requisition->ascent, + requisition->descent); + DBG_OBJ_MSG_END (); +} + +void Widget::correctExtremesOfChild (Widget *child, Extremes *extremes) +{ + // See comment in correctRequisitionOfChild. + + // TODO Extremes only corrected? + + DBG_OBJ_MSGF ("resize", 0, + "<b>correctExtremedOfChild</b> (%p, %d / %d)", + child, extremes->minWidth, extremes->maxWidth); + DBG_OBJ_MSG_START (); + + if (style::isAbsLength (child->getStyle()->width)) { + DBG_OBJ_MSGF ("resize", 1, "absolute width: %dpx", + style::absLengthVal (child->getStyle()->width)); + extremes->minWidth = extremes->maxWidth = + style::absLengthVal (child->getStyle()->width) + + child->boxDiffWidth (); + } else if (style::isPerLength (child->getStyle()->width)) { + DBG_OBJ_MSGF ("resize", 1, "percentage width: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (child->getStyle()->width)); + int availWidth = getAvailWidth (false); + if (availWidth != -1) { + int containerWidth = availWidth - boxDiffWidth (); + DBG_OBJ_MSGF ("resize", 1, "containerWidth = %d - %d = %d", + availWidth, boxDiffWidth (), containerWidth); + extremes->minWidth = extremes->maxWidth = + child->applyPerWidth (containerWidth, child->getStyle()->width); + } + } else + DBG_OBJ_MSG ("resize", 1, "no specification"); + + DBG_OBJ_MSGF ("resize", 1, "=> %d / %d", + extremes->minWidth, extremes->maxWidth); + DBG_OBJ_MSG_END (); +} + /** * \brief This method is called after a widget has been set as the top of a * widget tree. @@ -820,16 +1410,16 @@ void Widget::notifySetParent() { } -void Widget::setWidth (int width) -{ -} - -void Widget::setAscent (int ascent) +bool Widget::isBlockLevel () { + // Most widgets are not block-level. + return false; } -void Widget::setDescent (int descent) +bool Widget::isPossibleContainer () { + // In most (all?) cases identical to: + return isBlockLevel (); } bool Widget::buttonPressImpl (EventButton *event) @@ -849,7 +1439,7 @@ bool Widget::motionNotifyImpl (EventMotion *event) void Widget::enterNotifyImpl (EventCrossing *) { - core::style::Tooltip *tooltip = getStyle()->x_tooltip; + style::Tooltip *tooltip = getStyle()->x_tooltip; if (tooltip) tooltip->onEnter(); @@ -857,7 +1447,7 @@ void Widget::enterNotifyImpl (EventCrossing *) void Widget::leaveNotifyImpl (EventCrossing *) { - core::style::Tooltip *tooltip = getStyle()->x_tooltip; + style::Tooltip *tooltip = getStyle()->x_tooltip; if (tooltip) tooltip->onLeave(); @@ -869,5 +1459,25 @@ void Widget::removeChild (Widget *child) misc::assertNotReached (); } +// ---------------------------------------------------------------------- + +void splitHeightPreserveAscent (int height, int *ascent, int *descent) +{ + *descent = height - *ascent; + if (*descent < 0) { + *descent = 0; + *ascent = height; + } +} + +void splitHeightPreserveDescent (int height, int *ascent, int *descent) +{ + *ascent = height - *descent; + if (*ascent < 0) { + *ascent = 0; + *descent = height; + } +} + } // namespace core } // namespace dw diff --git a/dw/widget.hh b/dw/widget.hh index e3d9cbe1..00fc2aa5 100644 --- a/dw/widget.hh +++ b/dw/widget.hh @@ -90,11 +90,6 @@ protected: * The dw::Image widget uses this flag, see dw::Image::setBuffer. */ WAS_ALLOCATED = 1 << 8, - - /** - * \brief Set for block-level widgets (as opposed to inline widgets) - */ - BLOCK_LEVEL = 1 << 9, }; /** @@ -127,11 +122,18 @@ private: /** * \brief The generating widget, NULL for top-level widgets, or if - * not set; in the latter case, the effective generator (see - * getGenerator) is the parent. + * not set; in the latter case, the effective generator (see + * getGenerator) is the parent. */ Widget *generator; + /** + * \brief The containing widget, equivalent to the "containing + * block" defined by CSS. May be NULL, in this case the viewport + * is used. + */ + Widget *container; + style::Style *style; Flags flags; @@ -164,7 +166,10 @@ private: */ bool buttonSensitiveSet; - void actualQueueResize (int ref, bool extremesChanged); + void queueResize (int ref, bool extremesChanged, bool fast); + inline void queueResizeFast (int ref, bool extremesChanged) + { queueResize (ref, extremesChanged, true); } + void actualQueueResize (int ref, bool extremesChanged, bool fast); public: /** @@ -191,6 +196,12 @@ protected: Layout *layout; + /** + * \brief Space around the margin box. Allocation is extraSpace + + * margin + border + padding + contents; + */ + style::Box extraSpace; + /*inline void printFlags () { DBG_IF_RTFL { char buf[10 * 3 - 1 + 1]; @@ -255,12 +266,7 @@ protected: case WAS_ALLOCATED: DBG_OBJ_SET_SYM ("flags.WAS_ALLOCATED", (flags & WAS_ALLOCATED) ? "true" : "false"); - break; - - case BLOCK_LEVEL: - DBG_OBJ_SET_SYM ("flags.BLOCK_LEVEL", - (flags & BLOCK_LEVEL) ? "true" : "false"); - break; + break; } } } @@ -272,11 +278,10 @@ protected: inline void queueDraw () - { - queueDrawArea (0, 0, allocation.width, getHeight()); - } + { queueDrawArea (0, 0, allocation.width, getHeight()); } void queueDrawArea (int x, int y, int width, int height); - void queueResize (int ref, bool extremesChanged); + inline void queueResize (int ref, bool extremesChanged) + { queueResize (ref, extremesChanged, false); } /** * \brief See \ref dw-widget-sizes. @@ -309,6 +314,21 @@ protected: */ virtual void markExtremesChange (int ref); + virtual int getAvailWidthOfChild (Widget *child, bool forceValue); + virtual int getAvailHeightOfChild (Widget *child, bool forceValue); + virtual void correctRequisitionOfChild (Widget *child, + Requisition *requisition, + void (*splitHeightFun) (int, int*, + int*)); + virtual void correctExtremesOfChild (Widget *child, Extremes *extremes); + + virtual void containerSizeChangedForChildren (); + + virtual bool affectedByContainerSizeChange (); + virtual bool affectsSizeChangeContainerChild (Widget *child); + virtual bool usesAvailWidth (); + virtual bool usesAvailHeight (); + virtual void notifySetAsTopLevel(); virtual void notifySetParent(); @@ -396,7 +416,6 @@ public: inline bool wasAllocated () { return flags & WAS_ALLOCATED; } inline bool usesHints () { return flags & USES_HINTS; } inline bool hasContents () { return flags & HAS_CONTENTS; } - inline bool blockLevel () { return flags & BLOCK_LEVEL; } void setParent (Widget *parent); @@ -406,12 +425,34 @@ public: /** \todo I do not like this. */ inline Allocation *getAllocation () { return &allocation; } + inline int boxOffsetX () + { return extraSpace.left + getStyle()->boxOffsetX (); } + inline int boxRestWidth () + { return extraSpace.right + getStyle()->boxRestWidth (); } + inline int boxDiffWidth () { return boxOffsetX () + boxRestWidth (); } + inline int boxOffsetY () + { return extraSpace.top + getStyle()->boxOffsetY (); } + inline int boxRestHeight () + { return extraSpace.bottom + getStyle()->boxRestHeight (); } + inline int boxDiffHeight () { return boxOffsetY () + boxRestHeight (); } + void sizeRequest (Requisition *requisition); void getExtremes (Extremes *extremes); void sizeAllocate (Allocation *allocation); - virtual void setWidth (int width); - virtual void setAscent (int ascent); - virtual void setDescent (int descent); + + int getAvailWidth (bool forceValue); + int getAvailHeight (bool forceValue); + void correctRequisition (Requisition *requisition, + void (*splitHeightFun) (int, int*, int*)); + void correctExtremes (Extremes *extremes); + + virtual int applyPerWidth (int containerWidth, style::Length perWidth); + virtual int applyPerHeight (int containerHeight, style::Length perHeight); + + virtual bool isBlockLevel (); + virtual bool isPossibleContainer (); + + void containerSizeChanged (); bool intersects (Rectangle *area, Rectangle *intersection); @@ -470,6 +511,9 @@ public: virtual void removeChild (Widget *child); }; +void splitHeightPreserveAscent (int height, int *ascent, int *descent); +void splitHeightPreserveDescent (int height, int *ascent, int *descent); + } // namespace core } // namespace dw diff --git a/lout/debug.hh b/lout/debug.hh index b823f171..034cefcf 100644 --- a/lout/debug.hh +++ b/lout/debug.hh @@ -189,6 +189,13 @@ fflush (stdout); \ } D_STMT_END +#define DBG_OBJ_SET_BOOL(var, val) \ + D_STMT_START { \ + printf (RTFL_PREFIX_FMT "obj-set:%p:%s:%s\n", \ + RTFL_PREFIX_ARGS, this, var, val ? "true" : "false"); \ + fflush (stdout); \ + } D_STMT_END + #define DBG_OBJ_SET_PTR_O(obj, var, val) \ D_STMT_START { \ printf (RTFL_PREFIX_FMT "obj-set:%p:%s:%p\n", \ @@ -282,6 +289,7 @@ #define DBG_OBJ_SET_SYM(var, val) D_STMT_NOP #define DBG_OBJ_SET_STR(var, val) D_STMT_NOP #define DBG_OBJ_SET_PTR(var, val) D_STMT_NOP +#define DBG_OBJ_SET_BOOL(var, val) D_STMT_NOP #define DBG_OBJ_SET_PTR_O(obj, var, val) D_STMT_NOP #define DBG_OBJ_ARRSET_NUM(var, ind, val) D_STMT_NOP #define DBG_OBJ_ARRSET_SYM(var, ind, val) D_STMT_NOP diff --git a/lout/misc.hh b/lout/misc.hh index 3082f33c..4c20208a 100644 --- a/lout/misc.hh +++ b/lout/misc.hh @@ -223,6 +223,14 @@ public: assert (i >= 0 && this->num - i > 0); this->array[i] = t; } + + /** + * \brief Store an object at the end of the vector. + */ + inline void setLast (T t) { + assert (this->num > 0); + this->array[this->num - 1] = t; + } }; /** @@ -488,6 +496,13 @@ public: inline void set (int i, T t) { *(this->getRef(i)) = t; } + + /** + * \brief Store an object at the end of the vector. + */ + inline void setLast (T t) { + *(this->getLastRef()) = t; + } }; /** diff --git a/src/table.cc b/src/table.cc index a3002ebf..29b5fbc9 100644 --- a/src/table.cc +++ b/src/table.cc @@ -15,6 +15,7 @@ #include "dw/style.hh" #include "dw/textblock.hh" #include "dw/table.hh" +#include "dw/simpletablecell.hh" #include "prefs.h" #include "msg.h" @@ -445,11 +446,11 @@ static void Html_tag_content_table_cell(DilloHtml *html, rowspan = MAX(1, strtol (attrbuf, NULL, 10)); if (html->style ()->textAlign == TEXT_ALIGN_STRING) - col_tb = new dw::TableCell ( + col_tb = new AlignedTableCell ( ((dw::Table*)S_TOP(html)->table)->getCellRef (), prefs.limit_text_width); else - col_tb = new Textblock (prefs.limit_text_width); + col_tb = new SimpleTableCell (prefs.limit_text_width); if (html->style()->borderCollapse == BORDER_MODEL_COLLAPSE){ Html_set_collapsing_border_model(html, col_tb); diff --git a/test/dw_table.cc b/test/dw_table.cc index 5416d05b..9bec1a09 100644 --- a/test/dw_table.cc +++ b/test/dw_table.cc @@ -26,7 +26,6 @@ #include "../dw/fltkcore.hh" #include "../dw/fltkviewport.hh" #include "../dw/table.hh" -#include "../dw/tablecell.hh" using namespace dw; using namespace dw::core; diff --git a/test/dw_table_aligned.cc b/test/dw_table_aligned.cc index 96cb0602..bef3d521 100644 --- a/test/dw_table_aligned.cc +++ b/test/dw_table_aligned.cc @@ -26,7 +26,7 @@ #include "../dw/fltkcore.hh" #include "../dw/fltkviewport.hh" #include "../dw/table.hh" -#include "../dw/tablecell.hh" +#include "../dw/alignedtablecell.hh" using namespace dw; using namespace dw::core; @@ -87,10 +87,10 @@ int main(int argc, char **argv) Style *wordStyle = Style::create (&styleAttrs); - TableCell *ref = NULL; + AlignedTableCell *ref = NULL; for(int i = 0; i < 10; i++) { //for(int i = 0; i < 1; i++) { - TableCell *cell = new TableCell (ref, false); + AlignedTableCell *cell = new AlignedTableCell (ref, false); cell->setStyle (cellStyle); ref = cell; table->addRow (wordStyle); |