diff options
-rw-r--r-- | src/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/gui/login.cpp | 77 | ||||
-rw-r--r-- | src/gui/popupmenu.cpp | 2 | ||||
-rw-r--r-- | src/gui/widgets/layout.cpp | 327 | ||||
-rw-r--r-- | src/gui/widgets/layout.h | 320 | ||||
-rw-r--r-- | src/gui/window.cpp | 83 | ||||
-rw-r--r-- | src/gui/window.h | 62 |
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. */ |