Add new gui2 iterator framework.

This commit is contained in:
Mark de Wever 2011-04-10 16:48:33 +00:00
parent 4ac8cababc
commit 797142008b
26 changed files with 1845 additions and 0 deletions

View file

@ -20,6 +20,7 @@ Version 1.9.5+svn:
(fixes bug #17743).
* Applied patch #2611: removed redundant own_side attribute
* Applied patch #2600: improved MP creation screen logging
* Added: New gui2 iterator framework.
Version 1.9.5:
* Graphics:

View file

@ -8,6 +8,9 @@ src/gui/auxiliary/canvas.cpp
src/gui/auxiliary/event/dispatcher.cpp
src/gui/auxiliary/event/distributor.cpp
src/gui/auxiliary/event/handler.cpp
src/gui/auxiliary/iterator/iterator.cpp
src/gui/auxiliary/iterator/walker_grid.cpp
src/gui/auxiliary/iterator/walker_widget.cpp
src/gui/auxiliary/log.cpp
src/gui/auxiliary/tips.cpp
src/gui/auxiliary/widget_definition/button.cpp
@ -138,7 +141,9 @@ src/side_filter.cpp
src/terrain_filter.cpp
src/terrain_translation.cpp
src/tests/gui/fire_event.cpp
src/tests/gui/iterator.cpp
src/tests/gui/test_gui2.cpp
src/tests/gui/visitor.cpp
src/text.cpp
src/widgets/button.cpp
src/widgets/combo.cpp

View file

@ -363,6 +363,9 @@ set(wesnoth-main_SRC
gui/auxiliary/event/dispatcher.cpp
gui/auxiliary/event/distributor.cpp
gui/auxiliary/event/handler.cpp
gui/auxiliary/iterator/iterator.cpp
gui/auxiliary/iterator/walker_grid.cpp
gui/auxiliary/iterator/walker_widget.cpp
gui/auxiliary/log.cpp
gui/auxiliary/old_markup.cpp
gui/auxiliary/timer.cpp
@ -798,9 +801,11 @@ if(ENABLE_TESTS)
tests/test_util.cpp
tests/test_serialization.cpp
tests/test_version.cpp
tests/gui/iterator.cpp
tests/gui/fire_event.cpp
tests/gui/test_drop_target.cpp
tests/gui/test_gui2.cpp
tests/gui/visitor.cpp
tests/gui/test_save_dialog.cpp
)
if(NOT ENABLE_GAME)

View file

@ -229,6 +229,9 @@ wesnoth_sources = Split("""
gui/auxiliary/event/dispatcher.cpp
gui/auxiliary/event/distributor.cpp
gui/auxiliary/event/handler.cpp
gui/auxiliary/iterator/iterator.cpp
gui/auxiliary/iterator/walker_grid.cpp
gui/auxiliary/iterator/walker_widget.cpp
gui/auxiliary/log.cpp
gui/auxiliary/old_markup.cpp
gui/auxiliary/timer.cpp
@ -522,9 +525,11 @@ test_sources = Split("""
tests/test_serialization.cpp
tests/test_version.cpp
tests/gui/fire_event.cpp
tests/gui/iterator.cpp
tests/gui/test_drop_target.cpp
tests/gui/test_gui2.cpp
tests/gui/test_save_dialog.cpp
tests/gui/visitor.cpp
tests/utils/play_scenario.cpp
""")
test_sources.extend(test_env.Object("tests/test_config_cache.cpp"))

View file

@ -0,0 +1,79 @@
/* $Id$ */
/*
Copyright (C) 2011 by Mark de Wever <koraq@xs4all.nl>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program 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
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file
* Contains the exceptions throw by the @ref gui2::iterator::titerator classes.
*/
#ifndef GUI_WIDGETS_AUXILIARY_ITERATOR_EXCEPTION_HPP_INCLUDED
#define GUI_WIDGETS_AUXILIARY_ITERATOR_EXCEPTION_HPP_INCLUDED
#include "lua_jailbreak_exception.hpp"
#include <stdexcept>
namespace gui2 {
namespace iterator {
/**
* Thrown when deferring an invalid iterator.
*
* Invalid means the initial state at_end() == true.
*/
class tlogic_error
: public std::logic_error
, public tlua_jailbreak_exception
{
public:
explicit tlogic_error(const std::string message)
: std::logic_error("GUI2 ITERATOR: " + message)
, tlua_jailbreak_exception()
{
}
private:
IMPLEMENT_LUA_JAILBREAK_EXCEPTION(tlogic_error)
};
/**
* Thrown when moving an invalid iterator.
*
* Invalid means the initial state at_end() == true.
*/
class trange_error
: public std::range_error
, public tlua_jailbreak_exception
{
public:
explicit trange_error(const std::string message)
: std::range_error("GUI2 ITERATOR: " + message)
, tlua_jailbreak_exception()
{
}
private:
IMPLEMENT_LUA_JAILBREAK_EXCEPTION(trange_error)
};
} // namespace iterator
} // namespace gui2
#endif

View file

@ -0,0 +1,188 @@
/* $Id$ */
/*
Copyright (C) 2011 by Mark de Wever <koraq@xs4all.nl>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program 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
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#define GETTEXT_DOMAIN "wesnoth-lib"
#include "gui/auxiliary/iterator/iterator.hpp"
namespace gui2 {
namespace iterator {
} // namespace iterator
} // namespace gui2
/**
* @page gui2_iterator GUI2 Iterator.
*
* The iterator class allows the user to iterate over a group of widgets.
* The idea is to add a visitor class later as well, where the two classes
* can be combined.
*
* This page describes how the iterator class works. The iterator is build
* from several parts:
* - level, the part and subparts of the widget to visit.
* - walker, iterates over a single widget at several levels.
* - visit policy, whether a level should be visited or not.
* - order policy, the order in which the several levels are traversed.
* - iterator, the user interface for iteration.
*
*
* @section gui2_iterator_level Level
*
* The levels are defined in @ref gui2::iterator::twalker_::tlevel. The
* level allows the user to only visit a part of the widget tree.
*
* @note At the moment when gui2::iterator::twalker_::widget is skipped the
* child class also skips its children. This behaviour might change.
*
*
* @section gui2_iterator_walker Walker
*
* The is a group of classes inheriting from @ref gui2::iterator::twalker_
* the objects are created from @ref gui2::twidget::create_walker. The
* walker allows to visit the several levels of the widget. This means
* several widgets need to override the function in a subclass. For example
* most @em simple widgets don't have a grid or children so they can use the
* walker created from @ref gui2::tcontrol. But containers need to create a
* different walker.
*
*
* @section gui2_iterator_visit_policy Visit policy
*
* This policy simply defines whether or not to visit the widgets at a
* certain level. There are two visit policies:
* - @ref gui2::iterator::policy::visit::tvisit visits the widget at the level.
* - @ref gui2::iterator::policy::visit::tskip skips the widget at the level.
*
* There are no more visit policies expected for the future. These policies
* are normally not used directly, but set from the @ref
* gui2_iterator_order_policy.
*
*
* @section gui2_iterator_order_policy Order policy
*
* This policy determines in which order the widgets are traversed, children
* first, this level before diving down etc. @ref tests/gui/iterator.cpp
* shows more information.
* The following policies have been defined:
* - @ref gui2::iterator::policy::order::ttop_down
* - @ref gui2::iterator::policy::order::tbottom_up
*
* The next sections describe in which order the widgets are visited. In the
* description we use the following widget tree.
*
* [0] @n
* \ @n
* [1|2|3|4] @n
* \ @n
* [5|6|7|8] @n
*
* The types are:
* - grid 0, 1
* - control 2, 3, 4, 6, 7, 8
*
* The examples assume all levels will be visited.
*
*
* @subsection gui2_iterator_visit_policy_top_down Top down
*
* The widgets visited first is the initial widget. After that it tries to go
* down to a child widget and will continue down. Once that fails it will visit
* the siblings at that level before going up again.
*
* @todo Write the entire visiting algorithm.
*
* The visiting order in our example is:
* 0, 1, 5, 6, 7, 8, 2, 3, 4
*
*
* @subsection gui2_iterator_visit_policy_bottom_up Bottom up
*
* When the iterator is created the iterator tries to go down all the child
* widgets to get at the bottom level. That widget will be visited first. Then
* it will first visit all sibblings before going up the the next layer.
*
* @todo Write the entire visiting algorithm.
*
* The visiting order in our example is:
* 5, 6, 7, 8, 1, 2, 3, 4, 0
*
*
* @section gui2_iterator_iterator Iterator
*
* The iterator is the class the users should care about. The user creates the
* iterator with the selected policy and the root widget. Then the user can
* visit the widgets.
*
* When during the iteration a widget is added to or removed from the
* widget-tree being walked the iterator becomes invalid. Using the iterator
* when it is invalid results in Undefined Behaviour.
*
* When it's certain there's at least one widget to visit a simple do while loop
* can be used. It the policy visits the widget, it's certain there is at least
* one widget to visit. Below some sample code:
@code
titerator<policy> itor(root);
assert(!itor.at_end());
do {
...
...
} while(itor.next());
@endcode
*
* When there might be no widget to visit a simple for loop can be used:
@code
for(titerator<policy> itor(root); !itor.at_end(); ++itor) {
...
...
}
@endcode
*
*
* @subsection gui2_iterator_iterator_design Design
*
* As seen in the examples above the iterator doesn't look like a iterator in
* the C++ standard library. The iterator is more designed after the iterator
* design of the Gang of Four [GoF]. The reason for the different design is that
* GoF design fits better to the classes as a normal C++ iterator. The rest of
* this section explains some of the reasons why this design is chosen. The main
* reason is simple; the iteration of the widgets feels better suited for the
* GoF-style iteration as the C++-style iteration.
*
* The iterator is not lightweight like most C++ iterators, therefore the class
* in non-copyable. (This is for example also the reason why a std::list has no
* operator[].) Since operator++(int) should return a copy of the original
* object it's also omitted.
*
* The design makes it hard to back-track the iteration (or costs more memory),
* so the iterator is forward only. The order policy is added to allow the
* wanted walking direction, but one-way only.
*
* The iterator has a begin, but it's not easy to go back to it and the
* operation involves rewinding the state, which might be costly. Therefore no
* begin() function.
*
* The end is known at the moment it's reached, but not upfront. That combined
* with the forward only, makes implementing an end() hard and therefore it is
* omitted.
*
*
* [GoF] http://en.wikipedia.org/wiki/Design_Patterns_%28book%29
*/

View file

@ -0,0 +1,101 @@
/* $Id$ */
/*
Copyright (C) 2011 by Mark de Wever <koraq@xs4all.nl>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program 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
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file
* Contains the base iterator class for the gui2 widgets.
*
* For more information @see @ref gui2_iterator for more information.
*/
#ifndef GUI_WIDGETS_AUXILIARY_ITERATOR_ITERATOR_HPP_INCLUDED
#define GUI_WIDGETS_AUXILIARY_ITERATOR_ITERATOR_HPP_INCLUDED
#include "gui/auxiliary/iterator/policy_order.hpp"
namespace gui2 {
namespace iterator {
/**
* The iterator class.
*
* @see @ref gui2_iterator_iterator for more information.
*/
template<class order>
class titerator
: private order
, private boost::noncopyable
{
public:
/**
* Contstructor.
*
* @param root The widget where to start the iteration.
*/
titerator(twidget& root)
: order(root)
{
}
/**
* Has the iterator reached the end?
*
* @returns The status.
* @retval [true] At the end.
* @retval [false] Not at the end.
*/
bool at_end() const { return order::at_end(); }
/**
* Visit the next widget.
*
* @pre @ref at_end() == false
*
* @throws @ref trange_error upon pre condition violation.
*
* @returns Whether the next widget can be safely
* deferred.
*/
bool next() { return order::next(); }
/** See @ref next. */
titerator<order>& operator++()
{
order::next();
return *this;
}
/**
* Returns the current widget.
*
* @returns The current widget.
*/
twidget& operator*() { return order::operator*(); }
/** See @ref operator*. */
twidget* operator->()
{
return &(operator*());
}
};
} // namespace iterator
} // namespace gui2
#endif

View file

@ -0,0 +1,376 @@
/* $Id$ */
/*
Copyright (C) 2011 by Mark de Wever <koraq@xs4all.nl>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program 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
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#ifndef GUI_WIDGETS_AUXILIARY_ITERATOR_POLICY_ORDER_HPP_INCLUDED
#define GUI_WIDGETS_AUXILIARY_ITERATOR_POLICY_ORDER_HPP_INCLUDED
#include "gui/auxiliary/iterator/exception.hpp"
#include "gui/auxiliary/iterator/policy_visit.hpp"
#include "gui/auxiliary/log.hpp"
#include "gui/widgets/widget.hpp"
#include <iostream>
namespace gui2 {
namespace iterator {
namespace policy {
namespace order {
template<
bool visit_widget
, bool visit_grid
, bool visit_child
>
class tbottom_up
: public tvisit<visit_widget, twalker_::widget>
, public tvisit<visit_grid, twalker_::grid>
, public tvisit<visit_child, twalker_::child>
{
typedef tvisit<visit_widget, twalker_::widget> tvisit_widget;
typedef tvisit<visit_grid, twalker_::grid> tvisit_grid;
typedef tvisit<visit_child, twalker_::child> tvisit_child;
public:
explicit tbottom_up(twidget& root)
: root_(root.create_walker())
, stack_()
{
TST_GUI_I << "Constructor: ";
while(!tvisit_child::at_end(*root_)) {
stack_.push_back(root_);
root_ = tvisit_child::get(*root_)->create_walker();
TST_GUI_I << " Down widget '" << operator*().id() << "'.";
}
if(!at_end()) {
TST_GUI_I << " Finished at '" << operator*().id() << "'.\n";
} else {
TST_GUI_I << " Finished at the end.\n";
}
}
~tbottom_up()
{
delete root_;
for(std::vector<iterator::twalker_*>::iterator itor = stack_.begin()
; itor != stack_.end()
; ++itor) {
delete *itor;
}
}
bool at_end() const
{
return tvisit_widget::at_end(*root_)
&& tvisit_grid::at_end(*root_)
&& tvisit_child::at_end(*root_);
}
bool next()
{
if(at_end()) {
ERR_GUI_I << "Tried to move beyond end of the iteration range.\n";
throw trange_error("Tried to move beyond end of range.");
}
TST_GUI_I << "At '" << operator*().id() << "'.";
/***** WIDGET *****/
TST_GUI_I << " Iterate widget:";
if(!tvisit_widget::at_end(*root_)) {
switch(tvisit_widget::next(*root_)) {
case twalker_::valid :
TST_GUI_I << " visit '" << operator*().id() << "'.\n";
return true;
case twalker_::invalid :
TST_GUI_I << " reached the end.";
break;
case twalker_::fail:
TST_GUI_I << "\n";
ERR_GUI_E << "Tried to move beyond end of "
"the widget iteration range.\n";
throw trange_error("Tried to move beyond end of range.");
}
} else {
TST_GUI_I << " failed.";
}
/***** GRID *****/
TST_GUI_I << " Iterate grid:";
if(!tvisit_grid::at_end(*root_)) {
switch(tvisit_grid::next(*root_)) {
case twalker_::valid :
TST_GUI_I << " visit '" << operator*().id() << "'.\n";
return true;
case twalker_::invalid :
TST_GUI_I << " reached the end.";
break;
case twalker_::fail:
TST_GUI_I << "\n";
ERR_GUI_E << "Tried to move beyond end of "
"the grid iteration range.\n";
throw trange_error("Tried to move beyond end of range.");
}
} else {
TST_GUI_I << " failed.";
}
/***** TRAVERSE CHILDREN *****/
TST_GUI_I << " Iterate child:";
if(tvisit_child::at_end(*root_)) {
if(stack_.empty()) {
TST_GUI_I << " Finished iteration.\n";
return false;
} else {
delete root_;
root_ = stack_.back();
stack_.pop_back();
TST_GUI_I << " Up '" << operator*().id() << "'.";
}
}
TST_GUI_I << " Iterate child:";
if(!tvisit_child::at_end(*root_)) {
switch(tvisit_child::next(*root_)) {
case twalker_::valid :
TST_GUI_I << " visit '" << operator*().id() << "'.";
break;
case twalker_::invalid :
TST_GUI_I << " reached the end.";
break;
case twalker_::fail:
TST_GUI_I << "\n";
ERR_GUI_E << "Tried to move beyond end of "
"the child iteration range.\n";
throw trange_error("Tried to move beyond end of range.");
}
} else {
TST_GUI_I << " already at the end.";
}
while(!tvisit_child::at_end(*root_)) {
stack_.push_back(root_);
root_ = tvisit_child::get(*root_)->create_walker();
TST_GUI_I << " Down widget '"
<< operator*().id() << "'.";
}
TST_GUI_I << " Visit '" << operator*().id() << "'.\n";
return true;
}
twidget& operator*()
{
if(at_end()) {
ERR_GUI_I << "Tried to defer beyond end its "
"iteration range iterator.\n";
throw tlogic_error("Tried to defer an invalid iterator.");
}
if(!tvisit_widget::at_end(*root_)) {
return *tvisit_widget::get(*root_);
}
if(!tvisit_grid::at_end(*root_)) {
return *tvisit_grid::get(*root_);
}
if(!tvisit_child::at_end(*root_)) {
return *tvisit_child::get(*root_);
}
ERR_GUI_I << "The iterator ended in an unknown "
"state while deferring iteself.\n";
throw tlogic_error("Tried to defer an invalid iterator.");
}
private:
iterator::twalker_* root_;
std::vector<iterator::twalker_*> stack_;
};
template<
bool visit_widget
, bool visit_grid
, bool visit_child
>
class ttop_down
: public tvisit<visit_widget, twalker_::widget>
, public tvisit<visit_grid, twalker_::grid>
, public tvisit<visit_child, twalker_::child>
{
typedef tvisit<visit_widget, twalker_::widget> tvisit_widget;
typedef tvisit<visit_grid, twalker_::grid> tvisit_grid;
typedef tvisit<visit_child, twalker_::child> tvisit_child;
public:
explicit ttop_down (twidget& root)
: root_(root.create_walker())
, stack_()
{
}
~ttop_down()
{
delete root_;
for(std::vector<iterator::twalker_*>::iterator itor = stack_.begin()
; itor != stack_.end()
; ++itor) {
delete *itor;
}
}
bool at_end() const
{
return tvisit_widget::at_end(*root_)
&& tvisit_grid::at_end(*root_)
&& tvisit_child::at_end(*root_);
}
bool next()
{
if(at_end()) {
ERR_GUI_I << "Tried to move beyond end of the iteration range.\n";
throw trange_error("Tried to move beyond end of range.");
}
TST_GUI_I << "At '" << operator*().id() << "'.";
/***** WIDGET *****/
TST_GUI_I << " Iterate widget:";
if(!tvisit_widget::at_end(*root_)) {
switch(tvisit_widget::next(*root_)) {
case twalker_::valid :
TST_GUI_I << " visit '" << operator*().id() << "'.\n";
return true;
case twalker_::invalid :
TST_GUI_I << " reached the end.";
break;
case twalker_::fail:
TST_GUI_I << "\n";
ERR_GUI_E << "Tried to move beyond end of the "
"widget iteration range.\n";
throw trange_error("Tried to move beyond end of range.");
}
} else {
TST_GUI_I << " failed.";
}
/***** GRID *****/
TST_GUI_I << " Iterate grid:";
if(!tvisit_grid::at_end(*root_)) {
switch(tvisit_grid::next(*root_)) {
case twalker_::valid :
TST_GUI_I << " visit '" << operator*().id() << "'.\n";
return true;
case twalker_::invalid :
TST_GUI_I << " reached the end.";
break;
case twalker_::fail:
TST_GUI_I << "\n";
ERR_GUI_E << "Tried to move beyond end of the grid "
"iteration range.\n";
throw trange_error("Tried to move beyond end of range.");
}
} else {
TST_GUI_I << " failed.";
}
/***** TRAVERSE CHILDREN *****/
TST_GUI_I << " Iterate child:";
if(tvisit_child::at_end(*root_)) {
TST_GUI_I << " reached the end.";
up();
} else {
TST_GUI_I << " proceed.";
}
if(!tvisit_child::at_end(*root_)) {
stack_.push_back(root_);
root_ = tvisit_child::get(*root_)->create_walker();
assert(root_);
assert(!at_end());
TST_GUI_I << " Down and visit '" << operator*().id() << "'.\n";
return true;
}
TST_GUI_I << " Finished iteration.\n";
return false;
}
twidget& operator*()
{
if(at_end()) {
ERR_GUI_I << "Tried to defer beyond end of the iteration "
"range iterator.\n";
throw tlogic_error("Tried to defer an invalid iterator.");
}
if(!tvisit_widget::at_end(*root_)) {
return *tvisit_widget::get(*root_);
}
if(!tvisit_grid::at_end(*root_)) {
return *tvisit_grid::get(*root_);
}
if(!tvisit_child::at_end(*root_)) {
return *tvisit_child::get(*root_);
}
ERR_GUI_I << "The iterator ended in an unknown "
"state while deferring iteself.\n";
throw tlogic_error("Tried to defer an invalid iterator.");
}
private:
bool up()
{
while(!stack_.empty()) {
delete root_;
root_ = stack_.back();
stack_.pop_back();
TST_GUI_I << " Up widget '" << operator*().id() << "'. Iterate:";
switch(tvisit_child::next(*root_)) {
case twalker_::valid:
TST_GUI_I << " reached '" << operator*().id() << "'.";
return true;
case twalker_::invalid:
TST_GUI_I << " failed.";
break;
case twalker_::fail:
throw trange_error("Tried to move beyond end of range.");
}
}
return true;
}
iterator::twalker_* root_;
std::vector<iterator::twalker_*> stack_;
};
} // namespace order
} // namespace policy
} // namespace iterator
} // namespace gui2
#endif

View file

@ -0,0 +1,123 @@
/* $Id$ */
/*
Copyright (C) 2011 by Mark de Wever <koraq@xs4all.nl>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program 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
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#ifndef GUI_WIDGETS_AUXILIARY_ITERATOR_POLICY_VISIT_HPP_INCLUDED
#define GUI_WIDGETS_AUXILIARY_ITERATOR_POLICY_VISIT_HPP_INCLUDED
#include "gui/auxiliary/iterator/walker.hpp"
#include <cstring>
namespace gui2 {
namespace iterator {
namespace policy {
namespace visit {
/**
* This policy skips the current level.
*/
class tskip
{
public:
/**
* Acts like @ref twalker_::next for the level where the policy is used.
*/
twalker_::tstate next(twalker_&) { return twalker_::fail; }
/**
* Acts like @ref twalker_::at_end for the level where the policy is used.
*/
bool at_end(const twalker_&) const { return true; }
/**
* Acts like @ref twalker_::get for the level where the policy is used.
*/
gui2::twidget* get(twalker_&) { return NULL; }
};
/**
* This policy tries to visit the current level.
*
* @tparam level The level to visit.
*/
template<twalker_::tlevel level>
class tvisit
{
public:
/**
* Acts like @ref twalker_::next for the level where the policy is used.
*/
twalker_::tstate next(twalker_& visitor)
{
return visitor.next(level);
}
/**
* Acts like @ref twalker_::at_end for the level where the policy is used.
*/
bool at_end(const twalker_& visitor) const
{
return visitor.at_end(level);
}
/**
* Acts like @ref twalker_::get for the level where the policy is used.
*/
gui2::twidget* get(twalker_& visitor)
{
return visitor.get(level);
}
};
} // namespace visit
/**
* Helper class to select to visit or skip a level.
*
* @tparam level The level to determine the policy for.
*/
template<bool, twalker_::tlevel level>
class tvisit
{
};
/** Specialized to select the @ref visit::tskip policy. */
template<twalker_::tlevel level>
class tvisit<false, level>
: public visit::tskip
{
};
/** Specialized to select the @ref visit::tvisit policy. */
template<twalker_::tlevel level>
class tvisit<true, level>
: public visit::tvisit<level>
{
};
} // namespace policy
} // namespace iterator
} // namespace gui2
#endif

View file

@ -0,0 +1,121 @@
/* $Id$ */
/*
Copyright (C) 2011 by Mark de Wever <koraq@xs4all.nl>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program 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
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#ifndef GUI_WIDGETS_AUXILIARY_ITERATOR_WALKER_HPP_INCLUDED
#define GUI_WIDGETS_AUXILIARY_ITERATOR_WALKER_HPP_INCLUDED
namespace gui2 {
class twidget;
namespace iterator {
/** The walker abstract base class. */
class twalker_
{
public:
virtual ~twalker_() {}
/** The level to walk at. */
enum tlevel
{
/** Visit the widget itself. */
widget
/** Visit its nested grid. */
, grid
/** Visit the children of its nested grid. */
, child
};
/**
* The state of the walker.
*
* The enum is used to return the state of @ref next.
*/
enum tstate
{
/**
* When calling next the following it has the following results.
*
* @pre at_end == false
*
* @post the next widget became the current one.
* @post at_end == false
*/
valid
/**
* When calling next the following it has the following results.
*
* @pre at_end == false
*
* @post there is no longer a current widget.
* @post at_end == true
*/
, invalid
/**
* When calling next the following it has the following results.
*
* @pre at_end == true
*
* @post at_end == true
*/
, fail
};
/**
* Make the next widget the current one.
*
* @param level Determines on which level the next one should
* be selected.
*
* @returns The status of the operation.
*/
virtual tstate next(const tlevel level) = 0;
/**
* Returns whether the current widget is valid.
*
* @param level Determines on which level the test should be
* executed.
*
*
* @returns Whether the current widget is valid.
*/
virtual bool at_end(const tlevel level) const = 0;
/**
* Returns a pointer to the current widget.
*
* @pre @ref at_end(level) == false
*
* @param level Determines from which level should the
* current widget be returned.
*
* @returns Pointer to the current widget.
*/
virtual gui2::twidget* get(const tlevel level) = 0;
};
} // namespace iterator
} // namespace gui2
#endif

View file

@ -0,0 +1,101 @@
/* $Id$ */
/*
Copyright (C) 2011 by Mark de Wever <koraq@xs4all.nl>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program 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
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#define GETTEXT_DOMAIN "wesnoth-lib"
#include "gui/auxiliary/iterator/walker_grid.hpp"
#include "asserts.hpp"
namespace gui2 {
namespace iterator {
tgrid::tgrid(gui2::tgrid& grid)
: grid_(grid)
, widget_(&grid)
, itor_(grid.begin())
{
}
twalker_::tstate tgrid::next(const tlevel level)
{
if(at_end(level)) {
return fail;
}
switch(level) {
case widget:
if(widget_) {
widget_ = NULL;
return invalid;
} else {
/* FALL DOWN */
}
case grid:
assert(false);
return fail;
case child:
if(itor_ == grid_.end()) {
/* FALL DOWN */
} else {
++itor_;
return itor_ == grid_.end() ? invalid : valid;
}
}
assert(false);
return fail;
}
bool tgrid::at_end(const tlevel level) const
{
switch(level) {
case widget:
return widget_ == NULL;
case grid:
return true;
case child:
return (itor_ == grid_.end());
}
assert(false);
return true;
}
gui2::twidget* tgrid::get(const tlevel level)
{
switch(level) {
case widget:
return widget_;
case grid:
return NULL;
case child:
if(itor_ == grid_.end()) {
return NULL;
} else {
return *itor_;
}
}
assert(false);
return NULL;
}
} // namespace iterator
} // namespace gui2

View file

@ -0,0 +1,78 @@
/* $Id$ */
/*
Copyright (C) 2011 by Mark de Wever <koraq@xs4all.nl>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program 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
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#ifndef GUI_WIDGETS_AUXILIARY_WALKER_VISITOR_GRID_HPP_INCLUDED
#define GUI_WIDGETS_AUXILIARY_WALKER_VISITOR_GRID_HPP_INCLUDED
#include "gui/auxiliary/iterator/walker.hpp"
#include "gui/widgets/grid.hpp"
namespace gui2 {
namespace iterator {
/** A walker for a @ref gui2::tgrid. */
class tgrid
: public twalker_
{
public:
/**
* Constructor.
*
* @param grid The grid which the walker is attached to.
*/
explicit tgrid(gui2::tgrid& grid);
/** Inherited from @ref gui2::iterator::twalker_. */
virtual tstate next(const tlevel level);
/** Inherited from @ref gui2::iterator::twalker_. */
virtual bool at_end(const tlevel level) const;
/** Inherited from @ref gui2::iterator::twalker_. */
virtual gui2::twidget* get(const tlevel level);
private:
/** The grid which the walker is attached to. */
gui2::tgrid& grid_;
/**
* The grid which the walker is attached to.
*
* This variable is used to track whether the @ref
* gui2::iterator::twalker_::widget level has been visited.
*/
gui2::twidget* widget_;
/**
* The iterator to the children of @ref grid_.
*
* This variable is used to track where the @ref
* gui2::iterator::twalker_::child level visiting is.
*/
gui2::tgrid::iterator itor_;
};
} // namespace iterator
} // namespace gui2
#endif

View file

@ -0,0 +1,91 @@
/* $Id$ */
/*
Copyright (C) 2011 by Mark de Wever <koraq@xs4all.nl>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program 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
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#define GETTEXT_DOMAIN "wesnoth-lib"
#include "gui/auxiliary/iterator/walker_widget.hpp"
#include "asserts.hpp"
#include "gui/widgets/widget.hpp"
namespace gui2 {
namespace iterator {
namespace walker {
twidget::twidget(gui2::twidget& widget)
: widget_(&widget)
{
}
twalker_::tstate twidget::next(const tlevel level)
{
if(at_end(level)) {
return fail;
}
switch(level) {
case widget:
if(widget_) {
widget_ = NULL;
return invalid;
} else {
/* FALL DOWN */
}
case grid: /* FALL DOWN */
case child: /* FALL DOWN */
;
}
assert(false);
return fail;
}
bool twidget::at_end(const tlevel level) const
{
switch(level) {
case widget:
return widget_ == NULL;
case grid: /* FALL DOWN */
case child:
return true;
}
assert(false);
return true;
}
gui2::twidget* twidget::get(const tlevel level)
{
switch(level) {
case widget:
return widget_;
case grid: /* FALL DOWN */
case child:
return NULL;
}
assert(false);
return NULL;
}
} // namespace walker
} // namespace iterator
} // namespace gui2

View file

@ -0,0 +1,62 @@
/* $Id$ */
/*
Copyright (C) 2011 by Mark de Wever <koraq@xs4all.nl>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program 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
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#ifndef GUI_WIDGETS_AUXILIARY_ITERATOR_WALKER_WIDGET_HPP_INCLUDED
#define GUI_WIDGETS_AUXILIARY_ITERATOR_WALKER_WIDGET_HPP_INCLUDED
#include "gui/auxiliary/iterator/walker.hpp"
namespace gui2 {
namespace iterator {
namespace walker {
/** A walker for a @ref gui2::tcontrol. */
class twidget
: public twalker_
{
public:
/**
* Constructor.
*
* @param widget The control which the walker is attached to.
*/
explicit twidget(gui2::twidget& widget);
/** Inherited from @ref gui2::iterator::twalker_. */
virtual tstate next(const tlevel level);
/** Inherited from @ref gui2::iterator::twalker_. */
virtual bool at_end(const tlevel level) const;
/** Inherited from @ref gui2::iterator::twalker_. */
virtual gui2::twidget* get(const tlevel level);
private:
/** The control which the walker is attached to. */
gui2::twidget* widget_;
};
} // namespace walker
} // namespace iterator
} // namespace gui2
#endif

View file

@ -27,6 +27,7 @@ namespace gui2 {
lg::log_domain log_gui_draw ("gui/draw");
lg::log_domain log_gui_event ("gui/event");
lg::log_domain log_gui_general ("gui/general");
lg::log_domain log_gui_iterator("gui/iterator");
lg::log_domain log_gui_lifetime("gui/lifetime");
lg::log_domain log_gui_layout ("gui/layout");
lg::log_domain log_gui_parse ("gui/parse");

View file

@ -43,6 +43,15 @@ extern lg::log_domain log_gui_general;
#define WRN_GUI_G LOG_STREAM_INDENT(warn, gui2::log_gui_general)
#define ERR_GUI_G LOG_STREAM_INDENT(err, gui2::log_gui_general)
extern lg::log_domain log_gui_iterator;
#define TST_GUI_I \
if(lg::debug.dont_log(gui2::log_gui_iterator)); \
else lg::debug(gui2::log_gui_iterator, false, false)
#define DBG_GUI_I LOG_STREAM_INDENT(debug, gui2::log_gui_iterator)
#define LOG_GUI_I LOG_STREAM_INDENT(info, gui2::log_gui_iterator)
#define WRN_GUI_I LOG_STREAM_INDENT(warn, gui2::log_gui_iterator)
#define ERR_GUI_I LOG_STREAM_INDENT(err, gui2::log_gui_iterator)
extern lg::log_domain log_gui_layout;
#define DBG_GUI_L LOG_STREAM_INDENT(debug, gui2::log_gui_layout)
#define LOG_GUI_L LOG_STREAM_INDENT(info, gui2::log_gui_layout)

View file

@ -148,6 +148,13 @@ public:
/** Inherited from tcontrol. */
bool disable_click_dismiss() const;
/**
* Inherited from twidget.
*
* @todo Implement properly.
*/
virtual iterator::twalker_* create_walker() { return NULL; }
/**
* Initializes and builds the grid.
*

View file

@ -20,6 +20,7 @@
#include "font.hpp"
#include "foreach.hpp"
#include "formula_string_utils.hpp"
#include "gui/auxiliary/iterator/walker_widget.hpp"
#include "gui/auxiliary/log.hpp"
#include "gui/auxiliary/event/message.hpp"
#include "gui/dialogs/tip.hpp"
@ -111,6 +112,11 @@ bool tcontrol::disable_click_dismiss() const
return get_visible() == twidget::VISIBLE && get_active();
}
iterator::twalker_* tcontrol::create_walker()
{
return new iterator::walker::twidget(*this);
}
tpoint tcontrol::get_config_minimum_size() const
{
assert(config_);

View file

@ -80,6 +80,9 @@ public:
*/
bool disable_click_dismiss() const;
/** Inherited from twidget. */
virtual iterator::twalker_* create_walker();
/***** ***** ***** ***** layout functions ***** ***** ***** *****/
/**

View file

@ -846,6 +846,13 @@ public:
return false;
}
/**
* Inherited from twidget.
*
* @todo Implement properly.
*/
virtual iterator::twalker_* create_walker() { return NULL; }
/***** ***** ***** ***** keyboard functions ***** ***** ***** *****/
/** Inherited from tgenerator_. */

View file

@ -17,6 +17,7 @@
#include "gui/widgets/grid_private.hpp"
#include "gui/auxiliary/iterator/walker_grid.hpp"
#include "gui/auxiliary/log.hpp"
#include "gui/auxiliary/layout_exception.hpp"
@ -651,6 +652,11 @@ bool tgrid::disable_click_dismiss() const
return false;
}
iterator::twalker_* tgrid::create_walker()
{
return new gui2::iterator::tgrid(*this);
}
void tgrid::set_rows(const unsigned rows)
{
if(rows == rows_) {

View file

@ -272,6 +272,9 @@ public:
/** Inherited from tcontrol. */
bool disable_click_dismiss() const;
/** Inherited from twidget. */
virtual iterator::twalker_* create_walker();
/***** ***** ***** setters / getters for members ***** ****** *****/
void set_rows(const unsigned rows);
@ -375,6 +378,9 @@ public:
twidget* operator->() { return itor_->widget(); }
twidget* operator*() { return itor_->widget(); }
bool operator==(const iterator& i) const
{ return i.itor_ == this->itor_; }
bool operator!=(const iterator& i) const
{ return i.itor_ != this->itor_; }

View file

@ -121,6 +121,13 @@ public:
// void unfold(const texpand_mode mode); // FIXME implement
#endif
/**
* Inherited from twidget.
*
* @todo Implement properly.
*/
virtual iterator::twalker_* create_walker() { return NULL; }
/** Inherited from twidget.*/
twidget* find_at(const tpoint& coordinate, const bool must_be_active);

View file

@ -32,6 +32,10 @@ namespace gui2 {
class tdialog;
class twindow;
namespace iterator {
class twalker_;
} // namespace iterator
typedef std::map< std::string, t_string > string_map;
/**
@ -347,6 +351,9 @@ public:
/** Does the widget disable easy close? */
virtual bool disable_click_dismiss() const = 0;
/** Creates a new walker object on the heap. */
virtual iterator::twalker_* create_walker() = 0;
/***** ***** ***** setters / getters for members ***** ****** *****/
twidget* parent() { return parent_; }

282
src/tests/gui/iterator.cpp Normal file
View file

@ -0,0 +1,282 @@
/* $Id$ */
/*
Copyright (C) 2011 by Mark de Wever <koraq@xs4all.nl>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program 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
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#define GETTEXT_DOMAIN "wesnoth-lib"
#include <boost/test/unit_test.hpp>
#include "config_cache.hpp"
#include "gui/auxiliary/iterator/iterator.hpp"
#include "gui/widgets/label.hpp"
#include "gui/widgets/grid.hpp"
#include <iostream>
#include <sstream>
#include <typeinfo>
/*
* In the unit tests we use a widget tree that looks like:
*
* [0]
* \
* [1|2|3|4]
* \
* [5|6|7|8]
*
* Where widgets 0 and 1 are a grid and the rest of the widgets a label.
* The unit tests traverse the tree.
*/
static std::string top_down_t_t_t_result()
{
static const std::string result =
"At '0'. Iterate widget: reached the end. Iterate grid: failed. "
"Iterate child: proceed. Down and visit '1'.\n"
"At '1'. Iterate widget: reached the end. Iterate grid: failed. "
"Iterate child: proceed. Down and visit '5'.\n"
"At '5'. Iterate widget: reached the end. Iterate grid: failed. "
"Iterate child: reached the end. Up widget '5'. "
"Iterate: reached '6'. Down and visit '6'.\n"
"At '6'. Iterate widget: reached the end. Iterate grid: failed. "
"Iterate child: reached the end. Up widget '6'. "
"Iterate: reached '7'. Down and visit '7'.\n"
"At '7'. Iterate widget: reached the end. Iterate grid: failed. "
"Iterate child: reached the end. Up widget '7'. "
"Iterate: reached '8'. Down and visit '8'.\n"
"At '8'. Iterate widget: reached the end. Iterate grid: failed. "
"Iterate child: reached the end. Up widget '8'. "
"Iterate: failed. Up widget '1'. "
"Iterate: reached '2'. Down and visit '2'.\n"
"At '2'. Iterate widget: reached the end. Iterate grid: failed. "
"Iterate child: reached the end. Up widget '2'. "
"Iterate: reached '3'. Down and visit '3'.\n"
"At '3'. Iterate widget: reached the end. Iterate grid: failed. "
"Iterate child: reached the end. Up widget '3'. "
"Iterate: reached '4'. Down and visit '4'.\n"
"At '4'. Iterate widget: reached the end. Iterate grid: failed. "
"Iterate child: reached the end. Up widget '4'. "
"Iterate: failed. Finished iteration.\n";
return result;
}
static std::string bottom_up_t_t_t_result()
{
static const std::string result =
"Constructor: Down widget '1'. Down widget '5'. Finished at '5'.\n"
"At '5'. Iterate widget: reached the end. Iterate grid: failed. "
"Iterate child: Up '1'. Iterate child: visit '1'. "
"Down widget '6'. Visit '6'.\n"
"At '6'. Iterate widget: reached the end. Iterate grid: failed. "
"Iterate child: Up '1'. Iterate child: visit '1'. "
"Down widget '7'. Visit '7'.\n"
"At '7'. Iterate widget: reached the end. Iterate grid: failed. "
"Iterate child: Up '1'. Iterate child: visit '1'. "
"Down widget '8'. Visit '8'.\n"
"At '8'. Iterate widget: reached the end. Iterate grid: failed. "
"Iterate child: Up '1'. Iterate child: reached the end. Visit '1'.\n"
"At '1'. Iterate widget: reached the end. Iterate grid: failed. "
"Iterate child: Up '0'. Iterate child: visit '0'. "
"Down widget '2'. Visit '2'.\n"
"At '2'. Iterate widget: reached the end. Iterate grid: failed. "
"Iterate child: Up '0'. Iterate child: visit '0'. "
"Down widget '3'. Visit '3'.\n"
"At '3'. Iterate widget: reached the end. Iterate grid: failed. "
"Iterate child: Up '0'. Iterate child: visit '0'. "
"Down widget '4'. Visit '4'.\n"
"At '4'. Iterate widget: reached the end. Iterate grid: failed. "
"Iterate child: Up '0'. Iterate child: reached the end. Visit '0'.\n"
"At '0'. Iterate widget: reached the end. Iterate grid: failed. "
"Iterate child: Finished iteration.\n";
return result;
}
static void add_widget(gui2::tgrid& grid
, gui2::twidget* widget
, const std::string& id
, const unsigned row
, const unsigned column)
{
BOOST_REQUIRE_NE(widget, (gui2::twidget*)NULL);
widget->set_id(id);
grid.set_child(widget
, row
, column
, gui2::tgrid::VERTICAL_GROW_SEND_TO_CLIENT
| gui2::tgrid::HORIZONTAL_GROW_SEND_TO_CLIENT
, 0);
}
template<class T>
static void test_control()
{
T control;
{
gui2::iterator::titerator< gui2::iterator::policy::order::ttop_down<
true
, true
, true> >
iterator(control);
/***** INITIAL STATE *****/
BOOST_CHECK_EQUAL(iterator.at_end(), false);
BOOST_CHECK_EQUAL(&*iterator, &control);
/***** POST END *****/
BOOST_CHECK_EQUAL(iterator.next(), false);
BOOST_CHECK_EQUAL(iterator.at_end(), true);
}
{
gui2::iterator::titerator< gui2::iterator::policy::order::ttop_down<
false
, true
, true> >
iterator(control);
/***** INITIAL STATE *****/
BOOST_CHECK_EQUAL(iterator.at_end(), true);
}
{
gui2::iterator::titerator<gui2::iterator::policy::order::tbottom_up<true, true, true> > iterator(control);
BOOST_CHECK_EQUAL(iterator.at_end(), false);
}
{
gui2::iterator::titerator<gui2::iterator::policy::order::tbottom_up<false, false, false> > iterator(control);
BOOST_CHECK_EQUAL(iterator.at_end(), true);
}
}
static void test_control()
{
/* Could add more widgets to the list. */
test_control<gui2::tlabel>();
}
static void test_grid()
{
/* An empty grid behaves the same as a control so test here. */
test_control<gui2::tgrid>();
/* Test the child part here. */
gui2::tgrid grid(2 ,2);
grid.set_id("0");
gui2::tgrid* g = new gui2::tgrid(2, 2);
add_widget(grid, g, "1", 0, 0);
add_widget(grid, new gui2::tlabel(), "2", 1, 0);
add_widget(grid, new gui2::tlabel(), "3", 0, 1);
add_widget(grid, new gui2::tlabel(), "4", 1, 1);
add_widget(*g, new gui2::tlabel(), "5", 0, 0);
add_widget(*g, new gui2::tlabel(), "6", 1, 0);
add_widget(*g, new gui2::tlabel(), "7", 0, 1);
add_widget(*g, new gui2::tlabel(), "8", 1, 1);
{
std::stringstream sstr;
lg::tredirect_output_setter redirect_output(sstr);
gui2::iterator::titerator<gui2::iterator::policy::order::ttop_down<
true
, true
, true> >
iterator(grid);
while(iterator.next()) {
/* DO NOTHING */
}
BOOST_CHECK_EQUAL(top_down_t_t_t_result(), sstr.str());
}
{
std::stringstream sstr;
lg::tredirect_output_setter redirect_output(sstr);
gui2::iterator::titerator<gui2::iterator::policy::order::ttop_down<
true
, true
, true> >
iterator(grid);
for( ; !iterator.at_end(); ++iterator) {
/* DO NOTHING */
}
BOOST_CHECK_EQUAL(top_down_t_t_t_result(), sstr.str());
}
{
std::stringstream sstr;
lg::tredirect_output_setter redirect_output(sstr);
gui2::iterator::titerator<gui2::iterator::policy::order::tbottom_up<
true
, true
, true> >
iterator(grid);
while(iterator.next()) {
/* DO NOTHING */
}
BOOST_CHECK_EQUAL(bottom_up_t_t_t_result(), sstr.str());
}
{
std::stringstream sstr;
lg::tredirect_output_setter redirect_output(sstr);
gui2::iterator::titerator<gui2::iterator::policy::order::tbottom_up<
true
, true
, true> >
iterator(grid);
for( ; !iterator.at_end(); ++iterator) {
/* DO NOTHING */
}
BOOST_CHECK_EQUAL(bottom_up_t_t_t_result(), sstr.str());
}
}
BOOST_AUTO_TEST_CASE(test_gui2_iterator)
{
/**** Initialize the environment. *****/
game_config::config_cache& cache = game_config::config_cache::instance();
cache.clear_defines();
cache.add_define("EDITOR");
cache.add_define("MULTIPLAYER");
lg::set_log_domain_severity("gui/iterator", 3); // FIXME get_severity or something like it
lg::timestamps(false);
std::stringstream sstr;
lg::tredirect_output_setter redirect_output(sstr);
test_control();
test_grid();
}

168
src/tests/gui/visitor.cpp Normal file
View file

@ -0,0 +1,168 @@
/* $Id$ */
/*
Copyright (C) 2011 by Mark de Wever <koraq@xs4all.nl>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program 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
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#define GETTEXT_DOMAIN "wesnoth-lib"
#include <boost/test/unit_test.hpp>
#include "config_cache.hpp"
#include "gui/auxiliary/iterator/walker.hpp"
#include "gui/widgets/label.hpp"
#include "gui/widgets/grid.hpp"
#include <iostream>
#include <typeinfo>
static void add_widget(gui2::tgrid& grid
, gui2::twidget* widget
, const std::string& id
, const unsigned row
, const unsigned column)
{
BOOST_REQUIRE_NE(widget, (gui2::twidget*)NULL);
widget->set_id(id);
grid.set_child(widget
, row
, column
, gui2::tgrid::VERTICAL_GROW_SEND_TO_CLIENT
| gui2::tgrid::HORIZONTAL_GROW_SEND_TO_CLIENT
, 0);
}
template<class T>
static void test_control()
{
std::cerr << __func__ << ": " << typeid(T).name() << ".\n";
T control;
std::auto_ptr<gui2::iterator::twalker_> visitor(control.create_walker());
BOOST_REQUIRE_NE(visitor.get(), (void*)NULL);
/***** INITIAL STATE *****/
BOOST_CHECK_EQUAL(visitor->at_end(gui2::iterator::twalker_::widget), false);
BOOST_CHECK_EQUAL(visitor->at_end(gui2::iterator::twalker_::grid), true);
BOOST_CHECK_EQUAL(visitor->at_end(gui2::iterator::twalker_::child), true);
BOOST_CHECK_EQUAL(visitor->get(gui2::iterator::twalker_::widget), &control);
BOOST_CHECK_EQUAL(visitor->get(gui2::iterator::twalker_::grid), (void*)NULL);
BOOST_CHECK_EQUAL(visitor->get(gui2::iterator::twalker_::child), (void*)NULL);
/***** VISITING WIDGET *****/
BOOST_CHECK_EQUAL(visitor->next(gui2::iterator::twalker_::widget), gui2::iterator::twalker_::invalid);
BOOST_CHECK_EQUAL(visitor->next(gui2::iterator::twalker_::grid), gui2::iterator::twalker_::fail);
BOOST_CHECK_EQUAL(visitor->next(gui2::iterator::twalker_::child), gui2::iterator::twalker_::fail);
BOOST_CHECK_EQUAL(visitor->at_end(gui2::iterator::twalker_::widget), true);
BOOST_CHECK_EQUAL(visitor->at_end(gui2::iterator::twalker_::grid), true);
BOOST_CHECK_EQUAL(visitor->at_end(gui2::iterator::twalker_::child), true);
BOOST_CHECK_EQUAL(visitor->get(gui2::iterator::twalker_::widget), (void*)NULL);
BOOST_CHECK_EQUAL(visitor->get(gui2::iterator::twalker_::grid), (void*)NULL);
BOOST_CHECK_EQUAL(visitor->get(gui2::iterator::twalker_::child), (void*)NULL);
/***** POST END *****/
BOOST_CHECK_EQUAL(visitor->next(gui2::iterator::twalker_::widget), gui2::iterator::twalker_::fail);
}
static void test_control()
{
/* Could add more widgets to the list. */
test_control<gui2::tlabel>();
}
static void test_grid()
{
/* An empty grid behaves the same as a control so test here. */
test_control<gui2::tgrid>();
std::cerr << __func__ << ": Detailed test.\n";
/* Test the child part here. */
gui2::tgrid grid(2 ,2);
add_widget(grid, new gui2::tlabel(), "(1,1)", 0, 0);
add_widget(grid, new gui2::tlabel(), "(1,2)", 0, 1);
add_widget(grid, new gui2::tlabel(), "(2,1)", 1, 0);
add_widget(grid, new gui2::tlabel(), "(2,2)", 1, 1);
std::auto_ptr<gui2::iterator::twalker_> visitor(grid.create_walker());
/***** LABEL 1,1 *****/
BOOST_CHECK_EQUAL(visitor->at_end(gui2::iterator::twalker_::child), false);
BOOST_REQUIRE_NE(visitor->get(gui2::iterator::twalker_::child), (void*)NULL);
BOOST_CHECK_EQUAL(visitor->get(gui2::iterator::twalker_::child)->id(), "(1,1)");
/***** LABEL 2,1 *****/
BOOST_CHECK_EQUAL(visitor->next(gui2::iterator::twalker_::child), gui2::iterator::twalker_::valid);
BOOST_CHECK_EQUAL(visitor->at_end(gui2::iterator::twalker_::child), false);
BOOST_REQUIRE_NE(visitor->get(gui2::iterator::twalker_::child), (void*)NULL);
BOOST_CHECK_EQUAL(visitor->get(gui2::iterator::twalker_::child)->id(), "(2,1)");
/***** LABEL 1,2 *****/
BOOST_CHECK_EQUAL(visitor->next(gui2::iterator::twalker_::child), gui2::iterator::twalker_::valid);
BOOST_CHECK_EQUAL(visitor->at_end(gui2::iterator::twalker_::child), false);
BOOST_REQUIRE_NE(visitor->get(gui2::iterator::twalker_::child), (void*)NULL);
BOOST_CHECK_EQUAL(visitor->get(gui2::iterator::twalker_::child)->id(), "(1,2)");
/***** LABEL 2,2 *****/
BOOST_CHECK_EQUAL(visitor->next(gui2::iterator::twalker_::child), gui2::iterator::twalker_::valid);
BOOST_CHECK_EQUAL(visitor->at_end(gui2::iterator::twalker_::child), false);
BOOST_REQUIRE_NE(visitor->get(gui2::iterator::twalker_::child), (void*)NULL);
BOOST_CHECK_EQUAL(visitor->get(gui2::iterator::twalker_::child)->id(), "(2,2)");
/***** END *****/
BOOST_CHECK_EQUAL(visitor->next(gui2::iterator::twalker_::child), gui2::iterator::twalker_::invalid);
BOOST_CHECK_EQUAL(visitor->at_end(gui2::iterator::twalker_::child), true);
BOOST_CHECK_EQUAL(visitor->get(gui2::iterator::twalker_::child), (void*)NULL);
/***** POST END *****/
BOOST_CHECK_EQUAL(visitor->next(gui2::iterator::twalker_::child), gui2::iterator::twalker_::fail);
}
BOOST_AUTO_TEST_CASE(test_gui2_visitor)
{
/**** Initialize the environment. *****/
game_config::config_cache& cache = game_config::config_cache::instance();
cache.clear_defines();
cache.add_define("EDITOR");
cache.add_define("MULTIPLAYER");
test_control();
test_grid();
}