Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
add wmi pager class, to allow unlimited wml menu items
The pager is held by the play_controller and wraps around the
wmi_container, if there are too many items it adds "More Items"
or "Earlier Items" entries. These entries have special reserved
ids, if they are selected the pager traps them.

Future work might be to allow the page_size_ to be selected in
the preferences, that is currently left as a variable to make
it easy to support this.
  • Loading branch information
cbeck88 committed Jun 29, 2014
1 parent f46eaeb commit f24f6ad
Show file tree
Hide file tree
Showing 11 changed files with 216 additions and 16 deletions.
2 changes: 2 additions & 0 deletions projectfiles/CodeBlocks/wesnoth.cbp
Expand Up @@ -1123,6 +1123,8 @@
<Unit filename="..\..\src\widgets\textbox.hpp" />
<Unit filename="..\..\src\widgets\widget.cpp" />
<Unit filename="..\..\src\widgets\widget.hpp" />
<Unit filename="..\..\src\wmi_pager.cpp" />
<Unit filename="..\..\src\wmi_pager.hpp" />
<Unit filename="..\..\src\wml_exception.cpp" />
<Unit filename="..\..\src\wml_exception.hpp" />
<Unit filename="..\..\src\wml_separators.hpp" />
Expand Down
8 changes: 8 additions & 0 deletions projectfiles/VC9/wesnoth.vcproj
Expand Up @@ -21288,6 +21288,14 @@
RelativePath="..\..\src\notifications\windows_tray_notification.hpp"
>
</File>
<File
RelativePath="..\..\src\wmi_pager.cpp"
>
</File>
<File
RelativePath="..\..\src\wmi_pager.hpp"
>
</File>
<File
RelativePath="..\..\src\wml_exception.cpp"
>
Expand Down
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Expand Up @@ -916,6 +916,7 @@ set(wesnoth-main_SRC
whiteboard/side_actions.cpp
whiteboard/suppose_dead.cpp
whiteboard/utility.cpp
wmi_pager.cpp
${network_implementation_files} # network.cpp and network_worker.cpp are included by default (without USE_ANA_NETWORK)
)

Expand Down
1 change: 1 addition & 0 deletions src/SConscript
Expand Up @@ -553,6 +553,7 @@ wesnoth_sources = Split("""
widgets/combo_drag.cpp
widgets/drop_target.cpp
widgets/scrollpane.cpp
wmi_pager.cpp
""")

if env["PLATFORM"] == "win32":
Expand Down
2 changes: 2 additions & 0 deletions src/game_events/menu_item.hpp
Expand Up @@ -41,6 +41,8 @@ class wml_menu_item
wml_menu_item(const std::string& id, const vconfig & definition,
const wml_menu_item & original);

/// The id of this item.
const std::string & id() const { return item_id_; }
/// The image associated with this menu item.
const std::string & image() const;
/// If true, allow using the menu to trigger this item.
Expand Down
15 changes: 3 additions & 12 deletions src/game_events/wmi_container.cpp
Expand Up @@ -34,9 +34,6 @@ static lg::log_domain log_engine("engine");
#define WRN_NG LOG_STREAM(warn, log_engine)
#define LOG_NG LOG_STREAM(info, log_engine)

static const size_t MAX_WML_COMMANDS = 7;


