diff options
-rw-r--r-- | doc/dw-out-of-flow.doc | 145 | ||||
-rw-r--r-- | dw/Makefile.am | 2 | ||||
-rw-r--r-- | dw/findtext.cc | 4 | ||||
-rw-r--r-- | dw/iterator.cc | 36 | ||||
-rw-r--r-- | dw/iterator.hh | 2 | ||||
-rw-r--r-- | dw/layout.cc | 1 | ||||
-rw-r--r-- | dw/outofflowmgr.cc | 265 | ||||
-rw-r--r-- | dw/outofflowmgr.hh | 105 | ||||
-rw-r--r-- | dw/style.cc | 12 | ||||
-rw-r--r-- | dw/style.hh | 17 | ||||
-rw-r--r-- | dw/table.cc | 14 | ||||
-rw-r--r-- | dw/tablecell.cc | 2 | ||||
-rw-r--r-- | dw/textblock.cc | 226 | ||||
-rw-r--r-- | dw/textblock.hh | 141 | ||||
-rw-r--r-- | dw/textblock_iterator.cc | 272 | ||||
-rw-r--r-- | dw/textblock_linebreaking.cc | 94 | ||||
-rw-r--r-- | dw/types.hh | 22 | ||||
-rw-r--r-- | dw/widget.cc | 28 | ||||
-rw-r--r-- | dw/widget.hh | 3 | ||||
-rw-r--r-- | lout/identity.cc | 3 | ||||
-rw-r--r-- | src/cssparser.cc | 6 | ||||
-rw-r--r-- | src/html.cc | 4 | ||||
-rw-r--r-- | src/styleengine.cc | 3 | ||||
-rw-r--r-- | test/Makefile.am | 9 | ||||
-rw-r--r-- | test/dw_float_test.cc | 145 | ||||
-rw-r--r-- | test/floats1.html | 46 | ||||
-rw-r--r-- | test/floats2.html | 17 |
27 files changed, 1417 insertions, 207 deletions
diff --git a/doc/dw-out-of-flow.doc b/doc/dw-out-of-flow.doc new file mode 100644 index 00000000..bbdeab24 --- /dev/null +++ b/doc/dw-out-of-flow.doc @@ -0,0 +1,145 @@ +/** \page dw-out-of-flow Handling Elements Out Of Flow + +<div style="border: 2px solid #ffff00; margin-bottom: 0.5em; +padding: 0.5em 1em; background-color: #ffffe0"><b>Info:</b> +Should be incorporated into dw::Textblock.</div> + +This texts deals with both floats and absolute positions, which have +in common, that there is a distinction between generating block and +containing block (we are here using the same notation as in the +CSS 2 specification). Consider this snippet (regarding floats): + + + <ul> + <li>Some text.</li> + <li> + <div style="float:right; width=50%">Some longer text, so + that the effect described in this passage can be + demonstrated. + </div> + Some more and longer text.</li> + <li>Final text.</li> + </ul> + +which may be rendered like this + + - - - - - - - - - - - - - - - - - - - - - - - - - . + | * Some text. + * Some more and - - - - - - - - - - - -.| + | longer text. |Some longer text, so that + * Final text. the effect described || + ` - - - - - - - - - - - |above in this passage can ' + be demonstrated. | + ` - - - - - - - - - - - - + +The float (the DIV section) is defined ("generated") within the list +item, so, in CSS 2 terms, the list item is the generating block of the +float. However, as the image shows, the float is not contained by the +list item, but another block, several levels above (not shown +here). In terms of ::dw, this means that the dw::Textblock +representing the float cannot be a child of the dw::Textblock +representing the generating block, the list item, since the allocation +of a child widget must be within the allocation of the parent +widget. Instead, to each dw::Textblock, another dw::Textblock is +assigned as the containing box. + +Definition of "containing block" +================================ + +The following definition follows the definition in the CSS 2 +specification. For a given dw::Textblock, the containing block is +defined as the one dw::Textblock following these rules: + +- For the toplevel widget, the containing block is itself. +- Otherwise, the next dw::Textblock up in the tree (which may also + this widget) is searched, which is out of flow (float or absolutely + positioned) or a table cell. + +TODO: + +1. Compare this to + <http://www.w3.org/TR/CSS2/visudet.html#containing-block-details>. +2. Handle fixed positions: in this case, the viewport is the + containing box, but the viewport is not represented by a widget. +3. Clarify: the element out of flow must not be a dw::Textblock. + + +Implementation overview +======================= + +Both dw::Textblock::notifySetAsTopLevel and +dw::Textblock::notifySetParent set the member +dw::Textblock::containingBlock appropriately, according to the rules +defined above. + +Handling widgets out of flow is partly the task of the new class +dw::OutOfFlowMgr, which is managed by dw::Textblock::outOfFlowMgr, but +only for containing blocks. Generating blocks should refer to +_containingBlock->outOfFlowMgr_. + +dw::Textblock::addWidget is extended, so that floats and absolutely +positioned elements can be added. Notice that not this widget, but the +containing block becomes the parent of the newly added child, if it is +out of flow. dw::Textblock::addWidget decides this by calling +dw::OutOfFlowMgr::isOutOfFlow. + +There are still relations between the element out of flow and the +generating box; details are described below. + +(Some notes about what has do be done for fixed elements: (i) In the +current design, there is only one containing block for a given +generating block. However, a given generating block may contain an +element with absolute position (or, a float), + +As already mentioned, the containing box + + +Relation between generating box and containing box +================================================== + +... + +TODO: new implementation of dw::core::Widget::parentRef. + +Changing the size of an element out of flow +------------------------------------------- +The element out of flow will call dw::Widget::queueResize, which will +result in a call of dw::Widget::markSizeChange for the containing +block. The containing block will notice (by dw::Widget::parentRef) +that the size change is originated in an element out of flow, and so +delegate it to dw::OutOfFlowManager::markSizeChange. + +dw::OutOfFlowManager ::markSizeChange calls dw::Widget::queueResize for +the _generating_ block, with the line, where this float has been +generated, as an argument, so that the generating block will rewrap +down from this line. It is important here that an infinite loop is +prevented here (see next section). + +Eventually, dw::Widget::sizeRequest is called again. + +(TODO: This is only relevant for floats, not for elements with +absolute position. Only dw::OutOfFlowManager should have to care about +this.) + +Rewrapping a line with a float reference +---------------------------------------- + +Sketch: in dw::Textblock::rewrap, before the actual rewrapping, all +elements out of flow must be set as "non-located" (this means for +floats, that they are -- temporally -- not visible, and so do not have +an impact on the borders). The actual rewrapping will tell the +vertical position (and more? actually only plays a role for floats) of +elements out of flow to dw::OutOfFlowManager. + + +More +==== + +Types: WIDGET_IN_FLOW, WIDGET_OOF_CONTAINED, WIDGET_OOF_REFERENCE + +TODO: "generator" and "container" should become standard expressions +for dw::Widget. Perhaps a glossary? + +TODO: DeepIterator may need the generating parent widget in some cases. + +*/
\ No newline at end of file diff --git a/dw/Makefile.am b/dw/Makefile.am index e108da60..aa5c88c3 100644 --- a/dw/Makefile.am +++ b/dw/Makefile.am @@ -65,6 +65,8 @@ libDw_widgets_a_SOURCES = \ image.hh \ listitem.cc \ listitem.hh \ + outofflowmgr.cc \ + outofflowmgr.hh \ ruler.cc \ ruler.hh \ table.cc \ diff --git a/dw/findtext.cc b/dw/findtext.cc index 9793db91..9e9076dc 100644 --- a/dw/findtext.cc +++ b/dw/findtext.cc @@ -91,7 +91,7 @@ FindtextState::Result FindtextState::search (const char *key, bool caseSens, if (iterator) delete iterator; - iterator = new CharIterator (widget); + iterator = new CharIterator (widget, true); if (backwards) { /* Go to end */ @@ -123,7 +123,7 @@ FindtextState::Result FindtextState::search (const char *key, bool caseSens, } else { // Nothing found anymore, reset the state for the next trial. delete iterator; - iterator = new CharIterator (widget); + iterator = new CharIterator (widget, true); if (backwards) { /* Go to end */ while (iterator->next ()) ; diff --git a/dw/iterator.cc b/dw/iterator.cc index e9431e9b..73beca6f 100644 --- a/dw/iterator.cc +++ b/dw/iterator.cc @@ -343,7 +343,7 @@ Iterator *DeepIterator::searchDownward (Iterator *it, Content::Type mask, //DEBUG_MSG (1, "%*smoving down (%swards) from %s\n", // indent, "", from_end ? "back" : "for", a_Dw_iterator_text (it)); - assert (it->getContent()->type == Content::WIDGET); + assert (it->getContent()->type & Content::ANY_WIDGET); it2 = it->getContent()->widget->iterator (mask, fromEnd); if (it2 == NULL) { @@ -356,7 +356,7 @@ Iterator *DeepIterator::searchDownward (Iterator *it, Content::Type mask, //DEBUG_MSG (1, "%*sexamining %s\n", // indent, "", a_Dw_iterator_text (it2)); - if (it2->getContent()->type == Content::WIDGET) { + if (it2->getContent()->type & Content::ANY_WIDGET) { // Another widget. Search in it downwards. it3 = searchDownward (it2, mask, fromEnd); if (it3 != NULL) { @@ -390,11 +390,11 @@ Iterator *DeepIterator::searchSideward (Iterator *it, Content::Type mask, //DEBUG_MSG (1, "%*smoving %swards from %s\n", // indent, "", from_end ? "back" : "for", a_Dw_iterator_text (it)); - assert (it->getContent()->type == Content::WIDGET); + assert (it->getContent()->type & Content::ANY_WIDGET); it2 = it->cloneIterator (); while (fromEnd ? it2->prev () : it2->next ()) { - if (it2->getContent()->type == Content::WIDGET) { + if (it2->getContent()->type & Content::ANY_WIDGET) { // Search downwards in this widget. it3 = searchDownward (it2, mask, fromEnd); if (it3 != NULL) { @@ -422,7 +422,7 @@ Iterator *DeepIterator::searchSideward (Iterator *it, Content::Type mask, if (!it2->next ()) misc::assertNotReached (); - if (it2->getContent()->type == Content::WIDGET && + if (it2->getContent()->type & Content::ANY_WIDGET && it2->getContent()->widget == it->getWidget ()) { it3 = searchSideward (it2, mask, fromEnd); it2->unref (); @@ -467,7 +467,7 @@ DeepIterator::DeepIterator (Iterator *it) // If it points to a widget, find a near non-widget content, // since an DeepIterator should never return widgets. - if (it->getContent()->type == Content::WIDGET) { + if (it->getContent()->type & Content::ANY_WIDGET) { Iterator *it2; // The second argument of searchDownward is actually a matter of @@ -505,7 +505,7 @@ DeepIterator::DeepIterator (Iterator *it) bool hasNext = it->next(); assert (hasNext); - if (it->getContent()->type == Content::WIDGET && + if (it->getContent()->type & Content::ANY_WIDGET && it->getContent()->widget == w) break; } @@ -577,7 +577,7 @@ bool DeepIterator::next () Iterator *it = stack.getTop (); if (it->next ()) { - if (it->getContent()->type == Content::WIDGET) { + if (it->getContent()->type & Content::ANY_WIDGET) { // Widget: new iterator on stack, to search in this widget. stack.push (it->getContent()->widget->iterator (mask, false)); return next (); @@ -610,7 +610,7 @@ bool DeepIterator::prev () Iterator *it = stack.getTop (); if (it->prev ()) { - if (it->getContent()->type == Content::WIDGET) { + if (it->getContent()->type & Content::ANY_WIDGET) { // Widget: new iterator on stack, to search in this widget. stack.push (it->getContent()->widget->iterator (mask, true)); return prev (); @@ -642,9 +642,21 @@ CharIterator::CharIterator () it = NULL; } -CharIterator::CharIterator (Widget *widget) -{ - Iterator *i = widget->iterator (Content::SELECTION_CONTENT, false); +/** + * \brief ... + * + * If followReferences is true, only the reference are followed, when + * the container and generator for a widget is different. If false, + * only the container is followed. + */ +CharIterator::CharIterator (Widget *widget, bool followReferences) +{ + Content::Type widgetMask = (Content::Type) + (Content::WIDGET_IN_FLOW | + (followReferences ? Content::WIDGET_OOF_REF : Content::WIDGET_OOF_CONT)); + Iterator *i = + widget->iterator ((Content::Type) + (Content::SELECTION_CONTENT | widgetMask), false); it = new DeepIterator (i); i->unref (); ch = START; diff --git a/dw/iterator.hh b/dw/iterator.hh index 838d66a1..222a05c0 100644 --- a/dw/iterator.hh +++ b/dw/iterator.hh @@ -230,7 +230,7 @@ private: CharIterator (); public: - CharIterator (Widget *widget); + CharIterator (Widget *widget, bool followReferences); ~CharIterator (); lout::object::Object *clone(); diff --git a/dw/layout.cc b/dw/layout.cc index 9dfd5e9b..d2610687 100644 --- a/dw/layout.cc +++ b/dw/layout.cc @@ -248,6 +248,7 @@ void Layout::addWidget (Widget *widget) topLevel = widget; widget->layout = this; + widget->notifySetAsTopLevel(); findtextState.setWidget (widget); diff --git a/dw/outofflowmgr.cc b/dw/outofflowmgr.cc new file mode 100644 index 00000000..241e0d27 --- /dev/null +++ b/dw/outofflowmgr.cc @@ -0,0 +1,265 @@ +#include "outofflowmgr.hh" + +//#include <math.h> // testing + +using namespace lout::container::typed; +using namespace lout::misc; +using namespace dw::core; +using namespace dw::core::style; + +namespace dw { + +OutOfFlowMgr::OutOfFlowMgr (ContainingBlock *containingBlock) +{ + //printf ("OutOfFlowMgr::OutOfFlowMgr\n"); + this->containingBlock = containingBlock; + + leftFloats = new Vector<Float> (1, true); + rightFloats = new Vector<Float> (1, true); +} + +OutOfFlowMgr::~OutOfFlowMgr () +{ + //printf ("OutOfFlowMgr::~OutOfFlowMgr\n"); + + delete leftFloats; + delete rightFloats; +} + +void OutOfFlowMgr::sizeAllocate (Allocation *containingBlockAllocation) +{ + // TODO Much copy and paste. + + for (int i = 0; i < leftFloats->size(); i++) { + Float *vloat = leftFloats->get(i); + assert (vloat->y != -1); + + Allocation childAllocation; + childAllocation.x = containingBlockAllocation->x + + containingBlock->getCBStyle()->boxOffsetX(); + childAllocation.y = containingBlockAllocation->y + vloat->y; + childAllocation.width = + vloat->width - containingBlock->getCBStyle()->boxOffsetX(); + childAllocation.ascent = vloat->ascent; + childAllocation.descent = vloat->descent; + + vloat->widget->sizeAllocate (&childAllocation); + + //printf ("allocate left #%d -> (%d, %d), %d x (%d + %d)\n", + // i, childAllocation.x, childAllocation.y, childAllocation.width, + // childAllocation.ascent, childAllocation.descent); + } + + for (int i = 0; i < rightFloats->size(); i++) { + Float *vloat = rightFloats->get(i); + assert (vloat->y != -1); + + Allocation childAllocation; + childAllocation.x = containingBlockAllocation->x + + containingBlockAllocation->width - vloat->width; + childAllocation.y = containingBlockAllocation->y + vloat->y; + childAllocation.width = + vloat->width - containingBlock->getCBStyle()->boxRestWidth(); + childAllocation.ascent = vloat->ascent; + childAllocation.descent = vloat->descent; + + vloat->widget->sizeAllocate (&childAllocation); + + //printf ("allocate right #%d -> (%d, %d), %d x (%d + %d)\n", + // i, childAllocation.x, childAllocation.y, childAllocation.width, + // childAllocation.ascent, childAllocation.descent); + } +} + + +void OutOfFlowMgr::draw (View *view, Rectangle *area) +{ + draw (leftFloats, view, area); + draw (rightFloats, view, area); +} + +void OutOfFlowMgr::draw (Vector<Float> *list, View *view, Rectangle *area) +{ + for (int i = 0; i < list->size(); i++) { + Float *vloat = list->get(i); + assert (vloat->y != -1); + + core::Rectangle childArea; + if (vloat->widget->intersects (area, &childArea)) + vloat->widget->draw (view, &childArea); + } +} + +void OutOfFlowMgr::queueResize(int ref) +{ +} + +bool OutOfFlowMgr::isWidgetOutOfFlow (core::Widget *widget) +{ + // Will be extended for absolute positions. + return widget->getStyle()->vloat != FLOAT_NONE; +} + +void OutOfFlowMgr::addWidget (Widget *widget) +{ + if (widget->getStyle()->vloat != FLOAT_NONE) { + Float *vloat = new Float (); + vloat->widget = widget; + vloat->y = -1; + + Requisition requisition; + widget->sizeRequest (&requisition); + vloat->width = requisition.width; + vloat->ascent = requisition.ascent; + vloat->descent = requisition.descent; + + switch (widget->getStyle()->vloat) { + case FLOAT_LEFT: + vloat->width += containingBlock->getCBStyle()->boxOffsetX(); + leftFloats->put (vloat); + widget->parentRef = createRefLeftFloat (leftFloats->size() - 1); + break; + + case FLOAT_RIGHT: + vloat->width += containingBlock->getCBStyle()->boxRestWidth(); + rightFloats->put (vloat); + widget->parentRef = createRefRightFloat (rightFloats->size() - 1); + break; + + default: + assertNotReached(); + } + } else + // Will continue here for absolute positions. + assertNotReached(); +} + +OutOfFlowMgr::Float *OutOfFlowMgr::findFloatByWidget (Widget *widget) +{ + Vector<Float> *list = NULL; + + switch (widget->getStyle()->vloat) { + case FLOAT_LEFT: + list = leftFloats; + break; + + case FLOAT_RIGHT: + list = rightFloats; + break; + + default: + assertNotReached(); + } + + for(int i = 0; i < list->size(); i++) { + Float *vloat = list->get(i); + if(vloat->widget == widget) + return vloat; + } + + assertNotReached(); + return NULL; +} + + +void OutOfFlowMgr::markSizeChange (int ref) +{ + // TODO Much copy and paste; see addWidget. + if (isRefLeftFloat (ref)) + markSizeChange (leftFloats->get (getFloatIndexFromRef (ref)), + containingBlock->getCBStyle()->boxOffsetX()); + else if (isRefRightFloat (ref)) + markSizeChange (rightFloats->get (getFloatIndexFromRef (ref)), + containingBlock->getCBStyle()->boxRestWidth()); + else + // later: absolute positions + assertNotReached(); +} + +void OutOfFlowMgr::markSizeChange (Float *vloat, int widthDiff) +{ + int oldWidth = vloat->width; + int oldHeight = vloat->ascent + vloat->descent; + + Requisition requisition; + vloat->widget->sizeRequest (&requisition); + vloat->width = requisition.width + widthDiff; + vloat->ascent = requisition.ascent; + vloat->descent = requisition.descent; + + if (vloat->width != oldWidth) + containingBlock->borderChanged (vloat->y); + else if (vloat->ascent + vloat->descent != oldHeight) + // Width remains the same, so a small optimization is possible. + containingBlock->borderChanged (vloat->y + + min (vloat->ascent + vloat->descent, + oldHeight)); +} + +void OutOfFlowMgr::markExtremesChange (int ref) +{ +} + +void OutOfFlowMgr::tellNoPosition (Widget *widget) +{ + Float *vloat = findFloatByWidget(widget); + int oldY = vloat->y; + vloat->y = -1; + + if (oldY != -1) + containingBlock->borderChanged (oldY); +} + +void OutOfFlowMgr::tellPosition (Widget *widget, int y) +{ + assert (y >= 0); + + // TODO Test collisions; when floats overlap, the vloat->y must be larger + // than y. + + Float *vloat = findFloatByWidget(widget); + int oldY = vloat->y; + vloat->y = y; + + if (oldY == -1) + containingBlock->borderChanged (y); + else if (y != oldY) + containingBlock->borderChanged (min (oldY, y)); +} + +int OutOfFlowMgr::getLeftBorder (int y) +{ + //return 40 * sin ((double)y / 30); + + for(int i = 0; i < leftFloats->size(); i++) { + Float *vloat = leftFloats->get(i); + if(vloat->y != - 1 && y >= vloat->y && + y < vloat->y + vloat->ascent + vloat->descent) { + //printf (" LEFT: %d ==> %d (%d + %d)\n", y, + // vloat->width, vloat->ascent, vloat->descent); + return vloat->width; + } + } + + //printf (" LEFT: %d ==> %d\n", y, 0); + return 0; +} + +int OutOfFlowMgr::getRightBorder (int y) +{ + //return 40 * cos ((double)y / 30); + + for(int i = 0; i < rightFloats->size(); i++) { + Float *vloat = rightFloats->get(i); + if(vloat->y != - 1 && y >= vloat->y && + y < vloat->y + vloat->ascent + vloat->descent) + //printf (" RIGHT: %d ==> %d (%d + %d)\n", y, + // vloat->width, vloat->ascent, vloat->descent); + return vloat->width; + } + + //printf (" RIGHT: %d ==> %d\n", y, 0); + return 0; +} + +} // namespace dw diff --git a/dw/outofflowmgr.hh b/dw/outofflowmgr.hh new file mode 100644 index 00000000..5e96b639 --- /dev/null +++ b/dw/outofflowmgr.hh @@ -0,0 +1,105 @@ +#ifndef __DW_OUTOFFLOWMGR_HH__ +#define __DW_OUTOFFLOWMGR_HH__ + +#include "core.hh" + +namespace dw { + +/** + * \brief Represents additional data for containing blocks. + */ +class OutOfFlowMgr +{ +public: + class ContainingBlock + { + public: + virtual void borderChanged (int y) = 0; + + // An additional "CB" to resolve the ambiguity to the methods in Widget. + virtual core::style::Style *getCBStyle () = 0; + virtual core::Allocation *getCBAllocation () = 0; + }; + +private: + ContainingBlock *containingBlock; + + class Float: public lout::object::Object + { + public: + core::Widget *widget; + // width includes border of the containing block + int y, width, ascent, descent; + }; + + //lout::container::typed::HashTable<lout::object::TypedPointer + // <dw::core::Widget>, Float> *floatsByWidget; + lout::container::typed::Vector<Float> *leftFloats, *rightFloats; + + Float *findFloatByWidget (core::Widget *widget); + void markSizeChange (Float *vloat, int widthDiff); + + void draw (lout::container::typed::Vector<Float> *list, + core::View *view, core::Rectangle *area); + + inline static bool isRefLeftFloat (int ref) + { return ref != -1 && (ref & 3) == 1; } + inline static bool isRefRightFloat (int ref) + { return ref != -1 && (ref & 3) == 3; } + + inline static int createRefLeftFloat (int index) + { return (index << 2) | 1; } + inline static int createRefRightFloat (int index) + { return (index << 2) | 3; } + + inline static int getFloatIndexFromRef (int ref) + { return ref == -1 ? ref : (ref >> 2); } + +public: + OutOfFlowMgr (ContainingBlock *containingBlock); + ~OutOfFlowMgr (); + + void sizeAllocate(core::Allocation *containingBlockAllocation); + void draw (core::View *view, core::Rectangle *area); + void queueResize(int ref); + + void markSizeChange (int ref); + void markExtremesChange (int ref); + + static bool isWidgetOutOfFlow (core::Widget *widget); + void addWidget (core::Widget *widget); + + void tellNoPosition (core::Widget *widget); + void tellPosition (core::Widget *widget, int y); + + /** + * Get the left border for the vertical position of y, based on + * floats. The border includes marging/border/padding of the + * containging block, but is 0 if there is no float, so a caller + * should also consider other borders. + */ + int getLeftBorder (int y); + + int getRightBorder (int y); + + inline static bool isRefOutOfFlow (int ref) + { return ref != -1 && (ref & 1) != 0; } + inline static int createRefNormalFlow (int lineNo) { return lineNo << 1; } + inline static int getLineNoFromRef (int ref) + { return ref == -1 ? ref : (ref >> 1); } + + //inline static bool isRefOutOfFlow (int ref) { return false; } + //inline static int createRefNormalFlow (int lineNo) { return lineNo; } + //inline static int getLineNoFromRef (int ref) { return ref; } + + // for iterators + inline int getNumWidgets () { + return leftFloats->size() + rightFloats->size(); } + inline core::Widget *getWidget (int i) { + return i < leftFloats->size() ? leftFloats->get(i)->widget : + rightFloats->get(leftFloats->size())->widget; } +}; + +} // namespace dw + +#endif // __DW_OUTOFFLOWMGR_HH__ diff --git a/dw/style.cc b/dw/style.cc index 2a1d4088..b0868c1f 100644 --- a/dw/style.cc +++ b/dw/style.cc @@ -17,8 +17,6 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - - #include <stdio.h> #include <string.h> #include <unistd.h> @@ -49,6 +47,8 @@ void StyleAttrs::initValues () valign = VALIGN_BASELINE; backgroundColor = NULL; width = height = lineHeight = LENGTH_AUTO; + vloat = FLOAT_NONE; + clear = CLEAR_NONE; textIndent = 0; margin.setVal (0); borderWidth.setVal (0); @@ -75,6 +75,8 @@ void StyleAttrs::resetValues () valign = VALIGN_BASELINE; textAlignChar = '.'; + vloat = FLOAT_NONE; /** \todo Correct? Check specification. */ + clear = CLEAR_NONE; /** \todo Correct? Check specification. */ backgroundColor = NULL; width = LENGTH_AUTO; height = LENGTH_AUTO; @@ -120,6 +122,8 @@ bool StyleAttrs::equals (object::Object *other) { valign == otherAttrs->valign && textAlignChar == otherAttrs->textAlignChar && textTransform == otherAttrs->textTransform && + vloat == otherAttrs->vloat && + clear == otherAttrs->clear && hBorderSpacing == otherAttrs->hBorderSpacing && vBorderSpacing == otherAttrs->vBorderSpacing && wordSpacing == otherAttrs->wordSpacing && @@ -160,6 +164,8 @@ int StyleAttrs::hashValue () { valign + textAlignChar + textTransform + + vloat + + clear + hBorderSpacing + vBorderSpacing + wordSpacing + @@ -252,6 +258,8 @@ void Style::copyAttrs (StyleAttrs *attrs) valign = attrs->valign; textAlignChar = attrs->textAlignChar; textTransform = attrs->textTransform; + vloat = attrs->vloat; + clear = attrs->clear; hBorderSpacing = attrs->hBorderSpacing; vBorderSpacing = attrs->vBorderSpacing; wordSpacing = attrs->wordSpacing; diff --git a/dw/style.hh b/dw/style.hh index 2422bfa9..6b492793 100644 --- a/dw/style.hh +++ b/dw/style.hh @@ -281,7 +281,6 @@ enum ListStylePosition { LIST_STYLE_POSITION_INSIDE, LIST_STYLE_POSITION_OUTSIDE }; - enum ListStyleType { LIST_STYLE_TYPE_DISC, LIST_STYLE_TYPE_CIRCLE, @@ -333,6 +332,19 @@ enum WhiteSpace { WHITE_SPACE_PRE_LINE, }; +enum FloatType { + FLOAT_NONE, + FLOAT_LEFT, + FLOAT_RIGHT +}; + +enum ClearType { + CLEAR_LEFT, + CLEAR_RIGHT, + CLEAR_BOTH, + CLEAR_NONE +}; + /** * \brief Type for representing all lengths within dw::core::style. * @@ -450,6 +462,9 @@ public: VAlignType valign; char textAlignChar; /* In future, strings will be supported. */ TextTransform textTransform; + + FloatType vloat; /* "float" is a keyword. */ + ClearType clear; int hBorderSpacing, vBorderSpacing, wordSpacing; Length width, height, lineHeight, textIndent; diff --git a/dw/table.cc b/dw/table.cc index c21b7a09..c4108e47 100644 --- a/dw/table.cc +++ b/dw/table.cc @@ -1103,7 +1103,7 @@ Table::TableIterator::TableIterator (Table *table, else if (index >= table->children->size ()) content.type = core::Content::END; else { - content.type = core::Content::WIDGET; + content.type = core::Content::WIDGET_IN_FLOW; content.widget = table->children->get(index)->cell.widget; } } @@ -1125,8 +1125,8 @@ bool Table::TableIterator::next () if (content.type == core::Content::END) return false; - // tables only contain widgets: - if ((getMask() & core::Content::WIDGET) == 0) { + // tables only contain widgets (in flow): + if ((getMask() & core::Content::WIDGET_IN_FLOW) == 0) { content.type = core::Content::END; return false; } @@ -1140,7 +1140,7 @@ bool Table::TableIterator::next () } while (table->children->get(index) == NULL || table->children->get(index)->type != Child::CELL); - content.type = core::Content::WIDGET; + content.type = core::Content::WIDGET_IN_FLOW; content.widget = table->children->get(index)->cell.widget; return true; } @@ -1152,8 +1152,8 @@ bool Table::TableIterator::prev () if (content.type == core::Content::START) return false; - // tables only contain widgets: - if ((getMask() & core::Content::WIDGET) == 0) { + // tables only contain widgets (in flow): + if ((getMask() & core::Content::WIDGET_IN_FLOW) == 0) { content.type = core::Content::START; return false; } @@ -1167,7 +1167,7 @@ bool Table::TableIterator::prev () } while (table->children->get(index) == NULL || table->children->get(index)->type != Child::CELL); - content.type = core::Content::WIDGET; + content.type = core::Content::WIDGET_IN_FLOW; content.widget = table->children->get(index)->cell.widget; return true; } diff --git a/dw/tablecell.cc b/dw/tablecell.cc index 90dc310d..d1a8e3e1 100644 --- a/dw/tablecell.cc +++ b/dw/tablecell.cc @@ -102,7 +102,7 @@ int TableCell::getValue () void TableCell::setMaxValue (int maxValue, int value) { line1Offset = maxValue - value; - queueResize (0, true); + queueResize (OutOfFlowMgr::createRefNormalFlow (0), true); } } // namespace dw diff --git a/dw/textblock.cc b/dw/textblock.cc index e3d4a8b5..ee5d4f67 100644 --- a/dw/textblock.cc +++ b/dw/textblock.cc @@ -23,13 +23,14 @@ #include "../lout/misc.hh" #include <stdio.h> -#include <math.h> +#include <math.h> // remove again? +#include <limits.h> /* * Local variables */ - /* The tooltip under mouse pointer in current textblock. No ref. hold. - * (having one per view looks not worth the extra clutter). */ +/* The tooltip under mouse pointer in current textblock. No ref. hold. + * (having one per view looks not worth the extra clutter). */ static dw::core::style::Tooltip *hoverTooltip = NULL; @@ -68,6 +69,7 @@ Textblock::Textblock (bool limitTextWidth) nonTemporaryLines = 0; words = new misc::NotSoSimpleVector <Word> (1); anchors = new misc::SimpleVector <Anchor> (1); + outOfFlowMgr = NULL; //DBG_OBJ_SET_NUM(this, "num_lines", num_lines); @@ -105,8 +107,9 @@ Textblock::~Textblock () for (int i = 0; i < words->size(); i++) { Word *word = words->getRef (i); - if (word->content.type == core::Content::WIDGET) + if (word->content.type == core::Content::WIDGET_IN_FLOW) delete word->content.widget; + /** \todo Widget references? What about texts? */ word->style->unref (); word->spaceStyle->unref (); } @@ -121,6 +124,9 @@ Textblock::~Textblock () delete words; delete anchors; + if(outOfFlowMgr) + delete outOfFlowMgr; + /* Make sure we don't own widgets anymore. Necessary before call of parent class destructor. (???) */ words = NULL; @@ -187,7 +193,7 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition) */ void Textblock::getWordExtremes (Word *word, core::Extremes *extremes) { - if (word->content.type == core::Content::WIDGET) { + if (word->content.type == core::Content::WIDGET_IN_FLOW) { if (word->content.widget->usesHints ()) word->content.widget->getExtremes (extremes); else { @@ -359,7 +365,7 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) redrawY = misc::min (redrawY, lineYOffsetWidget (line)); } - if (word->content.type == core::Content::WIDGET) { + if (word->content.type == core::Content::WIDGET_IN_FLOW) { /** \todo Justification within the line is done here. */ childAllocation.x = xCursor + allocation->x; /* align=top: @@ -431,6 +437,9 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) } } + if(outOfFlowMgr) + outOfFlowMgr->sizeAllocate(allocation); + for (int i = 0; i < anchors->size(); i++) { Anchor *anchor = anchors->getRef(i); int y; @@ -461,16 +470,25 @@ void Textblock::resizeDrawImpl () void Textblock::markSizeChange (int ref) { - markChange (ref); + if (OutOfFlowMgr::isRefOutOfFlow (ref)) { + assert (outOfFlowMgr != NULL); + outOfFlowMgr->markSizeChange (ref); + } else + markChange (ref); } void Textblock::markExtremesChange (int ref) { - markChange (ref); + if (OutOfFlowMgr::isRefOutOfFlow (ref)) { + assert (outOfFlowMgr != NULL); + outOfFlowMgr->markExtremesChange (ref); + } else + markChange (ref); } /* - * Implementation for both mark_size_change and mark_extremes_change. + * Implementation for both markSizeChange and markExtremesChange. + * Only used for normal flow. */ void Textblock::markChange (int ref) { @@ -481,16 +499,66 @@ void Textblock::markChange (int ref) and (ii) a word may have parentRef == -1 , when it is not yet added to a line. In the latter case, nothing has to be done now, but addLine(...) will do everything necessary. */ + if (ref != -1) { if (wrapRef == -1) - wrapRef = ref; + wrapRef = OutOfFlowMgr::getLineNoFromRef (ref); else - wrapRef = misc::min (wrapRef, ref); + wrapRef = misc::min (wrapRef, OutOfFlowMgr::getLineNoFromRef (ref)); } PRINTF (" ... => %d\n", wrapRef); } +void Textblock::notifySetAsTopLevel() +{ + printf ("%p becomes toplevel\n", this); + containingBlock = this; + printf ("-> %p is its own containing block\n", this); +} + +bool Textblock::isContainingBlock (Widget *widget) +{ + return + // Of course, only textblocks are considered as containing + // blocks. + widget->instanceOf (Textblock::CLASS_ID) && + // The second condition: that this block is "out of flow", in a + // wider sense. + (// The toplevel widget is "out of flow", since there is no + // parent, and so no context. + widget->getParent() == NULL || + // A similar reasoning applies to a widget with another parent + // than a textblock (typical example: a table cell (this is + // also a text block) within a table widget). + !widget->getParent()->instanceOf (Textblock::CLASS_ID) || + // Finally, "out of flow" in a narrower sense: floats and + // absolute positions. + OutOfFlowMgr::isWidgetOutOfFlow (widget)); +} + +void Textblock::notifySetParent () +{ + printf ("%p becomes a child of %p\n", this, getParent()); + + // Search for containing Box. + containingBlock = NULL; + + for (Widget *widget = this; widget != NULL && containingBlock == NULL; + widget = widget->getParent()) + if (isContainingBlock (widget)) { + containingBlock = (Textblock*)widget; + + if (containingBlock == this) + printf ("-> %p is its own containing block\n", this); + else + printf ("-> %p becomes containing block of %p\n", + containingBlock, this); + } + + assert (containingBlock != NULL); +} + void Textblock::setWidth (int width) { /* If limitTextWidth is set to YES, a queueResize() may also be @@ -502,7 +570,7 @@ void Textblock::setWidth (int width) // words->size()); availWidth = width; - queueResize (0, false); + queueResize (OutOfFlowMgr::createRefNormalFlow (0), false); mustQueueResize = false; redrawY = 0; } @@ -517,7 +585,7 @@ void Textblock::setAscent (int ascent) // words->size()); availAscent = ascent; - queueResize (0, false); + queueResize (OutOfFlowMgr::createRefNormalFlow (0), false); mustQueueResize = false; } } @@ -531,7 +599,7 @@ void Textblock::setDescent (int descent) // words->size()); availDescent = descent; - queueResize (0, false); + queueResize (OutOfFlowMgr::createRefNormalFlow (0), false); mustQueueResize = false; } } @@ -733,7 +801,7 @@ bool Textblock::sendSelectionEvent (core::SelectionState::EventType eventType, } } it = new TextblockIterator (this, core::Content::SELECTION_CONTENT, - wordIndex); + false, wordIndex); r = selectionHandleEvent (eventType, it, charPos, link, event); it->unref (); return r; @@ -1107,10 +1175,10 @@ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area) if (xWidget + word->size.width + word->effSpace >= area->x) { if (word->content.type == core::Content::TEXT || - word->content.type == core::Content::WIDGET) { + word->content.type == core::Content::WIDGET_IN_FLOW) { if (word->size.width > 0) { - if (word->content.type == core::Content::WIDGET) { + if (word->content.type == core::Content::WIDGET_IN_FLOW) { core::Widget *child = word->content.widget; core::Rectangle childArea; @@ -1272,6 +1340,9 @@ void Textblock::draw (core::View *view, core::Rectangle *area) drawLine (line, view, area); } + + if(outOfFlowMgr) + outOfFlowMgr->draw(view, area); } /** @@ -1544,37 +1615,37 @@ void Textblock::addText0 (const char *text, size_t len, bool canBeHyphenated, */ void Textblock::addWidget (core::Widget *widget, core::style::Style *style) { - Word *word; - core::Requisition size; - /* We first assign -1 as parent_ref, since the call of widget->size_request * will otherwise let this Textblock be rewrapped from the beginning. * (parent_ref is actually undefined, but likely has the value 0.) At the, * end of this function, the correct value is assigned. */ widget->parentRef = -1; - PRINTF ("%p becomes child of %p\n", widget, this); - - widget->setParent (this); widget->setStyle (style); + + if (OutOfFlowMgr::isWidgetOutOfFlow (widget)) { + if (containingBlock->outOfFlowMgr == NULL) + containingBlock->outOfFlowMgr = new OutOfFlowMgr (containingBlock); + + widget->setParent (containingBlock); + containingBlock->outOfFlowMgr->addWidget (widget); + Word *word = addWord (0, 0, 0, false, style); + word->content.type = core::Content::WIDGET_OOF_REF; + word->content.breakSpace = 0; + word->content.widget = widget; + word->style = style; + } else { + widget->setParent (this); - calcWidgetSize (widget, &size); - word = addWord (size.width, size.ascent, size.descent, false, style); - - word->content.type = core::Content::WIDGET; - word->content.widget = widget; - - //DBG_OBJ_ARRSET_PTR (page, "words.%d.content.widget", words->size() - 1, - // word->content.widget); + core::Requisition size; + calcWidgetSize (widget, &size); + Word *word = + addWord (size.width, size.ascent, size.descent, false, style); + word->content.type = core::Content::WIDGET_IN_FLOW; + word->content.widget = widget; + } wordWrap (words->size () - 1, false); - //DBG_OBJ_SET_NUM (word->content.widget, "parent_ref", - // word->content.widget->parent_ref); - - //DEBUG_MSG(DEBUG_REWRAP_LEVEL, - // "Assigning parent_ref = %d to added word %d, " - // "in page with %d word(s)\n", - // lines->size () - 1, words->size() - 1, words->size()); } /** @@ -1711,7 +1782,7 @@ void Textblock::addParbreak (int space, core::style::Style *style) /* A break may not be the first word of a page, or directly after the bullet/number (which is the first word) in a list item. (See - also comment in Dw_page_size_request.) */ + also comment in sizeRequest.) */ if (words->size () == 0 || (hasListitemValue && words->size () == 1)) { /* This is a bit hackish: If a break is added as the @@ -1720,23 +1791,23 @@ void Textblock::addParbreak (int space, core::style::Style *style) a widget is used as a text box (lists, blockquotes, list items etc) -- then we simply adjust the break before, in a way that the space is in any case visible. */ - Widget *widget; - - /* Find the widget where to adjust the breakSpace. */ - for (widget = this; - widget->getParent() && - widget->getParent()->instanceOf (Textblock::CLASS_ID); + /* Find the widget where to adjust the breakSpace. (Only + consider normal flow, no floats etc.) */ + for (Widget *widget = this; + widget->getParent() != NULL && + widget->getParent()->instanceOf (Textblock::CLASS_ID) && + !OutOfFlowMgr::isRefOutOfFlow (widget->parentRef); widget = widget->getParent ()) { Textblock *textblock2 = (Textblock*)widget->getParent (); int index = textblock2->hasListitemValue ? 1 : 0; bool isfirst = (textblock2->words->getRef(index)->content.type - == core::Content::WIDGET + == core::Content::WIDGET_IN_FLOW && textblock2->words->getRef(index)->content.widget == widget); if (!isfirst) { - /* The page we searched for has been found. */ + /* The text block we searched for has been found. */ Word *word2; - int lineno = widget->parentRef; + int lineno = OutOfFlowMgr::getLineNoFromRef (widget->parentRef); if (lineno > 0 && (word2 = @@ -1745,7 +1816,8 @@ void Textblock::addParbreak (int space, core::style::Style *style) word2->content.type == core::Content::BREAK) { if (word2->content.breakSpace < space) { word2->content.breakSpace = space; - textblock2->queueResize (lineno, false); + textblock2->queueResize + (OutOfFlowMgr::createRefNormalFlow (lineno), false); textblock2->mustQueueResize = false; } } @@ -1753,6 +1825,7 @@ void Textblock::addParbreak (int space, core::style::Style *style) } /* Otherwise continue to examine parents. */ } + /* Return in any case. */ return; } @@ -1801,7 +1874,6 @@ void Textblock::addLinebreak (core::style::Style *style) wordWrap (words->size () - 1, false); } - /** * \brief Search recursively through widget. * @@ -1831,7 +1903,7 @@ core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level) for (wordIndex = line->firstWord; wordIndex <= line->lastWord;wordIndex++) { Word *word = words->getRef (wordIndex); - if (word->content.type == core::Content::WIDGET) { + if (word->content.type == core::Content::WIDGET_IN_FLOW) { core::Widget * childAtPoint; childAtPoint = word->content.widget->getWidgetAtPoint (x, y, level + 1); @@ -1913,7 +1985,7 @@ void Textblock::changeLinkColor (int link, int newColor) old_style->unref(); break; } - case core::Content::WIDGET: + case core::Content::WIDGET_IN_FLOW: { core::Widget *widget = word->content.widget; styleAttrs = *widget->getStyle(); styleAttrs.color = core::style::Color::create (layout, @@ -1966,4 +2038,54 @@ void Textblock::queueDrawRange (int index1, int index2) } } +void Textblock::borderChanged (int y) +{ + printf ("[%p] border has changed: %d\n", this, y); + borderChanged (y + allocation.y, true); +} + +void Textblock::borderChanged (int yCanvas, bool extremesChanges) +{ + // Notice that this method is, unlike the other "borderChanged", + // called (i) with canvas coordinates, not widget coordinates, and + // (ii) for all nested textblocks, not only the containing block. + + // findLineIndex expects widget coordinates + int lineIndex = findLineIndex (yCanvas - allocation.y); + // Nothing to do at all, when lineIndex >= lines->size (), + // i. e. the change is below the bottom od this widget. + if (lineIndex < lines->size ()) { + int wrapLineIndex; + if (lineIndex < 0) + // Rewrap all. + wrapLineIndex = 0; + else + wrapLineIndex = lineIndex; + + queueResize (OutOfFlowMgr::createRefNormalFlow (wrapLineIndex), + extremesChanges); + + for (int i = wrapLineIndex; i < lines->size (); i++) { + Word *word = words->getRef (lines->getRef(i)->firstWord); + if (word->content.type == core::Content::WIDGET_IN_FLOW && + word->content.widget->instanceOf (Textblock::CLASS_ID)) { + Textblock *childBlock = (Textblock*)word->content.widget; + // extremes only change for the containing block, so we pass + // extremesChanges = false for all other widgets. + childBlock->borderChanged (yCanvas, false); + } + } + } +} + +core::style::Style *Textblock::getCBStyle () +{ + return getStyle(); +} + +core::Allocation *Textblock::getCBAllocation () +{ + return &allocation; +} + } // namespace dw diff --git a/dw/textblock.hh b/dw/textblock.hh index 12950ec3..c386a391 100644 --- a/dw/textblock.hh +++ b/dw/textblock.hh @@ -4,6 +4,7 @@ #include <limits.h> #include "core.hh" +#include "outofflowmgr.hh" #include "../lout/misc.hh" // These were used when improved line breaking and hyphenation were @@ -18,10 +19,11 @@ namespace dw { * of paragraphs. * * <div style="border: 2px solid #ffff00; margin-top: 0.5em; - * margin-bottom: 0.5em; padding: 0.5em 1em; - * background-color: #ffffe0"><b>Info:</b> The recent changes (line - * breaking and hyphenation) have not yet been incorporated into this - * documentation. See \ref dw-line-breaking.</div> + * margin-bottom: 0.5em; padding: 0.5em 1em; background-color: + * #ffffe0"><b>Info:</b> The recent changes (line breaking and + * hyphenation on one hand, floats on the other hand) have not yet + * been incorporated into this documentation. See \ref + * dw-line-breaking and \ref dw-out-of-flow.</div> * * <h3>Signals</h3> * @@ -141,7 +143,7 @@ namespace dw { * necessary, or otherwise the line from which a rewrap is necessary. * */ -class Textblock: public core::Widget +class Textblock: public core::Widget, public OutOfFlowMgr::ContainingBlock { private: /** @@ -207,6 +209,9 @@ private: void print (); }; + Textblock *containingBlock; + OutOfFlowMgr *outOfFlowMgr; + protected: enum { /** @@ -310,13 +315,14 @@ protected: class TextblockIterator: public core::Iterator { private: + bool oofm; int index; public: TextblockIterator (Textblock *textblock, core::Content::Type mask, bool atEnd); TextblockIterator (Textblock *textblock, core::Content::Type mask, - int index); + bool oofm, int index); lout::object::Object *clone(); int compareTo(lout::misc::Comparable *other); @@ -367,7 +373,8 @@ protected: /* These values are set by set_... */ int availWidth, availAscent, availDescent; - int wrapRef; /* [0 based] */ + int wrapRef; /* 0-based. Important: This is the line number, not + the value stored in parentRef. */ lout::misc::SimpleVector <Line> *lines; int nonTemporaryLines; @@ -420,25 +427,115 @@ protected: core::Requisition *size); /** - * \brief Returns the x offset (the indentation plus any offset needed for - * centering or right justification) for the line. - * - * The offset returned is relative to the page *content* (i.e. without - * border etc.). + * Of nested text blocks, only the most inner one must regard the + * borders of floats. Extremely (perhaps too) simple + * implementation. */ - inline int lineXOffsetContents (Line *line) + inline bool mustBorderBeRegardedForWord (int firstWord) { - return innerPadding + line->leftOffset + - (line == lines->getFirstRef() ? line1OffsetEff : 0); + assert (firstWord < words->size ()); + Word *word = words->getRef (firstWord); + return !(word->content.type == core::Content::WIDGET_IN_FLOW && + word->content.widget->instanceOf (Textblock::CLASS_ID)); + } + + inline bool mustBorderBeRegarded (Line *line) + { + return mustBorderBeRegardedForWord (line->firstWord); + } + + inline bool mustBorderBeRegarded (int lineNo) + { + int firstWord; + if (lineNo == 0) + firstWord = 0; + else + firstWord = lines->getRef(lineNo - 1)->lastWord + 1; + + return mustBorderBeRegardedForWord (firstWord); } + void borderChanged (int yCanvas, bool extremesChanges); + /** - * \brief Like lineXOffset, but relative to the allocation (i.e. - * including border etc.). + * \brief Returns the x offset (the indentation plus any offset + * needed for centering or right justification) for the line, + * relative to the allocation (i.e. including border etc.). */ inline int lineXOffsetWidget (Line *line) { - return lineXOffsetContents (line) + getStyle()->boxOffsetX (); + int resultFromOOFM; + if (containingBlock->outOfFlowMgr && mustBorderBeRegarded (line)) + resultFromOOFM = + containingBlock->getAllocation()->x - allocation.x + + containingBlock->outOfFlowMgr->getLeftBorder + (allocation.y + line->top + getStyle()->boxOffsetY() + - containingBlock->getAllocation()->y); + else + resultFromOOFM = 0; + + return innerPadding + line->leftOffset + + (line == lines->getFirstRef() ? line1OffsetEff : 0) + + lout::misc::max (getStyle()->boxOffsetX(), resultFromOOFM); + } + + inline int lineLeftBorder (int lineNo) + { + // Note that the line must not exist yet (but unless it is not + // the first line, the previous line, lineNo - 1, must). But + // lineHeight should be known to the caller. + int top; + if (lineNo == 0) + top = 0; + else { + Line *prevLine = lines->getRef (lineNo - 1); + top = prevLine->top + prevLine->boxAscent + prevLine->boxDescent + + prevLine->breakSpace; + } + + int resultFromOOFM; + if (containingBlock->outOfFlowMgr && mustBorderBeRegarded (lineNo)) + resultFromOOFM = + containingBlock->getAllocation()->x - allocation.x + + containingBlock->outOfFlowMgr->getLeftBorder + (allocation.y + top + getStyle()->boxOffsetY() + - containingBlock->getAllocation()->y); + else + resultFromOOFM = 0; + + // TODO: line->leftOffset is not regarded, which is correct, depending + // on where this method is called. Document; perhaps rename this method. + // (Update: was renamed.) + return innerPadding + + (lineNo == 0 ? line1OffsetEff : 0) + + lout::misc::max (getStyle()->boxOffsetX(), resultFromOOFM); + } + + inline int lineRightBorder (int lineNo) + { + // Similar to lineLeftBorder(). + int top; + if (lineNo == 0) + top = 0; + else { + Line *prevLine = lines->getRef (lineNo - 1); + top = prevLine->top + prevLine->boxAscent + prevLine->boxDescent + + prevLine->breakSpace; + } + + int resultFromOOFM; + if (containingBlock->outOfFlowMgr && mustBorderBeRegarded (lineNo)) + resultFromOOFM = + (containingBlock->getAllocation()->x + + containingBlock->getAllocation()->width) - + (allocation.x + allocation.width) + + containingBlock->outOfFlowMgr->getRightBorder + (allocation.y + top + getStyle()->boxOffsetY() + - containingBlock->getAllocation()->y); + else + resultFromOOFM = 0; + + return lout::misc::max (getStyle()->boxRestWidth(), resultFromOOFM); } inline int lineYOffsetWidgetAllocation (Line *line, @@ -499,6 +596,8 @@ protected: void markSizeChange (int ref); void markExtremesChange (int ref); + void notifySetAsTopLevel(); + void notifySetParent(); void setWidth (int width); void setAscent (int ascent); void setDescent (int descent); @@ -518,6 +617,7 @@ protected: core::style::Style *style, int numBreaks, int *breakPos, core::Requisition *wordSize); + static bool isContainingBlock (Widget *widget); public: static int CLASS_ID; @@ -563,6 +663,11 @@ public: void changeLinkColor (int link, int newColor); void changeWordStyle (int from, int to, core::style::Style *style, bool includeFirstSpace, bool includeLastSpace); + + // From OutOfFlowMgr::ContainingBlock: + void borderChanged (int y); + core::style::Style *getCBStyle (); + core::Allocation *getCBAllocation (); }; } // namespace dw diff --git a/dw/textblock_iterator.cc b/dw/textblock_iterator.cc index b5d28885..9af98137 100644 --- a/dw/textblock_iterator.cc +++ b/dw/textblock_iterator.cc @@ -33,20 +33,34 @@ Textblock::TextblockIterator::TextblockIterator (Textblock *textblock, bool atEnd): core::Iterator (textblock, mask, atEnd) { - index = atEnd ? textblock->words->size () : -1; + if (atEnd) { + if (textblock->outOfFlowMgr) { + oofm = true; + index = textblock->outOfFlowMgr->getNumWidgets(); + } else { + oofm = false; + index = textblock->words->size(); + } + } else { + oofm = false; + index = -1; + } + content.type = atEnd ? core::Content::END : core::Content::START; } Textblock::TextblockIterator::TextblockIterator (Textblock *textblock, core::Content::Type mask, - int index): + bool oofm, int index): core::Iterator (textblock, mask, false) { + this->oofm = oofm; this->index = index; + // TODO To be completely exact, oofm should be considered here. if (index < 0) content.type = core::Content::START; - else if (index >= textblock->words->size ()) + else if (index >= textblock->words->size()) content.type = core::Content::END; else content = textblock->words->getRef(index)->content; @@ -54,12 +68,20 @@ Textblock::TextblockIterator::TextblockIterator (Textblock *textblock, object::Object *Textblock::TextblockIterator::clone() { - return new TextblockIterator ((Textblock*)getWidget(), getMask(), index); + return + new TextblockIterator ((Textblock*)getWidget(), getMask(), oofm, index); } int Textblock::TextblockIterator::compareTo(misc::Comparable *other) { - return index - ((TextblockIterator*)other)->index; + TextblockIterator *otherTI = (TextblockIterator*)other; + + if (oofm && !otherTI->oofm) + return +1; + else if (!oofm && otherTI->oofm) + return -1; + else + return index - otherTI->index; } bool Textblock::TextblockIterator::next () @@ -69,15 +91,53 @@ bool Textblock::TextblockIterator::next () if (content.type == core::Content::END) return false; + short type; + do { index++; - if (index >= textblock->words->size ()) { - content.type = core::Content::END; - return false; + + if (oofm) { + // Iterating over OOFM. + if (index >= textblock->outOfFlowMgr->getNumWidgets()) { + // End of OOFM list reached. + content.type = core::Content::END; + return false; + } + type = core::Content::WIDGET_OOF_CONT; + } else { + // Iterating over words list. + if (index < textblock->words->size ()) + // Still words left. + type = textblock->words->getRef(index)->content.type; + else { + // End of words list reached. + if (textblock->outOfFlowMgr) { + oofm = true; + index = 0; + if (textblock->outOfFlowMgr->getNumWidgets() > 0) + // Start with OOFM widgets. + type = core::Content::WIDGET_OOF_CONT; + else { + // No OOFM widgets (number is 0). + content.type = core::Content::END; + return false; + } + } else { + // No OOFM widgets (no OOFM agt all). + content.type = core::Content::END; + return false; + } + } } - } while ((textblock->words->getRef(index)->content.type & getMask()) == 0); + } while ((type & getMask()) == 0); + + if (oofm) { + content.type = core::Content::WIDGET_OOF_CONT; + content.type = false; + content.widget = textblock->outOfFlowMgr->getWidget (index); + } else + content = textblock->words->getRef(index)->content; - content = textblock->words->getRef(index)->content; return true; } @@ -88,107 +148,155 @@ bool Textblock::TextblockIterator::prev () if (content.type == core::Content::START) return false; + short type; + do { index--; - if (index < 0) { - content.type = core::Content::START; - return false; + + if (oofm) { + // Iterating over OOFM. + if (index >= 0) + // Still widgets left. + type = core::Content::WIDGET_OOF_CONT; + else { + // Beginning of OOFM list reached. Continue with words. + oofm = false; + index = textblock->words->size() - 1; + if (index < 0) { + // There are no words. (Actually, this case should not + // happen: When there are OOF widgets, ther must be OOF + // references, or widgets in flow, which contain + // references. + content.type = core::Content::END; + return false; + } + type = textblock->words->getRef(index)->content.type; + } + } else { + // Iterating over words list. + if (index < 0) { + // Beginning of words list reached. + content.type = core::Content::START; + return false; + } + type = textblock->words->getRef(index)->content.type; } - } while ((textblock->words->getRef(index)->content.type & getMask()) == 0); + } while ((type & getMask()) == 0); + + if (oofm) { + content.type = core::Content::WIDGET_OOF_CONT; + content.type = false; + content.widget = textblock->outOfFlowMgr->getWidget (index); + } else + content = textblock->words->getRef(index)->content; - content = textblock->words->getRef(index)->content; return true; } void Textblock::TextblockIterator::highlight (int start, int end, core::HighlightLayer layer) { - Textblock *textblock = (Textblock*)getWidget(); - int index1 = index, index2 = index; - - if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index) { - /* nothing is highlighted */ - textblock->hlStart[layer].index = index; - textblock->hlEnd[layer].index = index; - } - - if (textblock->hlStart[layer].index >= index) { - index2 = textblock->hlStart[layer].index; - textblock->hlStart[layer].index = index; - textblock->hlStart[layer].nChar = start; - } + if (!oofm) { + Textblock *textblock = (Textblock*)getWidget(); + int index1 = index, index2 = index; + + if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index) { + /* nothing is highlighted */ + textblock->hlStart[layer].index = index; + textblock->hlEnd[layer].index = index; + } + + if (textblock->hlStart[layer].index >= index) { + index2 = textblock->hlStart[layer].index; + textblock->hlStart[layer].index = index; + textblock->hlStart[layer].nChar = start; + } + + if (textblock->hlEnd[layer].index <= index) { + index2 = textblock->hlEnd[layer].index; + textblock->hlEnd[layer].index = index; + textblock->hlEnd[layer].nChar = end; + } - if (textblock->hlEnd[layer].index <= index) { - index2 = textblock->hlEnd[layer].index; - textblock->hlEnd[layer].index = index; - textblock->hlEnd[layer].nChar = end; + textblock->queueDrawRange (index1, index2); } - textblock->queueDrawRange (index1, index2); + // TODO What about OOF widgets? } void Textblock::TextblockIterator::unhighlight (int direction, core::HighlightLayer layer) { - Textblock *textblock = (Textblock*)getWidget(); - int index1 = index, index2 = index; - - if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index) - return; - - if (direction == 0) { - index1 = textblock->hlStart[layer].index; - index2 = textblock->hlEnd[layer].index; - textblock->hlStart[layer].index = 1; - textblock->hlEnd[layer].index = 0; - } else if (direction > 0 && textblock->hlStart[layer].index <= index) { - index1 = textblock->hlStart[layer].index; - textblock->hlStart[layer].index = index + 1; - textblock->hlStart[layer].nChar = 0; - } else if (direction < 0 && textblock->hlEnd[layer].index >= index) { - index1 = textblock->hlEnd[layer].index; - textblock->hlEnd[layer].index = index - 1; - textblock->hlEnd[layer].nChar = INT_MAX; + if (!oofm) { + Textblock *textblock = (Textblock*)getWidget(); + int index1 = index, index2 = index; + + if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index) + return; + + if (direction == 0) { + index1 = textblock->hlStart[layer].index; + index2 = textblock->hlEnd[layer].index; + textblock->hlStart[layer].index = 1; + textblock->hlEnd[layer].index = 0; + } else if (direction > 0 && textblock->hlStart[layer].index <= index) { + index1 = textblock->hlStart[layer].index; + textblock->hlStart[layer].index = index + 1; + textblock->hlStart[layer].nChar = 0; + } else if (direction < 0 && textblock->hlEnd[layer].index >= index) { + index1 = textblock->hlEnd[layer].index; + textblock->hlEnd[layer].index = index - 1; + textblock->hlEnd[layer].nChar = INT_MAX; + } + + textblock->queueDrawRange (index1, index2); } - textblock->queueDrawRange (index1, index2); + // TODO What about OOF widgets? } void Textblock::TextblockIterator::getAllocation (int start, int end, core::Allocation *allocation) { Textblock *textblock = (Textblock*)getWidget(); - int lineIndex = textblock->findLineOfWord (index); - Line *line = textblock->lines->getRef (lineIndex); - Word *word = textblock->words->getRef (index); - allocation->x = - textblock->allocation.x + textblock->lineXOffsetWidget (line); - - for (int i = line->firstWord; i < index; i++) { - Word *w = textblock->words->getRef(i); - allocation->x += w->size.width + w->effSpace; - } - if (start > 0 && word->content.type == core::Content::TEXT) { - allocation->x += textblock->textWidth (word->content.text, 0, start, - word->style); - } - allocation->y = textblock->lineYOffsetCanvas (line) + line->boxAscent - - word->size.ascent; - - allocation->width = word->size.width; - if (word->content.type == core::Content::TEXT) { - int wordEnd = strlen(word->content.text); - - if (start > 0 || end < wordEnd) { - end = misc::min(end, wordEnd); /* end could be INT_MAX */ - allocation->width = - textblock->textWidth (word->content.text, start, end - start, - word->style); + if (oofm) { + // TODO Consider start and end? + *allocation = + *(textblock->outOfFlowMgr->getWidget(index)->getAllocation()); + } else { + int lineIndex = textblock->findLineOfWord (index); + Line *line = textblock->lines->getRef (lineIndex); + Word *word = textblock->words->getRef (index); + + allocation->x = + textblock->allocation.x + textblock->lineXOffsetWidget (line); + + for (int i = line->firstWord; i < index; i++) { + Word *w = textblock->words->getRef(i); + allocation->x += w->size.width + w->effSpace; + } + if (start > 0 && word->content.type == core::Content::TEXT) { + allocation->x += textblock->textWidth (word->content.text, 0, start, + word->style); + } + allocation->y = textblock->lineYOffsetCanvas (line) + line->boxAscent - + word->size.ascent; + + allocation->width = word->size.width; + if (word->content.type == core::Content::TEXT) { + int wordEnd = strlen(word->content.text); + + if (start > 0 || end < wordEnd) { + end = misc::min(end, wordEnd); /* end could be INT_MAX */ + allocation->width = + textblock->textWidth (word->content.text, start, end - start, + word->style); + } } + allocation->ascent = word->size.ascent; + allocation->descent = word->size.descent; } - allocation->ascent = word->size.ascent; - allocation->descent = word->size.descent; } } // namespace dw diff --git a/dw/textblock_linebreaking.cc b/dw/textblock_linebreaking.cc index 09dfd63d..3835273d 100644 --- a/dw/textblock_linebreaking.cc +++ b/dw/textblock_linebreaking.cc @@ -212,8 +212,14 @@ void Textblock::printWord (Word *word) case core::Content::TEXT: printf ("\"%s\"", word->content.text); break; - case core::Content::WIDGET: - printf ("<widget: %p>\n", word->content.widget); + case core::Content::WIDGET_IN_FLOW: + printf ("<widget in flow: %p>\n", word->content.widget); + break; + case core::Content::WIDGET_OOF_REF: + printf ("<widget oof ref: %p>\n", word->content.widget); + break; + case core::Content::WIDGET_OOF_CONT: + printf ("<widge oof cont: %p>\n", word->content.widget); break; case core::Content::BREAK: printf ("<break>\n"); @@ -289,6 +295,12 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, PRINTF ("[%p] ADD_LINE (%d, %d) => %d\n", this, firstWord, lastWord, lines->size ()); + //for (int i = firstWord; i <= lastWord; i++) { + // printf (" word %d: ", i); + // printWord (words->getRef (i)); + // printf ("\n"); + //} + Word *lastWordOfLine = words->getRef(lastWord); // Word::totalWidth includes the hyphen (which is what we want here). int lineWidth = lastWordOfLine->totalWidth; @@ -446,19 +458,38 @@ void Textblock::wordWrap (int wordIndex, bool wrapAll) PRINTF ("[%p] WORD_WRAP (%d, %s)\n", this, wordIndex, wrapAll ? "true" : "false"); - Word *word; - //core::Extremes wordExtremes; - if (!wrapAll) removeTemporaryLines (); initLine1Offset (wordIndex); - word = words->getRef (wordIndex); + Word *word = words->getRef (wordIndex); word->effSpace = word->origSpace; accumulateWordData (wordIndex); + if (word->content.type == core::Content::WIDGET_OOF_REF) { + int top; + if (lines->size() == 0) + top = 0; + else { + Line *prevLine = lines->getLastRef (); + top = prevLine->top + prevLine->boxAscent + + prevLine->boxDescent + prevLine->breakSpace; + } + containingBlock->outOfFlowMgr->tellPosition + (word->content.widget, + allocation.y + top + getStyle()->boxOffsetY() + - containingBlock->getAllocation()->y); + + + // TODO: compare old/new values of calcAvailWidth(...); + int firstIndex = + lines->size() == 0 ? 0 : lines->getLastRef()->lastWord + 1; + for (int i = firstIndex; i <= wordIndex; i++) + accumulateWordData (i); + } + bool newLine; do { bool tempNewLine = false; @@ -478,7 +509,7 @@ void Textblock::wordWrap (int wordIndex, bool wrapAll) PRINTF (" NEW LINE: forced break\n"); } else if (wordIndex > firstIndex && word->badnessAndPenalty.lineTooTight () && - words->getRef(wordIndex- 1) + words->getRef(wordIndex - 1) ->badnessAndPenalty.lineCanBeBroken ()) { // TODO Comment the last condition (also below where the minimun is // searched for) @@ -497,6 +528,10 @@ void Textblock::wordWrap (int wordIndex, bool wrapAll) // newLine is calculated as "true". mustQueueResize = true; + PRINTF ("[%p] special case? newLine = %s, wrapAll = %s => " + "mustQueueResize = %s\n", this, newLine ? "true" : "false", + wrapAll ? "true" : "false", mustQueueResize ? "true" : "false"); + if(newLine) { accumulateWordData (wordIndex); int wordIndexEnd = wordIndex; @@ -690,8 +725,12 @@ void Textblock::accumulateWordForLine (int lineIndex, int wordIndex) Line *line = lines->getRef (lineIndex); Word *word = words->getRef (wordIndex); - PRINTF (" %d + %d / %d + %d\n", line->boxAscent, line->boxDescent, + PRINTF ("[%p] ACCUMULATE_WORD_FOR_LINE (%d, %d): %d + %d / %d + %d\n", + this, lineIndex, wordIndex, line->boxAscent, line->boxDescent, word->size.ascent, word->size.descent); + //printf (" "); + //printWord (word); + //printf ("\n"); line->boxAscent = misc::max (line->boxAscent, word->size.ascent); line->boxDescent = misc::max (line->boxDescent, word->size.descent); @@ -706,7 +745,7 @@ void Textblock::accumulateWordForLine (int lineIndex, int wordIndex) len += word->style->font->ascent / 3; line->contentDescent = misc::max (line->contentDescent, len); - if (word->content.type == core::Content::WIDGET) { + if (word->content.type == core::Content::WIDGET_IN_FLOW) { int collapseMarginTop = 0; line->marginDescent = @@ -730,7 +769,8 @@ void Textblock::accumulateWordForLine (int lineIndex, int wordIndex) + word->content.widget->getStyle()->margin.top - collapseMarginTop); - word->content.widget->parentRef = lineIndex; + word->content.widget->parentRef = + OutOfFlowMgr::createRefNormalFlow (lineIndex); } else { line->marginDescent = misc::max (line->marginDescent, line->boxDescent); @@ -796,6 +836,11 @@ void Textblock::accumulateWordData (int wordIndex) int Textblock::calcAvailWidth (int lineIndex) { + // BUG: This method must also include Line::boxLeft and Line::boxRight + // (introduced by floats), but since the recent changes in line breaking + // (together with hyphenation), this line is often not yet created, so + // these values cannot be determined. + int availWidth = this->availWidth - getStyle()->boxDiffWidth() - innerPadding; if (limitTextWidth && @@ -805,11 +850,14 @@ int Textblock::calcAvailWidth (int lineIndex) if (lineIndex == 0) availWidth -= line1OffsetEff; - //PRINTF("[%p] CALC_AVAIL_WIDTH => %d - %d - %d = %d\n", - // this, this->availWidth, getStyle()->boxDiffWidth(), innerPadding, - // availWidth); + PRINTF ("[%p] CALC_AVAIL_WIDTH (%d of %d) => %d - %d - %d = %d\n", + this, lineIndex, lines->size(), this->availWidth, + getStyle()->boxDiffWidth(), innerPadding, availWidth); - return availWidth; + // TODO if the result is too small, but only in some cases + // (e. g. because of floats), the caller should skip a + // line. General distinction? + return availWidth - lineLeftBorder (lineIndex) - lineRightBorder (lineIndex); } void Textblock::initLine1Offset (int wordIndex) @@ -824,7 +872,7 @@ void Textblock::initLine1Offset (int wordIndex) } else { int indent = 0; - if (word->content.type == core::Content::WIDGET && + if (word->content.type == core::Content::WIDGET_IN_FLOW && word->content.widget->blockLevel() == true) { /* don't use text-indent when nesting blocks */ } else { @@ -915,8 +963,14 @@ void Textblock::rewrap () for (int i = firstWord; i < words->size (); i++) { Word *word = words->getRef (i); - - if (word->content.type == core::Content::WIDGET) + if (word->content.type == core::Content::WIDGET_OOF_REF) + containingBlock->outOfFlowMgr->tellNoPosition (word->content.widget); + } + + for (int i = firstWord; i < words->size (); i++) { + Word *word = words->getRef (i); + + if (word->content.type == core::Content::WIDGET_IN_FLOW) calcWidgetSize (word->content.widget, &word->size); wordWrap (i, false); @@ -926,9 +980,9 @@ void Textblock::rewrap () // changed, so getRef() must be called again. word = words->getRef (i); - if (word->content.type == core::Content::WIDGET) { - word->content.widget->parentRef = lines->size () - 1; - } + if (word->content.type == core::Content::WIDGET_IN_FLOW) + word->content.widget->parentRef = + OutOfFlowMgr::createRefNormalFlow (lines->size () - 1); } /* Next time, the page will not have to be rewrapped. */ diff --git a/dw/types.hh b/dw/types.hh index 65983fad..61136673 100644 --- a/dw/types.hh +++ b/dw/types.hh @@ -188,11 +188,27 @@ struct Content START = 1 << 0, END = 1 << 1, TEXT = 1 << 2, - WIDGET = 1 << 3, - BREAK = 1 << 4, + + /** \brief widget in normal flow, so that _this_ widget + (containing this content) is both container (parent) and + generator */ + WIDGET_IN_FLOW = 1 << 3, + + /** \brief widget out of flow (OOF); _this_ widget (containing + this content) is only the container (parent), but _not_ + generator */ + WIDGET_OOF_CONT, + + /** \brief reference to a widget out of flow (OOF); _this_ + widget (containing this content) is only the generator + (parent), but _not_ container */ + WIDGET_OOF_REF = 1 << 4, + + BREAK = 1 << 5, ALL = 0xff, REAL_CONTENT = 0xff ^ (START | END), - SELECTION_CONTENT = TEXT | WIDGET | BREAK + SELECTION_CONTENT = TEXT | BREAK, // WIDGET_* must be set additionally + ANY_WIDGET = WIDGET_IN_FLOW | WIDGET_OOF_CONT | WIDGET_OOF_REF, }; /* Content is embedded in struct Word therefore we diff --git a/dw/widget.cc b/dw/widget.cc index 8ca0681a..8e619911 100644 --- a/dw/widget.cc +++ b/dw/widget.cc @@ -108,6 +108,8 @@ void Widget::setParent (Widget *parent) if (!buttonSensitiveSet) buttonSensitive = parent->buttonSensitive; + notifySetParent(); + //DBG_OBJ_ASSOC (widget, parent); //printf ("%p becomes a child of %p\n", this, parent); } @@ -115,7 +117,8 @@ void Widget::setParent (Widget *parent) void Widget::queueDrawArea (int x, int y, int width, int height) { /** \todo Maybe only the intersection? */ - layout->queueDraw (x + allocation.x, y + allocation.y, width, height); + if (layout) + layout->queueDraw (x + allocation.x, y + allocation.y, width, height); _MSG("Widget::queueDrawArea x=%d y=%d w=%d h=%d\n", x, y, width, height); } @@ -523,7 +526,9 @@ Widget *Widget::getWidgetAtPoint (int x, int y, int level) * is such a child, it is returned. Otherwise, this widget is returned. */ childAtPoint = NULL; - it = iterator (Content::WIDGET, false); + it = iterator ((Content::Type) + (Content::WIDGET_IN_FLOW | Content::WIDGET_OOF_CONT), + false); while (childAtPoint == NULL && it->next ()) childAtPoint = it->getContent()->widget->getWidgetAtPoint (x, y, @@ -567,6 +572,25 @@ void Widget::markExtremesChange (int ref) { } +/** + * \brief This method is called after a widget has been set as the top of a + * widget tree. + * + * A widget may override this method when it is necessary to be notified. + */ +void Widget::notifySetAsTopLevel() +{ +} + +/** + * \brief This method is called after a widget has been added to a parent. + * + * A widget may override this method when it is necessary to be notified. + */ +void Widget::notifySetParent() +{ +} + void Widget::setWidth (int width) { } diff --git a/dw/widget.hh b/dw/widget.hh index b751a282..e18344c7 100644 --- a/dw/widget.hh +++ b/dw/widget.hh @@ -178,6 +178,9 @@ protected: */ virtual void markExtremesChange (int ref); + virtual void notifySetAsTopLevel(); + virtual void notifySetParent(); + virtual bool buttonPressImpl (EventButton *event); virtual bool buttonReleaseImpl (EventButton *event); virtual bool motionNotifyImpl (EventMotion *event); diff --git a/lout/identity.cc b/lout/identity.cc index 7c650b24..ebe95ef0 100644 --- a/lout/identity.cc +++ b/lout/identity.cc @@ -17,8 +17,6 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - - #include "identity.hh" #include <stdio.h> @@ -78,6 +76,7 @@ void IdentifiableObject::registerName (const char *className, int *classId) } this->classId = klass->id; + *classId = klass->id; currentlyConstructedClass = klass; } diff --git a/src/cssparser.cc b/src/cssparser.cc index 8131372a..bd065234 100644 --- a/src/cssparser.cc +++ b/src/cssparser.cc @@ -76,6 +76,10 @@ static const char *const Css_display_enum_vals[] = { "table-cell", NULL }; +static const char *const Css_float_enum_vals[] = { + "none", "left", "right", NULL +}; + static const char *const Css_font_size_enum_vals[] = { "large", "larger", "medium", "small", "smaller", "xx-large", "xx-small", "x-large", "x-small", NULL @@ -183,7 +187,7 @@ const CssPropertyInfo Css_property_info[CSS_PROPERTY_LAST] = { {"direction", {CSS_TYPE_UNUSED}, NULL}, {"display", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_display_enum_vals}, {"empty-cells", {CSS_TYPE_UNUSED}, NULL}, - {"float", {CSS_TYPE_UNUSED}, NULL}, + {"float", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_float_enum_vals}, {"font-family", {CSS_TYPE_SYMBOL, CSS_TYPE_UNUSED}, NULL}, {"font-size", {CSS_TYPE_ENUM, CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED}, Css_font_size_enum_vals}, diff --git a/src/html.cc b/src/html.cc index b9d2428f..6f5e94ae 100644 --- a/src/html.cc +++ b/src/html.cc @@ -358,9 +358,11 @@ bool a_Html_tag_set_valign_attr(DilloHtml *html, const char *tag, int tagsize) static void Html_add_textblock(DilloHtml *html, int space) { Textblock *textblock = new Textblock (prefs.limit_text_width); + Style *style = html->styleEngine->style (); HT2TB(html)->addParbreak (space, html->styleEngine->wordStyle ()); - HT2TB(html)->addWidget (textblock, html->styleEngine->style ()); + HT2TB(html)->addWidget (textblock, style); // Works also for floats etc. + HT2TB(html)->addParbreak (space, html->styleEngine->wordStyle ()); S_TOP(html)->textblock = html->dw = textblock; S_TOP(html)->hand_over_break = true; diff --git a/src/styleengine.cc b/src/styleengine.cc index 84529861..b7814cde 100644 --- a/src/styleengine.cc +++ b/src/styleengine.cc @@ -508,6 +508,9 @@ void StyleEngine::apply (int i, StyleAttrs *attrs, CssPropertyList *props) { case CSS_PROPERTY_DISPLAY: attrs->display = (DisplayType) p->value.intVal; break; + case CSS_PROPERTY_FLOAT: + attrs->vloat = (FloatType) p->value.intVal; + break; case CSS_PROPERTY_LINE_HEIGHT: if (p->type == CSS_TYPE_ENUM) { //only valid enum value is "normal" attrs->lineHeight = dw::core::style::LENGTH_AUTO; diff --git a/test/Makefile.am b/test/Makefile.am index dbf2a25f..ab4c98b0 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -7,6 +7,7 @@ noinst_PROGRAMS = \ dw-anchors-test \ dw-example \ dw-find-test \ + dw-float-test \ dw-links \ dw-links2 \ dw-images-simple \ @@ -50,6 +51,14 @@ dw_find_test_LDADD = \ $(top_builddir)/lout/liblout.a \ @LIBFLTK_LIBS@ +dw_float_test_SOURCES = dw_float_test.cc +dw_float_test_LDADD = \ + ../dw/libDw-widgets.a \ + ../dw/libDw-fltk.a \ + ../dw/libDw-core.a \ + ../lout/liblout.a \ + @LIBFLTK_LIBS@ + dw_links_SOURCES = dw_links.cc dw_links_LDADD = \ $(top_builddir)/dw/libDw-widgets.a \ diff --git a/test/dw_float_test.cc b/test/dw_float_test.cc new file mode 100644 index 00000000..a3e91b6c --- /dev/null +++ b/test/dw_float_test.cc @@ -0,0 +1,145 @@ +#include <FL/Fl.H> +#include <FL/Fl_Window.H> + +#include "../dw/core.hh" +#include "../dw/fltkcore.hh" +#include "../dw/fltkviewport.hh" +#include "../dw/textblock.hh" + +using namespace dw; +using namespace dw::core; +using namespace dw::core::style; +using namespace dw::fltk; + +static Textblock *firstFloat; +static Style *wordStyle; + +static void addTextToFloatTimeout (void *data) +{ + printf("addTextToFloatTimeout\n"); + + const char *fWords[] = { "This", "is", "a", "float,", "which", "is", + "set", "aside", "from", "the", "main", + "text.", NULL }; + + for(int k = 0; fWords[k]; k++) { + firstFloat->addText(fWords[k], wordStyle); + firstFloat->addSpace(wordStyle); + } + + firstFloat->flush(); + + Fl::repeat_timeout (2, addTextToFloatTimeout, NULL); +} + +int main(int argc, char **argv) +{ + FltkPlatform *platform = new FltkPlatform (); + Layout *layout = new Layout (platform); + + Fl_Window *window = new Fl_Window(400, 600, "Dw Floats Example"); + window->begin(); + + FltkViewport *viewport = new FltkViewport (0, 0, 400, 600); + layout->attachView (viewport); + + StyleAttrs styleAttrs; + styleAttrs.initValues (); + styleAttrs.margin.setVal (5); + + FontAttrs fontAttrs; + fontAttrs.name = "Bitstream Charter"; + fontAttrs.size = 14; + fontAttrs.weight = 400; + fontAttrs.style = FONT_STYLE_NORMAL; + fontAttrs.letterSpacing = 0; + styleAttrs.font = core::style::Font::create (layout, &fontAttrs); + + styleAttrs.color = Color::create (layout, 0x000000); + styleAttrs.backgroundColor = Color::create (layout, 0xffffff); + + Style *widgetStyle = Style::create (layout, &styleAttrs); + + styleAttrs.borderWidth.setVal (1); + styleAttrs.setBorderColor (Color::create (layout, 0x808080)); + styleAttrs.setBorderStyle (BORDER_DASHED); + styleAttrs.width = createAbsLength(100); + styleAttrs.vloat = FLOAT_LEFT; + Style *leftFloatStyle = Style::create (layout, &styleAttrs); + + styleAttrs.width = createAbsLength(80); + styleAttrs.vloat = FLOAT_RIGHT; + Style *rightFloatStyle = Style::create (layout, &styleAttrs); + + Textblock *textblock = new Textblock (false); + textblock->setStyle (widgetStyle); + layout->setWidget (textblock); + + widgetStyle->unref(); + + styleAttrs.borderWidth.setVal (0); + styleAttrs.width = LENGTH_AUTO; + styleAttrs.vloat = FLOAT_NONE; + styleAttrs.margin.setVal (0); + styleAttrs.backgroundColor = NULL; + + wordStyle = Style::create (layout, &styleAttrs); + + for(int i = 1; i <= 10; i++) { + char buf[16]; + snprintf(buf, sizeof(buf), "%d%s", + i, (i == 1 ? "st" : (i == 2 ? "nd" : (i == 3 ? "rd" : "th")))); + + const char *words[] = { "This", "is", "the", buf, "paragraph.", + "Here", "comes", "some", "more", "text", + "to", "demonstrate", "word", "wrapping.", + NULL }; + + for(int j = 0; words[j]; j++) { + textblock->addText(words[j], wordStyle); + textblock->addSpace(wordStyle); + + if ((i == 3 || i == 5) && j == 8) { + textblock->addText("[float]", wordStyle); + textblock->addSpace(wordStyle); + + Textblock *vloat = new Textblock (false); + textblock->addWidget(vloat, i == 3 ? leftFloatStyle : rightFloatStyle); + + const char *fWords[] = { "This", "is", "a", "float,", "which", "is", + "set", "aside", "from", "the", "main", + "text.", NULL }; + + vloat->addText(i == 3 ? "Left:" : "Right:", wordStyle); + vloat->addSpace(wordStyle); + + for(int k = 0; fWords[k]; k++) { + vloat->addText(fWords[k], wordStyle); + vloat->addSpace(wordStyle); + } + + vloat->flush (); + + if(i == 3) + firstFloat = vloat; + } + } + + textblock->addParbreak(10, wordStyle); + } + + leftFloatStyle->unref(); + rightFloatStyle->unref(); + + textblock->flush (); + + window->resizable(viewport); + window->show(); + //Fl::add_timeout (2, addTextToFloatTimeout, NULL); + int errorCode = Fl::run(); + + wordStyle->unref(); + delete layout; + + return errorCode; +} diff --git a/test/floats1.html b/test/floats1.html new file mode 100644 index 00000000..eae30c4a --- /dev/null +++ b/test/floats1.html @@ -0,0 +1,46 @@ +<div>First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph. First +paragraph. <div style="float: left; border: 1px dashed black">Left +float. Left float. Left float. Left float. Left float. Left +float. Left float. Left float. Left float. Left float. Left +float. Left float. Left float. Left float. Left float. Left +float. Left float. Left float. Left float. Left float.</div> First +paragraph. First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph. First paragraph. First +paragraph. First paragraph. First paragraph.</div> +<div><div>Second paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph.<div style="float: +right; border: 1px dashed black">Right float. Right float. Right +float. Right float. Right float. Right float.</div> Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph. Second +paragraph. Second paragraph. Second paragraph.</div></div> diff --git a/test/floats2.html b/test/floats2.html new file mode 100644 index 00000000..b7978706 --- /dev/null +++ b/test/floats2.html @@ -0,0 +1,17 @@ +Sed ut perspiciatis, unde omnis iste natus error sit voluptatem +accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae +ab illo inventore veritatis et quasi architecto beatae vitae dicta +sunt, explicabo. +<div style="float:left; border: 1px dashed black">Some text in a +float.<br /><img src="http://www.dillo.org/dw/html/not-so-simple-container.png" /></div> +nemo enim ipsam voluptatem, quia voluptas sit, +aspernatur aut odit aut fugit, sed quia consequuntur magni dolores +eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, +qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci velit, +sed quia non numquam eius modi tempora incidunt, ut labore et dolore +magnam aliquam quaerat voluptatem. ut enim ad minima veniam, quis +nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut +aliquid ex ea commodi consequatur? quis autem vel eum iure +reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae +consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla +pariatur? |