aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dw/image.cc19
-rw-r--r--dw/image.hh1
-rw-r--r--dw/widget.cc230
-rw-r--r--dw/widget.hh14
4 files changed, 196 insertions, 68 deletions
diff --git a/dw/image.cc b/dw/image.cc
index 603f2964..9adf17f1 100644
--- a/dw/image.cc
+++ b/dw/image.cc
@@ -402,22 +402,6 @@ void Image::sizeRequestSimpl (core::Requisition *requisition)
DBG_OBJ_LEAVE ();
}
-void Image::setReqWidth(core::Requisition *requisition, int width)
-{
- /* If we have the image buffer, try to set the height to preserve the image
- * ratio */
- if (buffer) {
- int w = buffer->getRootWidth();
- int h = buffer->getRootHeight();
- float ratio = (float) h / (float) w;
- int height = (float) width * ratio;
- /* Preserve descent */
- requisition->ascent = height - requisition->descent;
- }
-
- requisition->width = width;
-}
-
void Image::getExtremesSimpl (core::Extremes *extremes)
{
int contentWidth;
@@ -682,6 +666,9 @@ void Image::setBuffer (core::Imgbuf *buffer, bool resize)
}
queueResize (0, true);
+ if (bufWidth)
+ this->ratio = (float) bufWidth / (float) bufHeight;
+
DBG_OBJ_ASSOC_CHILD (this->buffer);
DBG_OBJ_SET_NUM ("bufWidth", bufWidth);
DBG_OBJ_SET_NUM ("bufHeight", bufHeight);
diff --git a/dw/image.hh b/dw/image.hh
index 5d890c4e..ae47f307 100644
--- a/dw/image.hh
+++ b/dw/image.hh
@@ -130,7 +130,6 @@ private:
bool isMap;
protected:
- void setReqWidth(core::Requisition *requisition, int width);
void sizeRequestSimpl (core::Requisition *requisition);
void getExtremesSimpl (core::Extremes *extremes);
void sizeAllocateImpl (core::Allocation *allocation);
diff --git a/dw/widget.cc b/dw/widget.cc
index d40eed97..ef4d6b46 100644
--- a/dw/widget.cc
+++ b/dw/widget.cc
@@ -18,6 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+//#define DEBUG_LEVEL 1
#include "core.hh"
#include "../lout/msg.h"
@@ -30,6 +31,15 @@ using namespace lout::misc;
namespace dw {
namespace core {
+/* Used to determine which action to take when correcting the aspect ratio of a
+ * requisition in Widget::correctReqAspectRatio(). */
+enum {
+ PASS_INCREASE = 0,
+ PASS_DECREASE = 1,
+ PASS_KEEP = 2
+};
+
+
// ----------------------------------------------------------------------
bool Widget::WidgetImgRenderer::readyToDraw ()
@@ -99,6 +109,8 @@ Widget::Widget ()
widgetImgRenderer = NULL;
stackingContextMgr = NULL;
+
+ ratio = 0.0;
}
Widget::~Widget ()
@@ -746,6 +758,57 @@ int Widget::getAvailHeight (bool forceValue)
return height;
}
+/**
+ * Corrects a requisition to fit in the viewport.
+ *
+ * Instead of asking the parent widget, it uses the viewport dimensions to
+ * correct the requisition if needed. This is used for the top level widget (the
+ * body) which doesn't have any parent.
+ */
+void Widget::correctRequisitionViewport (Requisition *requisition,
+ void (*splitHeightFun) (int, int *, int *),
+ bool allowDecreaseWidth,
+ bool allowDecreaseHeight)
+{
+ int limitMinWidth = getMinWidth (NULL, true);
+ if (!allowDecreaseWidth && limitMinWidth < requisition->width)
+ limitMinWidth = requisition->width;
+
+ int viewportWidth =
+ layout->viewportWidth - (layout->canvasHeightGreater ?
+ layout->vScrollbarThickness : 0);
+ calcFinalWidth (getStyle (), viewportWidth, NULL, limitMinWidth, false,
+ &requisition->width);
+
+ // For layout->viewportHeight, see comment in getAvailHeight().
+ int height = calcHeight (getStyle()->height, false,
+ layout->viewportHeight, NULL, false);
+ adjustHeight (&height, allowDecreaseHeight, requisition->ascent,
+ requisition->descent);
+
+ int minHeight = calcHeight (getStyle()->minHeight, false,
+ layout->viewportHeight, NULL, false);
+ adjustHeight (&minHeight, allowDecreaseHeight, requisition->ascent,
+ requisition->descent);
+
+ int maxHeight = calcHeight (getStyle()->maxHeight, false,
+ layout->viewportHeight, NULL, false);
+ adjustHeight (&maxHeight, allowDecreaseHeight, requisition->ascent,
+ requisition->descent);
+
+ // TODO Perhaps split first, then add box ascent and descent.
+ if (height != -1)
+ splitHeightFun (height, &requisition->ascent, &requisition->descent);
+ if (minHeight != -1 &&
+ requisition->ascent + requisition->descent < minHeight)
+ splitHeightFun (minHeight, &requisition->ascent,
+ &requisition->descent);
+ if (maxHeight != -1 &&
+ requisition->ascent + requisition->descent > maxHeight)
+ splitHeightFun (maxHeight, &requisition->ascent,
+ &requisition->descent);
+}
+
void Widget::correctRequisition (Requisition *requisition,
void (*splitHeightFun) (int, int *, int *),
bool allowDecreaseWidth,
@@ -763,43 +826,16 @@ void Widget::correctRequisition (Requisition *requisition,
DBG_OBJ_MSG ("resize", 1, "no parent, regarding viewport");
DBG_OBJ_MSG_START ();
- int limitMinWidth = getMinWidth (NULL, true);
- if (!allowDecreaseWidth && limitMinWidth < requisition->width)
- limitMinWidth = requisition->width;
-
- int viewportWidth =
- layout->viewportWidth - (layout->canvasHeightGreater ?
- layout->vScrollbarThickness : 0);
- calcFinalWidth (getStyle (), viewportWidth, NULL, limitMinWidth, false,
- &requisition->width);
+ for (int pass = 0; pass < 3; pass++) {
+ correctRequisitionViewport (requisition, splitHeightFun,
+ allowDecreaseWidth, allowDecreaseHeight);
+ bool changed = correctReqAspectRatio (pass, this, requisition,
+ allowDecreaseWidth, allowDecreaseHeight, splitHeightFun);
- // For layout->viewportHeight, see comment in getAvailHeight().
- int height = calcHeight (getStyle()->height, false,
- layout->viewportHeight, NULL, false);
- adjustHeight (&height, allowDecreaseHeight, requisition->ascent,
- requisition->descent);
-
- int minHeight = calcHeight (getStyle()->minHeight, false,
- layout->viewportHeight, NULL, false);
- adjustHeight (&minHeight, allowDecreaseHeight, requisition->ascent,
- requisition->descent);
-
- int maxHeight = calcHeight (getStyle()->maxHeight, false,
- layout->viewportHeight, NULL, false);
- adjustHeight (&maxHeight, allowDecreaseHeight, requisition->ascent,
- requisition->descent);
-
- // TODO Perhaps split first, then add box ascent and descent.
- if (height != -1)
- splitHeightFun (height, &requisition->ascent, &requisition->descent);
- if (minHeight != -1 &&
- requisition->ascent + requisition->descent < minHeight)
- splitHeightFun (minHeight, &requisition->ascent,
- &requisition->descent);
- if (maxHeight != -1 &&
- requisition->ascent + requisition->descent > maxHeight)
- splitHeightFun (maxHeight, &requisition->ascent,
- &requisition->descent);
+ /* No need to repeat more passes */
+ if (!changed)
+ break;
+ }
DBG_OBJ_MSG_END ();
} else if (parent) {
@@ -1859,9 +1895,18 @@ void Widget::correctRequisitionOfChild (Widget *child, Requisition *requisition,
(child->container ? child->container : child->parent);
if (effContainer == this) {
- correctReqWidthOfChild (child, requisition, allowDecreaseWidth);
- correctReqHeightOfChild (child, requisition, splitHeightFun,
- allowDecreaseHeight);
+ /* Try several passes before giving up */
+ for (int pass = 0; pass < 3; pass++) {
+ correctReqWidthOfChild (child, requisition, allowDecreaseWidth);
+ correctReqHeightOfChild (child, requisition, splitHeightFun,
+ allowDecreaseHeight);
+ bool changed = correctReqAspectRatio (pass, child, requisition,
+ allowDecreaseWidth, allowDecreaseHeight, splitHeightFun);
+
+ /* No need to repeat more passes */
+ if (!changed)
+ break;
+ }
} else {
DBG_OBJ_MSG ("resize", 1, "delegated to (effective) container");
DBG_OBJ_MSG_START ();
@@ -1876,10 +1921,101 @@ void Widget::correctRequisitionOfChild (Widget *child, Requisition *requisition,
requisition->descent);
}
-void Widget::setReqWidth (Requisition *requisition, int width)
-{
- /* Naive implementation */
- requisition->width = width;
+/**
+ * Correct a child requisition aspect ratio if needed.
+ *
+ * After the parent widget corrects the requisition provided by the \param
+ * child, the preferred aspect ratio may no longer be the current ratio. This
+ * method tries to adjust the size of the requisition so the aspect ratio is the
+ * preferred aspect ratio of the child.
+ *
+ * It implements three passes: increase (0), decrease (1) or keep (2). In the
+ * increase pass, the size is increased to fill the aspect ratio. If after
+ * correcting the size, it is still not the preferred aspect ratio (maybe it
+ * breaks some other constraint), reducing the size will be attempted. If at the
+ * end, reducing the size doesn't fix the preferred aspect ratio, the size is
+ * kept as it is.
+ *
+ * It can be called from the parent or the child itself, as it doesn't read any
+ * information from the current widget.
+ *
+ * \return true if the requisition has been modified, false otherwise.
+ */
+bool Widget::correctReqAspectRatio (int pass, Widget *child, Requisition *requisition,
+ bool allowDecreaseWidth, bool allowDecreaseHeight,
+ void (*splitHeightFun) (int, int*, int*))
+{
+ /* Only correct the requisition if both dimensions are set, otherwise is left
+ * to the child how to proceed. */
+ int wReq = requisition->width;
+ int hReq = requisition->ascent + requisition->descent;
+
+ /* Prevent division by 0 */
+ bool sizeSet = wReq > 0 && hReq > 0;
+
+ float ratio = child->ratio;
+ bool changed = false;
+
+ DEBUG_MSG(1, "Widget::correctReqAspectRatio() -- wReq=%d, hReq=%d, pass=%d\n",
+ wReq, hReq, pass);
+ DEBUG_MSG(1, "child=%s, preferred ratio=%f\n", child->getClassName(), ratio);
+
+ if (pass != PASS_KEEP && ratio > 0.0 && sizeSet) {
+ /* TODO: Ensure we are dealing with the content box rather than the
+ * margin box (as in the CSS box model). */
+
+ /* Compute the current ratio from the content box. */
+ float curRatio = (float) wReq / (float) hReq;
+ DEBUG_MSG(1, "curRatio=%f, preferred ratio=%f\n", curRatio, ratio);
+
+ if (curRatio < ratio) {
+ /* W is too small compared to H. Try to increase W or decrease H. */
+ if (pass == PASS_INCREASE) {
+ /* Increase w */
+ int w = (float) hReq * ratio;
+ requisition->width = w;
+ changed = true;
+ DEBUG_MSG(1, "increase w: %d -> %d\n", wReq, w);
+ } else if (pass == PASS_DECREASE) {
+ /* Decrease h */
+ if (allowDecreaseHeight) {
+ /* FIXME: This may lose cases where allowDecreaseHeight is false, and
+ * the requisition has increased the height first, but we could still
+ * reduce the corrected hight above the original height, without
+ * making the requisition height smaller. */
+ int h = (float) wReq / ratio;
+ splitHeightFun (h, &requisition->ascent, &requisition->descent);
+ changed = true;
+ DEBUG_MSG(1, "decrease h: %d -> %d\n", hReq, h);
+ }
+ }
+ } else if (curRatio > ratio) {
+ /* W is too big compared to H. Try to decrease W or increase H. */
+ if (pass == PASS_INCREASE) {
+ /* Increase h */
+ int h = (float) wReq / ratio;
+ splitHeightFun (h, &requisition->ascent, &requisition->descent);
+ changed = true;
+ DEBUG_MSG(1, "increase h: %d -> %d\n", hReq, h);
+ } else if (pass == PASS_DECREASE) {
+ /* Decrease w */
+ if (allowDecreaseWidth) {
+ /* FIXME: This may lose cases where allowDecreaseWidth is false, and
+ * the requisition has increased the width first, but we could still
+ * reduce the corrected width above the original width, without
+ * making the requisition width smaller. */
+ int w = (float) hReq * ratio;
+ requisition->width = w;
+ changed = true;
+ DEBUG_MSG(1, "decrease w: %d -> %d\n", wReq, w);
+ }
+ }
+ } else {
+ /* Current ratio is the preferred one. */
+ }
+ }
+
+ return changed;
}
/** Correct a child requisition to fit the parent.
@@ -1901,14 +2037,8 @@ void Widget::correctReqWidthOfChild (Widget *child, Requisition *requisition,
if (!allowDecreaseWidth && limitMinWidth < requisition->width)
limitMinWidth = requisition->width;
- int width = requisition->width;
child->calcFinalWidth (child->getStyle(), -1, this, limitMinWidth, true,
- &width);
-
-
- /* Ask the child widget to adjust its own requisition in case it has to
- * modify the height too (like images to try to preserve the aspect ratio) */
- child->setReqWidth(requisition, width);
+ &requisition->width);
DBG_OBJ_LEAVE_VAL ("%d * (%d + %d)", requisition->width, requisition->ascent,
requisition->descent);
diff --git a/dw/widget.hh b/dw/widget.hh
index a7daa91d..8631b9db 100644
--- a/dw/widget.hh
+++ b/dw/widget.hh
@@ -207,6 +207,12 @@ protected:
inline int getContentWidth() { return allocation.width - boxDiffWidth (); }
inline int getContentHeight() { return getHeight () - boxDiffHeight (); }
+ /**
+ * Preferred aspect ratio of the widget. Set to 0 when there is none. It is
+ * computed as width / height.
+ */
+ float ratio;
+
Layout *layout;
/**
@@ -351,18 +357,24 @@ protected:
virtual int getAvailWidthOfChild (Widget *child, bool forceValue);
virtual int getAvailHeightOfChild (Widget *child, bool forceValue);
- virtual void setReqWidth (Requisition *requisition, int width);
+
virtual void correctRequisitionOfChild (Widget *child,
Requisition *requisition,
void (*splitHeightFun) (int, int*,
int*),
bool allowDecreaseWidth,
bool allowDecreaseHeight);
+ void correctRequisitionViewport (Requisition *requisition,
+ void (*splitHeightFun) (int, int*, int*),
+ bool allowDecreaseWidth, bool allowDecreaseHeight);
void correctReqWidthOfChild (Widget *child, Requisition *requisition,
bool allowDecreaseWidth);
void correctReqHeightOfChild (Widget *child, Requisition *requisition,
void (*splitHeightFun) (int, int*, int*),
bool allowDecreaseHeight);
+ bool correctReqAspectRatio (int pass, Widget *child, Requisition *requisition,
+ bool allowDecreaseWidth, bool allowDecreaseHeight,
+ void (*splitHeightFun) (int, int*, int*));
virtual void correctExtremesOfChild (Widget *child, Extremes *extremes,
bool useAdjustmentWidth);