made menu widgets sortable

This commit is contained in:
Dave White 2005-05-10 22:15:57 +00:00
parent d4062f31a6
commit 1943ac5873
31 changed files with 853 additions and 131 deletions

View file

@ -1,6 +1,7 @@
CVS HEAD:
* balancing change: reduced cost of dwarvish fighter by 1 gold; made Lieutenant more powerful
* user interface improvements:
* made menu widgets sortable
* added %-to-kill to Damage Calculations
* made it so a unit's defense in a terrain is displayed over the terrain when choosing to move the unit
* added 'Advanced' preferences section with 'binary save files' and 'show combat' as initial options

View file

@ -57,7 +57,7 @@ void checksumstreambuf::sum(unsigned char* begin, unsigned char* end)
}
checksumstream::checksumstream() : std::ostream(&sbuf)
checksumstream::checksumstream() : std::basic_ostream<char>(this), sbuf(*this)
{
}

View file

@ -39,13 +39,13 @@ private:
unsigned short csumb;
};
class checksumstream : public std::basic_ostream<char>
class checksumstream : private checksumstreambuf, public std::basic_ostream<char>
{
public:
checksumstream();
unsigned long checksum();
private:
checksumstreambuf sbuf;
checksumstreambuf& sbuf;
};
#endif // CHECKSUM_HPP_INCLUDED

View file

@ -392,6 +392,51 @@ void save_preview_pane::draw_contents()
font::draw_text(&video(), area, font::SIZE_SMALL, font::NORMAL_COLOUR, str.str(), area.x, ypos, true);
}
std::string format_time_summary(time_t t)
{
time_t curtime = time(NULL);
const struct tm* timeptr = localtime(&curtime);
if(timeptr == NULL) {
return "";
}
const struct tm current_time = *timeptr;
timeptr = localtime(&t);
if(timeptr == NULL) {
return "";
}
const struct tm save_time = *timeptr;
const char* format_string = _("%b %d %y");
if(current_time.tm_year == save_time.tm_year) {
const int days_apart = current_time.tm_yday - save_time.tm_yday;
if(days_apart == 0) {
//save is from today
format_string = _("%H:%M");
} else if(days_apart > 0 && days_apart <= current_time.tm_wday) {
//save is from this week
format_string = _("%A, %H:%M");
} else {
//save is from current year
format_string = _("%b %d");
}
} else {
//save is from a different year
format_string = _("%b %d %y");
}
char buf[40];
const size_t res = strftime(buf,sizeof(buf),format_string,&save_time);
if(res == 0) {
buf[0] = 0;
}
return buf;
}
} //end anon namespace
std::string load_game_dialog(display& disp, const config& game_config, const game_data& data, bool* show_replay)
@ -495,13 +540,23 @@ std::string load_game_dialog(display& disp, const config& game_config, const gam
}
std::vector<std::string> items;
std::ostringstream heading;
heading << HEADING_PREFIX << _("Name") << COLUMN_SEPARATOR << _("Date");
items.push_back(heading.str());
for(i = games.begin(); i != games.end(); ++i) {
std::string name = i->name;
name.resize(minimum<size_t>(name.size(),40));
items.push_back(name);
std::ostringstream str;
str << name << COLUMN_SEPARATOR << format_time_summary(i->time_modified);
items.push_back(str.str());
}
gui::menu::basic_sorter sorter;
sorter.set_alpha_sort(0).set_id_sort(1);
gamemap map_obj(game_config,"");
std::vector<gui::preview_pane*> preview_panes;
@ -517,7 +572,7 @@ std::string load_game_dialog(display& disp, const config& game_config, const gam
const int res = gui::show_dialog(disp,NULL,
_("Load Game"),
_("Choose the game to load"),
gui::OK_CANCEL,&items,&preview_panes,"",NULL,-1,NULL,&options,-1,-1,NULL,&buttons);
gui::OK_CANCEL,&items,&preview_panes,"",NULL,-1,NULL,&options,-1,-1,NULL,&buttons,"",&sorter);
if(res == -1)
return "";

View file

