diff options
author | Guillaume Melquiond <guillaume.melquiond@gmail.com> | 2007-10-27 20:23:48 +0000 |
---|---|---|
committer | Guillaume Melquiond <guillaume.melquiond@gmail.com> | 2007-10-27 20:23:48 +0000 |
commit | 97bbe57e21a28544646da087e2a522390bf2ce5c (patch) | |
tree | a1899345f3b3928b3b0747b2429a45d08f79ae59 /src/gui/widgets | |
parent | ee15a808a1e0d36167f80d9f96147103ba5583de (diff) | |
download | mana-97bbe57e21a28544646da087e2a522390bf2ce5c.tar.gz mana-97bbe57e21a28544646da087e2a522390bf2ce5c.tar.bz2 mana-97bbe57e21a28544646da087e2a522390bf2ce5c.tar.xz mana-97bbe57e21a28544646da087e2a522390bf2ce5c.zip |
Improved layout handler to support trees of nested arrays. Needed for converting and fixing the trade window.
Diffstat (limited to 'src/gui/widgets')
-rw-r--r-- | src/gui/widgets/layout.cpp | 231 | ||||
-rw-r--r-- | src/gui/widgets/layout.h | 276 |
2 files changed, 364 insertions, 143 deletions
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 |