summaryrefslogtreecommitdiff
path: root/src/gui/widgets/layout.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/widgets/layout.cpp')
-rw-r--r--src/gui/widgets/layout.cpp362
1 files changed, 362 insertions, 0 deletions
diff --git a/src/gui/widgets/layout.cpp b/src/gui/widgets/layout.cpp
new file mode 100644
index 000000000..38c6bb471
--- /dev/null
+++ b/src/gui/widgets/layout.cpp
@@ -0,0 +1,362 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2007-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ *
+ * This file is part of The Mana Client.
+ *
+ * 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 2 of the License, or
+ * 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 "gui/widgets/layout.h"
+
+#include "log.h"
+
+#include <cassert>
+
+ContainerPlacer ContainerPlacer::at(int x, int y)
+{
+ return ContainerPlacer(mContainer, &mCell->at(x, y));
+}
+
+LayoutCell &ContainerPlacer::operator()
+ (int x, int y, gcn::Widget *wg, int w, int h)
+{
+ mContainer->add(wg);
+ return mCell->place(wg, x, y, w, h);
+}
+
+LayoutCell::~LayoutCell()
+{
+ if (mType == ARRAY)
+ {
+ delete mArray;
+ mArray = 0;
+ }
+}
+
+LayoutArray &LayoutCell::getArray()
+{
+ assert(mType != WIDGET);
+ if (mType == ARRAY)
+ return *mArray;
+
+ mArray = new LayoutArray;
+ mType = ARRAY;
+ mExtent[0] = 1;
+ mExtent[1] = 1;
+ mPadding = 0;
+ mAlign[0] = FILL;
+ mAlign[1] = FILL;
+ return *mArray;
+}
+
+void LayoutCell::reflow(int nx, int ny, int nw, int nh)
+{
+ assert(mType != NONE);
+ nx += mPadding;
+ ny += mPadding;
+ nw -= 2 * mPadding;
+ nh -= 2 * mPadding;
+ if (mType == ARRAY)
+ mArray->reflow(nx, ny, nw, nh);
+ else
+ mWidget->setDimension(gcn::Rectangle(nx, ny, nw, nh));
+}
+
+void LayoutCell::computeSizes()
+{
+ assert(mType == ARRAY);
+
+ std::vector< std::vector< LayoutCell * > >::iterator
+ i = mArray->mCells.begin();
+
+ while (i != mArray->mCells.end())
+ {
+ std::vector< LayoutCell * >::iterator j = i->begin();
+ while (j != i->end())
+ {
+ LayoutCell *cell = *j;
+ if (cell && cell->mType == ARRAY)
+ cell->computeSizes();
+
+ ++j;
+ }
+ ++i;
+ }
+
+ mSize[0] = mArray->getSize(0);
+ mSize[1] = mArray->getSize(1);
+}
+
+LayoutArray::LayoutArray(): mSpacing(4)
+{
+}
+
+LayoutArray::~LayoutArray()
+{
+ std::vector< std::vector< LayoutCell * > >::iterator i = mCells.begin();
+ while (i != mCells.end())
+ {
+ std::vector< LayoutCell * >::iterator j = i->begin();
+ while (j != i->end())
+ {
+ delete *j;
+ ++j;
+ }
+ ++i;
+ }
+}
+
+LayoutCell &LayoutArray::at(int x, int y, int w, int h)
+{
+ resizeGrid(x + w, y + h);
+ LayoutCell *&cell = mCells[y][x];
+ if (!cell)
+ cell = new LayoutCell;
+ return *cell;
+}
+
+void LayoutArray::resizeGrid(int w, int h)
+{
+ bool extW = w && w > static_cast<int>(mSizes[0].size()),
+ extH = h && h > static_cast<int>(mSizes[1].size());
+
+ if (!extW && !extH)
+ return;
+
+ if (extH)
+ {
+ mSizes[1].resize(h, Layout::AUTO_DEF);
+ mCells.resize(h);
+ if (!extW)
+ w = static_cast<int>(mSizes[0].size());
+ }
+
+ if (extW)
+ mSizes[0].resize(w, Layout::AUTO_DEF);
+
+ std::vector< std::vector< LayoutCell * > >::iterator i = mCells.begin();
+ while (i != mCells.end())
+ {
+ i->resize(w, 0);
+ ++i;
+ }
+}
+
+void LayoutArray::setColWidth(int n, int w)
+{
+ resizeGrid(n + 1, 0);
+ mSizes[0][n] = w;
+}
+
+void LayoutArray::setRowHeight(int n, int h)
+{
+ resizeGrid(0, n + 1);
+ mSizes[1][n] = h;
+}
+
+void LayoutArray::matchColWidth(int n1, int n2)
+{
+ resizeGrid(std::max(n1, n2) + 1, 0);
+ std::vector<int> widths = getSizes(0, Layout::AUTO_DEF);
+ int s = std::max(widths[n1], widths[n2]);
+ mSizes[0][n1] = s;
+ mSizes[0][n2] = s;
+}
+
+void LayoutArray::extend(int x, int y, int w, int h)
+{
+ LayoutCell &cell = at(x, y, w, h);
+ cell.mExtent[0] = w;
+ cell.mExtent[1] = h;
+}
+
+LayoutCell &LayoutArray::place(gcn::Widget *widget, int x, int y, int w, int h)
+{
+ LayoutCell &cell = at(x, y, w, h);
+ assert(cell.mType == LayoutCell::NONE);
+ cell.mType = LayoutCell::WIDGET;
+ cell.mWidget = widget;
+ if (widget)
+ {
+ cell.mSize[0] = w == 1 ? widget->getWidth() : 0;
+ cell.mSize[1] = h == 1 ? widget->getHeight() : 0;
+ }
+ else
+ {
+ cell.mSize[0] = 1;
+ cell.mSize[1] = 1;
+ }
+ cell.mExtent[0] = w;
+ cell.mExtent[1] = h;
+ cell.mPadding = 0;
+ cell.mAlign[0] = LayoutCell::FILL;
+ cell.mAlign[1] = LayoutCell::FILL;
+ int &cs = mSizes[0][x], &rs = mSizes[1][y];
+ if (cs == Layout::AUTO_DEF && w == 1)
+ cs = 0;
+ if (rs == Layout::AUTO_DEF && h == 1)
+ rs = 0;
+ return cell;
+}
+
+void LayoutArray::align(int &pos, int &size, int dim,
+ LayoutCell const &cell, int *sizes) const
+{
+ int size_max = sizes[0];
+ for (int i = 1; i < cell.mExtent[dim]; ++i)
+ size_max += sizes[i] + mSpacing;
+ size = std::min<int>(cell.mSize[dim], size_max);
+
+ switch (cell.mAlign[dim])
+ {
+ case LayoutCell::LEFT:
+ return;
+ case LayoutCell::RIGHT:
+ pos += size_max - size;
+ return;
+ case LayoutCell::CENTER:
+ pos += (size_max - size) / 2;
+ return;
+ case LayoutCell::FILL:
+ size = size_max;
+ return;
+ default:
+ logger->log1("LayoutArray::align unknown layout");
+ return;
+ }
+}
+
+std::vector<int> LayoutArray::getSizes(int dim, int upp) const
+{
+ int gridW = static_cast<int>(mSizes[0].size()),
+ gridH = static_cast<int>(mSizes[1].size());
+ std::vector<int> sizes = mSizes[dim];
+
+ // Compute minimum sizes.
+ for (int gridY = 0; gridY < gridH; ++gridY)
+ {
+ for (int gridX = 0; gridX < gridW; ++gridX)
+ {
+ LayoutCell const *cell = mCells[gridY][gridX];
+ if (!cell || cell->mType == LayoutCell::NONE)
+ continue;
+
+ if (cell->mExtent[dim] == 1)
+ {
+ int n = (dim == 0 ? gridX : gridY);
+ int s = cell->mSize[dim] + cell->mPadding * 2;
+ if (s > sizes[n]) sizes[n] = s;
+ }
+ }
+ }
+
+ if (upp == Layout::AUTO_DEF) return sizes;
+
+ // Compute the FILL sizes.
+ int nb = static_cast<int>(sizes.size());
+ int nbFill = 0;
+ for (int i = 0; i < nb; ++i)
+ {
+ if (mSizes[dim][i] <= Layout::AUTO_DEF)
+ {
+ ++nbFill;
+ if (mSizes[dim][i] == Layout::AUTO_SET ||
+ sizes[i] <= Layout::AUTO_DEF)
+ {
+ sizes[i] = 0;
+ }
+ }
+ upp -= sizes[i] + mSpacing;
+ }
+ upp = upp + mSpacing;
+
+ if (nbFill == 0)
+ return sizes;
+
+ for (int i = 0; i < nb; ++i)
+ {
+ if (mSizes[dim][i] > Layout::AUTO_DEF)
+ continue;
+
+ int s = upp / nbFill;
+ sizes[i] += s;
+ upp -= s;
+ --nbFill;
+ }
+
+ return sizes;
+}
+
+int LayoutArray::getSize(int dim) const
+{
+ std::vector<int> sizes = getSizes(dim, Layout::AUTO_DEF);
+ int size = 0;
+ int nb = static_cast<int>(sizes.size());
+ for (int i = 0; i < nb; ++i)
+ {
+ if (sizes[i] > Layout::AUTO_DEF)
+ size += sizes[i];
+ size += mSpacing;
+ }
+ return size - mSpacing;
+}
+
+void LayoutArray::reflow(int nx, int ny, int nw, int nh)
+{
+ int gridW = static_cast<int>(mSizes[0].size()),
+ gridH = static_cast<int>(mSizes[1].size());
+
+ std::vector<int> widths = getSizes(0, nw);
+ std::vector<int> heights = getSizes(1, nh);
+
+ int y = ny;
+ for (int gridY = 0; gridY < gridH; ++gridY)
+ {
+ int x = nx;
+ for (int gridX = 0; gridX < gridW; ++gridX)
+ {
+ LayoutCell *cell = mCells[gridY][gridX];
+ if (cell && cell->mType != LayoutCell::NONE)
+ {
+ int dx = x, dy = y, dw, dh;
+ align(dx, dw, 0, *cell, &widths[gridX]);
+ align(dy, dh, 1, *cell, &heights[gridY]);
+ cell->reflow(dx, dy, dw, dh);
+ }
+ x += widths[gridX] + mSpacing;
+ }
+ y += heights[gridY] + mSpacing;
+ }
+}
+
+Layout::Layout(): mComputed(false)
+{
+ getArray();
+ setPadding(6);
+}
+
+void Layout::reflow(int &nw, int &nh)
+{
+ if (!mComputed)
+ {
+ computeSizes();
+ mComputed = true;
+ }
+
+ nw = (nw == 0 ? mSize[0] + 2 * mPadding : nw);
+ nh = (nh == 0 ? mSize[1] + 2 * mPadding : nh);
+ LayoutCell::reflow(0, 0, nw, nh);
+}