summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjørn Lindeijer <bjorn@lindeijer.nl>2009-01-06 17:50:52 +0100
committerIra Rice <irarice@gmail.com>2009-01-06 11:06:36 -0700
commit14a12cbe586f88ace6d90d9ca5b802edfb546768 (patch)
tree74a724358ec376df7347e566dab1f23fde92751a
parent394989d94081cbc462ba7d567fc88d0c4dff8134 (diff)
downloadmana-client-14a12cbe586f88ace6d90d9ca5b802edfb546768.tar.gz
mana-client-14a12cbe586f88ace6d90d9ca5b802edfb546768.tar.bz2
mana-client-14a12cbe586f88ace6d90d9ca5b802edfb546768.tar.xz
mana-client-14a12cbe586f88ace6d90d9ca5b802edfb546768.zip
Merged layout handler from mainline
Also fixed login dialog layout. This is the layout handler by Guillaume Melquiond, which he started in commit 59472ef68fdef3f7e8858a81a46e28c127119c58.
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/Makefile.am2
-rw-r--r--src/gui/login.cpp77
-rw-r--r--src/gui/popupmenu.cpp2
-rw-r--r--src/gui/widgets/layout.cpp327
-rw-r--r--src/gui/widgets/layout.h320
-rw-r--r--src/gui/window.cpp83
-rw-r--r--src/gui/window.h62
8 files changed, 762 insertions, 113 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 7f0560b5..f270f35c 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -53,6 +53,8 @@ SET(SRCS
gui/widgets/dropdown.h
gui/widgets/resizegrip.cpp
gui/widgets/resizegrip.h
+ gui/widgets/layout.cpp
+ gui/widgets/layout.h
gui/box.cpp
gui/box.h
gui/browserbox.cpp
diff --git a/src/Makefile.am b/src/Makefile.am
index d59198d1..ded201b7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,6 +1,8 @@
bin_PROGRAMS = aethyra
aethyra_SOURCES = gui/widgets/dropdown.cpp \
gui/widgets/dropdown.h \
+ gui/widgets/layout.cpp \
+ gui/widgets/layout.h \
gui/widgets/resizegrip.cpp \
gui/widgets/resizegrip.h \
gui/box.cpp \
diff --git a/src/gui/login.cpp b/src/gui/login.cpp
index 8fd77f44..18976b46 100644
--- a/src/gui/login.cpp
+++ b/src/gui/login.cpp
@@ -31,6 +31,8 @@
#include "passwordfield.h"
#include "textfield.h"
+#include "widgets/layout.h"
+
#include "../main.h"
#include "../logindata.h"
#include "../configuration.h"
@@ -75,43 +77,6 @@ LoginDialog::LoginDialog(LoginData *loginData):
mCancelButton = new Button(_("Cancel"), "cancel", this);
mRegisterButton = new Button(_("Register"), "register", this);
- setContentSize(LOGIN_DIALOG_WIDTH, LOGIN_DIALOG_HEIGHT);
-
- const int USER_TOP = 5;
- userLabel->setPosition(5, USER_TOP);
- mUserField->setPosition(65, USER_TOP);
- mUserField->setWidth(FIELD_WIDTH);
-
- const int PASS_TOP = 9 + USER_TOP + userLabel->getHeight();
- passLabel->setPosition(5, PASS_TOP);
- mPassField->setPosition(65, PASS_TOP);
- mPassField->setWidth(FIELD_WIDTH);
-
- const int SERVER_TOP = 9 + PASS_TOP + passLabel->getHeight();
- serverLabel->setPosition(5, SERVER_TOP);
- mServerField->setPosition(65, SERVER_TOP);
- mServerField->setWidth(FIELD_WIDTH);
-
- const int PORT_TOP = 9 + SERVER_TOP + serverLabel->getHeight();
- portLabel->setPosition(5, PORT_TOP);
- mPortField->setPosition(65, PORT_TOP);
- mPortField->setWidth(FIELD_WIDTH);
-
- const int DROP_DOWN_TOP = 9 + PORT_TOP + serverLabel->getHeight();
- dropdownLabel->setPosition(5, DROP_DOWN_TOP);
- mServerDropDown->setPosition(65, DROP_DOWN_TOP);
- mServerDropDown->setWidth(FIELD_WIDTH);
-
- const int REST_TOP = LOGIN_DIALOG_HEIGHT - mCancelButton->getHeight() - 5;
-
- mKeepCheck->setPosition(4, REST_TOP);
- mCancelButton->setPosition(
- LOGIN_DIALOG_WIDTH - mCancelButton->getWidth() - 5, REST_TOP);
- mOkButton->setPosition(
- mCancelButton->getX() - mOkButton->getWidth() - 5, REST_TOP);
- mRegisterButton->setPosition(
- mKeepCheck->getX() + mKeepCheck->getWidth() + 10, REST_TOP);
-
mUserField->setActionEventId("ok");
mPassField->setActionEventId("ok");
mServerField->setActionEventId("ok");
@@ -129,20 +94,21 @@ LoginDialog::LoginDialog(LoginData *loginData):
mServerDropDown->addActionListener(this);
mKeepCheck->addActionListener(this);
- add(userLabel);
- add(passLabel);
- add(serverLabel);
- add(portLabel);
- add(dropdownLabel);
- add(mUserField);
- add(mPassField);
- add(mServerField);
- add(mPortField);
- add(mServerDropDown);
- add(mKeepCheck);
- add(mOkButton);
- add(mCancelButton);
- add(mRegisterButton);
+ place(0, 0, userLabel);
+ place(0, 1, passLabel);
+ place(0, 2, serverLabel);
+ place(0, 3, portLabel);
+ place(0, 4, dropdownLabel);
+ place(1, 0, mUserField, 3).setPadding(2);
+ place(1, 1, mPassField, 3).setPadding(2);
+ place(1, 2, mServerField, 3).setPadding(2);
+ place(1, 3, mPortField, 3).setPadding(2);
+ place(1, 4, mServerDropDown, 3).setPadding(2);
+ place(0, 5, mKeepCheck, 4);
+ place(0, 6, mRegisterButton).setHAlign(LayoutCell::LEFT);
+ place(2, 6, mOkButton);
+ place(3, 6, mCancelButton);
+ reflowLayout(250, 0);
setLocationRelativeTo(getParent());
setVisible(true);
@@ -163,8 +129,7 @@ LoginDialog::~LoginDialog()
delete mServerScrollArea;
}
-void
-LoginDialog::action(const gcn::ActionEvent &event)
+void LoginDialog::action(const gcn::ActionEvent &event)
{
if (event.getId() == "ok" && canSubmit())
{
@@ -209,14 +174,12 @@ LoginDialog::action(const gcn::ActionEvent &event)
}
}
-void
-LoginDialog::keyPressed(gcn::KeyEvent &keyEvent)
+void LoginDialog::keyPressed(gcn::KeyEvent &keyEvent)
{
mOkButton->setEnabled(canSubmit());
}
-bool
-LoginDialog::canSubmit()
+bool LoginDialog::canSubmit()
{
return !mUserField->getText().empty() &&
!mPassField->getText().empty() &&
diff --git a/src/gui/popupmenu.cpp b/src/gui/popupmenu.cpp
index 2b2eb625..a02a9eae 100644
--- a/src/gui/popupmenu.cpp
+++ b/src/gui/popupmenu.cpp
@@ -56,7 +56,7 @@ PopupMenu::PopupMenu():
{
setResizable(false);
setTitleBarHeight(0);
- mShowTitle = false;
+ setShowTitle(false);
mBrowserBox = new BrowserBox();
mBrowserBox->setPosition(4, 4);
diff --git a/src/gui/widgets/layout.cpp b/src/gui/widgets/layout.cpp
new file mode 100644
index 00000000..bcc54cf7
--- /dev/null
+++ b/src/gui/widgets/layout.cpp
@@ -0,0 +1,327 @@
+/*
+ * The Mana World
+ * Copyright 2007 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World 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.
+ *
+ * The Mana World 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 The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <cassert>
+
+#include "layout.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();
+ if (!extW && !extH) return;
+
+ if (extH)
+ {
+ mSizes[1].resize(h, Layout::AUTO_DEF);
+ mCells.resize(h);
+ if (!extW) w = mSizes[0].size();
+ }
+
+ if (extW)
+ {
+ mSizes[0].resize(w, Layout::AUTO_DEF);
+ }
+
+ for (std::vector< std::vector< LayoutCell * > >::iterator
+ i = mCells.begin(), i_end = mCells.end(); i != i_end; ++i)
+ {
+ i->resize(w, NULL);
+ }
+}
+
+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< short > 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;
+ 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] = LayoutCell::FILL;
+ cell.mAlign[1] = LayoutCell::FILL;
+ short &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, short *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;
+ }
+}
+
+std::vector< short > LayoutArray::getSizes(int dim, int upp) const
+{
+ int gridW = mSizes[0].size(), gridH = mSizes[1].size();
+ std::vector< short > 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 = 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< short > sizes = getSizes(dim, Layout::AUTO_DEF);
+ int size = 0;
+ int nb = 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 = mSizes[0].size(), gridH = mSizes[1].size();
+
+ std::vector< short > widths = getSizes(0, nw);
+ std::vector< short > 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);
+}
diff --git a/src/gui/widgets/layout.h b/src/gui/widgets/layout.h
new file mode 100644
index 00000000..d631c154
--- /dev/null
+++ b/src/gui/widgets/layout.h
@@ -0,0 +1,320 @@
+/*
+ * The Mana World
+ * Copyright 2007 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World 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.
+ *
+ * The Mana World 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 The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _TMW_WIDGET_LAYOUT_H__
+#define _TMW_WIDGET_LAYOUT_H__
+
+#include <vector>
+
+#include <guichan/widgets/container.hpp>
+
+class LayoutCell;
+
+/**
+ * This class is a helper for adding widgets to nested tables in a window.
+ */
+class ContainerPlacer
+{
+ public:
+
+ ContainerPlacer(gcn::Container *c = NULL, LayoutCell *l = NULL):
+ mContainer(c), mCell(l)
+ {}
+
+ /**
+ * Gets the pointed cell.
+ */
+ LayoutCell &getCell()
+ { return *mCell; }
+
+ /**
+ * Returns a placer for the same container but to an inner cell.
+ */
+ ContainerPlacer at(int x, int y);
+
+ /**
+ * Adds the given widget to the container and places it in the layout.
+ * @see LayoutArray::place
+ */
+ LayoutCell &operator()
+ (int x, int y, gcn::Widget *, int w = 1, int h = 1);
+
+ private:
+
+ gcn::Container *mContainer;
+ LayoutCell *mCell;
+};
+
+/**
+ * This class contains a rectangular array of cells.
+ */
+class LayoutArray
+{
+ friend class LayoutCell;
+
+ public:
+
+ LayoutArray();
+
+ ~LayoutArray();
+
+ /**
+ * Returns a reference on the cell at given position.
+ */
+ LayoutCell &at(int x, int y, int w = 1, int h = 1);
+
+ /**
+ * 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 AUTO_DEF. Similarly for @a h.
+ */
+ LayoutCell &place(gcn::Widget *, int x, int y, int w = 1, int h = 1);
+
+ /**
+ * Sets the minimum width of a column.
+ */
+ void setColWidth(int n, int w);
+
+ /**
+ * Sets the minimum height of a row.
+ */
+ void setRowHeight(int n, int h);
+
+ /**
+ * Sets the widths of two columns to the maximum of their widths.
+ */
+ void matchColWidth(int n1, int n2);
+
+ /**
+ * Spawns a cell over several columns/rows.
+ */
+ void extend(int x, int y, int w, int h);
+
+ /**
+ * Computes and sets the positions of all the widgets.
+ * @param nW width of the array, used to resize the AUTO_ columns.
+ * @param nH height of the array, used to resize the AUTO_ rows.
+ */
+ 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 &);
+
+ /**
+ * Gets the position and size of a widget along a given axis
+ */
+ void align(int &pos, int &size, int dim,
+ LayoutCell const &cell, short *sizes) const;
+
+ /**
+ * Ensures the private vectors are large enough.
+ */
+ void resizeGrid(int w, int h);
+
+ /**
+ * Gets the column/row sizes along a given axis.
+ * @param upp target size for the array. Ignored if AUTO_DEF.
+ */
+ std::vector< short > getSizes(int dim, int upp) const;
+
+ /**
+ * Gets the total size along a given axis.
+ */
+ 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();
+
+ /**
+ * Sets the padding around the cell content.
+ */
+ 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); }
+
+ /**
+ * @see LayoutArray::matchColWidth
+ */
+ 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); }
+
+ /**
+ * @see LayoutArray::extend.
+ */
+ void extend(int x, int y, int w, int h)
+ { getArray().extend(x, y, w, 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
+ {
+ NONE, WIDGET, ARRAY
+ };
+
+ /**
+ * Returns the embedded array. Creates it if the cell does not contain
+ * anything yet. Aborts if it contains a widget.
+ */
+ LayoutArray &getArray();
+
+ /**
+ * @see LayoutArray::reflow
+ */
+ 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 AUTO_DEF 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();
+
+ /**
+ * Sets the margin around the layout.
+ */
+ void setMargin(int m)
+ { setPadding(m); }
+
+ /**
+ * Sets the positions of all the widgets.
+ * @see LayoutArray::reflow
+ */
+ void reflow(int &nW, int &nH);
+
+ /**
+ * When the minimum size of the layout is less than the available size,
+ * the remaining pixels are equally split amongst the FILL items.
+ */
+ enum
+ {
+ AUTO_DEF = -42, /**< Default value, behaves like AUTO_ADD. */
+ AUTO_SET = -43, /**< Uses the share as the new size. */
+ AUTO_ADD = -44 /**< Adds the share to the current size. */
+ };
+
+ private:
+
+ bool mComputed;
+};
+
+#endif
diff --git a/src/gui/window.cpp b/src/gui/window.cpp
index dee5832a..e82a370e 100644
--- a/src/gui/window.cpp
+++ b/src/gui/window.cpp
@@ -22,7 +22,6 @@
#include <algorithm>
#include <cassert>
#include <climits>
-#include <cassert>
#include <guichan/exception.hpp>
@@ -33,6 +32,7 @@
#include "window.h"
#include "windowcontainer.h"
+#include "widgets/layout.h"
#include "widgets/resizegrip.h"
#include "../configlistener.h"
@@ -68,6 +68,7 @@ Window::Window(const std::string& caption, bool modal, Window *parent, const std
gcn::Window(caption),
mGrip(0),
mParent(parent),
+ mLayout(NULL),
mWindowName("window"),
mShowTitle(true),
mModal(modal),
@@ -82,7 +83,7 @@ Window::Window(const std::string& caption, bool modal, Window *parent, const std
logger->log("Window::Window(\"%s\")", caption.c_str());
if (!windowContainer) {
- throw GCN_EXCEPTION("Window::Window. no windowContainer set");
+ throw GCN_EXCEPTION("Window::Window(): no windowContainer set");
}
// Loads the skin
@@ -104,11 +105,6 @@ Window::Window(const std::string& caption, bool modal, Window *parent, const std
setPadding(3);
setTitleBarHeight(20);
- // Add chrome
- mChrome = new GCContainer();
- mChrome->setOpaque(false);
- gcn::Window::add(mChrome);
-
// Add this window to the window container
windowContainer->add(this);
@@ -141,6 +137,15 @@ Window::~Window()
}
}
+ delete mLayout;
+
+ while (!mWidgets.empty())
+ {
+ gcn::Widget *w = mWidgets.front();
+ remove(w);
+ delete(w);
+ }
+
instances--;
// Clean up static resources
@@ -158,9 +163,6 @@ Window::~Window()
closeImage->decRef();
}
-
- delete mChrome;
- delete mGrip;
}
void Window::setWindowContainer(WindowContainer *wc)
@@ -195,7 +197,6 @@ void Window::draw(gcn::Graphics *graphics)
void Window::setContentSize(int width, int height)
{
- mChrome->setSize(width, height);
setSize(width + 2 * getPadding(),
height + getPadding() + getTitleBarHeight());
}
@@ -241,10 +242,11 @@ void Window::setResizable(bool r)
mGrip = new ResizeGrip();
mGrip->setX(getWidth() - mGrip->getWidth() - getChildrenArea().x);
mGrip->setY(getHeight() - mGrip->getHeight() - getChildrenArea().y);
- gcn::Window::add(mGrip);
+ add(mGrip);
}
else
{
+ remove(mGrip);
delete mGrip;
mGrip = 0;
}
@@ -252,15 +254,19 @@ void Window::setResizable(bool r)
void Window::widgetResized(const gcn::Event &event)
{
- const gcn::Rectangle area = getChildrenArea();
-
- mChrome->setSize(area.width, area.height);
-
if (mGrip)
{
+ const gcn::Rectangle area = getChildrenArea();
mGrip->setPosition(getWidth() - mGrip->getWidth() - area.x,
getHeight() - mGrip->getHeight() - area.y);
}
+
+ if (mLayout)
+ {
+ int w = getWidth() - 2 * getPadding();
+ int h = getHeight() - getPadding() - getTitleBarHeight();
+ mLayout->reflow(w, h);
+ }
}
void Window::setCloseButton(bool flag)
@@ -285,14 +291,7 @@ bool Window::isSticky()
void Window::setVisible(bool visible)
{
- if (isSticky())
- {
- gcn::Window::setVisible(true);
- }
- else
- {
- gcn::Window::setVisible(visible);
- }
+ gcn::Window::setVisible(isSticky() || visible);
}
void Window::scheduleDelete()
@@ -300,16 +299,6 @@ void Window::scheduleDelete()
windowContainer->scheduleDelete(this);
}
-void Window::add(gcn::Widget *w)
-{
- mChrome->add(w);
-}
-
-void Window::add(gcn::Widget *w, int x, int y)
-{
- mChrome->add(w, x, y);
-}
-
void Window::mousePressed(gcn::MouseEvent &event)
{
// Let Guichan move window to top and figure out title bar drag
@@ -710,3 +699,29 @@ void Window::loadSkin(const std::string filename)
closeImage = resman->getImage("graphics/gui/close_button.png");
}
+Layout &Window::getLayout()
+{
+ if (!mLayout) mLayout = new Layout;
+ return *mLayout;
+}
+
+LayoutCell &Window::place(int x, int y, gcn::Widget *wg, int w, int h)
+{
+ add(wg);
+ return getLayout().place(wg, x, y, w, h);
+}
+
+ContainerPlacer Window::getPlacer(int x, int y)
+{
+ return ContainerPlacer(this, &getLayout().at(x, y));
+}
+
+void Window::reflowLayout(int w, int h)
+{
+ assert(mLayout);
+ mLayout->reflow(w, h);
+ delete mLayout;
+ mLayout = NULL;
+ setContentSize(w, h);
+}
+
diff --git a/src/gui/window.h b/src/gui/window.h
index 9380fc88..89cc75a5 100644
--- a/src/gui/window.h
+++ b/src/gui/window.h
@@ -31,6 +31,11 @@
class ConfigListener;
class GCContainer;
+class ContainerPlacer;
+class Image;
+class ImageRect;
+class Layout;
+class LayoutCell;
class ResizeGrip;
class WindowContainer;
@@ -60,7 +65,7 @@ class Window : public gcn::Window, gcn::WidgetListener
Window *parent = NULL, const std::string &skin = "graphics/gui/gui.xml");
/**
- * Destructor.
+ * Destructor. Deletes all the added widgets.
*/
~Window();
@@ -75,18 +80,6 @@ class Window : public gcn::Window, gcn::WidgetListener
void draw(gcn::Graphics *graphics);
/**
- * Adds a widget to the window. The widget will be deleted by the
- * window.
- */
- void add(gcn::Widget *w);
-
- /**
- * Adds a widget to the window and also specifices its position. The
- * widget will be deleted by the window.
- */
- void add(gcn::Widget *w, int x, int y);
-
- /**
* Sets the size of this window.
*/
void setContentSize(int width, int height);
@@ -221,26 +214,56 @@ class Window : public gcn::Window, gcn::WidgetListener
* Don't forget to set these default values and resizable before
* calling this function.
*/
- virtual void loadWindowState();
+ void loadWindowState();
/**
* Set the default win pos and size.
* (which can be different of the actual ones.)
*/
- virtual void setDefaultSize(int defaultX, int defaultY,
- int defaultWidth, int defaultHeight);
+ void setDefaultSize(int defaultX, int defaultY,
+ int defaultWidth, int defaultHeight);
/**
* Reset the win pos and size to default. Don't forget to set defaults
* first.
*/
- virtual void resetToDefaultSize();
+ void resetToDefaultSize();
+
+ /**
+ * Gets the layout handler for this window.
+ */
+ Layout &getLayout();
+
+ /**
+ * 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 reflowLayout(int w = 0, int h = 0);
/**
* Loads a window skin
*/
void loadSkin(const std::string filename);
+ /**
+ * Adds a widget to the window and sets it at given cell.
+ */
+ LayoutCell &place(int x, int y, gcn::Widget *, int w = 1, int h = 1);
+
+ /**
+ * Returns a proxy for adding widgets in an inner table of the layout.
+ */
+ ContainerPlacer getPlacer(int x, int y);
+
+ protected:
+ /** The window container windows add themselves to. */
+ static WindowContainer *windowContainer;
+
+ private:
enum ResizeHandles
{
TOP = 0x01,
@@ -249,7 +272,6 @@ class Window : public gcn::Window, gcn::WidgetListener
LEFT = 0x08
};
- protected:
/**
* Determines if the mouse is in a resize area and returns appropriate
* resize handles. Also initializes drag offset in case the resize
@@ -264,6 +286,7 @@ class Window : public gcn::Window, gcn::WidgetListener
GCContainer *mChrome; /**< Contained container */
ResizeGrip *mGrip; /**< Resize grip */
Window *mParent; /**< The parent window */
+ Layout *mLayout; /**< Layout handler */
std::string mWindowName; /**< Name of the window */
bool mShowTitle; /**< Window has a title bar */
bool mModal; /**< Window is modal */
@@ -280,9 +303,6 @@ class Window : public gcn::Window, gcn::WidgetListener
int mDefaultHeight; /**< Default window height */
std::string mSkin; /**< Name of the skin to use */
- /** The window container windows add themselves to. */
- static WindowContainer *windowContainer;
-
/**
* The config listener that listens to changes relevant to all windows.
*/