1329 lines
39 KiB
C++
1329 lines
39 KiB
C++
/*
|
|
* Dillo Widget
|
|
*
|
|
* Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
|
|
* Copyright 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
|
|
*
|
|
* 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 "core.hh"
|
|
|
|
#include "dlib/dlib.hh"
|
|
#include "../lout/msg.h"
|
|
#include "../lout/debug.hh"
|
|
#include "../lout/misc.hh"
|
|
|
|
using namespace lout;
|
|
using namespace lout::container;
|
|
using namespace lout::object;
|
|
|
|
namespace dw {
|
|
namespace core {
|
|
|
|
bool Layout::LayoutImgRenderer::readyToDraw ()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void Layout::LayoutImgRenderer::getBgArea (int *x, int *y, int *width,
|
|
int *height)
|
|
{
|
|
// TODO Actually not padding area, but visible area?
|
|
getRefArea (x, y, width, height);
|
|
}
|
|
|
|
void Layout::LayoutImgRenderer::getRefArea (int *xRef, int *yRef, int *widthRef,
|
|
int *heightRef)
|
|
{
|
|
*xRef = 0;
|
|
*yRef = 0;
|
|
*widthRef = std::max (layout->viewportWidth
|
|
- (layout->canvasHeightGreater ?
|
|
layout->vScrollbarThickness : 0),
|
|
layout->canvasWidth);
|
|
*heightRef = std::max (layout->viewportHeight
|
|
- layout->hScrollbarThickness,
|
|
layout->canvasAscent + layout->canvasDescent);
|
|
}
|
|
|
|
style::StyleImage *Layout::LayoutImgRenderer::getBackgroundImage ()
|
|
{
|
|
return layout->bgImage;
|
|
}
|
|
|
|
style::BackgroundRepeat Layout::LayoutImgRenderer::getBackgroundRepeat ()
|
|
{
|
|
return layout->bgRepeat;
|
|
}
|
|
|
|
style::BackgroundAttachment
|
|
Layout::LayoutImgRenderer::getBackgroundAttachment ()
|
|
{
|
|
return layout->bgAttachment;
|
|
}
|
|
|
|
style::Length Layout::LayoutImgRenderer::getBackgroundPositionX ()
|
|
{
|
|
return layout->bgPositionX;
|
|
}
|
|
|
|
style::Length Layout::LayoutImgRenderer::getBackgroundPositionY ()
|
|
{
|
|
return layout->bgPositionY;
|
|
}
|
|
|
|
void Layout::LayoutImgRenderer::draw (int x, int y, int width, int height)
|
|
{
|
|
layout->queueDraw (x, y, width, height);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void Layout::Receiver::resizeQueued (bool extremesChanged)
|
|
{
|
|
}
|
|
|
|
void Layout::Receiver::canvasSizeChanged (int width, int ascent, int descent)
|
|
{
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
bool Layout::Emitter::emitToReceiver (lout::signal::Receiver *receiver,
|
|
int signalNo, int argc,
|
|
lout::object::Object **argv)
|
|
{
|
|
Receiver *layoutReceiver = (Receiver*)receiver;
|
|
|
|
switch (signalNo) {
|
|
case CANVAS_SIZE_CHANGED:
|
|
layoutReceiver->canvasSizeChanged (((Integer*)argv[0])->getValue (),
|
|
((Integer*)argv[1])->getValue (),
|
|
((Integer*)argv[2])->getValue ());
|
|
break;
|
|
|
|
case RESIZE_QUEUED:
|
|
layoutReceiver->resizeQueued (((Boolean*)argv[0])->getValue ());
|
|
break;
|
|
|
|
default:
|
|
misc::assertNotReached ();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void Layout::Emitter::emitResizeQueued (bool extremesChanged)
|
|
{
|
|
Boolean ec (extremesChanged);
|
|
Object *argv[1] = { &ec };
|
|
emitVoid (RESIZE_QUEUED, 1, argv);
|
|
}
|
|
|
|
void Layout::Emitter::emitCanvasSizeChanged (int width,
|
|
int ascent, int descent)
|
|
{
|
|
Integer w (width), a (ascent), d (descent);
|
|
Object *argv[3] = { &w, &a, &d };
|
|
emitVoid (CANVAS_SIZE_CHANGED, 3, argv);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
bool Layout::LinkReceiver::enter (Widget *widget, int link, int img,
|
|
int x, int y)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Layout::LinkReceiver::press (Widget *widget, int link, int img,
|
|
int x, int y, EventButton *event)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Layout::LinkReceiver::release (Widget *widget, int link, int img,
|
|
int x, int y, EventButton *event)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Layout::LinkReceiver::click (Widget *widget, int link, int img,
|
|
int x, int y, EventButton *event)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
bool Layout::LinkEmitter::emitToReceiver (lout::signal::Receiver *receiver,
|
|
int signalNo, int argc,
|
|
lout::object::Object **argv)
|
|
{
|
|
LinkReceiver *linkReceiver = (LinkReceiver*)receiver;
|
|
|
|
switch (signalNo) {
|
|
case ENTER:
|
|
return linkReceiver->enter ((Widget*)argv[0],
|
|
((Integer*)argv[1])->getValue (),
|
|
((Integer*)argv[2])->getValue (),
|
|
((Integer*)argv[3])->getValue (),
|
|
((Integer*)argv[4])->getValue ());
|
|
|
|
case PRESS:
|
|
return linkReceiver->press ((Widget*)argv[0],
|
|
((Integer*)argv[1])->getValue (),
|
|
((Integer*)argv[2])->getValue (),
|
|
((Integer*)argv[3])->getValue (),
|
|
((Integer*)argv[4])->getValue (),
|
|
(EventButton*)argv[5]);
|
|
|
|
case RELEASE:
|
|
return linkReceiver->release ((Widget*)argv[0],
|
|
((Integer*)argv[1])->getValue (),
|
|
((Integer*)argv[2])->getValue (),
|
|
((Integer*)argv[3])->getValue (),
|
|
((Integer*)argv[4])->getValue (),
|
|
(EventButton*)argv[5]);
|
|
|
|
case CLICK:
|
|
return linkReceiver->click ((Widget*)argv[0],
|
|
((Integer*)argv[1])->getValue (),
|
|
((Integer*)argv[2])->getValue (),
|
|
((Integer*)argv[3])->getValue (),
|
|
((Integer*)argv[4])->getValue (),
|
|
(EventButton*)argv[5]);
|
|
|
|
default:
|
|
misc::assertNotReached ();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Layout::LinkEmitter::emitEnter (Widget *widget, int link, int img,
|
|
int x, int y)
|
|
{
|
|
Integer ilink (link), iimg (img), ix (x), iy (y);
|
|
Object *argv[5] = { widget, &ilink, &iimg, &ix, &iy };
|
|
return emitBool (ENTER, 5, argv);
|
|
}
|
|
|
|
bool Layout::LinkEmitter::emitPress (Widget *widget, int link, int img,
|
|
int x, int y, EventButton *event)
|
|
{
|
|
Integer ilink (link), iimg (img), ix (x), iy (y);
|
|
Object *argv[6] = { widget, &ilink, &iimg, &ix, &iy, event };
|
|
return emitBool (PRESS, 6, argv);
|
|
}
|
|
|
|
bool Layout::LinkEmitter::emitRelease (Widget *widget, int link, int img,
|
|
int x, int y, EventButton *event)
|
|
{
|
|
Integer ilink (link), iimg (img), ix (x), iy (y);
|
|
Object *argv[6] = { widget, &ilink, &iimg, &ix, &iy, event };
|
|
return emitBool (RELEASE, 6, argv);
|
|
}
|
|
|
|
bool Layout::LinkEmitter::emitClick (Widget *widget, int link, int img,
|
|
int x, int y, EventButton *event)
|
|
{
|
|
Integer ilink (link), iimg (img), ix (x), iy (y);
|
|
Object *argv[6] = { widget, &ilink, &iimg, &ix, &iy, event };
|
|
return emitBool (CLICK, 6, argv);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
Layout::Layout (std::unique_ptr< Platform > platform, bool limit)
|
|
{
|
|
this->platform = std::move( platform );
|
|
view = NULL;
|
|
topLevel = NULL;
|
|
widgetAtPoint = NULL;
|
|
|
|
DBG_OBJ_CREATE ("dw::core::Layout");
|
|
|
|
bgColor = NULL;
|
|
bgImage = NULL;
|
|
cursor = style::CURSOR_DEFAULT;
|
|
|
|
canvasWidth = canvasAscent = canvasDescent = 0;
|
|
|
|
usesViewport = false;
|
|
drawAfterScrollReq = false;
|
|
scrollX = scrollY = 0;
|
|
viewportWidth = viewportHeight = 0;
|
|
hScrollbarThickness = vScrollbarThickness = 0;
|
|
|
|
DBG_OBJ_SET_NUM ("viewportWidth", viewportWidth);
|
|
DBG_OBJ_SET_NUM ("viewportHeight", viewportHeight);
|
|
DBG_OBJ_SET_NUM ("hScrollbarThickness", hScrollbarThickness);
|
|
DBG_OBJ_SET_NUM ("vScrollbarThickness", vScrollbarThickness);
|
|
|
|
scrollIdleId = -1;
|
|
scrollIdleNotInterrupted = false;
|
|
|
|
resizeIdleId = -1;
|
|
|
|
textZone = new misc::ZoneAllocator (16 * 1024);
|
|
|
|
DBG_OBJ_ASSOC_CHILD (&findtextState);
|
|
DBG_OBJ_ASSOC_CHILD (&selectionState);
|
|
|
|
this->platform->setLayout (this);
|
|
|
|
selectionState.setLayout(this);
|
|
|
|
queueResizeCounter = sizeAllocateCounter = sizeRequestCounter =
|
|
getExtremesCounter = 0;
|
|
|
|
layoutImgRenderer = NULL;
|
|
|
|
resizeIdleCounter = queueResizeCounter = sizeAllocateCounter
|
|
= sizeRequestCounter = getExtremesCounter = resizeCounter = 0;
|
|
resizeLimit = limit;
|
|
}
|
|
|
|
Layout::~Layout ()
|
|
{
|
|
widgetAtPoint = NULL;
|
|
|
|
if (layoutImgRenderer) {
|
|
if (bgImage)
|
|
bgImage->removeExternalImgRenderer (layoutImgRenderer);
|
|
delete layoutImgRenderer;
|
|
}
|
|
|
|
if (scrollIdleId != -1)
|
|
platform->removeIdle (scrollIdleId);
|
|
if (resizeIdleId != -1)
|
|
platform->removeIdle (resizeIdleId);
|
|
if (bgColor)
|
|
bgColor->unref ();
|
|
if (bgImage)
|
|
bgImage->unref ();
|
|
if (topLevel) {
|
|
detachWidget (topLevel.get());
|
|
topLevel.reset();
|
|
}
|
|
|
|
delete view;
|
|
delete textZone;
|
|
|
|
DBG_OBJ_DELETE ();
|
|
}
|
|
|
|
void Layout::detachWidget (Widget *widget)
|
|
{
|
|
// Called form ~Layout. Sometimes, the widgets (not only the toplevel widget)
|
|
// do some stuff after the layout has been deleted, so *all* widgets have to
|
|
// be detached, and check "layout != NULL" at relevant points.
|
|
|
|
// Could be replaced by a virtual method in Widget, like getWidgetAtPoint,
|
|
// if performance were really a problem.
|
|
|
|
widget->layout = NULL;
|
|
Iterator *it =
|
|
widget->iterator ((Content::Type)
|
|
(Content::WIDGET_IN_FLOW | Content::WIDGET_OOF_CONT),
|
|
false);
|
|
while (it->next ())
|
|
detachWidget (it->getContent()->widget);
|
|
|
|
it->unref ();
|
|
}
|
|
|
|
void Layout::addWidget (std::unique_ptr< Widget > widget)
|
|
{
|
|
if (topLevel) {
|
|
MSG_WARN("widget already set\n");
|
|
return;
|
|
}
|
|
|
|
// The toplevel widget always establishes a stacking context. It could
|
|
// already be set in Widget::setStyle().
|
|
if (widget->stackingContextMgr == NULL && IMPL_POS) {
|
|
widget->stackingContextMgr = new StackingContextMgr (widget.get());
|
|
DBG_OBJ_ASSOC (widget.get(), widget->stackingContextMgr);
|
|
widget->stackingContextWidget = widget.get();
|
|
}
|
|
|
|
Widget *widget_p= widget.get();
|
|
topLevel = std::move( widget );
|
|
widget_p->layout = this;
|
|
widget_p->container = NULL;
|
|
DBG_OBJ_SET_PTR_O (widget_p, "container", widget_p->container);
|
|
|
|
queueResizeList.clear ();
|
|
widget_p->notifySetAsTopLevel ();
|
|
|
|
findtextState.setWidget (widget_p);
|
|
|
|
canvasHeightGreater = false;
|
|
DBG_OBJ_SET_SYM ("canvasHeightGreater",
|
|
canvasHeightGreater ? "true" : "false");
|
|
|
|
// Do not directly call Layout::queueResize(), but
|
|
// Widget::queueResize(), so that all flags are set properly,
|
|
// queueResizeList is filled, etc.
|
|
topLevel->queueResize (-1, false);
|
|
}
|
|
|
|
void Layout::removeWidget ()
|
|
{
|
|
/**
|
|
* \bug Some more attributes must be reset here.
|
|
*/
|
|
topLevel = NULL;
|
|
queueResizeList.clear ();
|
|
widgetAtPoint = NULL;
|
|
canvasWidth = canvasAscent = canvasDescent = 0;
|
|
scrollX = scrollY = 0;
|
|
|
|
view->setCanvasSize (canvasWidth, canvasAscent, canvasDescent);
|
|
if (view->usesViewport ())
|
|
view->setViewportSize (viewportWidth, viewportHeight, 0, 0);
|
|
view->queueDrawTotal ();
|
|
|
|
setAnchor (std::nullopt);
|
|
updateAnchor ();
|
|
|
|
emitter.emitCanvasSizeChanged (canvasWidth, canvasAscent, canvasDescent);
|
|
|
|
findtextState.setWidget (NULL);
|
|
selectionState.reset ();
|
|
|
|
updateCursor ();
|
|
}
|
|
|
|
void Layout::setWidget (std::unique_ptr< Widget > widget)
|
|
{
|
|
DBG_OBJ_ASSOC_CHILD (widget.get());
|
|
|
|
widgetAtPoint = NULL;
|
|
if (topLevel) {
|
|
topLevel.reset();
|
|
}
|
|
textZone->zoneFree ();
|
|
addWidget (std::move( widget ));
|
|
|
|
updateCursor ();
|
|
|
|
/* Reset the resizeCounter when we change the top level widget, as we are
|
|
* changing to another page */
|
|
resizeCounter = 0;
|
|
}
|
|
|
|
/**
|
|
* \brief Attach a view to the layout.
|
|
*
|
|
* It will become a child of the layout,
|
|
* and so it will be destroyed, when the layout will be destroyed.
|
|
*/
|
|
void Layout::attachView (View *view)
|
|
{
|
|
if (this->view)
|
|
MSG_ERR("attachView: Multiple views for layout!\n");
|
|
|
|
DBG_OBJ_ASSOC_CHILD (view);
|
|
|
|
this->view = view;
|
|
platform->attachView (view);
|
|
|
|
/*
|
|
* The layout of the view is set later, first, we "project" the current
|
|
* state of the layout into the new view. A view must handle this without
|
|
* a layout. See also at the end of this function.
|
|
*/
|
|
if (bgColor)
|
|
view->setBgColor (bgColor);
|
|
view->setCursor (cursor);
|
|
view->setCanvasSize (canvasWidth, canvasAscent, canvasDescent);
|
|
|
|
if (view->usesViewport ()) {
|
|
if (usesViewport) {
|
|
view->scrollTo (scrollX, scrollY);
|
|
view->setViewportSize (viewportWidth, viewportHeight,
|
|
hScrollbarThickness, vScrollbarThickness);
|
|
hScrollbarThickness = std::max (hScrollbarThickness,
|
|
view->getHScrollbarThickness ());
|
|
vScrollbarThickness = std::max (vScrollbarThickness,
|
|
view->getVScrollbarThickness ());
|
|
}
|
|
else {
|
|
usesViewport = true;
|
|
scrollX = scrollY = 0;
|
|
viewportWidth = viewportHeight = 100; // random values
|
|
hScrollbarThickness = view->getHScrollbarThickness ();
|
|
vScrollbarThickness = view->getVScrollbarThickness ();
|
|
}
|
|
|
|
DBG_OBJ_SET_NUM ("viewportWidth", viewportWidth);
|
|
DBG_OBJ_SET_NUM ("viewportHeight", viewportHeight);
|
|
DBG_OBJ_SET_NUM ("hScrollbarThickness", hScrollbarThickness);
|
|
DBG_OBJ_SET_NUM ("vScrollbarThickness", vScrollbarThickness);
|
|
}
|
|
|
|
/*
|
|
* This is the last call within this function, so that it is safe for
|
|
* the implementation of dw::core::View::setLayout, to call methods
|
|
* of dw::core::Layout.
|
|
*/
|
|
view->setLayout (this);
|
|
}
|
|
|
|
void Layout::detachView (View *view)
|
|
{
|
|
if (this->view != view) {
|
|
MSG_ERR("detachView: this->view: %p view %p\n",
|
|
(void *) this->view, (void *) view);
|
|
}
|
|
|
|
view->setLayout (NULL);
|
|
platform->detachView (view);
|
|
this->view = NULL;
|
|
/**
|
|
* \todo Actually, viewportMarkerWidthDiff and
|
|
* viewportMarkerHeightDiff have to be recalculated here, since the
|
|
* effective (i.e. maximal) values may change, after the view has been
|
|
* detached. Same applies to the usage of viewports.
|
|
*/
|
|
}
|
|
|
|
void Layout::scroll(ScrollCommand cmd)
|
|
{
|
|
if (view->usesViewport ())
|
|
view->scroll(cmd);
|
|
}
|
|
|
|
/**
|
|
* \brief Scrolls all viewports, so that the region [x, y, width, height]
|
|
* is seen, according to hpos and vpos.
|
|
*/
|
|
void Layout::scrollTo (HPosition hpos, VPosition vpos,
|
|
int x, int y, int width, int height)
|
|
{
|
|
scrollTo0 (hpos, vpos, x, y, width, height, true);
|
|
}
|
|
|
|
void Layout::scrollTo0 (HPosition hpos, VPosition vpos,
|
|
int x, int y, int width, int height,
|
|
bool scrollingInterrupted)
|
|
{
|
|
if (usesViewport) {
|
|
_MSG("scrollTo (%d, %d, %s)\n",
|
|
x, y, scrollingInterrupted ? "true" : "false");
|
|
|
|
scrollTargetHpos = hpos;
|
|
scrollTargetVpos = vpos;
|
|
scrollTargetX = x;
|
|
scrollTargetY = y;
|
|
scrollTargetWidth = width;
|
|
scrollTargetHeight = height;
|
|
|
|
if (scrollIdleId == -1) {
|
|
scrollIdleId = platform->addIdle (&Layout::scrollIdle);
|
|
scrollIdleNotInterrupted = true;
|
|
}
|
|
|
|
scrollIdleNotInterrupted =
|
|
scrollIdleNotInterrupted || !scrollingInterrupted;
|
|
}
|
|
}
|
|
|
|
void Layout::scrollIdle ()
|
|
{
|
|
bool xChanged = true;
|
|
switch (scrollTargetHpos) {
|
|
case HPOS_LEFT:
|
|
scrollX = scrollTargetX;
|
|
break;
|
|
case HPOS_CENTER:
|
|
scrollX =
|
|
scrollTargetX
|
|
- (viewportWidth - currVScrollbarThickness() - scrollTargetWidth) / 2;
|
|
break;
|
|
case HPOS_RIGHT:
|
|
scrollX =
|
|
scrollTargetX
|
|
- (viewportWidth - currVScrollbarThickness() - scrollTargetWidth);
|
|
break;
|
|
case HPOS_INTO_VIEW:
|
|
xChanged = calcScrollInto (scrollTargetX, scrollTargetWidth, &scrollX,
|
|
viewportWidth - currVScrollbarThickness());
|
|
break;
|
|
case HPOS_NO_CHANGE:
|
|
xChanged = false;
|
|
break;
|
|
}
|
|
|
|
bool yChanged = true;
|
|
switch (scrollTargetVpos) {
|
|
case VPOS_TOP:
|
|
scrollY = scrollTargetY;
|
|
break;
|
|
case VPOS_CENTER:
|
|
scrollY =
|
|
scrollTargetY
|
|
- (viewportHeight - currHScrollbarThickness() - scrollTargetHeight)/2;
|
|
break;
|
|
case VPOS_BOTTOM:
|
|
scrollY =
|
|
scrollTargetY
|
|
- (viewportHeight - currHScrollbarThickness() - scrollTargetHeight);
|
|
break;
|
|
case VPOS_INTO_VIEW:
|
|
yChanged = calcScrollInto (scrollTargetY, scrollTargetHeight, &scrollY,
|
|
viewportHeight - currHScrollbarThickness());
|
|
break;
|
|
case VPOS_NO_CHANGE:
|
|
yChanged = false;
|
|
break;
|
|
}
|
|
|
|
if (xChanged || yChanged) {
|
|
adjustScrollPos ();
|
|
view->scrollTo (scrollX, scrollY);
|
|
if (drawAfterScrollReq) {
|
|
drawAfterScrollReq = false;
|
|
view->queueDrawTotal ();
|
|
}
|
|
}
|
|
|
|
scrollIdleId = -1;
|
|
}
|
|
|
|
void Layout::adjustScrollPos ()
|
|
{
|
|
scrollX = std::min (scrollX,
|
|
canvasWidth - (viewportWidth - vScrollbarThickness));
|
|
scrollX = std::max (scrollX, 0);
|
|
|
|
scrollY = std::min (scrollY,
|
|
canvasAscent + canvasDescent - (viewportHeight - hScrollbarThickness));
|
|
scrollY = std::max (scrollY, 0);
|
|
|
|
_MSG("adjustScrollPos: scrollX=%d scrollY=%d\n", scrollX, scrollY);
|
|
}
|
|
|
|
bool Layout::calcScrollInto (int requestedValue, int requestedSize,
|
|
int *value, int viewportSize)
|
|
{
|
|
if (requestedSize > viewportSize) {
|
|
// The viewport size is smaller than the size of the region which will
|
|
// be shown. If the region is already visible, do not change the
|
|
// position. Otherwise, show the left/upper border, this is most likely
|
|
// what is needed.
|
|
if (*value >= requestedValue &&
|
|
*value + viewportSize < requestedValue + requestedSize)
|
|
return false;
|
|
else
|
|
requestedSize = viewportSize;
|
|
}
|
|
|
|
if (requestedValue < *value) {
|
|
*value = requestedValue;
|
|
return true;
|
|
} else if (requestedValue + requestedSize > *value + viewportSize) {
|
|
*value = requestedValue - viewportSize + requestedSize;
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
void Layout::draw (View *view, Rectangle *area)
|
|
{
|
|
DBG_OBJ_ENTER ("draw", 0, "draw", "%d, %d, %d * %d",
|
|
area->x, area->y, area->width, area->height);
|
|
|
|
Rectangle widgetArea, intersection, widgetDrawArea;
|
|
|
|
// First of all, draw background image. (Unlike background *color*,
|
|
// this is not a feature of the views.)
|
|
if (bgImage != NULL && bgImage->getImgbufSrc() != NULL)
|
|
style::drawBackgroundImage (view, bgImage, bgRepeat, bgAttachment,
|
|
bgPositionX, bgPositionY,
|
|
area->x, area->y, area->width,
|
|
area->height, 0, 0,
|
|
// Reference area: maximum of canvas size and
|
|
// viewport size.
|
|
std::max (viewportWidth
|
|
- (canvasHeightGreater ?
|
|
vScrollbarThickness : 0),
|
|
canvasWidth),
|
|
std::max (viewportHeight
|
|
- hScrollbarThickness,
|
|
canvasAscent + canvasDescent));
|
|
|
|
if (scrollIdleId != -1) {
|
|
/* scroll is pending, defer draw until after scrollIdle() */
|
|
drawAfterScrollReq = true;
|
|
|
|
} else if (topLevel) {
|
|
/* Draw the top level widget. */
|
|
widgetArea.x = topLevel->allocation.x;
|
|
widgetArea.y = topLevel->allocation.y;
|
|
widgetArea.width = topLevel->allocation.width;
|
|
widgetArea.height = topLevel->getHeight ();
|
|
|
|
if (area->intersectsWith (&widgetArea, &intersection)) {
|
|
view->startDrawing (&intersection);
|
|
|
|
/* Intersection in widget coordinates. */
|
|
widgetDrawArea.x = intersection.x - topLevel->allocation.x;
|
|
widgetDrawArea.y = intersection.y - topLevel->allocation.y;
|
|
widgetDrawArea.width = intersection.width;
|
|
widgetDrawArea.height = intersection.height;
|
|
|
|
DrawingContext context (&widgetArea);
|
|
topLevel->draw (view, &widgetDrawArea, &context);
|
|
|
|
view->finishDrawing (&intersection);
|
|
}
|
|
}
|
|
|
|
DBG_OBJ_LEAVE ();
|
|
}
|
|
|
|
int Layout::currHScrollbarThickness()
|
|
{
|
|
return (canvasWidth > viewportWidth) ? hScrollbarThickness : 0;
|
|
}
|
|
|
|
int Layout::currVScrollbarThickness()
|
|
{
|
|
return (canvasAscent + canvasDescent > viewportHeight) ?
|
|
vScrollbarThickness : 0;
|
|
}
|
|
|
|
/**
|
|
* Sets the anchor to scroll to.
|
|
*/
|
|
void Layout::setAnchor (const std::optional< std::string_view > anchor)
|
|
{
|
|
_MSG("setAnchor (%s)\n", anchor.value_or("").string().c_str());
|
|
|
|
requestedAnchor = anchor;
|
|
updateAnchor ();
|
|
}
|
|
|
|
/**
|
|
* Used, when the widget is not allocated yet.
|
|
*/
|
|
std::optional< std::string >
|
|
Layout::addAnchor (Widget *widget, const std::string &name)
|
|
{
|
|
return addAnchor (widget, name, -1);
|
|
}
|
|
|
|
std::optional< std::string >
|
|
Layout::addAnchor (Widget *widget, const std::string &name, int y)
|
|
{
|
|
if (anchorsTable.contains (name))
|
|
return std::nullopt;
|
|
else {
|
|
auto anchor = std::make_unique< Anchor >();
|
|
std::string rv= anchor->name= name;
|
|
anchor->widget = widget;
|
|
anchor->y = y;
|
|
|
|
anchorsTable[ name ]= std::move( anchor );
|
|
updateAnchor ();
|
|
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
void Layout::changeAnchor (Widget *widget, const std::string &name, int y)
|
|
{
|
|
Anchor *const anchor = anchorsTable.at( name ).get();
|
|
assert (anchor);
|
|
assert (anchor->widget == widget);
|
|
anchor->y = y;
|
|
updateAnchor ();
|
|
}
|
|
|
|
void Layout::removeAnchor (Widget *widget, const std::string &name)
|
|
{
|
|
anchorsTable.erase( name );
|
|
}
|
|
|
|
void Layout::updateAnchor ()
|
|
{
|
|
Anchor *anchor= nullptr;
|
|
if (requestedAnchor and anchorsTable.contains( requestedAnchor.value() ) )
|
|
{
|
|
anchor= anchorsTable[ requestedAnchor.value() ].get();
|
|
}
|
|
|
|
if (anchor == NULL) {
|
|
/** \todo Copy comment from old docs. */
|
|
if (scrollIdleId != -1 && !scrollIdleNotInterrupted) {
|
|
platform->removeIdle (scrollIdleId);
|
|
scrollIdleId = -1;
|
|
}
|
|
} else
|
|
if (anchor->y != -1)
|
|
scrollTo0 (HPOS_NO_CHANGE, VPOS_TOP, 0, anchor->y, 0, 0, false);
|
|
}
|
|
|
|
void Layout::setCursor (style::Cursor cursor)
|
|
{
|
|
if (cursor != this->cursor) {
|
|
this->cursor = cursor;
|
|
view->setCursor (cursor);
|
|
}
|
|
}
|
|
|
|
void Layout::updateCursor ()
|
|
{
|
|
if (widgetAtPoint && widgetAtPoint->style)
|
|
setCursor (widgetAtPoint->style->cursor);
|
|
else
|
|
setCursor (style::CURSOR_DEFAULT);
|
|
}
|
|
|
|
void Layout::setBgColor (style::Color *color)
|
|
{
|
|
color->ref ();
|
|
|
|
if (bgColor)
|
|
bgColor->unref ();
|
|
|
|
bgColor = color;
|
|
|
|
if (view)
|
|
view->setBgColor (bgColor);
|
|
}
|
|
|
|
void Layout::setBgImage (style::StyleImage *bgImage,
|
|
style::BackgroundRepeat bgRepeat,
|
|
style::BackgroundAttachment bgAttachment,
|
|
style::Length bgPositionX, style::Length bgPositionY)
|
|
{
|
|
if (layoutImgRenderer && this->bgImage)
|
|
this->bgImage->removeExternalImgRenderer (layoutImgRenderer);
|
|
|
|
if (bgImage)
|
|
bgImage->ref ();
|
|
|
|
if (this->bgImage)
|
|
this->bgImage->unref ();
|
|
|
|
this->bgImage = bgImage;
|
|
this->bgRepeat = bgRepeat;
|
|
this->bgAttachment = bgAttachment;
|
|
this->bgPositionX = bgPositionX;
|
|
this->bgPositionY = bgPositionY;
|
|
|
|
if (bgImage) {
|
|
// Create instance of LayoutImgRenderer when needed. Until this
|
|
// layout is deleted, "layoutImgRenderer" will be kept, since it
|
|
// is not specific to the style, but only to this layout.
|
|
if (layoutImgRenderer == NULL)
|
|
layoutImgRenderer = std::make_unique< LayoutImgRenderer >( this ).release();
|
|
bgImage->putExternalImgRenderer( std::unique_ptr< LayoutImgRenderer >{ layoutImgRenderer } );
|
|
}
|
|
}
|
|
|
|
|
|
void Layout::resizeIdle ()
|
|
{
|
|
DBG_OBJ_ENTER0 ("resize", 0, "resizeIdle");
|
|
|
|
enterResizeIdle ();
|
|
|
|
// There are two commits, 2863:b749629fbfc9 and 4645:ab70f9ce4353, the second
|
|
// reverting the former. Interrestingly, the second fixes a bug. However, it
|
|
// should still examined what happens here, and what happens the other calls
|
|
// to Layout::resizeIdle() which should be still in the queue. (See
|
|
// Layout::queueResize(), where resizeIdleId is indeed checked.)
|
|
|
|
for (int i = 0; resizeIdleId != -1; i++) {
|
|
|
|
/* Prevent infinite resize loop, if we reach this point it is very likely
|
|
* there is a bug in the layouting process */
|
|
if (resizeLimit && resizeCounter >= 1000) {
|
|
MSG_ERR("Emergency layout stop after %d iterations\n", resizeCounter);
|
|
MSG_ERR("Please file a bug report with the complete console output\n");
|
|
resizeIdleId = -1;
|
|
break;
|
|
}
|
|
|
|
/* Only allow 100 iterations before returning to redraw the screen. */
|
|
if (i >= 100) {
|
|
MSG_WARN("Stopping layout loop after %d iterations\n", resizeCounter);
|
|
break;
|
|
}
|
|
|
|
resizeCounter++;
|
|
|
|
for( auto &widget: queueResizeList ) {
|
|
if (widget->resizeQueued ()) {
|
|
widget->setFlags (Widget::NEEDS_RESIZE);
|
|
widget->unsetFlags (Widget::RESIZE_QUEUED);
|
|
}
|
|
|
|
if (widget->allocateQueued ()) {
|
|
widget->setFlags (Widget::NEEDS_ALLOCATE);
|
|
widget->unsetFlags (Widget::ALLOCATE_QUEUED);
|
|
}
|
|
|
|
if (widget->extremesQueued ()) {
|
|
widget->setFlags (Widget::EXTREMES_CHANGED);
|
|
widget->unsetFlags (Widget::EXTREMES_QUEUED);
|
|
}
|
|
}
|
|
queueResizeList.clear ();
|
|
|
|
// Reset here, since below, queueResize() may be called again.
|
|
resizeIdleId = -1;
|
|
|
|
// If this method is triggered by a viewport change, we can save
|
|
// time when the toplevel widget is not affected (as for a toplevel
|
|
// image resource).
|
|
if (topLevel &&
|
|
(topLevel->needsResize () || topLevel->needsAllocate ())) {
|
|
Requisition requisition;
|
|
Allocation allocation;
|
|
|
|
topLevel->sizeRequest (&requisition);
|
|
DBG_OBJ_MSGF ("resize", 1, "toplevel size: %d * (%d + %d)",
|
|
requisition.width, requisition.ascent,
|
|
requisition.descent);
|
|
|
|
// This method is triggered by Widget::queueResize, which will,
|
|
// in any case, set NEEDS_ALLOCATE (indirectly, as ALLOCATE_QUEUED).
|
|
// This assertion helps to find inconsistencies. (Cases where
|
|
// this method is triggered by a viewport change, but the
|
|
// toplevel widget is not affected, are filtered out some lines
|
|
// above: "if (topLevel && topLevel->needsResize ())".)
|
|
assert (topLevel->needsAllocate ());
|
|
|
|
allocation.x = allocation.y = 0;
|
|
if (usesViewport && view->getScrollbarOnLeft())
|
|
allocation.x += currVScrollbarThickness();
|
|
allocation.width = requisition.width;
|
|
allocation.ascent = requisition.ascent;
|
|
allocation.descent = requisition.descent;
|
|
topLevel->sizeAllocate (&allocation);
|
|
|
|
canvasWidth = requisition.width;
|
|
canvasAscent = requisition.ascent;
|
|
canvasDescent = requisition.descent;
|
|
emitter.emitCanvasSizeChanged (canvasWidth,
|
|
canvasAscent, canvasDescent);
|
|
// Tell the view about the new world size.
|
|
view->setCanvasSize (canvasWidth, canvasAscent, canvasDescent);
|
|
|
|
if (usesViewport) {
|
|
int currHThickness = currHScrollbarThickness();
|
|
int currVThickness = currVScrollbarThickness();
|
|
|
|
if (!canvasHeightGreater &&
|
|
canvasAscent + canvasDescent > viewportHeight - currHThickness) {
|
|
canvasHeightGreater = true;
|
|
DBG_OBJ_SET_SYM ("canvasHeightGreater",
|
|
canvasHeightGreater ? "true" : "false");
|
|
containerSizeChanged ();
|
|
}
|
|
|
|
// Set viewport sizes.
|
|
view->setViewportSize (viewportWidth, viewportHeight,
|
|
currHThickness, currVThickness);
|
|
}
|
|
|
|
// views are redrawn via Widget::resizeDrawImpl ()
|
|
}
|
|
}
|
|
updateAnchor ();
|
|
|
|
DBG_OBJ_MSGF ("resize", 1,
|
|
"after resizeIdle: resizeIdleId = %d", resizeIdleId);
|
|
DBG_OBJ_LEAVE ();
|
|
|
|
leaveResizeIdle ();
|
|
}
|
|
|
|
void Layout::queueDraw (int x, int y, int width, int height)
|
|
{
|
|
Rectangle area;
|
|
area.x = x;
|
|
area.y = y;
|
|
area.width = width;
|
|
area.height = height;
|
|
|
|
if (area.isEmpty ()) return;
|
|
|
|
view->queueDraw (&area);
|
|
}
|
|
|
|
void Layout::queueDrawExcept (int x, int y, int width, int height,
|
|
int ex, int ey, int ewidth, int eheight) {
|
|
|
|
if (x == ex && y == ey && width == ewidth && height == eheight)
|
|
return;
|
|
|
|
// queueDraw() the four rectangles within rectangle (x, y, width, height)
|
|
// around rectangle (ex, ey, ewidth, eheight).
|
|
// Some or all of these may be empty.
|
|
|
|
// upper left corner of the intersection rectangle
|
|
int ix1 = std::max (x, ex);
|
|
int iy1 = std::max (y, ey);
|
|
// lower right corner of the intersection rectangle
|
|
int ix2 = std::min (x + width, ex + ewidth);
|
|
int iy2 = std::min (y + height, ey + eheight);
|
|
|
|
queueDraw (x, y, width, iy1 - y);
|
|
queueDraw (x, iy2, width, y + height - iy2);
|
|
queueDraw (x, iy1, ix1 - x, iy2 - iy1);
|
|
queueDraw (ix2, iy1, x + width - ix2, iy2 - iy1);
|
|
}
|
|
|
|
void Layout::queueResize (bool extremesChanged)
|
|
{
|
|
DBG_OBJ_ENTER ("resize", 0, "queueResize", "%s",
|
|
extremesChanged ? "true" : "false");
|
|
|
|
if (resizeIdleId == -1) {
|
|
view->cancelQueueDraw ();
|
|
|
|
resizeIdleId = platform->addIdle (&Layout::resizeIdle);
|
|
DBG_OBJ_MSGF ("resize", 1, "setting resizeIdleId = %d", resizeIdleId);
|
|
}
|
|
|
|
emitter.emitResizeQueued (extremesChanged);
|
|
|
|
DBG_OBJ_LEAVE ();
|
|
}
|
|
|
|
|
|
// Views
|
|
|
|
bool Layout::buttonEvent (ButtonEventType type, View *view, int numPressed,
|
|
int x, int y, ButtonState state, int button)
|
|
|
|
{
|
|
EventButton event;
|
|
|
|
moveToWidgetAtPoint (x, y, state);
|
|
|
|
event.xCanvas = x;
|
|
event.yCanvas = y;
|
|
event.state = state;
|
|
event.button = button;
|
|
event.numPressed = numPressed;
|
|
|
|
return processMouseEvent (&event, type);
|
|
}
|
|
|
|
/**
|
|
* \brief This function is called by a view, to delegate a motion notify
|
|
* event.
|
|
*
|
|
* Arguments are similar to dw::core::Layout::buttonPress.
|
|
*/
|
|
bool Layout::motionNotify (View *view, int x, int y, ButtonState state)
|
|
{
|
|
EventButton event;
|
|
|
|
moveToWidgetAtPoint (x, y, state);
|
|
|
|
event.xCanvas = x;
|
|
event.yCanvas = y;
|
|
event.state = state;
|
|
|
|
return processMouseEvent (&event, MOTION_NOTIFY);
|
|
}
|
|
|
|
/**
|
|
* \brief This function is called by a view, to delegate a enter notify event.
|
|
*
|
|
* Arguments are similar to dw::core::Layout::buttonPress.
|
|
*/
|
|
void Layout::enterNotify (View *view, int x, int y, ButtonState state)
|
|
{
|
|
Widget *lastWidget;
|
|
EventCrossing event;
|
|
|
|
lastWidget = widgetAtPoint;
|
|
moveToWidgetAtPoint (x, y, state);
|
|
|
|
if (widgetAtPoint) {
|
|
event.state = state;
|
|
event.lastWidget = lastWidget;
|
|
event.currentWidget = widgetAtPoint;
|
|
widgetAtPoint->enterNotify (&event);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief This function is called by a view, to delegate a leave notify event.
|
|
*
|
|
* Arguments are similar to dw::core::Layout::buttonPress.
|
|
*/
|
|
void Layout::leaveNotify (View *view, ButtonState state)
|
|
{
|
|
#if 0
|
|
Widget *lastWidget;
|
|
EventCrossing event;
|
|
|
|
lastWidget = widgetAtPoint;
|
|
moveOutOfView (state);
|
|
|
|
if (lastWidget) {
|
|
event.state = state;
|
|
event.lastWidget = lastWidget;
|
|
event.currentWidget = widgetAtPoint;
|
|
lastWidget->leaveNotify (&event);
|
|
}
|
|
#else
|
|
moveOutOfView (state);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Return the widget at position (x, y). Return NULL, if there is no widget.
|
|
*/
|
|
Widget *Layout::getWidgetAtPoint (int x, int y)
|
|
{
|
|
DBG_OBJ_ENTER ("events", 0, "getWidgetAtPoint", "%d, %d", x, y);
|
|
Widget *widget;
|
|
|
|
if (topLevel && topLevel->wasAllocated ()) {
|
|
GettingWidgetAtPointContext context;
|
|
widget = topLevel->getWidgetAtPoint (x, y, &context);
|
|
} else
|
|
widget = NULL;
|
|
|
|
DBG_OBJ_MSGF ("events", 0, "=> %p", widget);
|
|
DBG_OBJ_LEAVE ();
|
|
return widget;
|
|
}
|
|
|
|
|
|
/*
|
|
* Emit the necessary crossing events, when the mouse pointer has moved to
|
|
* the given widget (by mouse or scrolling).
|
|
*/
|
|
void Layout::moveToWidget (Widget *newWidgetAtPoint, ButtonState state)
|
|
{
|
|
DBG_OBJ_ENTER ("events", 0, "moveToWidget", "%p, %d",
|
|
newWidgetAtPoint, state);
|
|
|
|
Widget *ancestor, *w;
|
|
Widget **track;
|
|
int trackLen, i, i_a;
|
|
EventCrossing crossingEvent;
|
|
|
|
DBG_OBJ_MSGF ("events", 1, "(old) widgetAtPoint = %p", widgetAtPoint);
|
|
|
|
if (newWidgetAtPoint != widgetAtPoint) {
|
|
// The mouse pointer has been moved into another widget.
|
|
if (newWidgetAtPoint && widgetAtPoint)
|
|
ancestor =
|
|
newWidgetAtPoint->getNearestCommonAncestor (widgetAtPoint);
|
|
else if (newWidgetAtPoint)
|
|
ancestor = newWidgetAtPoint->getTopLevel ();
|
|
else
|
|
ancestor = widgetAtPoint->getTopLevel ();
|
|
|
|
// Construct the track.
|
|
trackLen = 0;
|
|
if (widgetAtPoint)
|
|
// first part
|
|
for (w = widgetAtPoint; w != ancestor; w = w->getParent ())
|
|
trackLen++;
|
|
trackLen++; // for the ancestor
|
|
if (newWidgetAtPoint)
|
|
// second part
|
|
for (w = newWidgetAtPoint; w != ancestor; w = w->getParent ())
|
|
trackLen++;
|
|
|
|
track = new Widget* [trackLen];
|
|
i = 0;
|
|
if (widgetAtPoint)
|
|
/* first part */
|
|
for (w = widgetAtPoint; w != ancestor; w = w->getParent ())
|
|
track[i++] = w;
|
|
i_a = i;
|
|
track[i++] = ancestor;
|
|
if (newWidgetAtPoint) {
|
|
/* second part */
|
|
i = trackLen - 1;
|
|
for (w = newWidgetAtPoint; w != ancestor; w = w->getParent ())
|
|
track[i--] = w;
|
|
}
|
|
#if 0
|
|
MSG("Track: %s[ ", widgetAtPoint ? "" : "nil ");
|
|
for (i = 0; i < trackLen; i++)
|
|
MSG("%s%p ", i == i_a ? ">" : "", track[i]);
|
|
MSG("] %s\n", newWidgetAtPoint ? "" : "nil");
|
|
#endif
|
|
|
|
/* Send events to the widgets on the track */
|
|
for (i = 0; i < trackLen; i++) {
|
|
crossingEvent.state = state;
|
|
crossingEvent.currentWidget = widgetAtPoint; // ???
|
|
crossingEvent.lastWidget = widgetAtPoint; // ???
|
|
if (i < i_a) {
|
|
track[i]->leaveNotify (&crossingEvent);
|
|
} else if (i == i_a) { /* ancestor */
|
|
/* Don't touch ancestor unless:
|
|
* - moving into/from NULL,
|
|
* - ancestor becomes the newWidgetAtPoint */
|
|
if (i_a == trackLen-1 && !newWidgetAtPoint)
|
|
track[i]->leaveNotify (&crossingEvent);
|
|
else if ((i_a == 0 && !widgetAtPoint) ||
|
|
(i_a == trackLen-1 && newWidgetAtPoint))
|
|
track[i]->enterNotify (&crossingEvent);
|
|
} else {
|
|
track[i]->enterNotify (&crossingEvent);
|
|
}
|
|
}
|
|
|
|
delete[] track;
|
|
|
|
widgetAtPoint = newWidgetAtPoint;
|
|
updateCursor ();
|
|
}
|
|
|
|
DBG_OBJ_LEAVE ();
|
|
}
|
|
|
|
/**
|
|
* \brief Common processing of press, release and motion events.
|
|
*
|
|
* This function depends on that move_to_widget_at_point()
|
|
* has been called before.
|
|
*/
|
|
bool Layout::processMouseEvent (MousePositionEvent *event,
|
|
ButtonEventType type)
|
|
{
|
|
Widget *widget;
|
|
|
|
/*
|
|
* If the event is outside of the visible region of the canvas, treat it
|
|
* as occurring at the region's edge. Notably, this helps when selecting
|
|
* text.
|
|
*/
|
|
if (event->xCanvas < scrollX)
|
|
event->xCanvas = scrollX;
|
|
else {
|
|
int maxX = scrollX + viewportWidth - currVScrollbarThickness() - 1;
|
|
|
|
if (event->xCanvas > maxX)
|
|
event->xCanvas = maxX;
|
|
}
|
|
if (event->yCanvas < scrollY)
|
|
event->yCanvas = scrollY;
|
|
else {
|
|
int maxY = std::min(scrollY + viewportHeight -currHScrollbarThickness(),
|
|
canvasAscent + canvasDescent) - 1;
|
|
|
|
if (event->yCanvas > maxY)
|
|
event->yCanvas = maxY;
|
|
}
|
|
|
|
widget = getWidgetAtPoint(event->xCanvas, event->yCanvas);
|
|
|
|
for (; widget; widget = widget->getParent ()) {
|
|
if (widget->isButtonSensitive ()) {
|
|
event->xWidget = event->xCanvas - widget->getAllocation()->x;
|
|
event->yWidget = event->yCanvas - widget->getAllocation()->y;
|
|
|
|
switch (type) {
|
|
case BUTTON_PRESS:
|
|
return widget->buttonPress ((EventButton*)event);
|
|
|
|
case BUTTON_RELEASE:
|
|
return widget->buttonRelease ((EventButton*)event);
|
|
|
|
case MOTION_NOTIFY:
|
|
return widget->motionNotify ((EventMotion*)event);
|
|
|
|
default:
|
|
misc::assertNotReached ();
|
|
}
|
|
}
|
|
}
|
|
if (type == BUTTON_PRESS)
|
|
return emitLinkPress (NULL, -1, -1, -1, -1, (EventButton*)event);
|
|
else if (type == BUTTON_RELEASE)
|
|
return emitLinkRelease(NULL, -1, -1, -1, -1, (EventButton*)event);
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* This function must be called by a view, when the user has manually changed
|
|
* the viewport position. It is *not* called, when the layout has requested the
|
|
* position change.
|
|
*/
|
|
void Layout::scrollPosChanged (View *view, int x, int y)
|
|
{
|
|
if (x != scrollX || y != scrollY) {
|
|
scrollX = x;
|
|
scrollY = y;
|
|
|
|
setAnchor( std::nullopt );
|
|
updateAnchor ();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This function must be called by a viewport view, when its viewport size has
|
|
* changed. It is *not* called, when the layout has requested the size change.
|
|
*/
|
|
void Layout::viewportSizeChanged (View *view, int width, int height)
|
|
{
|
|
DBG_OBJ_ENTER ("resize", 0, "viewportSizeChanged", "%p, %d, %d",
|
|
view, width, height);
|
|
|
|
/* If size changes, redraw this view. */
|
|
if (viewportWidth != width || viewportHeight != height) {
|
|
canvasHeightGreater = false; // reset value here
|
|
viewportWidth = width;
|
|
viewportHeight = height;
|
|
resizeCounter = 0;
|
|
containerSizeChanged ();
|
|
|
|
DBG_OBJ_SET_SYM ("canvasHeightGreater",
|
|
canvasHeightGreater ? "true" : "false");
|
|
DBG_OBJ_SET_NUM ("viewportWidth", viewportWidth);
|
|
DBG_OBJ_SET_NUM ("viewportHeight", viewportHeight);
|
|
}
|
|
|
|
DBG_OBJ_LEAVE ();
|
|
}
|
|
|
|
void Layout::containerSizeChanged ()
|
|
{
|
|
DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChanged");
|
|
|
|
if (topLevel) {
|
|
topLevel->containerSizeChanged ();
|
|
queueResize (true);
|
|
}
|
|
|
|
DBG_OBJ_LEAVE ();
|
|
}
|
|
|
|
} // namespace core
|
|
} // namespace dw
|