@ -492,6 +492,8 @@ text_surface &text_cache::find(text_surface const &t)
return cache_.front();
}
}
surface render_text(const std::string& text, int fontsize, const SDL_Color& colour, int style)
{
const std::vector<std::string> lines = utils::split(text, '\n', utils::REMOVE_EMPTY);
@ -547,6 +549,8 @@ surface render_text(const std::string& text, int fontsize, const SDL_Color& colo
}
}
namespace {
//function which will parse the markup tags at the front of a string
std::string::const_iterator parse_markup(std::string::const_iterator i1, std::string::const_iterator i2,
int* font_size, SDL_Color* colour, int* style)
@ -816,9 +820,20 @@ int get_max_height(int size)
bool is_format_char(char c)
{
//side coloring
if(c > 0 && c <= 10) {
return true;
}
switch(c) {
case LARGE_TEXT:
case SMALL_TEXT:
case GOOD_TEXT:
case BAD_TEXT:
case NORMAL_TEXT:
case BLACK_TEXT:
case BOLD_TEXT:
case NULL_MARKUP:
return true;
default:
return false;

View file

@ -875,17 +875,20 @@ void game_controller::download_campaigns()
}
std::vector<std::string> campaigns, options;
std::string sep(1, COLUMN_SEPARATOR);
options.push_back(sep + _("Name") +
sep + _("Version") +
sep + _("Author") +
sep + _("Downloads") +
sep + _("Size"));
std::stringstream heading;
heading << HEADING_PREFIX << sep << _("Name") << sep << _("Version") << sep
<< _("Author") << sep << _("Downloads") << sep << _("Size");
const config::child_list& cmps = campaigns_cfg->get_children("campaign");
const std::vector<std::string>& publish_options = available_campaigns();
std::vector<std::string> delete_options;
std::vector<int> sizes;
for(config::child_list::const_iterator i = cmps.begin(); i != cmps.end(); ++i) {
const std::string& name = (**i)["name"];
campaigns.push_back(name);
@ -914,6 +917,9 @@ void game_controller::download_campaigns()
if(author.size() > 16) {
author.resize(16);
}
//add negative sizes to reverse the sort order
sizes.push_back(-atoi((**i)["size"].c_str()));
options.push_back(IMAGE_PREFIX + (**i)["icon"].str() + COLUMN_SEPARATOR +
title + COLUMN_SEPARATOR +
@ -922,6 +928,8 @@ void game_controller::download_campaigns()
(**i)["downloads"].str() + COLUMN_SEPARATOR +
format_file_size((**i)["size"]));
}
options.push_back(heading.str());
for(std::vector<std::string>::const_iterator j = publish_options.begin(); j != publish_options.end(); ++j) {
options.push_back(sep + _("Publish campaign: ") + *j);
@ -936,7 +944,11 @@ void game_controller::download_campaigns()
return;
}
const int index = gui::show_dialog(disp(),NULL,_("Get Campaign"),_("Choose the campaign to download."),gui::OK_CANCEL,&options) - 1;
gui::menu::basic_sorter sorter;
sorter.set_alpha_sort(1).set_alpha_sort(2).set_alpha_sort(3).set_numeric_sort(4).set_position_sort(5,sizes);
const int index = gui::show_dialog(disp(),NULL,_("Get Campaign"),_("Choose the campaign to download."),gui::OK_CANCEL,&options,
NULL,"",NULL,0,NULL,NULL,-1,-1,NULL,NULL,"",&sorter) - 1;
if(index < 0) {
return;
}

View file

@ -32,7 +32,7 @@ namespace game_config
int recall_cost = 20;
int kill_experience = 8;
int leadership_bonus = 25;
const std::string version = VERSION;
const std::string version = "0.9.1"; //VERSION;
bool debug = false, editor = false, ignore_replay_errors = false;
std::string game_icon = "wesnoth-icon.png", game_title, game_logo, title_music;

View file

@ -234,7 +234,13 @@ bool show_intro_part(display &disp, const config& part,
//to find out if the next word will fit, or if it has to be wrapped
utils::utf8_iterator start_word = itor;
++start_word;
utils::utf8_iterator end_word = std::find(start_word, utils::utf8_iterator::end(story), ' ');
utils::utf8_iterator end_word = start_word;
const utils::utf8_iterator end_story = utils::utf8_iterator::end(story);
for(; end_word != end_story; ++end_word) {
if(*end_word == ' ') {
break;
}
}
std::string word;
for(; start_word != end_word; ++start_word) {

View file

@ -20,8 +20,86 @@
#include "game_config.hpp"
#include "gettext.hpp"
namespace {
std::string games_menu_heading()
{
std::ostringstream str;
str << HEADING_PREFIX << _("Map") << COLUMN_SEPARATOR << _("Name")
<< COLUMN_SEPARATOR << _("Status");
return str.str();
}
}
namespace mp {
lobby::lobby_sorter::lobby_sorter(const config& cfg) : cfg_(cfg)
{
set_alpha_sort(1);
}
bool lobby::lobby_sorter::column_sortable(int column) const
{
switch(column)
{
case MAP_COLUMN:
case STATUS_COLUMN:
return true;
default:
return basic_sorter::column_sortable(column);
}
}
bool lobby::lobby_sorter::less(int column, const gui::menu::item& row1, const gui::menu::item& row2) const
{
const config* const list = cfg_.child("gamelist");
if(list == NULL) {
return false;
}
const config::child_list& games = list->get_children("game");
if(row1.id >= games.size() || row2.id >= games.size()) {
return false;
}
const config& game1 = *games[row1.id];
const config& game2 = *games[row2.id];
if(column == MAP_COLUMN) {
size_t mapsize1 = game1["map_data"].size();
if(mapsize1 == 0) {
mapsize1 = game1["map"].size();
}
size_t mapsize2 = game2["map_data"].size();
if(mapsize2 == 0) {
mapsize2 = game2["map"].size();
}
return mapsize1 < mapsize2;
} else if(column == STATUS_COLUMN) {
const int nslots1 = atoi(game1["slots"].c_str());
const int nslots2 = atoi(game2["slots"].c_str());
const int turn1 = atoi(game1["turn"].c_str());
const int turn2 = atoi(game2["turn"].c_str());
if(nslots1 > nslots2) {
return true;
} else if(nslots1 < nslots2) {
return false;
} else {
return turn1 < turn2;
}
} else {
return basic_sorter::less(column,row1,row2);
}
return false;
}
lobby::lobby(display& disp, const config& cfg, chat& c, config& gamelist) :
mp::ui(disp, cfg, c, gamelist),
@ -29,7 +107,8 @@ lobby::lobby(display& disp, const config& cfg, chat& c, config& gamelist) :
join_game_(disp.video(), _("Join Game")),
create_game_(disp.video(), _("Create Game")),
quit_game_(disp.video(), _("Quit")),
games_menu_(disp.video(), std::vector<std::string>()),
sorter_(gamelist),
games_menu_(disp.video(), std::vector<std::string>(1,games_menu_heading()),false,-1,-1,&sorter_),
current_game_(0)
{
game_config::debug = false;

View file

@ -39,6 +39,18 @@ protected:
virtual void gamelist_updated(bool silent=true);
private:
class lobby_sorter : public gui::menu::basic_sorter
{
const config& cfg_;
bool column_sortable(int column) const;
bool less(int column, const gui::menu::item& row1, const gui::menu::item& row2) const;
enum { MAP_COLUMN = 0, STATUS_COLUMN = 2};
public:
lobby_sorter(const config& cfg);
};
std::vector<bool> game_vacant_slots_;
std::vector<bool> game_observers_;
@ -47,6 +59,7 @@ private:
gui::button create_game_;
gui::button quit_game_;
lobby_sorter sorter_;
gui::menu games_menu_;
int current_game_;
};

View file

@ -12,6 +12,8 @@
See the COPYING file for more details.
*/
#include "global.hpp"
#include <map>
#include "playcampaign.hpp"

View file

@ -1700,13 +1700,17 @@ void turn_info::status_table()
{
std::vector<std::string> items;
std::stringstream heading;
heading << _("Leader") << COLUMN_SEPARATOR << ' ' << COLUMN_SEPARATOR
heading << HEADING_PREFIX << _("Leader") << COLUMN_SEPARATOR << ' ' << COLUMN_SEPARATOR
<< _("Gold") << COLUMN_SEPARATOR
<< _("Villages") << COLUMN_SEPARATOR
<< _("Units") << COLUMN_SEPARATOR
<< _("Upkeep") << COLUMN_SEPARATOR
<< _("Income");
gui::menu::basic_sorter sorter;
sorter.set_redirect_sort(0,1).set_alpha_sort(1).set_numeric_sort(2).set_numeric_sort(3)
.set_numeric_sort(4).set_numeric_sort(5).set_numeric_sort(6).set_numeric_sort(7);
if(game_config::debug)
heading << COLUMN_SEPARATOR << _("Gold");
@ -1763,7 +1767,8 @@ void turn_info::status_table()
items.push_back(IMAGE_PREFIX + std::string("random-enemy.png") + COLUMN_SEPARATOR +
IMAGE_PREFIX + "random-enemy.png");
gui::show_dialog(gui_,NULL,"","",gui::CLOSE_ONLY,&items);
gui::show_dialog(gui_,NULL,"","",gui::CLOSE_ONLY,&items,
NULL,"",NULL,0,NULL,NULL,-1,-1,NULL,NULL,"",&sorter);
}
void turn_info::recruit()
@ -1969,14 +1974,26 @@ void turn_info::recall()
gui::show_dialog(gui_,NULL,"",msg.str());
} else {
std::vector<std::string> options;
std::ostringstream heading;
heading << HEADING_PREFIX << COLUMN_SEPARATOR << _("Type")
<< COLUMN_SEPARATOR << _("Name")
<< COLUMN_SEPARATOR << _("Level")
<< COLUMN_SEPARATOR << _("XP");
gui::menu::basic_sorter sorter;
sorter.set_alpha_sort(1).set_alpha_sort(2).set_id_sort(3).set_numeric_sort(4);
options.push_back(heading.str());
for(std::vector<unit>::const_iterator u = recall_list.begin(); u != recall_list.end(); ++u) {
std::stringstream option;
const std::string& description = u->description().empty() ? "-" : u->description();
option << IMAGE_PREFIX << u->type().image() << COLUMN_SEPARATOR
<< u->type().language_name() << COLUMN_SEPARATOR
<< description << COLUMN_SEPARATOR
<< _("level: ") << u->type().level() << COLUMN_SEPARATOR
<< _("XP: ") << u->experience() << "/";
<< u->type().level() << COLUMN_SEPARATOR
<< u->experience() << "/";
if(u->can_advance() == false) {
option << "-";
@ -2004,7 +2021,7 @@ void turn_info::recall()
_("Select unit:") + std::string("\n"),
gui::OK_CANCEL,&options,
&preview_panes,"",NULL,-1,
NULL,NULL,-1,-1,NULL,&buttons);
NULL,NULL,-1,-1,NULL,&buttons,"",&sorter);
}
if(res >= 0) {
@ -2135,7 +2152,8 @@ void turn_info::objectives()
void turn_info::unit_list()
{
const std::string heading = _("Type") + std::string(1, COLUMN_SEPARATOR) +
const std::string heading = std::string(1,HEADING_PREFIX) +
_("Type") + std::string(1, COLUMN_SEPARATOR) +
_("Name") + COLUMN_SEPARATOR +
_("HP") + COLUMN_SEPARATOR +
_("XP") + COLUMN_SEPARATOR +
@ -2143,6 +2161,10 @@ void turn_info::unit_list()
_("Moves") + COLUMN_SEPARATOR +
_("Location");
gui::menu::basic_sorter sorter;
sorter.set_alpha_sort(0).set_alpha_sort(1).set_numeric_sort(2).set_numeric_sort(3)
.set_alpha_sort(4).set_numeric_sort(5).set_numeric_sort(6);
std::vector<std::string> items;
items.push_back(heading);
@ -2190,7 +2212,8 @@ void turn_info::unit_list()
preview_panes.push_back(&unit_preview);
selected = gui::show_dialog(gui_,NULL,_("Unit List"),"",
gui::OK_ONLY,&items,&preview_panes);
gui::OK_ONLY,&items,&preview_panes,
"",NULL,0,NULL,NULL,-1,-1,NULL,NULL,"",&sorter);
}
if(selected > 0 && selected < int(locations_list.size())) {

View file

@ -1277,12 +1277,21 @@ void show_hotkeys_dialog (display & disp, config *save_config)
std::stringstream str,name;
name << i->get_description();
str << name.str();
str << COLUMN_SEPARATOR << " : " << COLUMN_SEPARATOR;
str << COLUMN_SEPARATOR;
str << i->get_name();
menu_items.push_back (str.str ());
menu_items.push_back(str.str());
}
gui::menu menu_(disp.video(), menu_items, false, height);
std::ostringstream heading;
heading << HEADING_PREFIX << _("Action") << COLUMN_SEPARATOR << _("Binding");
menu_items.push_back(heading.str());
gui::menu::basic_sorter sorter;
sorter.set_alpha_sort(0).set_alpha_sort(1);
gui::menu menu_(disp.video(), menu_items, false, height, -1, &sorter);
menu_.sort_by(0);
menu_.reset_selection();
menu_.set_width(font::relative_size(400));
menu_.set_location(xpos + font::relative_size(20), ypos);
@ -1340,7 +1349,7 @@ void show_hotkeys_dialog (display & disp, config *save_config)
newhk.set_key(key, (mod & KMOD_SHIFT) != 0,
(mod & KMOD_CTRL) != 0, (mod & KMOD_ALT) != 0, (mod & KMOD_LMETA) != 0);
menu_.change_item(menu_.selection(), 2, newhk.get_name());
menu_.change_item(menu_.selection(), 1, newhk.get_name());
};
}
if (save_button.pressed()) {

View file

@ -11,6 +11,11 @@
See the COPYING file for more details.
*/
#include "global.hpp"
#include <cstdio>
#include "config.hpp"
#include "random.hpp"
#include "wassert.hpp"

View file

@ -12,6 +12,8 @@
See the COPYING file for more details.
*/
#include "global.hpp"
#include "config.hpp"
#include "filesystem.hpp"
#include "serialization/binary_wml.hpp"

View file

@ -89,7 +89,7 @@ preprocessor_streambuf::preprocessor_streambuf(preproc_map *def)
}
preprocessor_streambuf::preprocessor_streambuf(preprocessor_streambuf const &t)
: std::streambuf(), current_(NULL), defines_(t.defines_),
: current_(NULL), defines_(t.defines_),
textdomain_(PACKAGE), depth_(t.depth_), quoted_(t.quoted_)
{
}
@ -676,10 +676,10 @@ bool preprocessor_data::get_chunk()
return true;
}
struct preprocessor_deleter: std::istream
struct preprocessor_deleter: std::basic_istream<char>
{
preprocessor_streambuf *buf_;
preprocessor_deleter(preprocessor_streambuf *buf): std::istream(buf), buf_(buf) {}
preprocessor_deleter(preprocessor_streambuf *buf) : std::basic_istream<char>(buf), buf_(buf) {}
~preprocessor_deleter() { rdbuf(NULL); delete buf_->defines_; delete buf_; }
};

View file

@ -39,7 +39,7 @@ std::ostream& operator<<(std::ostream& out, metrics& met)
const int seconds = time_up%60;
const int minutes = (time_up/60)%60;
const int hours = (time_up/(60*60))%24;
const int days = time_up/(60*60*14);
const int days = time_up/(60*60*24);
const int requests_immediate = met.nrequests_ - met.nrequests_waited_;
const int percent_immediate = (requests_immediate*100)/(met.nrequests_ > 0 ? met.nrequests_ : 1);
out << "METRICS\n----\nUp " << days << " days, " << hours << " hours, "

View file

@ -322,7 +322,7 @@ int show_dialog(display& disp, surface image,
int text_widget_max_chars,
dialog_action* action, std::vector<check_item>* options, int xloc, int yloc,
const std::string* dialog_style, std::vector<dialog_button>* action_buttons,
const std::string& help_topic)
const std::string& help_topic, const menu::sorter* sorter)
{
if(disp.update_locked())
return -1;
@ -372,7 +372,7 @@ int show_dialog(display& disp, surface image,
text_widget_height = text_widget.location().h + message_font_size;
}
menu menu_(screen,menu_items,type == MESSAGE);
menu menu_(screen,menu_items,type == MESSAGE,-1,-1,sorter);
menu_.set_numeric_keypress_selection(use_textbox == false);

View file

@ -24,6 +24,7 @@ class CVideo;
#include "tooltips.hpp"
#include "widgets/button.hpp"
#include "widgets/menu.hpp"
#include "SDL.h"
@ -133,7 +134,8 @@ int show_dialog(display &screen, surface image,
std::vector<check_item>* options=NULL, int xloc=-1, int yloc=-1,
const std::string* dialog_style=NULL,
std::vector<dialog_button>* buttons=NULL,
const std::string& help_topic=""
const std::string& help_topic="",
const menu::sorter* sorter=NULL
);
void show_error_message(display &disp, std::string const &message);

View file

@ -11,6 +11,9 @@
See the COPYING file for more details.
*/
#include "global.hpp"
#include <sstream>
#include <vector>
#include <map>

View file

@ -42,6 +42,8 @@ public:
bool translatable_;
};
friend class walker;
t_string();
t_string(const t_string&);
t_string(const std::string& string);