// This file is in the game_events namespace.
namespace game_events
{
Expand Down Expand Up @@ -104,16 +101,14 @@ bool wmi_container::fire_item(const std::string & id, const map_location & hex)

/**
* Returns the menu items that can be shown for the given location.
* The number of items returned is limited by MAX_WML_COMMANDS.
* Should be used with a wmi_pager to limit the number of items displayed at once.
* @param[out] items Pointers to applicable menu items will be pushed onto @a items.
* @param[out] descriptions Menu item text will be pushed onto @descriptions (in the same order as @a items).
*/
void wmi_container::get_items(const map_location& hex,
std::vector<boost::shared_ptr<const wml_menu_item> > & items,
std::vector<std::string> & descriptions) const
std::vector<std::string> & descriptions, const_iterator start, const_iterator finish) const
{
size_t item_count = 0;

if ( empty() )
// Nothing to do (skip setting game variables).
return;
Expand All @@ -124,18 +119,14 @@ void wmi_container::get_items(const map_location& hex,
scoped_xy_unit highlighted_unit("unit", hex.x, hex.y, *resources::units);

// Check each menu item.
BOOST_FOREACH( const item_ptr & item, *this )
BOOST_FOREACH( const item_ptr & item, std::make_pair<const_iterator> (start, finish) )
{
// Can this item be shown?
if ( item->use_wml_menu() && item->can_show(hex) )
{
// Include this item.
items.push_back(item);
descriptions.push_back(item->menu_text());

// Limit how many items can be returned.
if ( ++item_count >= MAX_WML_COMMANDS )
return;
}
}
}
Expand Down
12 changes: 10 additions & 2 deletions src/game_events/wmi_container.hpp
Expand Up @@ -76,8 +76,15 @@ class wmi_container{
bool fire_item(const std::string & id, const map_location & hex) const;
/// Returns the menu items that can be shown for the given location.
void get_items(const map_location& hex,
std::vector<boost::shared_ptr<const wml_menu_item> > & items,
std::vector<std::string> & descriptions) const;
std::vector<boost::shared_ptr<const wml_menu_item> > & items,
std::vector<std::string> & descriptions,
const_iterator start, const_iterator finish) const;
/// Range over all items by default
void get_items(const map_location& hex,
std::vector<boost::shared_ptr<const wml_menu_item> > & items,
std::vector<std::string> & descriptions) const {
get_items(hex, items, descriptions, begin(), end());
}
/// Initializes the implicit event handlers for inlined [command]s.
void init_handlers() const;
void to_config(config& cfg) const;
Expand All @@ -99,6 +106,7 @@ class wmi_container{
const_iterator begin() const { return const_iterator(wml_menu_items_.begin()); }
const_iterator end() const { return const_iterator(wml_menu_items_.end()); }

size_t size() const { return wml_menu_items_.size(); }
private: // data
map_t wml_menu_items_;
};
Expand Down
9 changes: 7 additions & 2 deletions src/play_controller.cpp
Expand Up @@ -54,6 +54,7 @@
#include "unit.hpp"
#include "unit_id.hpp"
#include "whiteboard/manager.hpp"
#include "wmi_pager.hpp"
#include "wml_exception.hpp"

#include <boost/foreach.hpp>
Expand Down Expand Up @@ -125,6 +126,7 @@ play_controller::play_controller(const config& level, saved_game& state_of_game,
init_side_done_(level["init_side_done"].to_bool(true)),
savenames_(),
wml_commands_(),
wml_command_pager_(new wmi_pager()),
victory_when_enemies_defeated_(true),
remove_from_carryover_on_defeat_(true),
end_level_data_(),
Expand Down Expand Up @@ -724,7 +726,9 @@ bool play_controller::execute_command(const hotkey::hotkey_command& cmd, int ind
throw game::load_game_exception(savenames_[i],false,false,false,"");

} else if ( i < wml_commands_.size() && wml_commands_[i] ) {
wml_commands_[i]->fire_event(mouse_handler_.get_last_hex());
if (!wml_command_pager_->capture(*wml_commands_[i])) {
wml_commands_[i]->fire_event(mouse_handler_.get_last_hex());
}
return true;
}
}
Expand Down Expand Up @@ -1072,7 +1076,8 @@ void play_controller::expand_wml_commands(std::vector<std::string>& items)

// Replace this placeholder entry with available menu items.
items.erase(items.begin() + i);
gamestate_.gamedata_.get_wml_menu_items().get_items(mouse_handler_.get_last_hex(),
wml_command_pager_->update_ref(&gamestate_.gamedata_.get_wml_menu_items());
wml_command_pager_->get_items(mouse_handler_.get_last_hex(),
wml_commands_, newitems);
items.insert(items.begin()+i, newitems.begin(), newitems.end());
// End the "for" loop.
Expand Down
2 changes: 2 additions & 0 deletions src/play_controller.hpp
Expand Up @@ -35,6 +35,7 @@ class saved_game;
class game_data;
class team;
class unit;
class wmi_pager;

namespace actions {
class undo_list;
Expand Down Expand Up @@ -279,6 +280,7 @@ class play_controller : public controller_base, public events::observer, public

void expand_wml_commands(std::vector<std::string>& items);
std::vector<const_item_ptr> wml_commands_;
boost::scoped_ptr<wmi_pager> wml_command_pager_;

bool victory_when_enemies_defeated_;
bool remove_from_carryover_on_defeat_;
Expand Down
131 changes: 131 additions & 0 deletions src/wmi_pager.cpp
@@ -0,0 +1,131 @@
/*
Copyright (C) 2014 by Chris Beck <render787@gmail.com>
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.
*/


#include "wmi_pager.hpp"
#include "global.hpp"

#include "config.hpp"
#include "game_events/menu_item.hpp"
#include "game_events/wmi_container.hpp"
#include "gettext.hpp"

#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <iterator> //std::advance
#include <string>
#include <vector>

struct map_location;

static const char * next_id = "__wml_items_next_page";
static const char * prev_id = "__wml_items_prev_page";

static void add_next_page_item( std::vector<boost::shared_ptr<const game_events::wml_menu_item> > & items,
std::vector<std::string> & descriptions)
{
std::string desc = _("More Items");
config temp;
temp["description"] = desc;
items.push_back(boost::make_shared<const game_events::wml_menu_item>(next_id, temp));
descriptions.push_back(desc);
}

static void add_prev_page_item( std::vector<boost::shared_ptr<const game_events::wml_menu_item> > & items,
std::vector<std::string> & descriptions)
{
std::string desc = _("Earlier Items");
config temp;
temp["description"] = desc;
items.push_back(boost::make_shared<const game_events::wml_menu_item>(prev_id, temp));
descriptions.push_back(desc);
}

bool wmi_pager::capture ( const game_events::wml_menu_item & item )
{
if (item.id() == next_id) {
page_num_++;
return true;
} else if (item.id() == prev_id) {
page_num_--;
return true;
}
return false;
}

typedef game_events::wmi_container::const_iterator wmi_it;

void wmi_pager::get_items(const map_location& hex,
std::vector<boost::shared_ptr<const game_events::wml_menu_item> > & items,
std::vector<std::string> & descriptions)
{
if (!foo_) {
return;
}

assert(page_size_ > 2u); //if we dont have at least 3 items, we can't display anything...

if (foo_->size() <= page_size_) { //In this case the first page is sufficient and we don't have to do anything.
foo_->get_items(hex, items, descriptions);
page_num_ = 0; //reset page num in case there are more items later.
return;
}

if (page_num_ < 0) //should never happen but maybe some wierd gui thing happens idk
{
page_num_ = 0;
}

if (page_num_ == 0) { //we are on the first page, so show page_size_-1 items and a next button
wmi_it end_first_page = foo_->begin();
std::advance(end_first_page, page_size_ - 1);

foo_->get_items(hex, items, descriptions, foo_->begin(), end_first_page);
add_next_page_item(items, descriptions);
return;
}

add_prev_page_item(items, descriptions); //this will be necessary since we aren't on the first page

// first page has page_size_ - 1.
// last page has page_size_ - 1.
// all other pages have page_size_ - 2;

size_t first_displayed_index = (page_size_ - 2) * page_num_ + 1; //this is the 0-based index of the first item displayed on this page.
//alternatively, the number of items displayed on earlier pages

while (first_displayed_index >= foo_->size())
{
page_num_--; //The list must have gotten shorter and our page counter is now off the end, so decrement
first_displayed_index = (page_size_ - 2) * page_num_ + 1; //recalculate
}
// ^ This loop terminates with first_displayed_index > 0, because foo_->size() > page_size_ or else we exited earlier, and we only decrease by (page_size_-2) each time.

wmi_it start_range = foo_->begin();
std::advance(start_range, first_displayed_index); // <-- get an iterator to the start of our range. begin() + n doesn't work because map is not random access
//^ = foo_->begin() + first_displayed_index

if (first_displayed_index + page_size_-1 >= foo_->size()) //if this can be the last page, then we won't put next page at the bottom.
{
foo_->get_items(hex, items, descriptions, start_range, foo_->end()); // display all of the remaining items
return;
} else { //we are in a middle page
wmi_it end_range = start_range;
std::advance(end_range, page_size_-2);

foo_->get_items(hex, items, descriptions, start_range, end_range);
add_next_page_item(items, descriptions);
return;
}
}
49 changes: 49 additions & 0 deletions src/wmi_pager.hpp
@@ -0,0 +1,49 @@
/*
Copyright (C) 2014 by Chris Beck <render787@gmail.com>
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.
*/


/** This class manages the paging of WML menu items results, from a
* container. It is an adapter, managing the production of items lists
* from the container, and screening the "fire" signals coming back
* in to intercept the paging signals.
*/

struct map_location;
namespace game_events { class wml_menu_item; }
namespace game_events { class wmi_container; }

#include "global.hpp"

#include <boost/shared_ptr.hpp>
#include <string>
#include <vector>

class wmi_pager {
private:
int page_num_; //!< Current page number
size_t page_size_; //!< Current size of a page
const game_events::wmi_container * foo_; //!< Internal pointer to the collection of wml menu items

public:
wmi_pager() : page_num_(0), page_size_(7), foo_(NULL) {}

void update_ref(game_events::wmi_container * ptr) { foo_ = ptr; } //!< Updates the internal wmi_container pointer

/** Adds the currently paged range of menu items to the given lists */
void get_items(const map_location& hex, //!< Game hex related to this context menu
std::vector<boost::shared_ptr<const game_events::wml_menu_item> > & items, //!< List of accumulated menu items so far.
std::vector<std::string> & descriptions); //!< List of menu item descriptions

bool capture(const game_events::wml_menu_item & item); //!< Captures a page up / page down event in the case that it is fired.
};

0 comments on commit f24f6ad

Please sign in to comment.