From 59472ef68fdef3f7e8858a81a46e28c127119c58 Mon Sep 17 00:00:00 2001 From: Guillaume Melquiond Date: Sat, 20 Oct 2007 11:29:18 +0000 Subject: Added a layout handler for automatically positioning widgets in a window. Fixed layout of login dialog box when native language is not English. --- ChangeLog | 10 +++ src/Makefile.am | 2 + src/gui/login.cpp | 44 ++++--------- src/gui/widgets/layout.cpp | 159 +++++++++++++++++++++++++++++++++++++++++++++ src/gui/widgets/layout.h | 116 +++++++++++++++++++++++++++++++++ src/gui/window.cpp | 65 ++++++++++-------- src/gui/window.h | 39 +++++++---- 7 files changed, 365 insertions(+), 70 deletions(-) create mode 100644 src/gui/widgets/layout.cpp create mode 100644 src/gui/widgets/layout.h diff --git a/ChangeLog b/ChangeLog index 88dfff79..7d93294f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2007-10-20 Guillaume Melquiond + + * src/gui/widgets/layout.h, src/gui/widgets/layout.cpp, + src/Makefile.am: Added a layout handler for automatically positioning + widgets in a window. + * src/gui/window.h, src/gui/window.cpp: Removed garbage-collected + container. Fixed dangling resize widget. Added layout handler. + * src/gui/login.cpp: Fixed layout of login dialog box when native + language is not English. + 2007-10-19 Guillaume Melquiond * data/equipment.xml: Removed obsolete file. diff --git a/src/Makefile.am b/src/Makefile.am index e0a5a41b..6e6cdfa4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,6 +3,8 @@ AUTOMAKE_OPTIONS = subdir-objects bin_PROGRAMS = tmw tmw_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.h \ diff --git a/src/gui/login.cpp b/src/gui/login.cpp index fccfc05a..9a7d1e5b 100644 --- a/src/gui/login.cpp +++ b/src/gui/login.cpp @@ -36,6 +36,8 @@ #include "passwordfield.h" #include "textfield.h" +#include "widgets/layout.h" + #include "../utils/gettext.h" LoginDialog::LoginDialog(LoginData *loginData): @@ -50,28 +52,6 @@ LoginDialog::LoginDialog(LoginData *loginData): mCancelButton = new Button(_("Cancel"), "cancel", this); mRegisterButton = new Button(_("Register"), "register", this); - const int width = 220; - const int height = 100; - - setContentSize(width, height); - - userLabel->setPosition(5, 5); - passLabel->setPosition(5, 14 + userLabel->getHeight()); - mUserField->setPosition(65, 5); - mPassField->setPosition(65, 14 + userLabel->getHeight()); - mUserField->setWidth(width - 70); - mPassField->setWidth(width - 70); - mKeepCheck->setPosition(4, 68); - mCancelButton->setPosition( - width - mCancelButton->getWidth() - 5, - height - mCancelButton->getHeight() - 5); - mOkButton->setPosition( - mCancelButton->getX() - mOkButton->getWidth() - 5, - height - mOkButton->getHeight() - 5); - mRegisterButton->setPosition( - mKeepCheck->getX() + mKeepCheck->getWidth() + 10, - height - mRegisterButton->getHeight() - 5); - mUserField->setActionEventId("ok"); mPassField->setActionEventId("ok"); @@ -81,14 +61,18 @@ LoginDialog::LoginDialog(LoginData *loginData): mPassField->addActionListener(this); mKeepCheck->addActionListener(this); - add(userLabel); - add(passLabel); - add(mUserField); - add(mPassField); - add(mKeepCheck); - add(mOkButton); - add(mCancelButton); - add(mRegisterButton); + setPadding(8); + place(0, 0, userLabel); + place(0, 1, passLabel); + 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(2, 3, mOkButton); + place(3, 3, mCancelButton); + getLayout().setColWidth(1, 20); + reflowLayout(); + forgetLayout(); setLocationRelativeTo(getParent()); setVisible(true); diff --git a/src/gui/widgets/layout.cpp b/src/gui/widgets/layout.cpp new file mode 100644 index 00000000..8a64acc0 --- /dev/null +++ b/src/gui/widgets/layout.cpp @@ -0,0 +1,159 @@ +/* + * 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 + * + * $Id$ + */ + +#include "layout.h" + +void Layout::resizeGrid(int w, int h) +{ + bool extW = w && w > (int)mColWidths.size(), + extH = h && h > (int)mRowHeights.size(); + if (!extW && !extH) return; + + if (extH) + { + mRowHeights.resize(h, -1); + mCells.resize(h); + if (!extW) w = (int)mColWidths.size(); + } + + mColWidths.resize(w, -1); + for (std::vector< std::vector< Cell > >::iterator + i = mCells.begin(), i_end = mCells.end(); i != i_end; ++i) + { + i->resize(w); + } +} + +void Layout::setColWidth(int n, int w) +{ + resizeGrid(n + 1, 0); + mColWidths[n] = w; +} + +void Layout::setRowHeight(int n, int h) +{ + resizeGrid(0, n + 1); + mRowHeights[n] = h; +} + +Cell &Layout::place(gcn::Widget *widget, int x, int y, int w, int h) +{ + resizeGrid(x + w, y + h); + Cell &cell = mCells[y][x]; + cell.mWidget = widget; + cell.mColExtent = w; + cell.mRowExtent = h; + cell.mPadding = 0; + cell.mHAlign = Cell::FILL; + cell.mVAlign = Cell::FILL; + return cell; +} + +static void align(int &pos, int &cur, int upp, Cell::Alignment a) +{ + cur = std::min(cur, upp); + switch (a) + { + case Cell::LEFT: + return; + case Cell::RIGHT: + pos += upp - cur; + return; + case Cell::CENTER: + pos += (upp - cur) / 2; + return; + case Cell::FILL: + cur = upp; + return; + } +} + +void Layout::reflow() +{ + int gridW = mColWidths.size(), gridH = mRowHeights.size(); + + std::vector< int > widths(gridW, 0); + std::vector< int > heights(gridH, 0); + + for (int gridY = 0; gridY < gridH; ++gridY) + { + for (int gridX = 0; gridX < gridW; ++gridX) + { + Cell &cell = mCells[gridY][gridX]; + if (!cell.mWidget) continue; + + if (cell.mColExtent == 1) + { + int w = cell.mWidget->getWidth() + cell.mPadding * 2; + if (w > widths[gridX]) widths[gridX] = w; + } + + if (cell.mRowExtent == 1) + { + int h = cell.mWidget->getHeight() + cell.mPadding * 2; + if (h > heights[gridY]) heights[gridY] = h; + } + } + } + + for (int gridX = 0; gridX < gridW; ++gridX) + { + int w = mColWidths[gridX]; + if (w != -1) widths[gridX] = w; + } + + for (int gridY = 0; gridY < gridH; ++gridY) + { + int h = mRowHeights[gridY]; + if (h != -1) heights[gridY] = h; + } + + int y = 0; + for (int gridY = 0; gridY < gridH; ++gridY) + { + int h = heights[gridY]; + int x = 0; + for (int gridX = 0; gridX < gridW; ++gridX) + { + int w = widths[gridX]; + Cell &cell = mCells[gridY][gridX]; + if (cell.mWidget) + { + int ew = w - cell.mPadding * 2, + eh = h - cell.mPadding * 2; + for (int i = 1; i < cell.mColExtent; ++i) + ew += widths[gridX + i] + mPadding; + for (int i = 1; i < cell.mRowExtent; ++i) + eh += heights[gridY + i] + mPadding; + int dw = cell.mWidget->getWidth(), + dh = cell.mWidget->getHeight(); + int dx = x + cell.mPadding, dy = y + cell.mPadding; + align(dx, dw, ew, cell.mHAlign); + align(dy, dh, eh, cell.mVAlign); + cell.mWidget->setDimension(gcn::Rectangle(dx, dy, dw, dh)); + } + x += w + mPadding; + } + y += h + mPadding; + } +} diff --git a/src/gui/widgets/layout.h b/src/gui/widgets/layout.h new file mode 100644 index 00000000..1c4df490 --- /dev/null +++ b/src/gui/widgets/layout.h @@ -0,0 +1,116 @@ +/* + * 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 + * + * $Id$ + */ + +#ifndef _TMW_WIDGET_LAYOUT_H__ +#define _TMW_WIDGET_LAYOUT_H__ + +#include + +#include + +class Cell +{ + friend class Layout; + + public: + + enum Alignment + { + LEFT, RIGHT, CENTER, FILL + }; + + Cell(): mWidget(0) {} + + /** + * Sets padding. + */ + Cell &setPadding(int p) + { mPadding = p; return *this; } + + /** + * Sets horizontal alignment of cell content. + */ + Cell &setHAlign(Alignment a) + { mHAlign = a; return *this; } + + /** + * Sets vertical alignment of cell content. + */ + Cell &setVAlign(Alignment a) + { mVAlign = a; return *this; } + + private: + + gcn::Widget *mWidget; + int mColExtent, mRowExtent; + int mPadding; + Alignment mHAlign, mVAlign; +}; + +class Layout +{ + public: + + Layout(): mPadding(4) {} + + /** + * Sets the width of a column. + */ + void setColWidth(int n, int w); + + /** + * Sets the height of a row. + */ + void setRowHeight(int n, int h); + + /** + * Sets padding between cells. + */ + void setPadding(int p) + { mPadding = p; } + + /** + * Places a widget in given cell. + */ + Cell &place(gcn::Widget *, int x, int y, int w, int h); + + /** + * Computes position of all the widgets. + */ + void reflow(); + + private: + + /** + * Ensures the private vectors are large enough. + */ + void resizeGrid(int w, int h); + + std::vector< int > mColWidths; + std::vector< int > mRowHeights; + std::vector< std::vector < Cell > > mCells; + + int mPadding; +}; + +#endif diff --git a/src/gui/window.cpp b/src/gui/window.cpp index 17447009..b5ac79b5 100644 --- a/src/gui/window.cpp +++ b/src/gui/window.cpp @@ -30,6 +30,7 @@ #include "gccontainer.h" #include "windowcontainer.h" +#include "widgets/layout.h" #include "widgets/resizegrip.h" #include "../configlistener.h" @@ -61,6 +62,7 @@ Window::Window(const std::string& caption, bool modal, Window *parent): gcn::Window(caption), mGrip(0), mParent(parent), + mLayout(NULL), mModal(modal), mResizable(false), mCloseButton(false), @@ -105,11 +107,6 @@ Window::Window(const std::string& caption, bool modal, Window *parent): 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 +138,15 @@ Window::~Window() } } + delete mLayout; + + while (!mWidgets.empty()) + { + gcn::Widget *w = mWidgets.front(); + remove(w); + delete(w); + } + instances--; if (instances == 0) @@ -161,9 +167,6 @@ Window::~Window() delete border.grid[8]; closeImage->decRef(); } - - delete mChrome; - delete mGrip; } void Window::setWindowContainer(WindowContainer *wc) @@ -198,13 +201,11 @@ void Window::draw(gcn::Graphics *graphics) void Window::setContentWidth(int width) { - mChrome->setWidth(width); setWidth(width + 2 * getPadding()); } void Window::setContentHeight(int height) { - mChrome->setHeight(height); setHeight(height + getPadding() + getTitleBarHeight()); } @@ -304,6 +305,7 @@ void Window::setMaxHeight(unsigned int height) void Window::setResizable(bool r) { + if (mResizable == r) return; mResizable = r; if (mResizable) @@ -311,10 +313,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; } @@ -357,16 +360,6 @@ void Window::scheduleDelete() windowContainer->scheduleDelete(this); } -void Window::add(gcn::Widget *w, bool delChild) -{ - mChrome->add(w, delChild); -} - -void Window::add(gcn::Widget *w, int x, int y, bool delChild) -{ - mChrome->add(w, x, y, delChild); -} - void Window::mousePressed(gcn::MouseEvent &event) { // Let Guichan move window to top and figure out title bar drag @@ -519,8 +512,6 @@ void Window::mouseDragged(gcn::MouseEvent &event) // Set the new window and content dimensions setDimension(newDim); - const gcn::Rectangle area = getChildrenArea(); - mChrome->setSize(area.width, area.height); updateContentSize(); } } @@ -536,9 +527,6 @@ void Window::loadWindowState(std::string const &name) { setSize((int) config.getValue(name + "WinWidth", getWidth()), (int) config.getValue(name + "WinHeight", getHeight())); - - const gcn::Rectangle area = getChildrenArea(); - mChrome->setSize(area.width, area.height); } } @@ -607,3 +595,28 @@ void Window::fireWindowEvent(const WindowEvent &event) break; } } + +Layout &Window::getLayout() +{ + if (!mLayout) mLayout = new Layout; + return *mLayout; +} + +void Window::forgetLayout() +{ + delete mLayout; + mLayout = 0; +} + +Cell &Window::place(int x, int y, gcn::Widget *wg, int w, int h) +{ + add(wg); + return getLayout().place(wg, x, y, w, h); +} + +void Window::reflowLayout() +{ + if (!mLayout) return; + mLayout->reflow(); + resizeToContent(); +} diff --git a/src/gui/window.h b/src/gui/window.h index ab266422..034c99da 100644 --- a/src/gui/window.h +++ b/src/gui/window.h @@ -30,12 +30,13 @@ #include "windowlistener.h" +class Cell; class ConfigListener; -class GCContainer; +class Image; class ImageRect; +class Layout; class ResizeGrip; class WindowContainer; -class Image; /** * A window. This window can be dragged around and has a title bar. Windows are @@ -62,7 +63,7 @@ class Window : public gcn::Window Window *parent = NULL); /** - * Destructor. + * Destructor. Deletes all the added widgets. */ ~Window(); @@ -76,16 +77,6 @@ class Window : public gcn::Window */ void draw(gcn::Graphics *graphics); - /** - * Adds a widget to the window. - */ - void add(gcn::Widget *wi, bool delChild = true); - - /** - * Adds a widget to the window and also specifices its position. - */ - void add(gcn::Widget *w, int x, int y, bool delChild = true); - /** * Sets the width of the window contents. */ @@ -291,6 +282,26 @@ class Window : public gcn::Window /** The window container windows add themselves to. */ static WindowContainer *windowContainer; + /** + * Gets the layout handler for this window. + */ + Layout &getLayout(); + + /** + * Deletes the layout handler. + */ + void forgetLayout(); + + /** + * Resizes the window after computing the position of the widgets. + */ + void reflowLayout(); + + /** + * Adds a widget to the window and sets it at given cell. + */ + Cell &place(int x, int y, gcn::Widget *, int w = 1, int h = 1); + private: /** * Determines if the mouse is in a resize area and returns appropriate @@ -301,9 +312,9 @@ class Window : public gcn::Window */ int getResizeHandles(gcn::MouseEvent &event); - GCContainer *mChrome; /**< Contained container */ ResizeGrip *mGrip; /**< Resize grip */ Window *mParent; /**< The parent window */ + Layout *mLayout; /**< Layout handler */ std::string mConfigName; /**< Name used for saving window-related data */ bool mShowTitle; /**< Window has a title bar */ bool mModal; /**< Window is modal */ -- cgit v1.2.3-70-g09d2