aboutsummaryrefslogtreecommitdiff
path: root/dw/oofawarewidget.cc
diff options
context:
space:
mode:
Diffstat (limited to 'dw/oofawarewidget.cc')
-rw-r--r--dw/oofawarewidget.cc570
1 files changed, 570 insertions, 0 deletions
diff --git a/dw/oofawarewidget.cc b/dw/oofawarewidget.cc
new file mode 100644
index 00000000..4f7f4bc8
--- /dev/null
+++ b/dw/oofawarewidget.cc
@@ -0,0 +1,570 @@
+/*
+ * Dillo Widget
+ *
+ * Copyright 2014 Sebastian Geerken <sgeerken@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "oofawarewidget.hh"
+#include "ooffloatsmgr.hh"
+#include "oofposabsmgr.hh"
+#include "oofposfixedmgr.hh"
+
+using namespace dw;
+using namespace dw::core;
+using namespace dw::core::style;
+using namespace lout::object;
+using namespace lout::misc;
+using namespace lout::container::typed;
+
+namespace dw {
+
+namespace oof {
+
+const char *OOFAwareWidget::OOFM_NAME[NUM_OOFM] = {
+ "FLOATS", "ABSOLUTE", "FIXED"
+};
+
+int OOFAwareWidget::CLASS_ID = -1;
+
+OOFAwareWidget::OOFAwareWidget ()
+{
+ DBG_OBJ_CREATE ("dw::oof::OOFAwareWidget");
+ registerName ("dw::oof::OOFAwareWidget", &CLASS_ID);
+
+ for (int i = 0; i < NUM_OOFM; i++) {
+ oofContainer[i] = NULL;
+ DBG_OBJ_ARRSET_PTR ("oofContainer", i, oofContainer[i]);
+ outOfFlowMgr[i] = NULL;
+ }
+}
+
+OOFAwareWidget::~OOFAwareWidget ()
+{
+ for (int i = 0; i < NUM_OOFM; i++) {
+ if(outOfFlowMgr[i]) {
+ // I feel more comfortable by letting the OOF aware widget delete
+ // these widgets, instead of doing this in ~OutOfFlowMgr.
+ for (int j = 0; j < outOfFlowMgr[i]->getNumWidgets (); j++)
+ delete outOfFlowMgr[i]->getWidget (j);
+
+ delete outOfFlowMgr[i];
+ }
+ }
+
+ DBG_OBJ_DELETE ();
+}
+
+const char *OOFAwareWidget::stackingLevelText (int level)
+{
+ switch (level) {
+ case SL_START: return "START";
+ case SL_BACKGROUND: return "BACKGROUND";
+ case SL_SC_BOTTOM: return "SC_BOTTOM";
+ case SL_IN_FLOW: return "IN_FLOW";
+ case SL_OOF_REF: return "OOF_REF";
+ case SL_OOF_CONT: return "OOF_CONT";
+ case SL_SC_TOP: return "SC_TOP";
+ case SL_END: return "END";
+ default: return "???";
+ }
+}
+
+void OOFAwareWidget::notifySetAsTopLevel ()
+{
+ for (int i = 0; i < NUM_OOFM; i++) {
+ oofContainer[i] = this;
+ DBG_OBJ_ARRSET_PTR ("oofContainer", i, oofContainer[i]);
+ }
+}
+
+bool OOFAwareWidget::getOOFMIndex (Widget *widget)
+{
+ if (testWidgetFloat (widget))
+ return OOFM_FLOATS;
+ else if (testWidgetAbsolutelyPositioned (widget))
+ return OOFM_ABSOLUTE;
+ else if (testWidgetFixedlyPositioned (widget))
+ return OOFM_FIXED;
+ else {
+ lout::misc::assertNotReached ();
+ return -1;
+ }
+}
+
+bool OOFAwareWidget::isOOFContainer (Widget *widget, int oofmIndex)
+{
+ // TODO The methods isPossibleContainer() and isPossibleContainerParent()
+ // are only used in few cases. Does not matter currently, however.
+
+ switch (oofmIndex) {
+ case OOFM_FLOATS:
+ return widget->instanceOf (OOFAwareWidget::CLASS_ID) &&
+ (// For floats, only some OOF aware widgets are considered as
+ // containers.
+ ((OOFAwareWidget*)widget)->isPossibleContainer (OOFM_FLOATS) &&
+ // 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 an
+ // unsuitable parent (typical example: a table cell (this
+ // is also a text block, so possible float container)
+ // within a table widget, which is not a suitable float
+ // container parent).
+ !(widget->getParent()->instanceOf (OOFAwareWidget::CLASS_ID) &&
+ ((OOFAwareWidget*)widget->getParent())
+ ->isPossibleContainerParent (OOFM_FLOATS)) ||
+ // Inline blocks are containing blocks, too.
+ widget->getStyle()->display == DISPLAY_INLINE_BLOCK ||
+ // Same for blocks with 'overview' set to another value than
+ // (the default value) 'visible'.
+ widget->getStyle()->overflow != OVERFLOW_VISIBLE ||
+ // Finally, "out of flow" in a narrower sense: floats;
+ // absolutely and fixedly positioned elements. (No
+ // relatively positioned elements; since the latters
+ // constitute a stacking context, drawing of floats gets
+ // somewhat more complicated; see "interrupting the drawing
+ // process" in "dw-stacking-context.doc".
+ testWidgetOutOfFlow (widget)));
+
+ case OOFM_ABSOLUTE:
+ // We use the toplevel widget as container, i. e. parent widget.
+ // See also OOFPosAbsMgr::isReference for the definition of the
+ // "reference widget".
+ //return widget->getParent() == NULL;
+
+ // The above does not work. Temporary re-enact old behaviour:
+ return widget->instanceOf (OOFAwareWidget::CLASS_ID) &&
+ (widget->getParent() == NULL ||
+ OOFAwareWidget::testWidgetPositioned (widget));
+
+
+ case OOFM_FIXED:
+ // The single container for fixedly positioned elements is the
+ // toplevel (canvas; actually the viewport). (The toplevel
+ // widget should always be a textblock; at least this is the
+ // case in dillo.)
+ return widget->getParent() == NULL;
+
+ default:
+ // compiler happiness
+ lout::misc::assertNotReached ();
+ return false;
+ }
+}
+
+void OOFAwareWidget::notifySetParent ()
+{
+ // Search for containing blocks.
+ for (int oofmIndex = 0; oofmIndex < NUM_OOFM; oofmIndex++) {
+ oofContainer[oofmIndex] = NULL;
+
+ for (Widget *widget = this;
+ widget != NULL && oofContainer[oofmIndex] == NULL;
+ widget = widget->getParent ())
+ if (isOOFContainer (widget, oofmIndex)) {
+ assert (widget->instanceOf (OOFAwareWidget::CLASS_ID));
+ oofContainer[oofmIndex] = (OOFAwareWidget*)widget;
+ }
+
+ DBG_OBJ_ARRSET_PTR ("oofContainer", oofmIndex, oofContainer[oofmIndex]);
+
+ assert (oofContainer[oofmIndex] != NULL);
+ }
+}
+
+void OOFAwareWidget::initOutOfFlowMgrs ()
+{
+ if (oofContainer[OOFM_FLOATS]->outOfFlowMgr[OOFM_FLOATS] == NULL) {
+ oofContainer[OOFM_FLOATS]->outOfFlowMgr[OOFM_FLOATS] =
+ new OOFFloatsMgr (oofContainer[OOFM_FLOATS]);
+ DBG_OBJ_ASSOC (oofContainer[OOFM_FLOATS],
+ oofContainer[OOFM_FLOATS]->outOfFlowMgr[OOFM_FLOATS]);
+ }
+
+ if (oofContainer[OOFM_ABSOLUTE]->outOfFlowMgr[OOFM_ABSOLUTE] == NULL) {
+ oofContainer[OOFM_ABSOLUTE]->outOfFlowMgr[OOFM_ABSOLUTE] =
+ new OOFPosAbsMgr (oofContainer[OOFM_ABSOLUTE]);
+ DBG_OBJ_ASSOC (oofContainer[OOFM_ABSOLUTE],
+ oofContainer[OOFM_ABSOLUTE]->outOfFlowMgr[OOFM_ABSOLUTE]);
+ }
+
+ if (oofContainer[OOFM_FIXED]->outOfFlowMgr[OOFM_FIXED] == NULL) {
+ oofContainer[OOFM_FIXED]->outOfFlowMgr[OOFM_FIXED] =
+ new OOFPosFixedMgr (oofContainer[OOFM_FIXED]);
+ DBG_OBJ_ASSOC (oofContainer[OOFM_FIXED],
+ oofContainer[OOFM_FIXED]->outOfFlowMgr[OOFM_FIXED]);
+ }
+}
+
+void OOFAwareWidget::correctRequisitionByOOF (Requisition *requisition,
+ void (*splitHeightFun) (int, int*,
+ int*))
+{
+ DBG_OBJ_ENTER ("resize", 0, "correctRequisitionByOOF", "%d * (%d + %d), ...",
+ requisition->width, requisition->ascent,
+ requisition->descent);
+
+ for (int i = 0; i < NUM_OOFM; i++) {
+ if (outOfFlowMgr[i]) {
+ DBG_OBJ_MSGF ("resize", 1, "OOFM for %s", OOFM_NAME[i]);
+ DBG_OBJ_MSG_START ();
+
+ int oofWidth, oofHeight;
+
+ outOfFlowMgr[i]->getSize (requisition, &oofWidth, &oofHeight);
+ DBG_OBJ_MSGF ("resize", 1, "result: %d * %d", oofWidth, oofHeight);
+
+ if (oofWidth > requisition->width) {
+ if (outOfFlowMgr[i]->containerMustAdjustExtraSpace () &&
+ adjustExtraSpaceWhenCorrectingRequisitionByOOF ()) {
+ extraSpace.right = max (extraSpace.right,
+ oofWidth - requisition->width);
+ DBG_OBJ_SET_NUM ("extraSpace.right", extraSpace.right);
+ }
+
+ requisition->width = oofWidth;
+ }
+
+ if (oofHeight > requisition->ascent + requisition->descent) {
+ if (outOfFlowMgr[i]->containerMustAdjustExtraSpace () &&
+ adjustExtraSpaceWhenCorrectingRequisitionByOOF ()) {
+ extraSpace.bottom = max (extraSpace.bottom,
+ oofHeight - (requisition->ascent +
+ requisition->descent));
+ DBG_OBJ_SET_NUM ("extraSpace.bottom", extraSpace.bottom);
+ }
+
+ splitHeightFun (oofHeight,
+ &requisition->ascent, &requisition->descent);
+ }
+
+ DBG_OBJ_MSGF ("resize", 1, "after correction: %d * (%d + %d)",
+ requisition->width, requisition->ascent,
+ requisition->descent);
+ DBG_OBJ_MSG_END ();
+ } else
+ DBG_OBJ_MSGF ("resize", 1, "no OOFM for %s", OOFM_NAME[i]);
+ }
+
+ DBG_OBJ_LEAVE ();
+}
+
+void OOFAwareWidget::correctExtremesByOOF (Extremes *extremes)
+{
+ DBG_OBJ_ENTER ("resize", 0, "correctExtremesByOOF", "%d (%d) / %d (%d)",
+ extremes->minWidth, extremes->minWidthIntrinsic,
+ extremes->maxWidth, extremes->maxWidthIntrinsic);
+
+ for (int i = 0; i < NUM_OOFM; i++) {
+ if (outOfFlowMgr[i]) {
+ DBG_OBJ_MSGF ("resize", 1, "OOFM for %s", OOFM_NAME[i]);
+ DBG_OBJ_MSG_START ();
+
+ int oofMinWidth, oofMaxWidth;
+ outOfFlowMgr[i]->getExtremes (extremes, &oofMinWidth, &oofMaxWidth);
+ DBG_OBJ_MSGF ("resize", 1, "result: %d / %d",
+ oofMinWidth, oofMaxWidth);
+
+ extremes->minWidth = max (extremes->minWidth, oofMinWidth);
+ extremes->minWidthIntrinsic = max (extremes->minWidthIntrinsic,
+ oofMinWidth);
+ extremes->maxWidth = max (extremes->maxWidth, oofMaxWidth);
+ extremes->maxWidthIntrinsic = max (extremes->maxWidthIntrinsic,
+ oofMinWidth);
+ extremes->adjustmentWidth = max (extremes->adjustmentWidth,
+ oofMinWidth);
+
+ DBG_OBJ_MSGF ("resize", 1, "after correction: %d (%d) / %d (%d)",
+ extremes->minWidth, extremes->minWidthIntrinsic,
+ extremes->maxWidth, extremes->maxWidthIntrinsic);
+ DBG_OBJ_MSG_END ();
+ } else
+ DBG_OBJ_MSGF ("resize", 1, "no OOFM for %s", OOFM_NAME[i]);
+ }
+
+ DBG_OBJ_LEAVE ();
+}
+
+void OOFAwareWidget::sizeAllocateStart (Allocation *allocation)
+{
+
+ for (int i = 0; i < NUM_OOFM; i++)
+ if (oofContainer[i]->outOfFlowMgr[i])
+ oofContainer[i]->outOfFlowMgr[i]->sizeAllocateStart (this, allocation);
+}
+
+void OOFAwareWidget::sizeAllocateEnd ()
+{
+ for (int i = 0; i < NUM_OOFM; i++)
+ if (oofContainer[i]->outOfFlowMgr[i])
+ oofContainer[i]->outOfFlowMgr[i]->sizeAllocateEnd (this);
+}
+
+void OOFAwareWidget::containerSizeChangedForChildrenOOF ()
+{
+ for (int i = 0; i < NUM_OOFM; i++)
+ if (outOfFlowMgr[i])
+ outOfFlowMgr[i]->containerSizeChangedForChildren ();
+}
+
+bool OOFAwareWidget::doesWidgetOOFInterruptDrawing (Widget *widget)
+{
+ DBG_OBJ_ENTER ("draw", 0, "doesWidgetOOFInterruptDrawing", "%p", widget);
+
+ // This is the generator of the widget.
+ int oofmIndex = getOOFMIndex (widget);
+ DBG_OBJ_MSGF ("draw", 1, "oofmIndex = %d", oofmIndex);
+
+ int cl = oofContainer[oofmIndex]->stackingContextWidget->getLevel (),
+ gl = stackingContextWidget->getLevel ();
+
+ DBG_OBJ_MSGF ("draw", 1,"%d < %d => %s", cl, gl, cl < gl ? "true" : "false");
+
+ DBG_OBJ_LEAVE ();
+ return cl < gl;
+}
+
+void OOFAwareWidget::draw (View *view, Rectangle *area, DrawingContext *context)
+{
+ DBG_OBJ_ENTER ("draw", 0, "draw", "%d, %d, %d * %d",
+ area->x, area->y, area->width, area->height);
+
+ for (int level = SL_START + 1; level < SL_END; level++)
+ drawLevel (view, area, level, context);
+
+ DBG_OBJ_LEAVE ();
+}
+
+void OOFAwareWidget::drawLevel (View *view, Rectangle *area, int level,
+ DrawingContext *context)
+{
+ DBG_OBJ_ENTER ("draw", 0, "OOFAwareWidget::drawLevel",
+ "(%d, %d, %d * %d), %s",
+ area->x, area->y, area->width, area->height,
+ stackingLevelText (level));
+
+ switch (level) {
+ case SL_START:
+ break;
+
+ case SL_BACKGROUND:
+ drawWidgetBox (view, area, false);
+ break;
+
+ case SL_SC_BOTTOM:
+ if (stackingContextMgr)
+ stackingContextMgr->drawBottom (view, area, context);
+ break;
+
+ case SL_IN_FLOW:
+ // Should be implemented in the sub class.
+ break;
+
+ case SL_OOF_REF:
+ // Should be implemented in the sub class (when references are hold).
+ break;
+
+ case SL_OOF_CONT:
+ drawOOF (view, area, context);
+ break;
+
+ case SL_SC_TOP:
+ if (stackingContextMgr)
+ stackingContextMgr->drawTop (view, area, context);
+ break;
+
+ case SL_END:
+ break;
+ }
+
+ DBG_OBJ_LEAVE ();
+}
+
+void OOFAwareWidget::drawOOF (View *view, Rectangle *area,
+ DrawingContext *context)
+{
+ for (int i = 0; i < NUM_OOFM; i++) {
+ if(outOfFlowMgr[i])
+ outOfFlowMgr[i]->draw (view, area, context);
+ }
+}
+
+Widget *OOFAwareWidget::getWidgetAtPoint (int x, int y,
+ GettingWidgetAtPointContext *context)
+{
+ DBG_OBJ_ENTER ("events", 0, "getWidgetAtPoint", "%d, %d", x, y);
+ Widget *widgetAtPoint = NULL;
+
+ if (inAllocation (x, y)) {
+ for (int level = SL_END - 1; widgetAtPoint == NULL && level > SL_START;
+ level--)
+ widgetAtPoint = getWidgetAtPointLevel (x, y, level, context);
+ }
+
+ DBG_OBJ_MSGF ("events", 1, "=> %p", widgetAtPoint);
+ DBG_OBJ_LEAVE ();
+ return widgetAtPoint;
+}
+
+Widget *OOFAwareWidget::getWidgetAtPointLevel (int x, int y, int level,
+ GettingWidgetAtPointContext
+ *context)
+{
+ DBG_OBJ_ENTER ("events", 0, "OOFAwareWidget::getWidgetAtPointLevel",
+ "%d, %d, %s", x, y, stackingLevelText (level));
+
+ Widget *widgetAtPoint = NULL;
+
+ switch (level) {
+ case SL_BACKGROUND:
+ if (inAllocation (x, y))
+ widgetAtPoint = this;
+ break;
+
+ case SL_SC_BOTTOM:
+ if (stackingContextMgr)
+ widgetAtPoint =
+ stackingContextMgr->getBottomWidgetAtPoint (x, y, context);
+ break;
+
+ case SL_IN_FLOW:
+ // Should be implemented in the sub class.
+ assertNotReached ();
+ break;
+
+ case SL_OOF_REF:
+ // Should be implemented in the sub class (when references are hold).
+ break;
+
+ case SL_OOF_CONT:
+ widgetAtPoint = getWidgetOOFAtPoint (x, y, context);
+ break;
+
+ case SL_SC_TOP:
+ if (stackingContextMgr)
+ widgetAtPoint =
+ stackingContextMgr->getTopWidgetAtPoint (x, y, context);
+ break;
+
+ default:
+ assertNotReached ();
+ }
+
+ DBG_OBJ_MSGF ("events", 1, "=> %p", widgetAtPoint);
+ DBG_OBJ_LEAVE ();
+ return widgetAtPoint;
+}
+
+Widget *OOFAwareWidget::getWidgetOOFAtPoint (int x, int y,
+ GettingWidgetAtPointContext
+ *context)
+{
+ Widget *widgetAtPoint = NULL;
+
+ for (int i = NUM_OOFM -1; widgetAtPoint == NULL && i >= 0; i--) {
+ if(outOfFlowMgr[i])
+ widgetAtPoint = outOfFlowMgr[i]->getWidgetAtPoint (x, y, context);
+ }
+
+ return widgetAtPoint;
+}
+
+int OOFAwareWidget::getAvailWidthOfChild (Widget *child, bool forceValue)
+{
+ if (isWidgetOOF(child)) {
+ assert (getWidgetOutOfFlowMgr(child) &&
+ getWidgetOutOfFlowMgr(child)->dealingWithSizeOfChild (child));
+ return getWidgetOutOfFlowMgr(child)->getAvailWidthOfChild (child,
+ forceValue);
+ } else
+ return Widget::getAvailWidthOfChild (child, forceValue);
+}
+
+int OOFAwareWidget::getAvailHeightOfChild (Widget *child, bool forceValue)
+{
+ if (isWidgetOOF(child)) {
+ assert (getWidgetOutOfFlowMgr(child) &&
+ getWidgetOutOfFlowMgr(child)->dealingWithSizeOfChild (child));
+ return getWidgetOutOfFlowMgr(child)->getAvailHeightOfChild (child,
+ forceValue);
+ } else
+ return Widget::getAvailWidthOfChild (child, forceValue);
+}
+
+void OOFAwareWidget::removeChild (Widget *child)
+{
+ // Sub classes should implement this method (and Textblock and
+ // Table do so), so this point is only reached from
+ // ~OOFAwareWidget, which removes widgets out of flow.
+ assert (isWidgetOOF (child));
+}
+
+bool OOFAwareWidget::mustBeWidenedToAvailWidth ()
+{
+ // Only used for floats.
+ assertNotReached ();
+ return false;
+}
+
+void OOFAwareWidget::borderChanged (int y, Widget *vloat)
+{
+ assertNotReached ();
+}
+
+void OOFAwareWidget::clearPositionChanged ()
+{
+ assertNotReached ();
+}
+
+void OOFAwareWidget::oofSizeChanged (bool extremesChanged)
+{
+ DBG_OBJ_ENTER ("resize", 0, "oofSizeChanged", "%s",
+ extremesChanged ? "true" : "false");
+ queueResize (-1, extremesChanged);
+
+ // Extremes changes may become also relevant for the children.
+ if (extremesChanged)
+ containerSizeChanged ();
+
+ DBG_OBJ_LEAVE ();
+}
+
+int OOFAwareWidget::getLineBreakWidth ()
+{
+ assertNotReached ();
+ return 0;
+}
+
+bool OOFAwareWidget::isPossibleContainer (int oofmIndex)
+{
+ return oofmIndex != OOFM_FLOATS;
+}
+
+bool OOFAwareWidget::isPossibleContainerParent (int oofmIndex)
+{
+ return oofmIndex != OOFM_FLOATS;
+}
+
+bool OOFAwareWidget::adjustExtraSpaceWhenCorrectingRequisitionByOOF ()
+{
+ return true;
+}
+
+} // namespace oof
+
+} // namespace dw