diff --git a/plasma/layouts/borderlayout.cpp b/plasma/layouts/borderlayout.cpp index 6324585198..a9d2fb56ba 100644 --- a/plasma/layouts/borderlayout.cpp +++ b/plasma/layouts/borderlayout.cpp @@ -1,239 +1,243 @@ /* * Copyright (C) 2007 Ivan Cukic * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library/Lesser General Public License * version 2, or (at your option) any later version, as published by the * Free Software Foundation * * 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 Library/Lesser General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "borderlayout.h" namespace Plasma { class BorderLayout::Private { public: BorderLayout * q; explicit Private(BorderLayout * parent = 0) : q(parent) { sizes[LeftPositioned] = -1; sizes[RightPositioned] = -1; sizes[TopPositioned] = -1; sizes[BottomPositioned] = -1; sizes[CenterPositioned] = -1; } virtual ~Private() { } QMap< Position, LayoutItem * > itemPositions; QMap< Position, qreal > sizes; }; BorderLayout::BorderLayout(LayoutItem * parent) : Layout(parent), d(new Private(this)) { if (parent) { parent->setLayout(this); } } BorderLayout::~BorderLayout() { + foreach (Plasma::LayoutItem *item, d->itemPositions) { + item->unsetManagingLayout(this); + } + delete d; } Qt::Orientations BorderLayout::expandingDirections() const { return Qt::Horizontal | Qt::Vertical; } void BorderLayout::relayout() { QRectF rect = geometry(); rect.setTopLeft(rect.topLeft() + QPointF(margin(LeftMargin), margin(TopMargin))); rect.setBottomRight(rect.bottomRight() - QPointF(margin(RightMargin), margin(BottomMargin))); QPointF origin = rect.topLeft(); qreal top, bottom, left, right; top = (d->sizes[TopPositioned] >= 0) ? d->sizes[TopPositioned] : 0; left = (d->sizes[LeftPositioned] >= 0) ? d->sizes[LeftPositioned] : 0; bottom = rect.height() - ((d->sizes[BottomPositioned] >= 0) ? d->sizes[BottomPositioned] : 0); right = rect.width() - ((d->sizes[RightPositioned] >= 0) ? d->sizes[RightPositioned] : 0); if (d->itemPositions[TopPositioned] /*&& d->itemPositions[TopPositioned]->isVisible()*/) { top = (d->sizes[TopPositioned] >= 0) ? d->sizes[TopPositioned] : d->itemPositions[TopPositioned]->sizeHint().height(); d->itemPositions[TopPositioned]->setGeometry(QRectF(origin, QSizeF( rect.width(), top))); top += spacing(); } if (d->itemPositions[BottomPositioned] /*&& d->itemPositions[BottomPositioned]->isVisible()*/) { bottom = (d->sizes[BottomPositioned] >= 0) ? d->sizes[BottomPositioned] : d->itemPositions[BottomPositioned]->sizeHint().height(); d->itemPositions[BottomPositioned]->setGeometry(QRectF(origin + QPointF(0, rect.height() - bottom), QSizeF(rect.width(), bottom))); bottom = rect.height() - bottom - spacing(); } if (d->itemPositions[LeftPositioned] /*&& d->itemPositions[LeftPositioned]->isVisible()*/) { left = (d->sizes[LeftPositioned] >= 0) ? d->sizes[LeftPositioned] : d->itemPositions[LeftPositioned]->sizeHint().width(); d->itemPositions[LeftPositioned]->setGeometry(QRectF(origin + QPointF(0, top), QSizeF(left, bottom - top))); left += spacing(); } if (d->itemPositions[RightPositioned] /*&& d->itemPositions[RightPositioned]->isVisible()*/) { right = (d->sizes[RightPositioned] >= 0) ? d->sizes[RightPositioned] : d->itemPositions[RightPositioned]->sizeHint().width(); d->itemPositions[RightPositioned]->setGeometry(QRectF(origin + QPointF( rect.width() - right, top), QSizeF(right, bottom - top))); right = rect.width() - right - spacing(); } if (d->itemPositions[CenterPositioned] /*&& d->itemPositions[CenterPositioned]->isVisible()*/) { d->itemPositions[CenterPositioned]->setGeometry(QRectF( origin + QPointF(left, top), QSizeF(right - left, bottom - top))); } } QSizeF BorderLayout::sizeHint() const { qreal hintHeight = 0.0; qreal hintWidth = 0.0; if (d->itemPositions[TopPositioned] /*&& d->itemPositions[TopPositioned]->isVisible()*/) { hintHeight += d->itemPositions[TopPositioned]->sizeHint().height(); } if (d->itemPositions[BottomPositioned] /*&& d->itemPositions[BottomPositioned]->isVisible()*/) { hintHeight += d->itemPositions[BottomPositioned]->sizeHint().height(); } if (d->itemPositions[LeftPositioned] /*&& d->itemPositions[LeftPositioned]->isVisible()*/) { hintWidth += d->itemPositions[LeftPositioned]->sizeHint().width(); } if (d->itemPositions[RightPositioned] /*&& d->itemPositions[RightPositioned]->isVisible()*/) { hintWidth += d->itemPositions[RightPositioned]->sizeHint().width(); } if (d->itemPositions[CenterPositioned] /*&& d->itemPositions[CenterPositioned]->isVisible()*/) { hintHeight += d->itemPositions[CenterPositioned]->sizeHint().height(); hintWidth += d->itemPositions[CenterPositioned]->sizeHint().width(); } return QSizeF(hintWidth + 2 + margin(LeftMargin) + margin(RightMargin), hintHeight + 2 + margin(TopMargin) + margin(BottomMargin)); } void BorderLayout::addItem(Plasma::LayoutItem * item) { BorderLayout::addItem (item, CenterPositioned); } void BorderLayout::addItem(Plasma::LayoutItem * item, Position position) { removeItem(item); d->itemPositions[position] = item; item->setManagingLayout(this); updateGeometry(); } void BorderLayout::removeItem(LayoutItem * item) { QMutableMapIterator< Position, Plasma::LayoutItem * > i(d->itemPositions); while (i.hasNext()) { i.next(); if (i.value() == item) { i.remove(); item->unsetManagingLayout(this); } } updateGeometry(); } int BorderLayout::count() const { int count = 0; foreach (Plasma::LayoutItem * i, d->itemPositions) { if (i) { ++count; } } return count; } int BorderLayout::indexOf(LayoutItem * item) const { int count = 0; foreach (Plasma::LayoutItem * i, d->itemPositions) { if (i) { if (item == i) { return count; } ++count; } } return -1; } LayoutItem * BorderLayout::itemAt(int index) const { int count = 0; foreach (Plasma::LayoutItem * i, d->itemPositions) { if (i) { if (index == count) { return i; } count++; } } return 0; } Plasma::LayoutItem * BorderLayout::takeAt(int i) { Plasma::LayoutItem * item = itemAt(i); removeItem(item); return item; } void BorderLayout::setSize(qreal size, Position border) { d->sizes[border] = size; updateGeometry(); } void BorderLayout::setAutoSize(Position border) { d->sizes[border] = -1; updateGeometry(); } qreal BorderLayout::size(Position border) { if (border == CenterPositioned) { return -1; } return d->sizes[border]; } } diff --git a/plasma/layouts/flowlayout.cpp b/plasma/layouts/flowlayout.cpp index 56298fec1d..9a59fd0e09 100644 --- a/plasma/layouts/flowlayout.cpp +++ b/plasma/layouts/flowlayout.cpp @@ -1,239 +1,243 @@ /* * Copyright 2007 by Robert Knight * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License * as published by the Free Software Foundation; either * version 2 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 Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "flowlayout.h" #include #include #include #include #include #include #include "layoutanimator.h" using namespace Plasma; class FlowLayout::Private { public: Private() : columnWidth( -1 ) {} QList items; qreal columnWidth; }; FlowLayout::FlowLayout(LayoutItem* parent) : Layout(parent) , d(new Private) { } FlowLayout::~FlowLayout() { + foreach (LayoutItem *item, d->items) { + item->unsetManagingLayout(this); + } + delete d; } int FlowLayout::count() const { return d->items.count(); } void FlowLayout::addItem(LayoutItem* item) { if (!item || d->items.contains(item)) { return; } item->setManagingLayout(this); d->items << item; if (animator()) { animator()->setCurrentState(item,LayoutAnimator::InsertedState); } updateGeometry(); } void FlowLayout::removeItem(LayoutItem* item) { if (!item) { return; } item->unsetManagingLayout(this); d->items.removeAll(item); if (animator()) { animator()->setCurrentState(item,LayoutAnimator::RemovedState); } updateGeometry(); } int FlowLayout::indexOf(LayoutItem* item) const { if (!item) { return -1; } return d->items.indexOf(item); } LayoutItem* FlowLayout::itemAt(int i) const { if (i >= d->items.count()) { return 0; } return d->items[i]; } QSizeF FlowLayout::sizeHint() const { // TODO A proper algorithm here // // Idea: Return a size hint based on the golden ratio to // make it aesthetically good // eg. Longer side is 1.61x the length of the shorter side // // testing return QSizeF(500,500); } LayoutItem* FlowLayout::takeAt(int i) { if (i >= d->items.count()) { return 0; } return d->items.takeAt(i); // FIXME: Should updateGeometry() be called? } template T qSum(const QList& container) { T total = 0; foreach( const T& item , container ) { total += item; } return total; } void FlowLayout::relayout() { QRectF rect = geometry().adjusted(margin(LeftMargin), margin(TopMargin), -margin(RightMargin), -margin(BottomMargin)); qDebug() << "Flow layout geometry set to " << geometry(); // calculate average size of items qreal totalWidth = 0; qreal totalHeight = 0; foreach(LayoutItem *item , d->items) { totalWidth += item->sizeHint().width(); totalHeight += item->sizeHint().height(); } // use the average item width as the column width. // Also include the spacing either side of each item as part of the // average width, this provides the spacing between the items and // also allows some tolerance for small differences in item widths qreal averageWidth; if (d->columnWidth == -1) { averageWidth = totalWidth / d->items.count() + 2*spacing(); } else { averageWidth = d->columnWidth; } const int columnCount = (int)(rect.width() / averageWidth); int insertColumn = 0; qreal rowPos = 0; qreal rowHeight = 0; // lay the items out in left-to-right , top-to-bottom order foreach(LayoutItem *item , d->items) { const QSizeF& itemSize = item->sizeHint(); int columnSpan = (int)ceil(itemSize.width() / averageWidth); if ( insertColumn + columnSpan > columnCount ) { // start a new row insertColumn = 0; rowPos += rowHeight + spacing(); } // qDebug() << "Inserting item at column" << insertColumn // << "spanning" << columnSpan << "columns" // << "with offset" << offset; // try to expand the item to fill its allocated number of columns qreal itemWidth = itemSize.width(); const qreal idealWidth = columnSpan * averageWidth - spacing(); if ( itemWidth < idealWidth && idealWidth < item->maximumSize().width() ) { itemWidth = idealWidth; } // calculate offset to horizontally center item qreal offset = (columnSpan * averageWidth) - itemWidth; if ( insertColumn == 0 ) offset -= spacing(); offset /= 2; // try to restrict the item width to the available geometry's // width if ( itemWidth > rect.width() ) { itemWidth = qMax(rect.width(),item->minimumSize().width()); offset = 0; } // position the item const QRectF newGeometry(rect.left() + insertColumn * averageWidth + offset, rect.top() + rowPos, itemWidth, itemSize.height()); rowHeight = qMax(rowHeight,itemSize.height()); insertColumn += columnSpan; if ( animator() ) animator()->setGeometry( item , newGeometry ); else item->setGeometry( newGeometry ); } startAnimation(); } Qt::Orientations FlowLayout::expandingDirections() const { return Qt::Vertical | Qt::Horizontal; } qreal FlowLayout::columnWidth() const { return d->columnWidth; } void FlowLayout::setColumnWidth( const qreal width ) { d->columnWidth = width; } diff --git a/plasma/layouts/freelayout.cpp b/plasma/layouts/freelayout.cpp index 14a0dab992..3c8e2c2773 100644 --- a/plasma/layouts/freelayout.cpp +++ b/plasma/layouts/freelayout.cpp @@ -1,123 +1,127 @@ /* * Copyright 2007 by Robert Knight * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, 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 Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "freelayout.h" #include #include namespace Plasma { class FreeLayout::Private { public: QList children; }; FreeLayout::FreeLayout(LayoutItem *parent) : Layout(parent), d(new Private) { } FreeLayout::~FreeLayout() { + foreach (LayoutItem *item, d->children) { + item->unsetManagingLayout(this); + } + delete d; } Qt::Orientations FreeLayout::expandingDirections() const { return Qt::Horizontal | Qt::Vertical; } void FreeLayout::addItem(LayoutItem *item) { if (d->children.contains(item)) { return; } d->children << item; item->setManagingLayout(this); } void FreeLayout::removeItem(LayoutItem *item) { if (!item) { return; } d->children.removeAll(item); item->unsetManagingLayout(this); } int FreeLayout::indexOf(LayoutItem *item) const { return d->children.indexOf(item); } LayoutItem * FreeLayout::itemAt(int i) const { return d->children[i]; } int FreeLayout::count() const { return d->children.count(); } LayoutItem * FreeLayout::takeAt(int i) { return d->children.takeAt(i); } void FreeLayout::relayout() { foreach (LayoutItem *child , d->children) { if (child->geometry().size() != child->sizeHint()) { QSizeF newSize = child->sizeHint().expandedTo(minimumSize()).boundedTo(maximumSize()); child->setGeometry(QRectF(child->geometry().topLeft(), newSize)); } } } QRectF FreeLayout::geometry() const { if (parent()) { return parent()->geometry(); } return QRectF(QPointF(0, 0), maximumSize()); } QSizeF FreeLayout::sizeHint() const { if (parent()) { //kDebug() << "returning size hint from freelayout of" << parent()->geometry().size(); return parent()->geometry().size(); } //kDebug() << "returning size hint from freelayout of" << maximumSize(); return maximumSize(); } } diff --git a/plasma/layouts/layout.h b/plasma/layouts/layout.h index 28b6625806..4ba44ab50b 100644 --- a/plasma/layouts/layout.h +++ b/plasma/layouts/layout.h @@ -1,195 +1,195 @@ /* * Copyright 2007 by Matias Valdenegro T. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, 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 Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __LAYOUT__ #define __LAYOUT__ #include #include #include #include namespace Plasma { class LayoutAnimator; /** * Base class for Plasma Layout managers * * @author Matias Valdenegro T. * * All layout managers must implement this class. Normal users should use the specific layouts, * like Plasma::VBoxLayout, Plasma::HBoxLayout and Plasma::GridLayout. */ class PLASMA_EXPORT Layout : public LayoutItem { public: enum MarginEdge { TopMargin, BottomMargin, LeftMargin, RightMargin }; /** * Constructor. */ explicit Layout(LayoutItem *parent); /** * Virtual Destructor. */ virtual ~Layout(); /** * Returns the margin of this Layout. */ qreal margin(MarginEdge edge) const; /** * Sets the margin of this Layout. */ void setMargin(MarginEdge edge, qreal m); /** * Sets all the margins of this Layout. */ void setMargin(qreal m); /** * Returns the spacing between Layout elements of this Layout. */ qreal spacing() const; /** * Sets the spacing of this Layout. */ void setSpacing(qreal s); /** * Returns the parent of this Layout. */ LayoutItem *parent() const; /** * Sets the parent of this layout. */ void setParent(LayoutItem *parent); /** * Returns the number of elements of this Layout. */ virtual int count() const = 0; /** * Returns true if this Layout contains no elements, false otherwise. */ bool isEmpty() const; /** * Adds a Item to this Layout. * @param l Pointer to the Item to be added. */ virtual void addItem(LayoutItem *l) = 0; /** * Removes a Item from this Layout. * @param l Pointer to the Item to be removed. */ virtual void removeItem(LayoutItem *l) = 0; /** * Returns the index of a Item in this Layout. * @param l Pointer to an Item to be queryed. */ virtual int indexOf(LayoutItem *l) const = 0; /** * Returns a Pointer to an Item in this Layout. * @param i Index of the desired Item. */ virtual LayoutItem *itemAt(int i) const = 0; /** * Takes the Pointer of an Item in this Layout. * @param i Index of the desired Item. */ virtual LayoutItem *takeAt(int i) = 0; /** * Returns the object controlling animation of changes * in this layout or 0 if no animator has been set. */ virtual LayoutAnimator* animator() const; /** * Sets the object controlling animation of changes in this * layout. */ virtual void setAnimator( LayoutAnimator* animator ); /** * Returns the current geometry for this layout */ virtual QRectF geometry() const; /** * Changes the geometry of this layout */ void setGeometry(const QRectF &geometry); /** Triggers an update of the layout. */ void updateGeometry(); /** * Returns the minimum size of this layout. * The default implementation allows unlimited resizing. */ virtual QSizeF minimumSize() const; /** * Returns the maximum size of this layout. The default * implementation allows unlimited resizing. */ virtual QSizeF maximumSize() const; /** TODO Document me */ - void invalidate(); + void invalidate(); protected: /** * Triggers a layout, usually after a change in geometry */ virtual void relayout() = 0; /** * Starts a layout animation. Subclasses may call this * at the end of their relayout() implementation to * start the timeline associated with the layout's animator() * if there is one. If an animation is already in progress then * the timeline is reset to 0ms and the animation continues. */ void startAnimation(); private: class Private; Private *const d; }; } #endif /* __LAYOUT__ */ diff --git a/plasma/layouts/layoutitem.cpp b/plasma/layouts/layoutitem.cpp index bcc2cc1a5f..158b0c32a7 100644 --- a/plasma/layouts/layoutitem.cpp +++ b/plasma/layouts/layoutitem.cpp @@ -1,144 +1,135 @@ /* * Copyright 2007 by Matias Valdenegro T. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, 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 Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "layoutitem.h" #include #include "layout.h" namespace Plasma { class LayoutItem::Private { public: Private() : layout(0), managingLayout(0) { } ~Private() {} Layout* layout; Layout* managingLayout; }; LayoutItem::LayoutItem() : d(new Private()) { } LayoutItem::~LayoutItem() { if (d->managingLayout) { d->managingLayout->removeItem(this); } delete d->layout; delete d; } QGraphicsItem* LayoutItem::graphicsItem() { return 0; } bool LayoutItem::hasHeightForWidth() const { return false; } qreal LayoutItem::heightForWidth(qreal w) const { Q_UNUSED (w); return 0.0; } bool LayoutItem::hasWidthForHeight() const { return false; } qreal LayoutItem::widthForHeight(qreal h) const { Q_UNUSED (h); return 0.0; } void LayoutItem::setLayout(Layout* layout) { - if (d->layout && layout) { - kDebug() << "already have a layout."; - return; - } + delete d->layout; if (layout) { layout->setParent(this); - } else if (d->layout) { - // FIXME: we had a layout, but now it has been removed - // and we are without layout; we should tell our - // children about this, but LayoutItem doesn't know - // about children =/ - kDebug() << "layout removed from under us. expect crashes"; } d->layout = layout; } Layout* LayoutItem::layout() const { return d->layout; } void LayoutItem::setManagingLayout(Layout* layout) { if (layout == d->managingLayout) { return; } if (d->managingLayout) { d->managingLayout->removeItem(this); } d->managingLayout = layout; managingLayoutChanged(); } void LayoutItem::unsetManagingLayout(Layout* layout) { if (d->managingLayout == layout) { d->managingLayout = 0; } managingLayoutChanged(); } void LayoutItem::managingLayoutChanged() { } Layout* LayoutItem::managingLayout() const { return d->managingLayout; } } diff --git a/plasma/layouts/nodelayout.cpp b/plasma/layouts/nodelayout.cpp index 2eace2766a..7737a7a8d1 100644 --- a/plasma/layouts/nodelayout.cpp +++ b/plasma/layouts/nodelayout.cpp @@ -1,257 +1,261 @@ /* * Copyright (C) 2007 Ivan Cukic * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 2, or (at your option) any later version, as published by the * Free Software Foundation * * 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 Lesser General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "nodelayout.h" #include #include #include namespace Plasma { NodeLayout::NodeCoordinate::NodeCoordinate(qreal xRelative, qreal yRelative, qreal xAbsolute, qreal yAbsolute) : xr(xRelative), xa(xAbsolute), yr(yRelative), ya(yAbsolute) {} NodeLayout::NodeCoordinate NodeLayout::NodeCoordinate::simple(qreal x, qreal y, CoordinateType xType, CoordinateType yType) { NodeLayout::NodeCoordinate coo; switch (xType) { case Relative: coo.xr = x; coo.xa = 0; break; case Absolute: coo.xr = 0; coo.xa = x; break; case InnerRelative: coo.xr = x; coo.xa = std::numeric_limits::infinity(); break; } switch (yType) { case Relative: coo.yr = y; coo.ya = 0; break; case Absolute: coo.yr = 0; coo.ya = y; break; case InnerRelative: coo.yr = y; coo.ya = std::numeric_limits::infinity(); break; } return coo; } class NodeLayout::Private { public: QMap > items; //QRectF geometry; NodeLayout * parent; QSizeF sizeHint; Private(NodeLayout * parentLayout) { parent = parentLayout; } qreal calculateXPosition(const NodeCoordinate & coo, const QRectF & parentGeometry) const { return parentGeometry.left() + (coo.xr * parentGeometry.width()) + coo.xa; } qreal calculateYPosition(const NodeCoordinate & coo, const QRectF & parentGeometry) const { return parentGeometry.top() + (coo.yr * parentGeometry.height()) + coo.ya; } QPointF calculatePosition(const NodeCoordinate & coo, const QRectF & parentGeometry) const { Q_UNUSED( parentGeometry ); return QPointF( calculateXPosition(coo, parent->geometry()), calculateYPosition(coo, parent->geometry()) ); } QRectF calculateRectangle(LayoutItem * item, QRectF geometry = QRectF()) const { if (geometry == QRectF()) geometry = parent->geometry(); QRectF result; if (!item || !items.contains(item)) return QRectF(); result.setTopLeft(calculatePosition(items[item].first, geometry)); if (items[item].second.xa != std::numeric_limits::infinity()) { result.setRight(calculateXPosition(items[item].second, geometry)); } else { result.setWidth(item->sizeHint().width()); result.moveLeft(result.left() - items[item].second.xr * result.width()); } if (items[item].second.ya != std::numeric_limits::infinity()) { result.setBottom(calculateYPosition(items[item].second, geometry)); } else { result.setHeight(item->sizeHint().height()); result.moveTop(result.top() - items[item].second.yr * result.height()); } return result; } void calculateSizeHint(LayoutItem * item = NULL) { if (item == NULL) { // Recalculate the sizeHint using all items sizeHint = QSizeF(); foreach (LayoutItem * item, items.keys()) { if (item) { calculateSizeHint(item); } } } else { // Calculate size hint for current item QRectF scaled = calculateRectangle(item, QRectF(0, 0, 1, 1)); // qMin(..., 1.0) so that for autosized elements we don't get smaller // size than the item's size itself. The sizeHint for NodeLayout can // not do anything smarter concerning the sizeHint when there are // autosized elements. qreal width = item->sizeHint().width() / qMin(scaled.width(), qreal(1.0)); qreal height = item->sizeHint().height() / qMin(scaled.height(), qreal(1.0)); if (width > sizeHint.width()) sizeHint.setWidth(width); if (height > sizeHint.height()) sizeHint.setHeight(height); } } }; NodeLayout::NodeLayout(LayoutItem * parent) : Layout(parent), d(new Private(this)) { } NodeLayout::~NodeLayout() { + foreach (LayoutItem * item, d->items.keys()) { + item->unsetManagingLayout(this); + } + delete d; } Qt::Orientations NodeLayout::expandingDirections() const { return Qt::Horizontal | Qt::Vertical; } void NodeLayout::relayout() { foreach (LayoutItem * item, d->items.keys()) { if (item) { item->setGeometry(d->calculateRectangle(item)); } } } QSizeF NodeLayout::sizeHint() const { return d->sizeHint; } void NodeLayout::addItem(LayoutItem * item) { NodeLayout::addItem(item, NodeCoordinate()); } void NodeLayout::addItem(LayoutItem * item, NodeCoordinate topLeft, NodeCoordinate bottomRight) { if (!item) { return; } d->items[item] = QPair(topLeft, bottomRight); item->setManagingLayout(this); d->calculateSizeHint(item); } void NodeLayout::addItem(LayoutItem * item, NodeCoordinate node, qreal xr, qreal yr) { if (!item) { return; } d->items[item] = QPair(node, NodeCoordinate::simple(xr, yr, NodeCoordinate::InnerRelative, NodeCoordinate::InnerRelative)); item->setManagingLayout(this); d->calculateSizeHint(item); } void NodeLayout::removeItem(LayoutItem * item) { if (!item) { return; } item->unsetManagingLayout(this); d->items.remove(item); d->calculateSizeHint(); } int NodeLayout::count() const { return d->items.count(); } int NodeLayout::indexOf(LayoutItem * item) const { if (!item) { return -1; } return d->items.keys().indexOf(item); } LayoutItem * NodeLayout::itemAt(int i) const { if (i >= d->items.count()) { return 0; } return d->items.keys()[i]; } LayoutItem * NodeLayout::takeAt(int i) { if (i >= d->items.count()) { return 0; } LayoutItem * item = itemAt(i); removeItem(item); return item; } }