Files
flenser/dw/oofawarewidget.cc

621 lines
20 KiB
C++

/*
* 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 "oofposrelmgr.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", "RELATIVE", "FIXED"
};
OOFAwareWidget::OOFAwareWidget ()
{
DBG_OBJ_CREATE ("dw::oof::OOFAwareWidget");
registerName ("dw::oof::OOFAwareWidget", typeid(*this));
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]);
}
}
int OOFAwareWidget::getOOFMIndex (Widget *widget)
{
DBG_OBJ_ENTER_O ("construct", 0, NULL, "getOOFMIndex", "%p", widget);
DBG_OBJ_MSGF_O ("construct", 1, NULL, "position = %s, float = %s",
widget->getStyle()->position
== style::POSITION_STATIC ? "static" :
(widget->getStyle()->position
== style::POSITION_RELATIVE ? "relative" :
(widget->getStyle()->position
== style::POSITION_ABSOLUTE ? "absolute" :
(widget->getStyle()->position
== style::POSITION_FIXED ? "fixed" : "???"))),
widget->getStyle()->vloat == style::FLOAT_NONE ? "none" :
(widget->getStyle()->vloat == style::FLOAT_LEFT ? "left" :
(widget->getStyle()->vloat == style::FLOAT_RIGHT ?
"right" : "???")));
int index = -1;
if (testWidgetFloat (widget))
index = OOFM_FLOATS;
else if (testWidgetAbsolutelyPositioned (widget))
index = OOFM_ABSOLUTE;
else if (testWidgetRelativelyPositioned (widget))
index = OOFM_RELATIVE;
else if (testWidgetFixedlyPositioned (widget))
index = OOFM_FIXED;
else
lout::misc::assertNotReached ();
DBG_OBJ_LEAVE_VAL_O (NULL, "%d (%s)", index, OOFM_NAME[index]);
return index;
}
bool OOFAwareWidget::isOOFContainer (Widget *widget, int oofmIndex)
{
// TODO The methods isPossibleContainer() and isPossibleContainerParent()
// are only used in few cases. Does not matter currently, however.
// This was created to cleanup the `instanceOf` calls in the
// complex conditionals, below. It relies upon certain
// sequence point and ordering rules of short-circuit evaluation.
OOFAwareWidget *oaw;
switch (oofmIndex) {
case OOFM_FLOATS:
return (oaw= dynamic_cast< OOFAwareWidget *>( widget )) &&
(// For floats, only some OOF aware widgets are considered as
// containers.
oaw->isPossibleOOFContainer (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).
!((oaw= dynamic_cast <OOFAwareWidget * >( widget->getParent() )) &&
oaw->isPossibleOOFContainerParent (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_RELATIVE:
case OOFM_ABSOLUTE:
return dynamic_cast< OOFAwareWidget * >( widget ) &&
(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)) {
auto *oaw= dynamic_cast< OOFAwareWidget * >( widget );
assert (oaw);
oofContainer[oofmIndex]= oaw;
}
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], 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_RELATIVE]->outOfFlowMgr[OOFM_RELATIVE] == NULL) {
oofContainer[OOFM_RELATIVE]->outOfFlowMgr[OOFM_RELATIVE] =
new OOFPosRelMgr (oofContainer[OOFM_RELATIVE]);
DBG_OBJ_ASSOC (oofContainer[OOFM_RELATIVE],
oofContainer[OOFM_RELATIVE]->outOfFlowMgr[OOFM_RELATIVE]);
}
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);
requisitionWithoutOOF = *requisition;
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 = std::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 = std::max (extraSpace.bottom,
oofHeight - (requisition->ascent +
requisition->descent));
DBG_OBJ_SET_NUM ("extraSpace.bottom", extraSpace.bottom);
}
splitHeightFun (oofHeight,
&requisition->ascent, &requisition->descent);
}
if (!adjustExtraSpaceWhenCorrectingRequisitionByOOF ()) {
requisitionWithoutOOF.width = std::max (requisitionWithoutOOF.width,
oofWidth);
if (oofHeight >
requisitionWithoutOOF.ascent + requisitionWithoutOOF.descent)
splitHeightFun (oofHeight, &requisitionWithoutOOF.ascent,
&requisitionWithoutOOF.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_SET_NUM ("requisitionWithoutOOF.width", requisitionWithoutOOF.width);
DBG_OBJ_SET_NUM ("requisitionWithoutOOF.ascent",
requisitionWithoutOOF.ascent);
DBG_OBJ_SET_NUM ("requisitionWithoutOOF.descent",
requisitionWithoutOOF.descent);
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 = std::max (extremes->minWidth, oofMinWidth);
extremes->minWidthIntrinsic = std::max (extremes->minWidthIntrinsic,
oofMinWidth);
extremes->maxWidth = std::max (extremes->maxWidth, oofMaxWidth);
extremes->maxWidthIntrinsic = std::max (extremes->maxWidthIntrinsic,
oofMinWidth);
extremes->adjustmentWidth = std::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);
bool result;
if (IMPL_POS) {
// 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 ();
result = cl < gl;
DBG_OBJ_MSGF ("draw", 1,"%d < %d => %s", cl, gl, boolToStr (result));
} else
result = false;
DBG_OBJ_LEAVE_VAL ("%s", boolToStr (result));
return result;
}
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;
default:
fprintf (stderr, "OOFAwareWidget::drawLevel: invalid level %s (%d)",
stackingLevelText (level), level);
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 ("getWidgetAtPoint (SL_IN_FLOW) for %s",
getClassName ());
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:
fprintf (stderr,
"OOFAwareWidget::getWidgetAtPointLevel: invalid level %s (%d)",
stackingLevelText (level), level);
break;
}
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;
}
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));
}
void OOFAwareWidget::updateReference (int ref)
{
notImplemented ("OOFAwareWidget::updateReference");
}
void OOFAwareWidget::widgetRefSizeChanged (int externalIndex)
{
notImplemented ("OOFAwareWidget::widgetRefSizeChanged");
}
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::getGeneratorX (int oofmIndex)
{
notImplemented ("OOFAwareWidget::getGeneratorX");
return 0;
}
int OOFAwareWidget::getGeneratorY (int oofmIndex)
{
notImplemented ("OOFAwareWidget::getGeneratorY");
return 0;
}
int OOFAwareWidget::getGeneratorWidth ()
{
notImplemented ("OOFAwareWidget::getGeneratorWidth");
return 0;
}
int OOFAwareWidget::getMaxGeneratorWidth ()
{
notImplemented ("OOFAwareWidget::getMaxGeneratorWidth");
return 0;
}
bool OOFAwareWidget::usesMaxGeneratorWidth ()
{
notImplemented ("OOFAwareWidget::usesMaxGeneratorWidth");
return false;
}
bool OOFAwareWidget::isPossibleOOFContainer (int oofmIndex)
{
return oofmIndex != OOFM_FLOATS;
}
bool OOFAwareWidget::isPossibleOOFContainerParent (int oofmIndex)
{
return oofmIndex != OOFM_FLOATS;
}
bool OOFAwareWidget::adjustExtraSpaceWhenCorrectingRequisitionByOOF ()
{
return true;
}
} // namespace oof
} // namespace dw