View file

@ -163,8 +163,6 @@ void unit::generate_traits()
traits.push_back(candidate_traits[num]);
candidate_traits.erase(candidate_traits.begin()+num);
}
std::vector<std::string> description;
for(std::vector<config*>::const_iterator j = traits.begin(); j != traits.end(); ++j) {
modifications_.add_child("trait",**j);

View file

@ -80,6 +80,7 @@ std::string str_cast(From a)
}
inline bool chars_equal_insensitive(char a, char b) { return tolower(a) == tolower(b); }
inline bool chars_less_insensitive(char a, char b) { return tolower(a) < tolower(b); }
//a definition of 'push_back' for strings, since some implementations
//don't support string::push_back

View file

@ -13,6 +13,8 @@
See the COPYING file for more details.
*/
#include "global.hpp"
#include "variable.hpp"
#include "gamestatus.hpp"
#include "config.hpp"
@ -78,7 +80,8 @@ const config vconfig::get_parsed_config() const
vconfig::child_list vconfig::get_children(const std::string& key) const
{
const config::child_list& list = cfg_->get_children(key);
vconfig::child_list res(list.begin(), list.end());
vconfig::child_list res(list.size());
std::copy(list.begin(), list.end(),res.begin());
return res;
}

View file

@ -20,7 +20,7 @@
class config;
class t_string;
class game_state;
struct game_state;
/**
* A variable-expanding proxy for the config class. This class roughly behaves

View file

@ -10,6 +10,8 @@
#include "../wml_separators.hpp"
#include "serialization/string_utils.hpp"
#include <algorithm>
#include <cassert>
#include <numeric>
namespace {
@ -19,59 +21,240 @@ const size_t menu_cell_padding = font::SIZE_NORMAL * 3/5;
namespace gui {
menu::basic_sorter& menu::basic_sorter::set_alpha_sort(int column)
{
alpha_sort_.insert(column);
return *this;
}
menu::basic_sorter& menu::basic_sorter::set_numeric_sort(int column)
{
numeric_sort_.insert(column);
return *this;
}
menu::basic_sorter& menu::basic_sorter::set_id_sort(int column)
{
id_sort_.insert(column);
return *this;
}
menu::basic_sorter& menu::basic_sorter::set_redirect_sort(int column, int to)
{
if(column != to) {
redirect_sort_.insert(std::pair<int,int>(column,to));
}
return *this;
}
menu::basic_sorter& menu::basic_sorter::set_position_sort(int column, const std::vector<int>& pos)
{
pos_sort_[column] = pos;
return *this;
}
bool menu::basic_sorter::column_sortable(int column) const
{
const std::map<int,int>::const_iterator redirect = redirect_sort_.find(column);
if(redirect != redirect_sort_.end()) {
return column_sortable(redirect->second);
}
return alpha_sort_.count(column) == 1 || numeric_sort_.count(column) == 1 ||
pos_sort_.count(column) == 1 || id_sort_.count(column) == 1;
}
bool menu::basic_sorter::less(int column, const item& row1, const item& row2) const
{
const std::map<int,int>::const_iterator redirect = redirect_sort_.find(column);
if(redirect != redirect_sort_.end()) {
return less(redirect->second,row1,row2);
}
if(column < 0 || column >= int(row2.fields.size())) {
return false;
}
if(column >= int(row1.fields.size())) {
return true;
}
const std::string& item1 = row1.fields[column];
const std::string& item2 = row2.fields[column];
if(alpha_sort_.count(column) == 1) {
std::string::const_iterator begin1 = item1.begin(), end1 = item1.end(),
begin2 = item2.begin(), end2 = item2.end();
while(begin1 != end1 && (font::is_format_char(*begin1) || is_wml_separator(*begin1))) {
++begin1;
}
while(begin2 != end2 && (font::is_format_char(*begin2) || is_wml_separator(*begin2))) {
++begin2;
}
return std::lexicographical_compare(begin1,end1,begin2,end2,chars_less_insensitive);
} else if(numeric_sort_.count(column) == 1) {
const char* a = item1.c_str();
const char* b = item2.c_str();
while(*a != 0 && !isdigit(*a)) {
++a;
}
while(*b != 0 && !isdigit(*b)) {
++b;
}
return atoi(a) > atoi(b);
} else if(id_sort_.count(column) == 1) {
return row1.id < row2.id;
}
const std::map<int,std::vector<int> >::const_iterator itor = pos_sort_.find(column);
if(itor != pos_sort_.end()) {
const std::vector<int>& pos = itor->second;
if(row1.id >= pos.size()) {
return false;
}
if(row2.id >= pos.size()) {
return true;
}
return pos[row1.id] < pos[row2.id];
}
return false;
}
menu::menu(CVideo& video, const std::vector<std::string>& items,
bool click_selects, int max_height, int max_width)
bool click_selects, int max_height, int max_width,
const sorter* sorter_obj)
: scrollarea(video),
max_height_(max_height), max_width_(max_width), max_items_(-1), item_height_(-1),
heading_height_(-1),
cur_help_(-1,-1), help_string_(-1),
selected_(0), click_selects_(click_selects),
previous_button_(true), show_result_(false),
double_clicked_(false),
num_selects_(true),
ignore_next_doubleclick_(false),
last_was_doubleclick_(false)
last_was_doubleclick_(false),
sorter_(sorter_obj), sortby_(-1), highlight_heading_(-1)
{
fill_items(items, true);
}
void menu::fill_items(const std::vector<std::string>& items, bool strip_spaces)
{
for(std::vector<std::string>::const_iterator item = items.begin();
item != items.end(); ++item) {
items_.push_back(utils::quoted_split(*item, COLUMN_SEPARATOR, !strip_spaces));
for(std::vector<std::string>::const_iterator itor = items.begin();
itor != items.end(); ++itor) {
if(itor->empty() == false && (*itor)[0] == HEADING_PREFIX) {
heading_ = utils::quoted_split(itor->substr(1),COLUMN_SEPARATOR, !strip_spaces);
continue;
}
const size_t id = items_.size();
item_pos_.push_back(id);
const item new_item(utils::quoted_split(*itor, COLUMN_SEPARATOR, !strip_spaces),id);
items_.push_back(new_item);
//make sure there is always at least one item
if(items_.back().empty())
items_.back().push_back(" ");
if(items_.back().fields.empty()) {
items_.back().fields.push_back(" ");
}
//if the first character in an item is an asterisk,
//it means this item should be selected by default
std::string& first_item = items_.back().front();
std::string& first_item = items_.back().fields.front();
if(first_item.empty() == false && first_item[0] == DEFAULT_ITEM) {
selected_ = items_.size()-1;
selected_ = id;
first_item.erase(first_item.begin());
}
}
create_help_strings();
do_sort();
update_size();
}
namespace {
class sort_func
{
public:
sort_func(const menu::sorter& pred, int column) : pred_(&pred), column_(column)
{}
bool operator()(const menu::item& a, const menu::item& b) const
{
return pred_->less(column_,a,b);
}
private:
const menu::sorter* pred_;
int column_;
};
}
void menu::do_sort(SORT_TYPE type)
{
if(sortby_ < 0 || sorter_ == NULL || sorter_->column_sortable(sortby_) == false) {
return;
}
const int selectid = selection();
if(type == NORMAL_SORT) {
std::stable_sort(items_.begin(),items_.end(),sort_func(*sorter_,sortby_));
} else if(type == INVERT_SORT) {
std::reverse(items_.begin(),items_.end());
}
recalculate_pos();
if(selectid >= 0 && selectid < int(item_pos_.size())) {
move_selection(selectid,NO_MOVE_VIEWPORT);
}
set_dirty();
}
void menu::recalculate_pos()
{
item_pos_.resize(items_.size());
for(std::vector<item>::const_iterator i = items_.begin(); i != items_.end(); ++i) {
item_pos_[i->id] = i - items_.begin();
}
assert_pos();
}
void menu::assert_pos()
{
assert(item_pos_.size() == items_.size());
for(size_t n = 0; n != item_pos_.size(); ++n) {
assert(n == items_[item_pos_[n]].id);
}
}
void menu::create_help_strings()
{
help_.clear();
for(std::vector<std::vector<std::string> >::iterator i = items_.begin(); i != items_.end(); ++i) {
help_.resize(help_.size()+1);
for(std::vector<std::string>::iterator j = i->begin(); j != i->end(); ++j) {
for(std::vector<item>::iterator i = items_.begin(); i != items_.end(); ++i) {
i->help.clear();
for(std::vector<std::string>::iterator j = i->fields.begin(); j != i->fields.end(); ++j) {
if(std::find(j->begin(),j->end(),static_cast<char>(HELP_STRING_SEPARATOR)) == j->end()) {
help_.back().push_back("");
i->help.push_back("");
} else {
const std::vector<std::string>& items = utils::split(*j, HELP_STRING_SEPARATOR, 0);
if(items.size() >= 2) {
*j = items.front();
help_.back().push_back(items.back());
i->help.push_back(items.back());
} else {
help_.back().push_back("");
i->help.push_back("");
}
}
}
@ -84,8 +267,9 @@ void menu::update_scrollbar_grip_height()
set_shown_size(max_items_onscreen());
}
void menu::update_size() {
unsigned h = 0;
void menu::update_size()
{
unsigned int h = heading_height();
for(size_t i = get_position(),
i_end = minimum(items_.size(), i + max_items_onscreen());
i != i_end; ++i)
@ -106,7 +290,14 @@ void menu::update_size() {
set_measurements(w, h);
}
int menu::selection() const { return selected_; }
int menu::selection() const
{
if(selected_ < 0 || selected_ >= int(items_.size())) {
return -1;
}
return items_[selected_].id;
}
void menu::set_inner_location(SDL_Rect const &rect)
{
@ -115,10 +306,14 @@ void menu::set_inner_location(SDL_Rect const &rect)
bg_register(rect);
}
void menu::change_item(int pos1, int pos2,std::string str)
void menu::change_item(int pos1, int pos2,const std::string& str)
{
items_[pos1][pos2] = str;
//undrawn_items_.insert(pos1);
if(pos1 < 0 || pos1 >= int(item_pos_.size()) ||
pos2 < 0 || pos2 >= int(items_[item_pos_[pos1]].fields.size())) {
return;
}
items_[item_pos_[pos1]].fields[pos2] = str;
set_dirty();
}
@ -129,7 +324,17 @@ void menu::erase_item(size_t index)
return;
clear_item(nb_items - 1);
items_.erase(items_.begin() + index);
const size_t pos = item_pos_[index];
item_pos_.erase(item_pos_.begin() + index);
for(std::vector<size_t>::iterator i = item_pos_.begin(); i != item_pos_.end(); ++i) {
if(*i > pos) {
*i = *i-1;
}
}
items_.erase(items_.begin() + pos);
if (selected_ >= nb_items - 1)
selected_ = nb_items - 2;
@ -139,9 +344,21 @@ void menu::erase_item(size_t index)
set_dirty();
}
void menu::set_heading(const std::vector<std::string>& heading)
{
itemRects_.clear();
column_widths_.clear();
heading_ = heading;
max_items_ = -1;
set_dirty();
}
void menu::set_items(const std::vector<std::string>& items, bool strip_spaces, bool keep_viewport)
{
items_.clear();
item_pos_.clear();
itemRects_.clear();
column_widths_.clear();
//undrawn_items_.clear();
@ -153,10 +370,15 @@ void menu::set_items(const std::vector<std::string>& items, bool strip_spaces, b
}
fill_items(items, strip_spaces);
if (!keep_viewport)
if(!keep_viewport) {
set_position(0);
}
update_scrollbar_grip_height();
adjust_viewport_to_selection();
if(!keep_viewport) {
adjust_viewport_to_selection();
}
set_dirty();
}
@ -174,7 +396,7 @@ size_t menu::max_items_onscreen() const
return size_t(max_items_);
}
const size_t max_height = max_height_ == -1 ? (video().gety()*66)/100 : max_height_;
const size_t max_height = (max_height_ == -1 ? (video().gety()*66)/100 : max_height_) - heading_height();
std::vector<int> heights;
size_t n;
for(n = 0; n != items_.size(); ++n) {
@ -202,25 +424,39 @@ void menu::adjust_viewport_to_selection()
void menu::move_selection_up(size_t dep)
{
move_selection(selected_ > dep ? selected_ - dep : 0);
set_selection_pos(selected_ > dep ? selected_ - dep : 0);
}
void menu::reset_selection()
{
set_selection_pos(0);
}
void menu::move_selection_down(size_t dep)
{
size_t nb_items = items_.size();
move_selection(selected_ + dep >= nb_items ? nb_items - 1 : selected_ + dep);
set_selection_pos(selected_ + dep >= nb_items ? nb_items - 1 : selected_ + dep);
}
void menu::move_selection(size_t new_selected)
void menu::set_selection_pos(size_t new_selected, SELECTION_MOVE_VIEWPORT move_viewport)
{
if (new_selected == selected_ || new_selected >= items_.size())
return;
//undrawn_items_.insert(selected_);
//undrawn_items_.insert(new_selected);
set_dirty();
invalidate_row_pos(selected_);
invalidate_row_pos(new_selected);
selected_ = new_selected;
adjust_viewport_to_selection();
if(move_viewport == MOVE_VIEWPORT) {
adjust_viewport_to_selection();
}
}
void menu::move_selection(size_t id, SELECTION_MOVE_VIEWPORT move_viewport)
{
if(id < item_pos_.size()) {
set_selection_pos(item_pos_[id],move_viewport);
}
}
void menu::key_press(SDLKey key)
@ -240,10 +476,10 @@ void menu::key_press(SDLKey key)
move_selection_down(max_items_onscreen());
break;
case SDLK_HOME:
move_selection(0);
set_selection_pos(0);
break;
case SDLK_END:
move_selection(items_.size() - 1);
set_selection_pos(items_.size() - 1);
break;
//case SDLK_RETURN:
// double_clicked_ = true;
@ -254,7 +490,7 @@ void menu::key_press(SDLKey key)
}
if (num_selects_ && key >= SDLK_1 && key <= SDLK_9)
move_selection(key - SDLK_1);
set_selection_pos(key - SDLK_1);
}
void menu::handle_event(const SDL_Event& event)
@ -303,10 +539,26 @@ void menu::handle_event(const SDL_Event& event)
last_was_doubleclick_ = false;
}
}
} else if(event.type == SDL_MOUSEMOTION && click_selects_) {
const int item = hit(event.motion.x,event.motion.y);
if (item != -1)
move_selection(item);
if(sorter_ != NULL) {
const int heading = hit_heading(x,y);
if(heading >= 0 && sorter_->column_sortable(heading)) {
sort_by(heading);
}
}
} else if(event.type == SDL_MOUSEMOTION) {
if(click_selects_) {
const int item = hit(event.motion.x,event.motion.y);
if (item != -1)
move_selection(item);
}
const int heading_item = hit_heading(event.motion.x,event.motion.y);
if(heading_item != highlight_heading_) {
highlight_heading_ = heading_item;
invalidate_heading();
}
}
}
@ -327,6 +579,11 @@ bool menu::double_clicked()
return old;
}
void menu::set_click_selects(bool value)
{
click_selects_ = value;
}
void menu::set_numeric_keypress_selection(bool value)
{
num_selects_ = value;
@ -338,6 +595,15 @@ void menu::scroll(int)
set_dirty();
}
void menu::sort_by(int column)
{
const bool already_sorted = (column == sortby_);
sortby_ = column;
do_sort(already_sorted ? INVERT_SORT : NORMAL_SORT);
itemRects_.clear();
set_dirty();
}
namespace {
SDL_Rect item_size(const std::string& item) {
SDL_Rect res = {0,0,0,0};
@ -368,19 +634,25 @@ namespace {
}
}
void menu::column_widths_item(const std::vector<std::string>& row, std::vector<int>& widths) const
{
for(size_t col = 0; col != row.size(); ++col) {
const SDL_Rect res = item_size(row[col]);
if(col == widths.size()) {
widths.push_back(res.w + menu_cell_padding);
} else if(res.w > widths[col] - menu_cell_padding) {
widths[col] = res.w + menu_cell_padding;
}
}
}
const std::vector<int>& menu::column_widths() const
{
if(column_widths_.empty()) {
column_widths_item(heading_,column_widths_);
for(size_t row = 0; row != items_.size(); ++row) {
for(size_t col = 0; col != items_[row].size(); ++col) {
const SDL_Rect res = item_size(items_[row][col]);
if(col == column_widths_.size()) {
column_widths_.push_back(res.w + menu_cell_padding);
} else if(res.w > column_widths_[col] - menu_cell_padding) {
column_widths_[col] = res.w + menu_cell_padding;
}
}
column_widths_item(items_[row].fields,column_widths_);
}
}
@ -395,29 +667,50 @@ void menu::clear_item(int item)
bg_restore(rect);
}
void menu::draw_item(int item)
void menu::draw_row(const std::vector<std::string>& row, const SDL_Rect& rect, ROW_TYPE type)
{
SDL_Rect rect = get_item_rect(item);
if(rect.w == 0) {
if(rect.w == 0 || rect.h == 0) {
return;
}
clear_item(item);
bg_restore(rect);
int rgb = 0;
double alpha = 0.0;
switch(type) {
case NORMAL_ROW:
rgb = 0x000000;
alpha = 0.2;
break;
case SELECTED_ROW:
rgb = 0x990000;
alpha = 0.6;
break;
case HEADING_ROW:
rgb = 0x333333;
alpha = 0.3;
break;
}
draw_solid_tinted_rectangle(rect.x, rect.y, rect.w, rect.h,
item == selected_ ? 150:0,0,0,
item == selected_ ? 0.6 : 0.2,
(rgb&0xff0000) >> 16,(rgb&0xff00) >> 8,rgb&0xff,alpha,
video().getSurface());
SDL_Rect const &area = screen_area();
//SDL_Rect area = { 0, 0, rect.w, rect.h };
SDL_Rect const &loc = inner_location();
const std::vector<int>& widths = column_widths();
int xpos = rect.x;
for(size_t i = 0; i != items_[item].size(); ++i) {
for(size_t i = 0; i != row.size(); ++i) {
if(type == HEADING_ROW && highlight_heading_ == int(i)) {
draw_solid_tinted_rectangle(xpos,rect.y,widths[i],rect.h,255,255,255,0.3,video().getSurface());
}
const int last_x = xpos;
std::string str = items_[item][i];
std::string str = row[i];
std::vector<std::string> img_text_items = utils::split(str, IMG_TEXT_SEPARATOR);
for (std::vector<std::string>::const_iterator it = img_text_items.begin();
it != img_text_items.end(); it++) {
@ -448,27 +741,55 @@ void menu::draw_item(int item)
void menu::draw_contents()
{
#if 0
if (undrawn_items_.empty())
return;
SDL_Rect heading_rect = inner_location();
heading_rect.h = heading_height();
draw_row(heading_,heading_rect,HEADING_ROW);
if (!dirty()) {
for(std::set<size_t>::const_iterator i = undrawn_items_.begin(); i != undrawn_items_.end(); ++i) {
if(*i < items_.size()) {
draw_item(*i);
update_rect(get_item_rect(*i));
for(size_t i = 0; i != item_pos_.size(); ++i) {
draw_row(items_[item_pos_[i]].fields,get_item_rect(i),item_pos_[i] == selected_ ? SELECTED_ROW : NORMAL_ROW);
}
}
void menu::draw()
{
if(hidden()) {
return;
}
if(!dirty()) {
for(std::set<int>::const_iterator i = invalid_.begin(); i != invalid_.end(); ++i) {
if(*i == -1) {
SDL_Rect heading_rect = inner_location();
heading_rect.h = heading_height();
bg_restore(heading_rect);
draw_row(heading_,heading_rect,HEADING_ROW);
update_rect(heading_rect);
} else if(*i >= 0 && *i < int(item_pos_.size())) {
const int pos = item_pos_[*i];
const SDL_Rect& rect = get_item_rect(*i);
bg_restore(rect);
draw_row(items_[pos].fields,rect,pos == selected_ ? SELECTED_ROW : NORMAL_ROW);
update_rect(rect);
}
}
undrawn_items_.clear();
invalid_.clear();
return;
}
undrawn_items_.clear();
#endif
invalid_.clear();
for(size_t i = 0; i != items_.size(); ++i)
draw_item(i);
bg_restore();
util::scoped_ptr<clip_rect_setter> clipper(NULL);
if(clip_rect())
clipper.assign(new clip_rect_setter(video().getSurface(), *clip_rect()));
draw_contents();
update_rect(location());
set_dirty(false);
}
int menu::hit(int x, int y) const
@ -485,24 +806,52 @@ int menu::hit(int x, int y) const
return -1;
}
std::pair<int,int> menu::hit_cell(int x, int y) const
int menu::hit_column(int x, int y) const
{
int i = hit(x, y);
if (i < 0)
return std::pair<int,int>(-1, -1);
std::vector<int> const &widths = column_widths();
x -= location().x;
for(int j = 0, j_end = widths.size(); j != j_end; ++j) {
x -= widths[j];
if (x < 0)
return std::pair<int,int>(i, j);
if (x < 0) {
return j;
}
}
return std::pair<int,int>(-1, -1);
return -1;
}
std::pair<int,int> menu::hit_cell(int x, int y) const
{
const int row = hit(x, y);
if(row < 0) {
return std::pair<int,int>(-1, -1);
}
const int col = hit_column(x,y);
if(col < 0) {
return std::pair<int,int>(-1, -1);
}
return std::pair<int,int>(x,y);
}
int menu::hit_heading(int x, int y) const
{
const size_t height = heading_height();
const SDL_Rect& loc = inner_location();
if(y >= loc.y && y < loc.y + height) {
return hit_column(x,y);
} else {
return -1;
}
}
SDL_Rect menu::get_item_rect(int item) const
{
return get_item_rect_internal(item_pos_[item]);
}
SDL_Rect menu::get_item_rect_internal(size_t item) const
{
const SDL_Rect empty_rect = {0,0,0,0};
int first_item_on_screen = get_position();
@ -517,9 +866,9 @@ SDL_Rect menu::get_item_rect(int item) const
SDL_Rect const &loc = inner_location();
int y = loc.y;
int y = loc.y + heading_height();
if (item != first_item_on_screen) {
const SDL_Rect& prev = get_item_rect(item-1);
const SDL_Rect& prev = get_item_rect_internal(item-1);
y = prev.y + prev.h;
}
@ -547,17 +896,26 @@ SDL_Rect menu::get_item_rect(int item) const
return res;
}
size_t menu::get_item_height_internal(int item) const
size_t menu::get_item_height_internal(const std::vector<std::string>& item) const
{
size_t res = 0;
for(size_t n = 0; n != items_[item].size(); ++n) {
SDL_Rect rect = item_size(items_[item][n]);
for(std::vector<std::string>::const_iterator i = item.begin(); i != item.end(); ++i) {
SDL_Rect rect = item_size(*i);
res = maximum<int>(rect.h,res);
}
return res;
}
size_t menu::heading_height() const
{
if(heading_height_ == -1) {
heading_height_ = int(get_item_height_internal(heading_));
}
return minimum<unsigned int>(heading_height_,max_height_);
}
size_t menu::get_item_height(int) const
{
if(item_height_ != -1)
@ -565,7 +923,7 @@ size_t menu::get_item_height(int) const
size_t max_height = 0;
for(size_t n = 0; n != items_.size(); ++n) {
max_height = maximum<int>(max_height,get_item_height_internal(n));
max_height = maximum<int>(max_height,get_item_height_internal(items_[n].fields));
}
return item_height_ = max_height;
@ -585,8 +943,8 @@ void menu::process_help_string(int mousex, int mousey)
help_string_ = -1;
}
if(size_t(loc.first) < help_.size()) {
const std::vector<std::string>& row = help_[loc.first];
if(size_t(loc.first) < items_.size()) {
const std::vector<std::string>& row = items_[loc.first].help;
if(size_t(loc.second) < row.size()) {
const std::string& help = row[loc.second];
if(help.empty() == false) {
@ -600,4 +958,27 @@ void menu::process_help_string(int mousex, int mousey)
cur_help_ = loc;
}
void menu::invalidate_row(size_t id)
{
if(id >= items_.size()) {
return;
}
invalid_.insert(int(id));
}
void menu::invalidate_row_pos(size_t pos)
{
if(pos >= items_.size()) {
return;
}
invalidate_row(items_[pos].id);
}
void menu::invalidate_heading()
{
invalid_.insert(-1);
}
}

View file

@ -1,6 +1,8 @@
#ifndef WIDGET_MENU_HPP_INCLUDED
#define WIDGET_MENU_HPP_INCLUDED
#include <map>
#include <set>
#include <string>
#include <vector>
@ -17,17 +19,66 @@ namespace gui {
class menu : public scrollarea
{
public:
struct item
{
item() : id(0)
{}
item(const std::vector<std::string>& fields, size_t id)
: fields(fields), id(id)
{}
std::vector<std::string> fields;
std::vector<std::string> help;
size_t id;
};
class sorter
{
public:
virtual ~sorter() {}
virtual bool column_sortable(int column) const = 0;
virtual bool less(int column, const item& row1, const item& row2) const = 0;
};
class basic_sorter : public sorter
{
public:
virtual ~basic_sorter() {}
basic_sorter& set_alpha_sort(int column);
basic_sorter& set_numeric_sort(int column);
basic_sorter& set_id_sort(int column);
basic_sorter& set_redirect_sort(int column, int to);
basic_sorter& set_position_sort(int column, const std::vector<int>& pos);
protected:
virtual bool column_sortable(int column) const;
virtual bool less(int column, const item& row1, const item& row2) const;
private:
std::set<int> alpha_sort_, numeric_sort_, id_sort_;
std::map<int,int> redirect_sort_;
std::map<int,std::vector<int> > pos_sort_;
};
menu(CVideo& video, const std::vector<std::string>& items,
bool click_selects=false, int max_height=-1, int max_width=-1);
bool click_selects=false, int max_height=-1, int max_width=-1,
const sorter* sorter_obj=NULL);
int selection() const;
void move_selection(size_t pos);
enum SELECTION_MOVE_VIEWPORT { MOVE_VIEWPORT, NO_MOVE_VIEWPORT };
void move_selection(size_t id, SELECTION_MOVE_VIEWPORT move_viewport=MOVE_VIEWPORT);
void reset_selection();
// allows user to change_item while running (dangerous)
void change_item(int pos1,int pos2,std::string str);
void change_item(int pos1,int pos2,const std::string& str);
void erase_item(size_t index);
void set_heading(const std::vector<std::string>& heading);
/// Set new items to show and redraw/recalculate everything. If
/// strip_spaces is false, spaces will remain at the item edges. If
/// keep_viewport is true, the menu tries to keep the selection at
@ -47,10 +98,13 @@ public:
bool double_clicked();
void set_click_selects(bool value);
void set_numeric_keypress_selection(bool value);
void scroll(int pos);
void sort_by(int column);
protected:
void handle_event(const SDL_Event& event);
void set_inner_location(const SDL_Rect& rect);
@ -58,13 +112,19 @@ protected:
private:
size_t max_items_onscreen() const;
size_t heading_height() const;
int max_height_, max_width_;
mutable int max_items_, item_height_;
void adjust_viewport_to_selection();
void key_press(SDLKey key);
std::vector<std::vector<std::string> > items_, help_;
std::vector<item> items_;
std::vector<size_t> item_pos_;
std::vector<std::string> heading_;
mutable int heading_height_;
void create_help_strings();
void process_help_string(int mousex, int mousey);
@ -84,17 +144,25 @@ private:
bool double_clicked_;
const std::vector<int>& column_widths() const;
void draw_item(int item);
void column_widths_item(const std::vector<std::string>& row, std::vector<int>& widths) const;
enum ROW_TYPE { NORMAL_ROW, SELECTED_ROW, HEADING_ROW };
void draw_row(const std::vector<std::string>& row, const SDL_Rect& rect, ROW_TYPE type);
void clear_item(int item);
void draw_contents();
void draw();
int hit(int x, int y) const;
std::pair<int,int> hit_cell(int x, int y) const;
int hit_column(int x, int y) const;
int hit_heading(int x, int y) const;
mutable std::map<int,SDL_Rect> itemRects_;
SDL_Rect get_item_rect(int item) const;
size_t get_item_height_internal(int item) const;
SDL_Rect get_item_rect_internal(size_t pos) const;
size_t get_item_height_internal(const std::vector<std::string>& item) const;
size_t get_item_height(int item) const;
int items_start() const;
@ -112,13 +180,29 @@ private:
bool ignore_next_doubleclick_;
bool last_was_doubleclick_;
const sorter* sorter_;
int sortby_;
int highlight_heading_;
/// Set new items to show. If strip_spaces is false, spaces will
/// remain at the item edges.
void fill_items(const std::vector<std::string>& items, bool strip_spaces);
enum SORT_TYPE { NORMAL_SORT, INVERT_SORT };
void do_sort(SORT_TYPE type=NORMAL_SORT);
void recalculate_pos();
void assert_pos();
void update_size();
void set_selection_pos(size_t pos, SELECTION_MOVE_VIEWPORT move_viewport=MOVE_VIEWPORT);
void move_selection_up(size_t dep);
void move_selection_down(size_t dep);
void invalidate_row(size_t id);
void invalidate_row_pos(size_t pos);
void invalidate_heading();
std::set<int> invalid_;
};
}

View file

@ -38,7 +38,10 @@ public:
int min_value() const;
virtual void set_location(SDL_Rect const &rect);
using widget::set_location;
//VC++ doesn't like a 'using scrollarea::set_location' directive here, so we declare
//an inline forwarding function instead
void set_location(int x, int y) { widget::set_location(x,y); }
protected:
virtual void handle_event(const SDL_Event& event);

View file

@ -54,6 +54,11 @@ void widget::update_location(SDL_Rect const &rect)
bg_register(rect);
}
const SDL_Rect* widget::clip_rect() const
{
return clip_ ? &clip_rect_ : NULL;
}
void widget::bg_register(SDL_Rect const &rect)
{
restorer_.push_back(surface_restorer(&video(), rect));

View file

@ -72,6 +72,8 @@ protected:
virtual void draw_contents() {};
virtual void update_location(SDL_Rect const &rect);
const SDL_Rect* clip_rect() const;
private:
void volatile_draw();
void volatile_undraw();

View file

@ -2,6 +2,22 @@
#define WIDGET_DEFINES_HPP_INCLUDED
char const HELP_STRING_SEPARATOR = '|', DEFAULT_ITEM = '*', COLUMN_SEPARATOR = '=',
IMAGE_PREFIX = '&', IMG_TEXT_SEPARATOR = 1;
IMAGE_PREFIX = '&', IMG_TEXT_SEPARATOR = 1, HEADING_PREFIX = 2;
inline bool is_wml_separator(char c)
{
switch(c)
{
case HELP_STRING_SEPARATOR:
case DEFAULT_ITEM:
case COLUMN_SEPARATOR:
case IMAGE_PREFIX:
case IMG_TEXT_SEPARATOR:
case HEADING_PREFIX:
return true;
default:
return false;
}
}
#endif