diff options
Diffstat (limited to 'dw/widget.cc')
-rw-r--r-- | dw/widget.cc | 875 |
1 files changed, 801 insertions, 74 deletions
diff --git a/dw/widget.cc b/dw/widget.cc index a3d85d0a..628aa2b4 100644 --- a/dw/widget.cc +++ b/dw/widget.cc @@ -62,6 +62,7 @@ void Widget::WidgetImgRenderer::draw (int x, int y, int width, int height) // ---------------------------------------------------------------------- +bool Widget::adjustMinWidth = false; int Widget::CLASS_ID = -1; Widget::Widget () @@ -69,8 +70,10 @@ Widget::Widget () DBG_OBJ_CREATE ("dw::core::Widget"); registerName ("dw::core::Widget", &CLASS_ID); - flags = (Flags)(NEEDS_RESIZE | EXTREMES_CHANGED | HAS_CONTENTS); - parent = generator = NULL; + flags = (Flags)(NEEDS_RESIZE | EXTREMES_CHANGED); + parent = quasiParent = generator = container = NULL; + DBG_OBJ_SET_PTR ("container", container); + layout = NULL; allocation.x = -1; @@ -79,6 +82,8 @@ Widget::Widget () allocation.ascent = 1; allocation.descent = 0; + extraSpace.top = extraSpace.right = extraSpace.bottom = extraSpace.left = 0; + style = NULL; bgColor = NULL; buttonSensitive = true; @@ -153,16 +158,38 @@ void Widget::setParent (Widget *parent) //printf ("The %s %p becomes a child of the %s %p\n", // getClassName(), this, parent->getClassName(), parent); + // Determine the container. Currently rather simple; will become + // more complicated when absolute and fixed positions are + // supported. + container = NULL; + for (Widget *widget = getParent (); widget != NULL && container == NULL; + widget = widget->getParent()) + if (widget->isPossibleContainer ()) + container = widget; + // If there is no possible container widget, there is + // (surprisingly!) also no container (i. e. the viewport is + // used). Does not occur in dillo, where the toplevel widget is a + // Textblock. + DBG_OBJ_SET_PTR ("container", container); + notifySetParent(); } +void Widget::setQuasiParent (Widget *quasiParent) +{ + this->quasiParent = quasiParent; + + // More to do? Compare with setParent(). + + DBG_OBJ_SET_PTR ("quasiParent", quasiParent); +} + void Widget::queueDrawArea (int x, int y, int width, int height) { /** \todo Maybe only the intersection? */ - DBG_OBJ_MSGF ("draw", 0, "<b>queueDrawArea</b> (%d, %d, %d, %d)", - x, y, width, height); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("draw", 0, "queueDrawArea", "%d, %d, %d, %d", + x, y, width, height); _MSG("Widget::queueDrawArea alloc(%d %d %d %d) wid(%d %d %d %d)\n", allocation.x, allocation.y, @@ -171,17 +198,21 @@ void Widget::queueDrawArea (int x, int y, int width, int height) if (layout) layout->queueDraw (x + allocation.x, y + allocation.y, width, height); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } /** * \brief This method should be called, when a widget changes its size. + * + * A "fast" queueResize will ignore the anchestors, and furthermore + * not trigger the idle function. Used only within + * viewportSizeChanged, and not available outside Layout and Widget. */ -void Widget::queueResize (int ref, bool extremesChanged) +void Widget::queueResize (int ref, bool extremesChanged, bool fast) { - DBG_OBJ_MSGF ("resize", 0, "<b>queueResize</b> (%d, %s)", - ref, extremesChanged ? "true" : "false"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("resize", 0, "queueResize", "%d, %s, %s", + ref, extremesChanged ? "true" : "false", + fast ? "true" : "false"); // queueResize() can be called recursively; calls are queued, so // that actualQueueResize() is clean. @@ -189,38 +220,60 @@ void Widget::queueResize (int ref, bool extremesChanged) if (queueResizeEntered ()) { DBG_OBJ_MSG ("resize", 1, "put into queue"); layout->queueQueueResizeList->put (new Layout::QueueResizeItem - (this, ref, extremesChanged)); + (this, ref, extremesChanged, fast)); } else { - actualQueueResize (ref, extremesChanged); - + actualQueueResize (ref, extremesChanged, fast); + + DBG_IF_RTFL { + if (layout == NULL) + DBG_OBJ_MSG ("resize", 1, "layout is not set"); + else if (layout->queueQueueResizeList->size () == 0) + DBG_OBJ_MSG ("resize", 1, "queue item list is empty"); + } + while (layout != NULL && layout->queueQueueResizeList->size () > 0) { + DBG_IF_RTFL { + DBG_OBJ_MSGF ("resize", 1, "queue item list has %d elements:", + layout->queueQueueResizeList->size ()); + DBG_OBJ_MSG_START (); + for (int i = 0; i < layout->queueQueueResizeList->size (); i++) { + DBG_OBJ_MSGF + ("resize", 1, + "#%d: widget = %p, ref = %d, extremesChanged = %s, " + "fast = %s", + i, layout->queueQueueResizeList->get(i)->widget, + layout->queueQueueResizeList->get(i)->ref, + layout->queueQueueResizeList->get(i)->extremesChanged ? + "true" : "false", + layout->queueQueueResizeList->get(i)->fast ? + "true" : "false"); + } + DBG_OBJ_MSG_END (); + DBG_OBJ_MSG ("resize", 1, "taking #0 out of list"); + } + Layout::QueueResizeItem *item = layout->queueQueueResizeList->get (0); - DBG_OBJ_MSGF ("resize", 1, "taken out of queue queue (size = %d)", - layout->queueQueueResizeList->size ()); - item->widget->actualQueueResize (item->ref, item->extremesChanged); + item->widget->actualQueueResize (item->ref, item->extremesChanged, + item->fast); layout->queueQueueResizeList->remove (0); // hopefully not too large } } - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } -void Widget::actualQueueResize (int ref, bool extremesChanged) +void Widget::actualQueueResize (int ref, bool extremesChanged, bool fast) { assert (!queueResizeEntered ()); - DBG_OBJ_MSGF ("resize", 0, "<b>actualQueueResize</b> (%d, %s)", - ref, extremesChanged ? "true" : "false"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("resize", 0, "actualQueueResize", "%d, %s, %s", + ref, extremesChanged ? "true" : "false", + fast ? "true" : "false"); enterQueueResize (); Widget *widget2, *child; - //printf("The %stop-level %s %p with parentRef = %d has changed its size. " - // "Layout = %p.\n", - // parent ? "non-" : "", getClassName(), this, parentRef, layout); - Flags resizeFlag, extremesFlag; if (layout) { @@ -243,36 +296,173 @@ void Widget::actualQueueResize (int ref, bool extremesChanged) setFlags (extremesFlag); markExtremesChange (ref); } - - for (widget2 = parent, child = this; widget2; - child = widget2, widget2 = widget2->parent) { - //printf (" Setting %s and ALLOCATE_QUEUED for the " - // "%stop-level %s %p with parentRef = %d\n", - // resizeFlag == RESIZE_QUEUED ? "RESIZE_QUEUED" : "NEEDS_RESIZE", - // widget2->parent ? "non-" : "", widget2->getClassName(), widget2, - // widget2->parentRef); - - if (layout && !widget2->resizeQueued ()) - layout->queueResizeList->put (widget2); - - widget2->setFlags (resizeFlag); - widget2->markSizeChange (child->parentRef); - widget2->setFlags (ALLOCATE_QUEUED); - - if (extremesChanged) { - widget2->setFlags (extremesFlag); - widget2->markExtremesChange (child->parentRef); + + if (fast) { + if (parent) { + // In this case, queueResize is called from top (may be a + // random entry point) to bottom, so markSizeChange and + // markExtremesChange have to be called explicitly for the + // parent. The tests (needsResize etc.) are uses to check + // whether queueResize has been called for the parent, or + // whether this widget is the enty point. + if (parent->needsResize () || parent->resizeQueued ()) + parent->markSizeChange (parentRef); + if (parent->extremesChanged () || parent->extremesQueued ()) + parent->markExtremesChange (parentRef); + } + } else { + for (widget2 = parent, child = this; widget2; + child = widget2, widget2 = widget2->parent) { + if (layout && !widget2->resizeQueued ()) + layout->queueResizeList->put (widget2); + + DBG_OBJ_MSGF ("resize", 2, "setting %s and ALLOCATE_QUEUED for %p", + resizeFlag == RESIZE_QUEUED ? + "RESIZE_QUEUED" : "NEEDS_RESIZE", + widget2); + + widget2->setFlags (resizeFlag); + widget2->markSizeChange (child->parentRef); + widget2->setFlags (ALLOCATE_QUEUED); + + if (extremesChanged) { + widget2->setFlags (extremesFlag); + widget2->markExtremesChange (child->parentRef); + } } - } - if (layout) - layout->queueResize (extremesChanged); + if (layout) + layout->queueResize (extremesChanged); + } leaveQueueResize (); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); +} + +void Widget::containerSizeChanged () +{ + DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChanged"); + + // If there is a container widget (not the viewport), which has not + // changed its size (which can be determined by the respective + // flags: this method is called recursively), this widget will + // neither change its size. Also, the recursive iteration can be + // stopped, since the children of this widget will + if (container == NULL || + container->needsResize () || container->resizeQueued () || + container->extremesChanged () || container->extremesQueued ()) { + // Viewport (container == NULL) or container widget has changed + // its size. + if (affectedByContainerSizeChange ()) + queueResizeFast (0, true); + + // Even if *this* widget is not affected, children may be, so + // iterate over children. + containerSizeChangedForChildren (); + } + + DBG_OBJ_LEAVE (); +} + +bool Widget::affectedByContainerSizeChange () +{ + DBG_OBJ_ENTER0 ("resize", 0, "affectedByContainerSizeChange"); + + bool ret; + + // This standard implementation is suitable for all widgets which + // call correctRequisition() and correctExtremes(), even in the way + // how Textblock and Image do (see comments there). Has to be kept + // in sync. + + if (container == NULL) { + if (style::isAbsLength (getStyle()->width) && + style::isAbsLength (getStyle()->height)) + // Both absolute, i. e. fixed: no dependency. + ret = false; + else if (style::isPerLength (getStyle()->width) || + style::isPerLength (getStyle()->height)) { + // Any percentage: certainly dependenant. + ret = true; + } else + // One or both is "auto": depends ... + ret = + (getStyle()->width == style::LENGTH_AUTO ? + usesAvailWidth () : false) || + (getStyle()->height == style::LENGTH_AUTO ? + usesAvailHeight () : false); + } else + ret = container->affectsSizeChangeContainerChild (this); + + DBG_OBJ_MSGF ("resize", 1, "=> %s", ret ? "true" : "false"); + DBG_OBJ_LEAVE (); + return ret; +} + +bool Widget::affectsSizeChangeContainerChild (Widget *child) +{ + DBG_OBJ_ENTER ("resize", 0, "affectsSizeChangeContainerChild", "%p", child); + + bool ret; + + // From the point of view of the container. This standard + // implementation should be suitable for most (if not all) + // containers. + + if (style::isAbsLength (child->getStyle()->width) && + style::isAbsLength (child->getStyle()->height)) + // Both absolute, i. e. fixed: no dependency. + ret = false; + else if (style::isPerLength (child->getStyle()->width) || + style::isPerLength (child->getStyle()->height)) { + // Any percentage: certainly dependenant. + ret = true; + } else + // One or both is "auto": depends ... + ret = + (child->getStyle()->width == style::LENGTH_AUTO ? + child->usesAvailWidth () : false) || + (child->getStyle()->height == style::LENGTH_AUTO ? + child->usesAvailHeight () : false); + + DBG_OBJ_MSGF ("resize", 1, "=> %s", ret ? "true" : "false"); + DBG_OBJ_LEAVE (); + return ret; +} + +void Widget::containerSizeChangedForChildren () +{ + DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren"); + + // Working, but inefficient standard implementation. + Iterator *it = iterator ((Content::Type)(Content::WIDGET_IN_FLOW | + Content::WIDGET_OOF_CONT), + false); + while (it->next ()) + it->getContent()->widget->containerSizeChanged (); + it->unref (); + + DBG_OBJ_LEAVE (); +} + +/** + * \brief Must be implemengted by a method returning true, when + * getAvailWidth() is called. + */ +bool Widget::usesAvailWidth () +{ + return false; } +/** + * \brief Must be implemengted by a method returning true, when + * getAvailHeight() is called. + */ +bool Widget::usesAvailHeight () +{ + return false; +} /** * \brief This method is a wrapper for Widget::sizeRequestImpl(); it calls @@ -282,8 +472,7 @@ void Widget::sizeRequest (Requisition *requisition) { assert (!queueResizeEntered ()); - DBG_OBJ_MSG ("resize", 0, "<b>sizeRequest</b>"); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER0 ("resize", 0, "sizeRequest"); enterSizeRequest (); @@ -319,7 +508,240 @@ void Widget::sizeRequest (Requisition *requisition) leaveSizeRequest (); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); +} + +/** + * \brief Used to evaluate Widget::adjustMinWidth. + * + * If extremes == NULL, getExtremes is called. + */ +int Widget::getMinWidth (Extremes *extremes) +{ + DBG_OBJ_ENTER0 ("resize", 0, "getMinWidth"); + int minWidth; + + if (adjustMinWidth) { + Extremes extremes2; + if (extremes == NULL) { + getExtremes (&extremes2); + extremes = &extremes2; + } + + minWidth = extremes->minWidthIntrinsic; + } else + minWidth = 0; + + DBG_OBJ_MSGF ("resize", 1, "=> %d", minWidth); + DBG_OBJ_LEAVE (); + + return minWidth; +} + +/** + * Return available width including margin/border/padding + * (extraSpace?), not only the content width. + */ +int Widget::getAvailWidth (bool forceValue) +{ + DBG_OBJ_ENTER ("resize", 0, "getAvailWidth", "%s", + forceValue ? "true" : "false"); + + int width; + + if (parent == NULL && quasiParent == NULL) { + DBG_OBJ_MSG ("resize", 1, "no parent, regarding viewport"); + DBG_OBJ_MSG_START (); + + // TODO Consider nested layouts (e. g. <button>). + if (style::isAbsLength (getStyle()->width)) { + DBG_OBJ_MSGF ("resize", 1, "absolute width: %dpx", + style::absLengthVal (getStyle()->width)); + width = style::absLengthVal (getStyle()->width) + boxDiffWidth (); + } else { + int viewportWidth = + layout->viewportWidth - (layout->canvasHeightGreater ? + layout->vScrollbarThickness : 0); + if (style::isPerLength (getStyle()->width)) { + DBG_OBJ_MSGF ("resize", 1, "percentage width: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (getStyle()->width)); + width = applyPerWidth (viewportWidth, getStyle()->width); + } else { + DBG_OBJ_MSG ("resize", 1, "no specification"); + width = viewportWidth; + } + } + DBG_OBJ_MSG_END (); + } else if (parent) { + DBG_OBJ_MSG ("resize", 1, "delegated to parent"); + DBG_OBJ_MSG_START (); + width = parent->getAvailWidthOfChild (this, forceValue); + DBG_OBJ_MSG_END (); + } else /* if (quasiParent) */ { + DBG_OBJ_MSG ("resize", 1, "delegated to quasiParent"); + DBG_OBJ_MSG_START (); + width = quasiParent->getAvailWidthOfChild (this, forceValue); + DBG_OBJ_MSG_END (); + } + + if (width != -1) + width = misc::max (width, getMinWidth (NULL)); + + DBG_OBJ_MSGF ("resize", 1, "=> %d", width); + DBG_OBJ_LEAVE (); + + return width; +} + +/** + * Return available height including margin/border/padding + * (extraSpace?), not only the content height. + */ +int Widget::getAvailHeight (bool forceValue) +{ + // TODO Correct by ... not extremes, but ...? (Height extremes?) + + DBG_OBJ_ENTER ("resize", 0, "getAvailHeight", "%s", + forceValue ? "true" : "false"); + + int height; + + if (parent == NULL && quasiParent == NULL) { + DBG_OBJ_MSG ("resize", 1, "no parent, regarding viewport"); + DBG_OBJ_MSG_START (); + + // TODO Consider nested layouts (e. g. <button>). + if (style::isAbsLength (getStyle()->height)) { + DBG_OBJ_MSGF ("resize", 1, "absolute height: %dpx", + style::absLengthVal (getStyle()->height)); + height = style::absLengthVal (getStyle()->height) + boxDiffHeight (); + } else if (style::isPerLength (getStyle()->height)) { + DBG_OBJ_MSGF ("resize", 1, "percentage height: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (getStyle()->height)); + // Notice that here -- unlike getAvailWidth() -- + // layout->hScrollbarThickness is not considered here; + // something like canvasWidthGreater (analogue to + // canvasHeightGreater) would be complicated and lead to + // possibly contradictory self-references. + height = applyPerHeight (layout->viewportHeight, getStyle()->height); + } else { + DBG_OBJ_MSG ("resize", 1, "no specification"); + height = layout->viewportHeight; + } + + DBG_OBJ_MSG_END (); + } else if (parent) { + DBG_OBJ_MSG ("resize", 1, "delegated to parent"); + DBG_OBJ_MSG_START (); + height = quasiParent->getAvailHeightOfChild (this, forceValue); + DBG_OBJ_MSG_END (); + } else /* if (quasiParent) */ { + DBG_OBJ_MSG ("resize", 1, "delegated to quasiParent"); + DBG_OBJ_MSG_START (); + height = quasiParent->getAvailHeightOfChild (this, forceValue); + DBG_OBJ_MSG_END (); + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d", height); + DBG_OBJ_LEAVE (); + + return height; +} + +void Widget::correctRequisition (Requisition *requisition, + void (*splitHeightFun) (int, int *, int *)) +{ + // TODO Correct height by ... not extremes, but ...? (Height extremes?) + + DBG_OBJ_ENTER ("resize", 0, "correctRequisition", "%d * (%d + %d), ...", + requisition->width, requisition->ascent, + requisition->descent); + + if (container == NULL && quasiParent == NULL) { + if (style::isAbsLength (getStyle()->width)) { + DBG_OBJ_MSGF ("resize", 1, "absolute width: %dpx", + style::absLengthVal (getStyle()->width)); + requisition->width = + misc::max (style::absLengthVal (getStyle()->width) + + boxDiffWidth (), + getMinWidth (NULL)); + } else if (style::isPerLength (getStyle()->width)) { + DBG_OBJ_MSGF ("resize", 1, "percentage width: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (getStyle()->width)); + int viewportWidth = + layout->viewportWidth - (layout->canvasHeightGreater ? + layout->vScrollbarThickness : 0); + requisition->width = + misc::max (applyPerWidth (viewportWidth, getStyle()->width), + getMinWidth (NULL)); + } + + // TODO Perhaps split first, then add box ascent and descent. + if (style::isAbsLength (getStyle()->height)) { + DBG_OBJ_MSGF ("resize", 1, "absolute height: %dpx", + style::absLengthVal (getStyle()->height)); + splitHeightFun (style::absLengthVal (getStyle()->height) + + boxDiffHeight (), + &requisition->ascent, &requisition->descent); + } else if (style::isPerLength (getStyle()->height)) { + DBG_OBJ_MSGF ("resize", 1, "percentage height: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (getStyle()->height)); +#if 0 + // TODO Percentage heights are somewhat more complicated. Has + // to be clarified. + + // For layout->viewportHeight, see comment in getAvailHeight(). + splitHeightFun (applyPerHeight (layout->viewportHeight, + getStyle()->height), + &requisition->ascent, &requisition->descent); +#endif + } + } else if (container) + container->correctRequisitionOfChild (this, requisition, splitHeightFun); + else // if (quasiParent) + // Here, quasiParent plays the same role as the container. + quasiParent->correctRequisitionOfChild (this, requisition, + splitHeightFun); + + DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)", + requisition->width, requisition->ascent, + requisition->descent); + DBG_OBJ_LEAVE (); +} + +void Widget::correctExtremes (Extremes *extremes) +{ + DBG_OBJ_ENTER ("resize", 0, "correctExtremes", "%d (%d) / %d (%d)", + extremes->minWidth, extremes->minWidthIntrinsic, + extremes->maxWidth, extremes->maxWidthIntrinsic); + + if (container == NULL && quasiParent == NULL) { + if (style::isAbsLength (getStyle()->width)) + extremes->minWidth = extremes->maxWidth = + misc::max (style::absLengthVal (getStyle()->width) + + boxDiffWidth (), + getMinWidth (extremes)); + else if (style::isPerLength (getStyle()->width)) { + int viewportWidth = + layout->viewportWidth - (layout->canvasHeightGreater ? + layout->vScrollbarThickness : 0); + extremes->minWidth = extremes->maxWidth = + misc::max (applyPerWidth (viewportWidth, getStyle()->width), + getMinWidth (extremes)); + } + } else if (container) + container->correctExtremesOfChild (this, extremes); + else // if (quasiParent) + // Here, quasiParent plays the same role as the container. + quasiParent->correctExtremesOfChild (this, extremes); + + DBG_OBJ_MSGF ("resize", 1, "=> %d / %d", + extremes->minWidth, extremes->maxWidth); + DBG_OBJ_LEAVE (); } /** @@ -329,7 +751,7 @@ void Widget::getExtremes (Extremes *extremes) { assert (!queueResizeEntered ()); - DBG_OBJ_MSG ("resize", 0, "<b>getExtremes</b>"); + DBG_OBJ_ENTER0 ("resize", 0, "getExtremes"); DBG_OBJ_MSG_START (); enterGetExtremes (); @@ -344,18 +766,32 @@ void Widget::getExtremes (Extremes *extremes) } if (extremesChanged ()) { + // For backward compatibility (part 1/2): + extremes->minWidthIntrinsic = extremes->maxWidthIntrinsic = -1; + getExtremesImpl (extremes); + + // For backward compatibility (part 2/2): + if (extremes->minWidthIntrinsic == -1) + extremes->minWidthIntrinsic = extremes->minWidth; + if (extremes->maxWidthIntrinsic == -1) + extremes->maxWidthIntrinsic = extremes->maxWidth; + this->extremes = *extremes; unsetFlags (EXTREMES_CHANGED); DBG_OBJ_SET_NUM ("extremes.minWidth", extremes->minWidth); + DBG_OBJ_SET_NUM ("extremes.minWidthIntrinsic", + extremes->minWidthIntrinsic); DBG_OBJ_SET_NUM ("extremes.maxWidth", extremes->maxWidth); + DBG_OBJ_SET_NUM ("extremes.maxWidthIntrinsic", + extremes->maxWidthIntrinsic); } else *extremes = this->extremes; leaveGetExtremes (); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } /** @@ -369,10 +805,9 @@ void Widget::sizeAllocate (Allocation *allocation) assert (!getExtremesEntered ()); assert (resizeIdleEntered ()); - DBG_OBJ_MSGF ("resize", 0, "<b>sizeAllocate</b> (%d, %d; %d * (%d + %d))", - allocation->x, allocation->y, allocation->width, - allocation->ascent, allocation->descent); - DBG_OBJ_MSG_START (); + DBG_OBJ_ENTER ("resize", 0, "sizeAllocate", "%d, %d; %d * (%d + %d)", + allocation->x, allocation->y, allocation->width, + allocation->ascent, allocation->descent); DBG_OBJ_MSGF ("resize", 1, "old allocation (%d, %d; %d * (%d + %d)); needsAllocate: %s", @@ -435,7 +870,7 @@ void Widget::sizeAllocate (Allocation *allocation) leaveSizeAllocate (); - DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } bool Widget::buttonPress (EventButton *event) @@ -507,6 +942,28 @@ void Widget::setStyle (style::Style *style) queueResize (0, true); else queueDraw (); + + // These should better be attributed to the style itself, and a + // script processing RTFL messages could transfer it to something + // equivalent: + + DBG_OBJ_SET_NUM ("style.margin.top", style->margin.top); + DBG_OBJ_SET_NUM ("style.margin.bottom", style->margin.bottom); + DBG_OBJ_SET_NUM ("style.margin.left", style->margin.left); + DBG_OBJ_SET_NUM ("style.margin.right", style->margin.right); + + DBG_OBJ_SET_NUM ("style.border-width.top", style->borderWidth.top); + DBG_OBJ_SET_NUM ("style.border-width.bottom", style->borderWidth.bottom); + DBG_OBJ_SET_NUM ("style.border-width.left", style->borderWidth.left); + DBG_OBJ_SET_NUM ("style.border-width.right", style->borderWidth.right); + + DBG_OBJ_SET_NUM ("style.padding.top", style->padding.top); + DBG_OBJ_SET_NUM ("style.padding.bottom", style->padding.bottom); + DBG_OBJ_SET_NUM ("style.padding.left", style->padding.left); + DBG_OBJ_SET_NUM ("style.padding.right", style->padding.right); + + DBG_OBJ_SET_NUM ("style.border-spacing (h)", style->hBorderSpacing); + DBG_OBJ_SET_NUM ("style.border-spacing (v)", style->vBorderSpacing); } /** @@ -781,14 +1238,6 @@ void Widget::getPaddingArea (int *xPad, int *yPad, int *widthPad, - style->margin.bottom - style->borderWidth.bottom; } -void Widget::getExtremesImpl (Extremes *extremes) -{ - /* Simply return the requisition width */ - Requisition requisition; - sizeRequest (&requisition); - extremes->minWidth = extremes->maxWidth = requisition.width; -} - void Widget::sizeAllocateImpl (Allocation *allocation) { } @@ -801,6 +1250,264 @@ void Widget::markExtremesChange (int ref) { } +int Widget::applyPerWidth (int containerWidth, style::Length perWidth) +{ + return style::multiplyWithPerLength (containerWidth, perWidth) + + boxDiffWidth (); +} + +int Widget::applyPerHeight (int containerHeight, style::Length perHeight) +{ + return style::multiplyWithPerLength (containerHeight, perHeight) + + boxDiffHeight (); +} + +int Widget::getAvailWidthOfChild (Widget *child, bool forceValue) +{ + // This is a halfway suitable implementation for all + // containers. For simplification, this will be used during the + // development; then, a differentiation could be possible. + + // TODO Correct by extremes? + + DBG_OBJ_ENTER ("resize", 0, "getAvailWidthOfChild", "%p, %s", + child, forceValue ? "true" : "false"); + + int width; + + if (child->getStyle()->width == style::LENGTH_AUTO) { + DBG_OBJ_MSG ("resize", 1, "no specification"); + int availWidth = getAvailWidth (forceValue); + if (availWidth == -1) + width = -1; + else + width = misc::max (availWidth - boxDiffWidth (), 0); + } else { + // In most cases, the toplevel widget should be a container, so + // the container is non-NULL when the parent is non-NULL. Just + // in case ...: + Widget *effContainer = + child->container ? child->container : child->parent; + + if (effContainer == this) { + if (style::isAbsLength (child->getStyle()->width)) { + DBG_OBJ_MSGF ("resize", 1, "absolute width: %dpx", + style::absLengthVal (child->getStyle()->width)); + width = misc::max (style::absLengthVal (child->getStyle()->width) + + child->boxDiffWidth (), 0); + } else { + assert (style::isPerLength (child->getStyle()->width)); + DBG_OBJ_MSGF ("resize", 1, "percentage width: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (child->getStyle()->width)); + + int availWidth = getAvailWidth (forceValue); + if (availWidth == -1) + width = -1; + else + width = + misc::max (child->applyPerWidth (availWidth - boxDiffWidth (), + child->getStyle()->width), + 0); + } + } else { + DBG_OBJ_MSG ("resize", 1, "delegated to (effective) container"); + DBG_OBJ_MSG_START (); + width = effContainer->getAvailWidthOfChild (child, forceValue); + DBG_OBJ_MSG_END (); + } + } + + if (width != -1) + width = misc::max (width, child->getMinWidth (NULL)); + + DBG_OBJ_MSGF ("resize", 1, "=> %d", width); + DBG_OBJ_LEAVE (); + + return width; +} + +int Widget::getAvailHeightOfChild (Widget *child, bool forceValue) +{ + // Again, a suitable implementation for all widgets (perhaps). + + // TODO Correct by extremes? (Height extemes?) + + DBG_OBJ_ENTER ("resize", 0, "getAvailHeightOfChild", "%p, %s", + child, forceValue ? "true" : "false"); + + int height; + + if (child->getStyle()->height == style::LENGTH_AUTO) { + DBG_OBJ_MSG ("resize", 1, "no specification"); + int availHeight = getAvailHeight (forceValue); + if (availHeight == -1) + height = -1; + else + height = misc::max (availHeight - boxDiffHeight (), 0); + } else { + // In most cases, the toplevel widget should be a container, so + // the container is non-NULL when the parent is non-NULL. Just + // in case ...: + Widget *effContainer = + child->container ? child->container : child->parent; + + if (effContainer == this) { + if (style::isAbsLength (child->getStyle()->height)) { + DBG_OBJ_MSGF ("resize", 1, "absolute height: %dpx", + style::absLengthVal (child->getStyle()->height)); + height = misc::max (style::absLengthVal (child->getStyle()->height) + + child->boxDiffHeight (), 0); + } else { + assert (style::isPerLength (child->getStyle()->height)); + DBG_OBJ_MSGF ("resize", 1, "percentage height: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (child->getStyle()->height)); + + int availHeight = getAvailHeight (forceValue); + if (availHeight == -1) + height = -1; + else + height = + misc::max (child->applyPerHeight (availHeight - + boxDiffHeight (), + child->getStyle()->height), + 0); + } + } else { + DBG_OBJ_MSG ("resize", 1, "delegated to (effective) container"); + DBG_OBJ_MSG_START (); + height = effContainer->getAvailHeightOfChild (child, forceValue); + DBG_OBJ_MSG_END (); + } + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d", height); + DBG_OBJ_LEAVE (); + + return height; +} + +void Widget::correctRequisitionOfChild (Widget *child, Requisition *requisition, + void (*splitHeightFun) (int, int*, + int*)) +{ + // Again, a suitable implementation for all widgets (perhaps). + + DBG_OBJ_ENTER ("resize", 0, "correctRequisitionOfChild", + "%p, %d * (%d + %d), ...", child, requisition->width, + requisition->ascent, requisition->descent); + + correctReqWidthOfChild (child, requisition); + correctReqHeightOfChild (child, requisition, splitHeightFun); + + DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)", + requisition->width, requisition->ascent, + requisition->descent); + DBG_OBJ_LEAVE (); +} + +void Widget::correctReqWidthOfChild (Widget *child, Requisition *requisition) +{ + DBG_OBJ_ENTER ("resize", 0, "correctReqWidthOfChild", "%p, %d * (%d + %d)", + child, requisition->width, requisition->ascent, + requisition->descent); + + if (style::isAbsLength (child->getStyle()->width)) + requisition->width = + misc::max (style::absLengthVal (child->getStyle()->width) + + child->boxDiffWidth (), + child->getMinWidth (NULL)); + else if (style::isPerLength (child->getStyle()->width)) { + int availWidth = getAvailWidth (false); + if (availWidth != -1) { + int containerWidth = availWidth - boxDiffWidth (); + requisition->width = + misc::max (child->applyPerWidth (containerWidth, + child->getStyle()->width), + child->getMinWidth (NULL)); + } + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)", + requisition->width, requisition->ascent, + requisition->descent); + DBG_OBJ_LEAVE (); +} + +void Widget::correctReqHeightOfChild (Widget *child, Requisition *requisition, + void (*splitHeightFun) (int, int*, int*)) +{ + // TODO Correct height by extremes? (Height extemes?) + + DBG_OBJ_ENTER ("resize", 0, "correctReqHeightOfChild", + "%p, %d * (%d + %d), ...", child, requisition->width, + requisition->ascent, requisition->descent); + + // TODO Perhaps split first, then add box ascent and descent. + if (style::isAbsLength (child->getStyle()->height)) + splitHeightFun (style::absLengthVal (child->getStyle()->height) + + child->boxDiffHeight (), + &requisition->ascent, &requisition->descent); + else if (style::isPerLength (child->getStyle()->height)) { +#if 0 + // TODO Percentage heights are somewhat more complicated. Has to + // be clarified. See also Widget::correctRequisition. + + int availHeight = getAvailHeight (false); + if (availHeight != -1) { + int containerHeight = availHeight - boxDiffHeight (); + splitHeightFun (child->applyPerHeight (containerHeight, + child->getStyle()->height), + &requisition->ascent, &requisition->descent); + } +#endif + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)", + requisition->width, requisition->ascent, + requisition->descent); + DBG_OBJ_LEAVE (); +} + +void Widget::correctExtremesOfChild (Widget *child, Extremes *extremes) +{ + // See comment in correctRequisitionOfChild. + + DBG_OBJ_ENTER ("resize", 0, "correctExtremesOfChild", + "%p, %d (%d) / %d (%d)", + child, extremes->minWidth, extremes->minWidthIntrinsic, + extremes->maxWidth, extremes->maxWidthIntrinsic); + + if (style::isAbsLength (child->getStyle()->width)) { + DBG_OBJ_MSGF ("resize", 1, "absolute width: %dpx", + style::absLengthVal (child->getStyle()->width)); + extremes->minWidth = extremes->maxWidth = + misc::max (style::absLengthVal (child->getStyle()->width) + + child->boxDiffWidth (), + child->getMinWidth (extremes)); + } else if (style::isPerLength (child->getStyle()->width)) { + DBG_OBJ_MSGF ("resize", 1, "percentage width: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (child->getStyle()->width)); + int availWidth = getAvailWidth (false); + if (availWidth != -1) { + int containerWidth = availWidth - boxDiffWidth (); + DBG_OBJ_MSGF ("resize", 1, "containerWidth = %d - %d = %d", + availWidth, boxDiffWidth (), containerWidth); + extremes->minWidth = extremes->maxWidth = + misc::max (child->applyPerWidth (containerWidth, + child->getStyle()->width), + child->getMinWidth (extremes)); + } + } else + DBG_OBJ_MSG ("resize", 1, "no specification"); + + DBG_OBJ_MSGF ("resize", 1, "=> %d / %d", + extremes->minWidth, extremes->maxWidth); + DBG_OBJ_LEAVE (); +} + /** * \brief This method is called after a widget has been set as the top of a * widget tree. @@ -820,16 +1527,16 @@ void Widget::notifySetParent() { } -void Widget::setWidth (int width) -{ -} - -void Widget::setAscent (int ascent) +bool Widget::isBlockLevel () { + // Most widgets are not block-level. + return false; } -void Widget::setDescent (int descent) +bool Widget::isPossibleContainer () { + // In most (all?) cases identical to: + return isBlockLevel (); } bool Widget::buttonPressImpl (EventButton *event) @@ -849,7 +1556,7 @@ bool Widget::motionNotifyImpl (EventMotion *event) void Widget::enterNotifyImpl (EventCrossing *) { - core::style::Tooltip *tooltip = getStyle()->x_tooltip; + style::Tooltip *tooltip = getStyle()->x_tooltip; if (tooltip) tooltip->onEnter(); @@ -857,7 +1564,7 @@ void Widget::enterNotifyImpl (EventCrossing *) void Widget::leaveNotifyImpl (EventCrossing *) { - core::style::Tooltip *tooltip = getStyle()->x_tooltip; + style::Tooltip *tooltip = getStyle()->x_tooltip; if (tooltip) tooltip->onLeave(); @@ -869,5 +1576,25 @@ void Widget::removeChild (Widget *child) misc::assertNotReached (); } +// ---------------------------------------------------------------------- + +void splitHeightPreserveAscent (int height, int *ascent, int *descent) +{ + *descent = height - *ascent; + if (*descent < 0) { + *descent = 0; + *ascent = height; + } +} + +void splitHeightPreserveDescent (int height, int *ascent, int *descent) +{ + *ascent = height - *descent; + if (*ascent < 0) { + *ascent = 0; + *descent = height; + } +} + } // namespace core } // namespace dw |