aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/dw-out-of-flow.doc145
-rw-r--r--dw/Makefile.am2
-rw-r--r--dw/findtext.cc4
-rw-r--r--dw/iterator.cc36
-rw-r--r--dw/iterator.hh2
-rw-r--r--dw/layout.cc1
-rw-r--r--dw/outofflowmgr.cc265
-rw-r--r--dw/outofflowmgr.hh98
-rw-r--r--dw/style.cc12
-rw-r--r--dw/style.hh17
-rw-r--r--dw/table.cc14
-rw-r--r--dw/tablecell.cc2
-rw-r--r--dw/textblock.cc224
-rw-r--r--dw/textblock.hh138
-rw-r--r--dw/textblock_linebreaking.cc94
-rw-r--r--dw/types.hh22
-rw-r--r--dw/widget.cc28
-rw-r--r--dw/widget.hh3
-rw-r--r--lout/identity.cc3
-rw-r--r--src/cssparser.cc6
-rw-r--r--src/html.cc4
-rw-r--r--src/styleengine.cc3
-rw-r--r--test/Makefile.am9
-rw-r--r--test/dw_float_test.cc145
-rw-r--r--test/floats1.html46
-rw-r--r--test/floats2.html17
26 files changed, 1217 insertions, 123 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&nbsp;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..c59a62b2
--- /dev/null
+++ b/dw/outofflowmgr.hh
@@ -0,0 +1,98 @@
+#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; }
+};
+
+} // 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 bfce0561..ff94486b 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;
}
}
@@ -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,
@@ -1941,4 +2013,54 @@ void Textblock::changeWordStyle (int from, int to, core::style::Style *style,
{
}
+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..3a927464 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 {
/**
@@ -367,7 +372,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 +426,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 +595,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 +616,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 +662,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_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?