summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillaume Melquiond <guillaume.melquiond@gmail.com>2007-10-27 20:23:48 +0000
committerGuillaume Melquiond <guillaume.melquiond@gmail.com>2007-10-27 20:23:48 +0000
commit97bbe57e21a28544646da087e2a522390bf2ce5c (patch)
treea1899345f3b3928b3b0747b2429a45d08f79ae59
parentee15a808a1e0d36167f80d9f96147103ba5583de (diff)
downloadmana-client-97bbe57e21a28544646da087e2a522390bf2ce5c.tar.gz
mana-client-97bbe57e21a28544646da087e2a522390bf2ce5c.tar.bz2
mana-client-97bbe57e21a28544646da087e2a522390bf2ce5c.tar.xz
mana-client-97bbe57e21a28544646da087e2a522390bf2ce5c.zip
Improved layout handler to support trees of nested arrays. Needed for converting and fixing the trade window.
-rw-r--r--ChangeLog6
-rw-r--r--src/gui/buy.cpp1
-rw-r--r--src/gui/char_select.cpp18
-rw-r--r--src/gui/login.cpp6
-rw-r--r--src/gui/serverdialog.cpp12
-rw-r--r--src/gui/widgets/layout.cpp231
-rw-r--r--src/gui/widgets/layout.h276
-rw-r--r--src/gui/window.cpp30
-rw-r--r--src/gui/window.h26
9 files changed, 407 insertions, 199 deletions
diff --git a/ChangeLog b/ChangeLog
index d382dd8b..5d2d6221 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -14,6 +14,12 @@
src/resources/monsterinfo.cpp, src/resources/itemdb.cpp: Fixed missing
dependencies, spurious const qualifiers, and weak brackets, so that it
compiles with GCC 4.3.
+ * src/gui/widgets/layout.h, src/gui/widgets/layout.cpp: Improved layout
+ handler to support trees of nested arrays.
+ * src/gui/window.cpp, src/gui/window.h: Removed unused function
+ updateContentSize. Simplified layout accessors.
+ * src/gui/char_select.cpp: Replaced flushed-layout hack with a tree.
+ * src/gui/serverdialog.cpp, src/gui/buy.cpp: Simplified layouts.
2007-10-24 Bjørn Lindeijer <bjorn@lindeijer.nl>
diff --git a/src/gui/buy.cpp b/src/gui/buy.cpp
index b95a8b25..bf449b2f 100644
--- a/src/gui/buy.cpp
+++ b/src/gui/buy.cpp
@@ -87,7 +87,6 @@ BuyDialog::BuyDialog():
place(4, 5, mQuitButton);
Layout &layout = getLayout();
layout.setRowHeight(0, Layout::FILL);
- layout.setColWidth(2, Layout::FILL);
loadWindowState("Buy");
setLocationRelativeTo(getParent());
diff --git a/src/gui/char_select.cpp b/src/gui/char_select.cpp
index f7042c70..c2306880 100644
--- a/src/gui/char_select.cpp
+++ b/src/gui/char_select.cpp
@@ -100,9 +100,11 @@ CharSelectDialog::CharSelectDialog(LockedArray<LocalPlayer*> *charInfo,
mNameLabel = new gcn::Label(strprintf(_("Name: %s"), ""));
mLevelLabel = new gcn::Label(strprintf(_("Level: %d"), 0));
mMoneyLabel = new gcn::Label(strprintf(_("Money: %d"), 0));
- mPlayerBox = new PlayerBox();
+ mPlayerBox = new PlayerBox;
+ mPlayerBox->setWidth(74);
- Layout &layout = getLayout();
+ ContainerPlacer place;
+ place = getPlacer(0, 0);
place(0, 0, mPlayerBox, 1, 5).setPadding(3);
place(1, 0, mNameLabel, 3);
place(1, 1, mLevelLabel, 3);
@@ -111,18 +113,12 @@ CharSelectDialog::CharSelectDialog(LockedArray<LocalPlayer*> *charInfo,
place(2, 3, mNextButton);
place(1, 4, mNewCharButton);
place(2, 4, mDelCharButton);
- layout.setWidth(250);
- layout.setColWidth(0, 80);
- layout.setColWidth(3, Layout::FILL);
- layout.matchColWidth(1, 2);
- layout.setRowHeight(5, 5);
- layout.flush();
+ place.getCell().matchColWidth(1, 2);
+ place = getPlacer(0, 1);
place(0, 0, mUnRegisterButton);
place(2, 0, mSelectButton);
place(3, 0, mCancelButton);
- layout.setColWidth(1, Layout::FILL);
- reflowLayout();
- forgetLayout();
+ reflowLayout(250, 0);
setLocationRelativeTo(getParent());
setVisible(true);
diff --git a/src/gui/login.cpp b/src/gui/login.cpp
index f35f427a..565277a0 100644
--- a/src/gui/login.cpp
+++ b/src/gui/login.cpp
@@ -66,12 +66,10 @@ LoginDialog::LoginDialog(LoginData *loginData):
place(1, 0, mUserField, 3).setPadding(2);
place(1, 1, mPassField, 3).setPadding(2);
place(0, 2, mKeepCheck, 4);
- place(0, 3, mRegisterButton).setHAlign(Cell::LEFT);
+ place(0, 3, mRegisterButton).setHAlign(LayoutCell::LEFT);
place(2, 3, mOkButton);
place(3, 3, mCancelButton);
- getLayout().setColWidth(1, 20);
- reflowLayout();
- forgetLayout();
+ reflowLayout(250, 0);
setLocationRelativeTo(getParent());
setVisible(true);
diff --git a/src/gui/serverdialog.cpp b/src/gui/serverdialog.cpp
index 70ed7fe8..00490d31 100644
--- a/src/gui/serverdialog.cpp
+++ b/src/gui/serverdialog.cpp
@@ -120,16 +120,12 @@ ServerDialog::ServerDialog(LoginData *loginData):
place(0, 0, serverLabel);
place(0, 1, portLabel);
- place(1, 0, mServerNameField, 3).setPadding(3);
- place(1, 1, mPortField, 3).setPadding(3);
- place(0, 2, mMostUsedServersDropDown, 4).setPadding(3);
+ place(1, 0, mServerNameField, 3).setPadding(2);
+ place(1, 1, mPortField, 3).setPadding(2);
+ place(0, 2, mMostUsedServersDropDown, 4).setPadding(2);
place(2, 3, mOkButton);
place(3, 3, mCancelButton);
- Layout &layout = getLayout();
- layout.setWidth(250);
- layout.setColWidth(1, Layout::FILL);
- reflowLayout();
- forgetLayout();
+ reflowLayout(250, 0);
setLocationRelativeTo(getParent());
setVisible(true);
diff --git a/src/gui/widgets/layout.cpp b/src/gui/widgets/layout.cpp
index 252cf198..2bcc558d 100644
--- a/src/gui/widgets/layout.cpp
+++ b/src/gui/widgets/layout.cpp
@@ -21,9 +21,103 @@
* $Id$
*/
+#include <cassert>
+
#include "layout.h"
-void Layout::resizeGrid(int w, int h)
+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;
+}
+
+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);
+
+ for (std::vector< std::vector< LayoutCell * > >::iterator
+ i = mArray->mCells.begin(), i_end = mArray->mCells.end();
+ i != i_end; ++i)
+ {
+ for (std::vector< LayoutCell * >::iterator
+ j = i->begin(), j_end = i->end(); j != j_end; ++j)
+ {
+ LayoutCell *cell = *j;
+ if (cell && cell->mType == ARRAY) cell->computeSizes();
+ }
+ }
+
+ mSize[0] = mArray->getSize(0);
+ mSize[1] = mArray->getSize(1);
+}
+
+LayoutArray::LayoutArray(): mSpacing(4)
+{
+}
+
+LayoutArray::~LayoutArray()
+{
+ for (std::vector< std::vector< LayoutCell * > >::iterator
+ i = mCells.begin(), i_end = mCells.end(); i != i_end; ++i)
+ {
+ for (std::vector< LayoutCell * >::iterator
+ j = i->begin(), j_end = i->end(); j != j_end; ++j)
+ {
+ delete *j;
+ }
+ }
+}
+
+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 > (int)mSizes[0].size(),
extH = h && h > (int)mSizes[1].size();
@@ -31,127 +125,128 @@ void Layout::resizeGrid(int w, int h)
if (extH)
{
- mSizes[1].resize(h, FILL);
+ mSizes[1].resize(h, Layout::FILL);
mCells.resize(h);
if (!extW) w = mSizes[0].size();
}
if (extW)
{
- mSizes[0].resize(w, FILL);
+ mSizes[0].resize(w, Layout::FILL);
}
- for (std::vector< std::vector< Cell > >::iterator
+ for (std::vector< std::vector< LayoutCell * > >::iterator
i = mCells.begin(), i_end = mCells.end(); i != i_end; ++i)
{
- i->resize(w);
+ i->resize(w, NULL);
}
}
-void Layout::setColWidth(int n, int w)
+void LayoutArray::setColWidth(int n, int w)
{
resizeGrid(n + 1, 0);
mSizes[0][n] = w;
}
-void Layout::setRowHeight(int n, int h)
+void LayoutArray::setRowHeight(int n, int h)
{
resizeGrid(0, n + 1);
mSizes[1][n] = h;
}
-void Layout::matchColWidth(int n1, int n2)
+void LayoutArray::matchColWidth(int n1, int n2)
{
resizeGrid(std::max(n1, n2) + 1, 0);
- std::vector< int > widths = compute(0, mW);
+ std::vector< short > widths = getSizes(0, Layout::FILL);
int s = std::max(widths[n1], widths[n2]);
mSizes[0][n1] = s;
mSizes[0][n2] = s;
}
-Cell &Layout::place(gcn::Widget *widget, int x, int y, int w, int h)
+LayoutCell &LayoutArray::place(gcn::Widget *widget, int x, int y, int w, int h)
{
- resizeGrid(x + w, y + h);
- Cell &cell = mCells[y][x];
+ LayoutCell &cell = at(x, y, w, h);
+ assert(cell.mType == LayoutCell::NONE);
+ cell.mType = LayoutCell::WIDGET;
cell.mWidget = widget;
+ cell.mSize[0] = w == 1 ? widget->getWidth() : 0;
+ cell.mSize[1] = h == 1 ? widget->getHeight() : 0;
cell.mExtent[0] = w;
cell.mExtent[1] = h;
cell.mPadding = 0;
- cell.mAlign[0] = Cell::FILL;
- cell.mAlign[1] = Cell::FILL;
- int &cs = mSizes[0][x], &rs = mSizes[1][y];
- if (cs == FILL) cs = 0;
- if (rs == FILL) rs = 0;
+ cell.mAlign[0] = LayoutCell::FILL;
+ cell.mAlign[1] = LayoutCell::FILL;
+ short &cs = mSizes[0][x], &rs = mSizes[1][y];
+ if (cs == Layout::FILL && w == 1) cs = 0;
+ if (rs == Layout::FILL && h == 1) rs = 0;
return cell;
}
-void Layout::align(int &pos, int &size, int dim,
- Cell const &cell, int *sizes) const
+void LayoutArray::align(int &pos, int &size, int dim,
+ LayoutCell const &cell, short *sizes) const
{
- int size_max = sizes[0] - cell.mPadding * 2;
+ int size_max = sizes[0];
for (int i = 1; i < cell.mExtent[dim]; ++i)
size_max += sizes[i] + mSpacing;
- size = std::min(dim == 0 ? cell.mWidget->getWidth()
- : cell.mWidget->getHeight(), size_max);
- pos += cell.mPadding;
+ size = std::min<int>(cell.mSize[dim], size_max);
switch (cell.mAlign[dim])
{
- case Cell::LEFT:
+ case LayoutCell::LEFT:
return;
- case Cell::RIGHT:
+ case LayoutCell::RIGHT:
pos += size_max - size;
return;
- case Cell::CENTER:
+ case LayoutCell::CENTER:
pos += (size_max - size) / 2;
return;
- case Cell::FILL:
+ case LayoutCell::FILL:
size = size_max;
return;
}
}
-std::vector< int > Layout::compute(int dim, int upp) const
+std::vector< short > LayoutArray::getSizes(int dim, int upp) const
{
int gridW = mSizes[0].size(), gridH = mSizes[1].size();
- std::vector< int > sizes = mSizes[dim];
+ std::vector< short > sizes = mSizes[dim];
// Compute minimum sizes.
for (int gridY = 0; gridY < gridH; ++gridY)
{
for (int gridX = 0; gridX < gridW; ++gridX)
{
- Cell const &cell = mCells[gridY][gridX];
- if (!cell.mWidget) continue;
+ LayoutCell const *cell = mCells[gridY][gridX];
+ if (!cell || cell->mType == LayoutCell::NONE) continue;
- if (cell.mExtent[dim] == 1)
+ if (cell->mExtent[dim] == 1)
{
- int s = dim == 0 ? cell.mWidget->getWidth()
- : cell.mWidget->getHeight();
int n = dim == 0 ? gridX : gridY;
- s += cell.mPadding * 2;
+ int s = cell->mSize[dim] + cell->mPadding * 2;
if (s > sizes[n]) sizes[n] = s;
}
}
}
+ if (upp == Layout::FILL) return sizes;
+
// Compute the FILL sizes.
int nb = sizes.size();
int nbFill = 0;
for (int i = 0; i < nb; ++i)
{
- if (mSizes[dim][i] == FILL) ++nbFill;
- if (sizes[i] == FILL) sizes[i] = 0;
+ if (mSizes[dim][i] == Layout::FILL) ++nbFill;
+ if (sizes[i] == Layout::FILL) sizes[i] = 0;
else upp -= sizes[i];
upp -= mSpacing;
}
- upp = upp + mSpacing - mMargin * 2;
+ upp = upp + mSpacing;
if (nbFill == 0) return sizes;
for (int i = 0; i < nb; ++i)
{
- if (mSizes[dim][i] != FILL) continue;
+ if (mSizes[dim][i] != Layout::FILL) continue;
int s = upp / nbFill;
sizes[i] += s;
upp -= s;
@@ -161,43 +256,61 @@ std::vector< int > Layout::compute(int dim, int upp) const
return sizes;
}
-void Layout::reflow(int &nw, int &nh)
+int LayoutArray::getSize(int dim) const
+{
+ std::vector< short > sizes = getSizes(dim, Layout::FILL);
+ int size = 0;
+ int nb = sizes.size();
+ for (int i = 0; i < nb; ++i)
+ {
+ if (sizes[i] != Layout::FILL) size += sizes[i];
+ size += mSpacing;
+ }
+ return size - mSpacing;
+}
+
+void LayoutArray::reflow(int nx, int ny, int nw, int nh)
{
int gridW = mSizes[0].size(), gridH = mSizes[1].size();
- std::vector< int > widths = compute(0, mW);
- std::vector< int > heights = compute(1, mH);
+ std::vector< short > widths = getSizes(0, nw);
+ std::vector< short > heights = getSizes(1, nh);
- int x, y = mY + mMargin;
+ int y = ny;
for (int gridY = 0; gridY < gridH; ++gridY)
{
- x = mX + mMargin;
+ int x = nx;
for (int gridX = 0; gridX < gridW; ++gridX)
{
- Cell &cell = mCells[gridY][gridX];
- if (cell.mWidget)
+ 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.mWidget->setDimension(gcn::Rectangle(dx, dy, 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;
}
+}
- nw = x - mX - mSpacing + mMargin;
- nh = y - mY - mSpacing + mMargin;
+Layout::Layout(): mComputed(false)
+{
+ getArray();
+ setPadding(6);
}
-void Layout::flush()
+void Layout::reflow(int &nw, int &nh)
{
- int w, h;
- reflow(w, h);
- mY += h;
- mW = w;
- mSizes[0].clear();
- mSizes[1].clear();
- mCells.clear();
+ 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);
}
diff --git a/src/gui/widgets/layout.h b/src/gui/widgets/layout.h
index ae03ed9d..a6e630c2 100644
--- a/src/gui/widgets/layout.h
+++ b/src/gui/widgets/layout.h
@@ -26,172 +26,280 @@
#include <vector>
-#include <guichan/widget.hpp>
+#include <guichan/widgets/container.hpp>
+
+class LayoutCell;
/**
- * This class describes the formatting of a widget in the cell of a layout
- * table. Horizontally, a widget can either fill the width of the cell (minus
- * the cell padding), or it can retain its size and be flushed left, or flush
- * right, or centered in the cell. The process is similar for the vertical
- * alignment, except that top is represented by LEFT and bottom by RIGHT.
+ * This class is a helper for adding widgets to nested tables in a window.
*/
-class Cell
+class ContainerPlacer
{
- friend class Layout;
-
public:
- enum Alignment
- {
- LEFT, RIGHT, CENTER, FILL
- };
-
- Cell(): mWidget(0) {}
+ ContainerPlacer(gcn::Container *c = NULL, LayoutCell *l = NULL):
+ mContainer(c), mCell(l)
+ {}
/**
- * Sets the padding around the cell content.
+ * Gets the pointed cell.
*/
- Cell &setPadding(int p)
- { mPadding = p; return *this; }
+ LayoutCell &getCell()
+ { return *mCell; }
/**
- * Sets the horizontal alignment of the cell content.
+ * Returns a placer for the same container but to an inner cell.
*/
- Cell &setHAlign(Alignment a)
- { mAlign[0] = a; return *this; }
+ ContainerPlacer at(int x, int y);
/**
- * Sets the vertical alignment of the cell content.
+ * Adds the given widget to the container and places it in the layout.
+ * @see LayoutArray::place
*/
- Cell &setVAlign(Alignment a)
- { mAlign[1] = a; return *this; }
+ LayoutCell &operator()
+ (int x, int y, gcn::Widget *, int w = 1, int h = 1);
private:
- gcn::Widget *mWidget;
- int mPadding;
- int mExtent[2];
- Alignment mAlign[2];
+ gcn::Container *mContainer;
+ LayoutCell *mCell;
};
/**
- * This class is an helper for setting the position of widgets. They are
- * positioned along the cells of a rectangular table.
- *
- * The size of a given table column can either be set manually or be chosen
- * from the widest widget of the column. An empty column has a FILL width,
- * which means it will be extended so that the layout fits its minimum width.
- *
- * The process is similar for table rows. By default, there is a spacing of 4
- * pixels between rows and between columns, and a margin of 6 pixels around the
- * whole layout.
+ * This class contains a rectangular array of cells.
*/
-class Layout
+class LayoutArray
{
+ friend class LayoutCell;
+
public:
- Layout(): mSpacing(4), mMargin(6), mX(0), mY(0), mW(0), mH(0) {}
+ LayoutArray();
+
+ ~LayoutArray();
/**
- * Gets the x-coordinate of the top left point of the layout.
+ * Returns a reference on the cell at given position.
*/
- int getX() const
- { return mX; }
+ LayoutCell &at(int x, int y, int w = 1, int h = 1);
/**
- * Gets the y-coordinate of the top left point of the layout.
+ * Places a widget in a given cell.
+ * @param w number of columns the widget spawns.
+ * @param h number of rows the widget spawns.
+ * @note When @a w is 1, the width of column @a x is reset to zero if
+ * it was FILL.
*/
- int getY() const
- { return mY; }
+ LayoutCell &place(gcn::Widget *, int x, int y, int w = 1, int h = 1);
/**
* Sets the minimum width of a column.
- * @note Setting the width to FILL and then placing a widget in the
- * column will reset the width to zero.
*/
void setColWidth(int n, int w);
/**
* Sets the minimum height of a row.
- * @note Setting the height to FILL and then placing a widget in the
- * row will reset the height to zero.
*/
void setRowHeight(int n, int h);
/**
- * Matchs widths of two columns.
+ * Sets the widths of two columns to the maximum of their widths.
*/
void matchColWidth(int n1, int n2);
/**
- * Sets the minimum width of the layout.
+ * Computes and sets the positions of all the widgets.
+ * @param nW width of the array, used to resize the FILL columns.
+ * @param nH height of the array, used to resize the FILL rows.
*/
- void setWidth(int w)
- { mW = w; }
+ void reflow(int nX, int nY, int nW, int nH);
+
+ private:
+
+ // Copy not allowed, as the array owns all its cells.
+ LayoutArray(LayoutArray const &);
+ LayoutArray &operator=(LayoutArray const &);
/**
- * Sets the minimum height of the layout.
+ * Gets the position and size of a widget along a given axis
*/
- void setHeight(int h)
- { mH = h; }
+ void align(int &pos, int &size, int dim,
+ LayoutCell const &cell, short *sizes) const;
/**
- * Sets the spacing between cells.
+ * Ensures the private vectors are large enough.
*/
- void setSpacing(int p)
- { mSpacing = p; }
+ void resizeGrid(int w, int h);
/**
- * Sets the margin around the layout.
+ * Gets the column/row sizes along a given axis.
+ * @param upp target size for the array. Ignored if FILL.
*/
- void setMargin(int m)
- { mMargin = m; }
+ std::vector< short > getSizes(int dim, int upp) const;
/**
- * Places a widget in a given cell.
+ * Gets the total size along a given axis.
*/
- Cell &place(gcn::Widget *, int x, int y, int w, int h);
+ int getSize(int dim) const;
+
+ std::vector< short > mSizes[2];
+ std::vector< std::vector < LayoutCell * > > mCells;
+
+ char mSpacing;
+};
+
+/**
+ * This class describes the formatting of a widget in the cell of a layout
+ * table. Horizontally, a widget can either fill the width of the cell (minus
+ * the cell padding), or it can retain its size and be flushed left, or flush
+ * right, or centered in the cell. The process is similar for the vertical
+ * alignment, except that top is represented by LEFT and bottom by RIGHT.
+ */
+class LayoutCell
+{
+ friend class Layout;
+ friend class LayoutArray;
+
+ public:
+
+ enum Alignment
+ {
+ LEFT, RIGHT, CENTER, FILL
+ };
+
+ LayoutCell(): mType(NONE) {}
+
+ ~LayoutCell();
/**
- * Computes the positions of all the widgets. Returns the size of the
- * layout.
+ * Sets the padding around the cell content.
*/
- void reflow(int &nW, int &nH);
+ LayoutCell &setPadding(int p)
+ { mPadding = p; return *this; }
+
+ /**
+ * Sets the horizontal alignment of the cell content.
+ */
+ LayoutCell &setHAlign(Alignment a)
+ { mAlign[0] = a; return *this; }
+
+ /**
+ * Sets the vertical alignment of the cell content.
+ */
+ LayoutCell &setVAlign(Alignment a)
+ { mAlign[1] = a; return *this; }
+
+ /**
+ * @see LayoutArray::at
+ */
+ LayoutCell &at(int x, int y)
+ { return getArray().at(x, y); }
+
+ /**
+ * @see LayoutArray::place
+ */
+ LayoutCell &place(gcn::Widget *wg, int x, int y, int w = 1, int h = 1)
+ { return getArray().place(wg, x, y, w, h); }
/**
- * Reflows the current layout. Then starts a new layout just below with
- * the same width.
+ * @see LayoutArray::matchColWidth
*/
- void flush();
+ void matchColWidth(int n1, int n2)
+ { getArray().matchColWidth(n1, n2); }
+
+ /**
+ * @see LayoutArray::setColWidth
+ */
+ void setColWidth(int n, int w)
+ { getArray().setColWidth(n, w); }
+
+ /**
+ * @see LayoutArray::setRowHeight
+ */
+ void setRowHeight(int n, int h)
+ { getArray().setRowHeight(n, h); }
+
+ /**
+ * Sets the minimum widths and heights of this cell and of all the
+ * inner cells.
+ */
+ void computeSizes();
+
+ private:
+
+ // Copy not allowed, as the cell may own an array.
+ LayoutCell(LayoutCell const &);
+ LayoutCell &operator=(LayoutCell const &);
+
+ union
+ {
+ gcn::Widget *mWidget;
+ LayoutArray *mArray;
+ };
enum
{
- FILL = -42, /**< Expand until the layout as the expected size. */
+ NONE, WIDGET, ARRAY
};
- private:
+ /**
+ * Returns the embedded array. Creates it if the cell does not contain
+ * anything yet. Aborts if it contains a widget.
+ */
+ LayoutArray &getArray();
/**
- * Gets the position and size of a widget along a given axis
+ * @see LayoutArray::reflow
*/
- void align(int &pos, int &size, int dim,
- Cell const &cell, int *sizes) const;
+ void reflow(int nx, int ny, int nw, int nh);
+
+ short mSize[2];
+ char mPadding;
+ char mExtent[2];
+ char mAlign[2];
+ char mNbFill[2];
+ char mType;
+};
+
+/**
+ * This class is an helper for setting the position of widgets. They are
+ * positioned along the cells of some rectangular tables. The layout may either
+ * be a single table or a tree of nested tables.
+ *
+ * The size of a given table column can either be set manually or be chosen
+ * from the widest widget of the column. An empty column has a FILL width,
+ * which means it will be extended so that the layout fits its minimum width.
+ *
+ * The process is similar for table rows. By default, there is a spacing of 4
+ * pixels between rows and between columns, and a margin of 6 pixels around the
+ * whole layout.
+ */
+class Layout: public LayoutCell
+{
+ public:
+
+ Layout();
/**
- * Ensures the private vectors are large enough.
+ * Sets the margin around the layout.
*/
- void resizeGrid(int w, int h);
+ void setMargin(int m)
+ { setPadding(m); }
/**
- * Gets the column/row sizes along a given axis.
+ * Sets the positions of all the widgets.
+ * @see LayoutArray::reflow
*/
- std::vector< int > compute(int dim, int upp) const;
+ void reflow(int &nW, int &nH);
+
+ enum
+ {
+ FILL = -42, /**< Expand until the layout as the expected size. */
+ };
- std::vector< int > mSizes[2];
- std::vector< std::vector < Cell > > mCells;
+ private:
- int mSpacing, mMargin;
- int mX, mY, mW, mH, mNW, mNH;
+ bool mComputed;
};
#endif
diff --git a/src/gui/window.cpp b/src/gui/window.cpp
index 68b79367..7629e2e7 100644
--- a/src/gui/window.cpp
+++ b/src/gui/window.cpp
@@ -22,6 +22,7 @@
*/
#include <algorithm>
+#include <cassert>
#include <climits>
#include <guichan/exception.hpp>
@@ -234,9 +235,8 @@ void Window::setSize(int width, int height)
if (mLayout)
{
- mLayout->setWidth(width - 2 * getPadding());
- mLayout->setHeight(height - getPadding() - getTitleBarHeight());
- int w, h;
+ int w = width - 2 * getPadding(),
+ h = height - getPadding() - getTitleBarHeight();
mLayout->reflow(w, h);
}
@@ -520,7 +520,6 @@ void Window::mouseDragged(gcn::MouseEvent &event)
// Set the new window and content dimensions
setDimension(newDim);
- updateContentSize();
}
}
@@ -555,7 +554,6 @@ void Window::resetToDefaultSize()
{
setPosition(mDefaultX, mDefaultY);
setContentSize(mDefaultWidth, mDefaultHeight);
- updateContentSize();
}
int Window::getResizeHandles(gcn::MouseEvent &event)
@@ -611,28 +609,22 @@ Layout &Window::getLayout()
return *mLayout;
}
-void Window::forgetLayout()
+LayoutCell &Window::place(int x, int y, gcn::Widget *wg, int w, int h)
{
- delete mLayout;
- mLayout = 0;
+ add(wg);
+ return getLayout().place(wg, x, y, w, h);
}
-Cell &Window::place(int x, int y, gcn::Widget *wg, int w, int h)
+ContainerPlacer Window::getPlacer(int x, int y)
{
- add(wg);
- return getLayout().place(wg, x, y, w, h);
+ return ContainerPlacer(this, &getLayout().at(x, y));
}
-void Window::reflowLayout()
+void Window::reflowLayout(int w, int h)
{
- if (!mLayout) return;
- int w, h;
+ assert(mLayout);
mLayout->reflow(w, h);
- w += mLayout->getX();
- h += mLayout->getY();
- Layout *tmp = mLayout;
- // Hide it so that the incoming resize does not reflow the layout again.
+ delete mLayout;
mLayout = NULL;
setContentSize(w, h);
- mLayout = tmp;
}
diff --git a/src/gui/window.h b/src/gui/window.h
index a09a9bbc..df756be3 100644
--- a/src/gui/window.h
+++ b/src/gui/window.h
@@ -30,11 +30,12 @@
#include "windowlistener.h"
-class Cell;
class ConfigListener;
+class ContainerPlacer;
class Image;
class ImageRect;
class Layout;
+class LayoutCell;
class ResizeGrip;
class WindowContainer;
@@ -93,12 +94,6 @@ class Window : public gcn::Window
void setContentSize(int width, int height);
/**
- * Called when either the user or resetToDefaultSize resizes the
- * windows, so that the windows can manage its widgets.
- */
- virtual void updateContentSize() {}
-
- /**
* Sets the size of this window.
*/
void setSize(int width, int height);
@@ -292,19 +287,24 @@ class Window : public gcn::Window
Layout &getLayout();
/**
- * Deletes the layout handler.
+ * Computes the position of the widgets according to the current
+ * layout. Resizes the window so that the layout fits. Deletes the
+ * layout.
+ * @param w if non-zero, force the window to this width.
+ * @param h if non-zero, force the window to this height.
+ * @note This function is meant to be called with fixed-size windows.
*/
- void forgetLayout();
+ void reflowLayout(int w = 0, int h = 0);
/**
- * Resizes the window after computing the position of the widgets.
+ * Adds a widget to the window and sets it at given cell.
*/
- void reflowLayout();
+ LayoutCell &place(int x, int y, gcn::Widget *, int w = 1, int h = 1);
/**
- * Adds a widget to the window and sets it at given cell.
+ * Returns a proxy for adding widgets in an inner table of the layout.
*/
- Cell &place(int x, int y, gcn::Widget *, int w = 1, int h = 1);
+ ContainerPlacer getPlacer(int x, int y);
private:
/**