sortable listboxes for gui2
Fixes http://gna.org/bugs/index.php?23751 Gui2 listboxes can now be sorted. As an example how to use it i changed the gui2 loadgame dialog to use this new feature. To make your listbox sortable you have to add togglebuttons/panels with the id sort_n where n is a number in [0, numer_of_columns) to the [header] of the listbox, then the listbox will be sorted when a user clicks on that togglebutton/panel. Also you have to register the comparision functions for each column with listbox::set_column_order. Alternatively you can sort the listbox manually by calling listbox::order_by This feautre still suffers from http://gna.org/bugs/?15763 . But for small listboxes without scrollbars this is already a good feature.
This commit is contained in:
parent
1359e6a885
commit
a4806343f5
9 changed files with 437 additions and 45 deletions
190
data/gui/default/widget/toggle_button_listbox_header.cfg
Normal file
190
data/gui/default/widget/toggle_button_listbox_header.cfg
Normal file
|
@ -0,0 +1,190 @@
|
|||
#textdomain wesnoth-lib
|
||||
###
|
||||
### Definition of the default toggle button.
|
||||
### Since for this class 'default' is a bit hard we now use the checkbox as default.
|
||||
###
|
||||
|
||||
#define _GUI_TEXT FONT_SIZE FONT_COLOR
|
||||
[text]
|
||||
x = 0
|
||||
y = {GUI__TEXT_VERTICALLY_CENTRED}
|
||||
w = "(width)"
|
||||
h = "(text_height)"
|
||||
font_size = {FONT_SIZE}
|
||||
color = {FONT_COLOR}
|
||||
text = "(text)"
|
||||
[/text]
|
||||
#enddef
|
||||
|
||||
#define _GUI_RESOLUTION RESOLUTION WIDTH HEIGHT IMAGE_Y FONT_SIZE
|
||||
[resolution]
|
||||
|
||||
{RESOLUTION}
|
||||
|
||||
min_width = {WIDTH}
|
||||
min_height = {HEIGHT}
|
||||
|
||||
default_width = {WIDTH}
|
||||
default_height = {HEIGHT}
|
||||
|
||||
max_width = 0
|
||||
max_height = {HEIGHT}
|
||||
|
||||
text_extra_width = 0
|
||||
text_font_size = {FONT_SIZE}
|
||||
|
||||
[state]
|
||||
[enabled]
|
||||
|
||||
[draw]
|
||||
|
||||
{_GUI_TEXT ({FONT_SIZE}) ({GUI__FONT_COLOR_ENABLED__TITLE}) }
|
||||
|
||||
[/draw]
|
||||
|
||||
[/enabled]
|
||||
|
||||
[disabled]
|
||||
|
||||
[draw]
|
||||
|
||||
{_GUI_TEXT ({FONT_SIZE}) ({GUI__FONT_COLOR_DISABLED__TITLE}) }
|
||||
|
||||
[/draw]
|
||||
|
||||
[/disabled]
|
||||
|
||||
[focussed]
|
||||
|
||||
[draw]
|
||||
|
||||
{_GUI_TEXT ({FONT_SIZE}) ({GUI__FONT_COLOR_ENABLED__TITLE}) }
|
||||
|
||||
[/draw]
|
||||
|
||||
[/focussed]
|
||||
[/state]
|
||||
|
||||
###
|
||||
### Down
|
||||
###
|
||||
|
||||
[state]
|
||||
[enabled]
|
||||
|
||||
[draw]
|
||||
|
||||
|
||||
|
||||
{_GUI_TEXT ({FONT_SIZE}) ({GUI__FONT_COLOR_ENABLED__TITLE}) }
|
||||
|
||||
[image]
|
||||
x = "(width - 10)"
|
||||
y = {IMAGE_Y}
|
||||
name = "buttons/sliders/slider_arrow_blue.png~ROTATE(180)"
|
||||
[/image]
|
||||
[/draw]
|
||||
|
||||
[/enabled]
|
||||
|
||||
[disabled]
|
||||
|
||||
[draw]
|
||||
{_GUI_TEXT ({FONT_SIZE}) ({GUI__FONT_COLOR_DISABLED__TITLE}) }
|
||||
|
||||
[image]
|
||||
x = "(width - 10)"
|
||||
y = {IMAGE_Y}
|
||||
name = "buttons/sliders/slider_arrow_blue.png~ROTATE(180)~GS()"
|
||||
[/image]
|
||||
|
||||
[/draw]
|
||||
|
||||
[/disabled]
|
||||
|
||||
[focussed]
|
||||
|
||||
[draw]
|
||||
|
||||
{_GUI_TEXT ({FONT_SIZE}) ({GUI__FONT_COLOR_ENABLED__TITLE}) }
|
||||
|
||||
[image]
|
||||
x = "(width - 10)"
|
||||
y = {IMAGE_Y}
|
||||
name = "buttons/sliders/slider_arrow_blue.png~ROTATE(180)"
|
||||
[/image]
|
||||
|
||||
[/draw]
|
||||
|
||||
[/focussed]
|
||||
[/state]
|
||||
|
||||
###
|
||||
### Up
|
||||
###
|
||||
[state]
|
||||
[enabled]
|
||||
|
||||
[draw]
|
||||
|
||||
|
||||
|
||||
{_GUI_TEXT ({FONT_SIZE}) ({GUI__FONT_COLOR_ENABLED__TITLE}) }
|
||||
|
||||
[image]
|
||||
x = "(width - 10)"
|
||||
y = {IMAGE_Y}
|
||||
name = "buttons/sliders/slider_arrow_blue.png"
|
||||
[/image]
|
||||
[/draw]
|
||||
|
||||
[/enabled]
|
||||
|
||||
[disabled]
|
||||
|
||||
[draw]
|
||||
{_GUI_TEXT ({FONT_SIZE}) ({GUI__FONT_COLOR_DISABLED__TITLE}) }
|
||||
|
||||
[image]
|
||||
x = "(width - 10)"
|
||||
y = {IMAGE_Y}
|
||||
name = "buttons/sliders/slider_arrow_blue.png~GS()"
|
||||
[/image]
|
||||
|
||||
[/draw]
|
||||
|
||||
[/disabled]
|
||||
|
||||
[focussed]
|
||||
|
||||
[draw]
|
||||
|
||||
{_GUI_TEXT ({FONT_SIZE}) ({GUI__FONT_COLOR_ENABLED__TITLE}) }
|
||||
|
||||
[image]
|
||||
x = "(width - 10)"
|
||||
y = {IMAGE_Y}
|
||||
name = "buttons/sliders/slider_arrow_blue.png"
|
||||
[/image]
|
||||
|
||||
[/draw]
|
||||
|
||||
[/focussed]
|
||||
[/state]
|
||||
|
||||
[/resolution]
|
||||
#enddef
|
||||
|
||||
[toggle_button_definition]
|
||||
|
||||
id = "listbox_header"
|
||||
description = "Checkbox."
|
||||
|
||||
# Tiny gui sizes haven't been tested yet so might need some tuning.
|
||||
{_GUI_RESOLUTION ({GUI_TINY__RESOLUTION}) 20 11 0 ({GUI_TINY__FONT_SIZE__SMALL}) }
|
||||
{_GUI_RESOLUTION () 30 18 5 ({GUI_NORMAL__FONT_SIZE__SMALL}) }
|
||||
|
||||
[/toggle_button_definition]
|
||||
|
||||
#undef _GUI_TEXT
|
||||
#undef _GUI_RESOLUTION
|
|
@ -256,15 +256,13 @@
|
|||
horizontal_grow = "true"
|
||||
border = "all"
|
||||
border_size = 5
|
||||
|
||||
[label]
|
||||
id = "filename"
|
||||
definition = "default"
|
||||
[toggle_button]
|
||||
id = "sort_0"
|
||||
definition = "listbox_header"
|
||||
linked_group = "filename"
|
||||
|
||||
label = _ "Name"
|
||||
[/label]
|
||||
|
||||
[/toggle_button]
|
||||
[/column]
|
||||
|
||||
[column]
|
||||
|
@ -272,15 +270,13 @@
|
|||
horizontal_grow = "true"
|
||||
border = "all"
|
||||
border_size = 5
|
||||
|
||||
[label]
|
||||
id = "date"
|
||||
definition = "default"
|
||||
[toggle_button]
|
||||
id = "sort_1"
|
||||
definition = "listbox_header"
|
||||
linked_group = "date"
|
||||
|
||||
label = _ "Date"
|
||||
[/label]
|
||||
|
||||
[/toggle_button]
|
||||
[/column]
|
||||
|
||||
[/row]
|
||||
|
|
|
@ -147,6 +147,26 @@ void tgame_load::pre_show(CVideo& /*video*/, twindow& window)
|
|||
display_savegame(window);
|
||||
}
|
||||
|
||||
bool tgame_load::compare_name(unsigned i1, unsigned i2) const
|
||||
{
|
||||
return games_[i1].name() < games_[i2].name();
|
||||
}
|
||||
|
||||
bool tgame_load::compare_date(unsigned i1, unsigned i2) const
|
||||
{
|
||||
return games_[i1].modified() < games_[i2].modified();
|
||||
}
|
||||
|
||||
bool tgame_load::compare_name_rev(unsigned i1, unsigned i2) const
|
||||
{
|
||||
return games_[i1].name() > games_[i2].name();
|
||||
}
|
||||
|
||||
bool tgame_load::compare_date_rev(unsigned i1, unsigned i2) const
|
||||
{
|
||||
return games_[i1].modified() > games_[i2].modified();
|
||||
}
|
||||
|
||||
void tgame_load::fill_game_list(twindow& window,
|
||||
std::vector<savegame::save_info>& games)
|
||||
{
|
||||
|
@ -166,6 +186,13 @@ void tgame_load::fill_game_list(twindow& window,
|
|||
|
||||
list.add_row(data);
|
||||
}
|
||||
std::vector<tgenerator_::torder_func> order_funcs(2);
|
||||
order_funcs[0] = boost::bind(&tgame_load::compare_name, this, _1, _2);
|
||||
order_funcs[1] = boost::bind(&tgame_load::compare_name_rev, this, _1, _2);
|
||||
list.set_column_order(0, order_funcs);
|
||||
order_funcs[0] = boost::bind(&tgame_load::compare_date, this, _1, _2);
|
||||
order_funcs[1] = boost::bind(&tgame_load::compare_date_rev, this, _1, _2);
|
||||
list.set_column_order(1, order_funcs);
|
||||
}
|
||||
|
||||
void tgame_load::list_item_clicked(twindow& window)
|
||||
|
|
|
@ -64,6 +64,12 @@ private:
|
|||
void display_savegame(twindow& window);
|
||||
void evaluate_summary_string(std::stringstream& str,
|
||||
const config& cfg_summary);
|
||||
|
||||
bool compare_name(unsigned i1, unsigned i2) const;
|
||||
bool compare_date(unsigned i1, unsigned i2) const;
|
||||
bool compare_name_rev(unsigned i1, unsigned i2) const;
|
||||
bool compare_date_rev(unsigned i1, unsigned i2) const;
|
||||
|
||||
void fill_game_list(twindow& window,
|
||||
std::vector<savegame::save_info>& games);
|
||||
|
||||
|
|
|
@ -154,9 +154,9 @@ void thorizontal_list::place(const tpoint& origin, const tpoint& size)
|
|||
tpoint current_origin = origin;
|
||||
for(size_t i = 0; i < get_item_count(); ++i) {
|
||||
|
||||
tgrid& grid = item(i);
|
||||
tgrid& grid = item_ordered(i);
|
||||
if(grid.get_visible() == twidget::tvisible::invisible
|
||||
|| !get_item_shown(i)) {
|
||||
|| !get_item_shown(get_item_at_ordered(i))) {
|
||||
|
||||
continue;
|
||||
}
|
||||
|
@ -179,9 +179,9 @@ void thorizontal_list::set_origin(const tpoint& origin)
|
|||
tpoint current_origin = origin;
|
||||
for(size_t i = 0; i < get_item_count(); ++i) {
|
||||
|
||||
tgrid& grid = item(i);
|
||||
tgrid& grid = item_ordered(i);
|
||||
if(grid.get_visible() == twidget::tvisible::invisible
|
||||
|| !get_item_shown(i)) {
|
||||
|| !get_item_shown(get_item_at_ordered(i))) {
|
||||
|
||||
continue;
|
||||
}
|
||||
|
@ -201,7 +201,7 @@ void thorizontal_list::set_visible_rectangle(const SDL_Rect& rectangle)
|
|||
*/
|
||||
for(size_t i = 0; i < get_item_count(); ++i) {
|
||||
|
||||
tgrid& grid = item(i);
|
||||
tgrid& grid = item_ordered(i);
|
||||
grid.set_visible_rectangle(rectangle);
|
||||
}
|
||||
}
|
||||
|
@ -261,13 +261,13 @@ void thorizontal_list::handle_key_left_arrow(SDLMod /*modifier*/, bool& handled)
|
|||
// NOTE maybe this should only work if we can select only one item...
|
||||
handled = true;
|
||||
|
||||
for(int i = get_selected_item() - 1; i >= 0; --i) {
|
||||
for(int i = get_ordered_index(get_selected_item()) - 1; i >= 0; --i) {
|
||||
|
||||
// NOTE we check the first widget to be active since grids have no
|
||||
// active flag. This method might not be entirely reliable.
|
||||
tcontrol* control = dynamic_cast<tcontrol*>(item(i).widget(0, 0));
|
||||
tcontrol* control = dynamic_cast<tcontrol*>(item(get_item_at_ordered(i)).widget(0, 0));
|
||||
if(control && control->get_active()) {
|
||||
select_item(i, true);
|
||||
select_item(get_item_at_ordered(i), true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -283,19 +283,19 @@ void thorizontal_list::handle_key_right_arrow(SDLMod /*modifier*/,
|
|||
// NOTE maybe this should only work if we can select only one item...
|
||||
handled = true;
|
||||
|
||||
for(size_t i = get_selected_item() + 1; i < get_item_count(); ++i) {
|
||||
for(size_t i = get_ordered_index(get_selected_item()) + 1; i < get_item_count(); ++i) {
|
||||
|
||||
if(item(i).get_visible() == twidget::tvisible::invisible
|
||||
|| !get_item_shown(i)) {
|
||||
if(item(get_item_at_ordered(i)).get_visible() == twidget::tvisible::invisible
|
||||
|| !get_item_shown(get_item_at_ordered(i))) {
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// NOTE we check the first widget to be active since grids have no
|
||||
// active flag. This method might not be entirely reliable.
|
||||
tcontrol* control = dynamic_cast<tcontrol*>(item(i).widget(0, 0));
|
||||
tcontrol* control = dynamic_cast<tcontrol*>(item(get_item_at_ordered(i)).widget(0, 0));
|
||||
if(control && control->get_active()) {
|
||||
select_item(i, true);
|
||||
select_item(get_item_at_ordered(i), true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -353,9 +353,9 @@ void tvertical_list::place(const tpoint& origin, const tpoint& size)
|
|||
tpoint current_origin = origin;
|
||||
for(size_t i = 0; i < get_item_count(); ++i) {
|
||||
|
||||
tgrid& grid = item(i);
|
||||
tgrid& grid = item_ordered(i);
|
||||
if(grid.get_visible() == twidget::tvisible::invisible
|
||||
|| !get_item_shown(i)) {
|
||||
|| !get_item_shown(get_item_at_ordered(i))) {
|
||||
|
||||
continue;
|
||||
}
|
||||
|
@ -378,9 +378,9 @@ void tvertical_list::set_origin(const tpoint& origin)
|
|||
tpoint current_origin = origin;
|
||||
for(size_t i = 0; i < get_item_count(); ++i) {
|
||||
|
||||
tgrid& grid = item(i);
|
||||
tgrid& grid = item_ordered(i);
|
||||
if(grid.get_visible() == twidget::tvisible::invisible
|
||||
|| !get_item_shown(i)) {
|
||||
|| !get_item_shown(get_item_at_ordered(i))) {
|
||||
|
||||
continue;
|
||||
}
|
||||
|
@ -461,13 +461,13 @@ void tvertical_list::handle_key_up_arrow(SDLMod /*modifier*/, bool& handled)
|
|||
// NOTE maybe this should only work if we can select only one item...
|
||||
handled = true;
|
||||
|
||||
for(int i = get_selected_item() - 1; i >= 0; --i) {
|
||||
for(int i = get_ordered_index(get_selected_item()) - 1; i >= 0; --i) {
|
||||
|
||||
// NOTE we check the first widget to be active since grids have no
|
||||
// active flag. This method might not be entirely reliable.
|
||||
tcontrol* control = dynamic_cast<tcontrol*>(item(i).widget(0, 0));
|
||||
tcontrol* control = dynamic_cast<tcontrol*>(item_ordered(i).widget(0, 0));
|
||||
if(control && control->get_active()) {
|
||||
select_item(i, true);
|
||||
select_item(get_item_at_ordered(i), true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -482,19 +482,20 @@ void tvertical_list::handle_key_down_arrow(SDLMod /*modifier*/, bool& handled)
|
|||
// NOTE maybe this should only work if we can select only one item...
|
||||
handled = true;
|
||||
|
||||
for(size_t i = get_selected_item() + 1; i < get_item_count(); ++i) {
|
||||
|
||||
if(item(i).get_visible() == twidget::tvisible::invisible
|
||||
|| !get_item_shown(i)) {
|
||||
for(size_t i = get_ordered_index(get_selected_item()) + 1; i < get_item_count(); ++i) {
|
||||
|
||||
// why do we do this check here but not in handle_key_up_arrow?
|
||||
if(item_ordered(i).get_visible() == twidget::tvisible::invisible
|
||||
|| !get_item_shown(get_item_at_ordered(i))) {
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// NOTE we check the first widget to be active since grids have no
|
||||
// active flag. This method might not be entirely reliable.
|
||||
tcontrol* control = dynamic_cast<tcontrol*>(item(i).widget(0, 0));
|
||||
tcontrol* control = dynamic_cast<tcontrol*>(item_ordered(i).widget(0, 0));
|
||||
if(control && control->get_active()) {
|
||||
select_item(i, true);
|
||||
select_item(get_item_at_ordered(i), true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -236,6 +236,9 @@ public:
|
|||
data,
|
||||
const boost::function<void(twidget&)>& callback) = 0;
|
||||
|
||||
typedef boost::function<bool (unsigned, unsigned)> torder_func;
|
||||
virtual void set_order(const torder_func& order) = 0;
|
||||
|
||||
/***** ***** ***** ***** Inherited ***** ***** ***** *****/
|
||||
|
||||
/*
|
||||
|
@ -355,6 +358,15 @@ protected:
|
|||
* @param index The index of a selected item.
|
||||
*/
|
||||
virtual void do_deselect_item(const unsigned index) = 0;
|
||||
|
||||
/** Gets the grid of an item. */
|
||||
virtual tgrid& item_ordered(const unsigned index) = 0;
|
||||
|
||||
/** Gets the grid of an item. */
|
||||
virtual const tgrid& item_ordered(const unsigned index) const = 0;
|
||||
|
||||
virtual unsigned get_ordered_index(unsigned index) const = 0;
|
||||
virtual unsigned get_item_at_ordered(unsigned index_ordered) const = 0;
|
||||
};
|
||||
|
||||
} // namespace gui2
|
||||
|
|
|
@ -602,6 +602,9 @@ public:
|
|||
, selected_item_count_(0)
|
||||
, last_selected_item_(-1)
|
||||
, items_()
|
||||
, order_()
|
||||
, order_dirty_(true)
|
||||
, order_func_()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -628,6 +631,7 @@ public:
|
|||
|
||||
delete items_[index];
|
||||
items_.erase(items_.begin() + index);
|
||||
order_dirty_ = true;
|
||||
}
|
||||
|
||||
/** Inherited from tgenerator_. */
|
||||
|
@ -637,6 +641,7 @@ public:
|
|||
{
|
||||
delete item;
|
||||
}
|
||||
order_dirty_ = true;
|
||||
selected_item_count_ = 0;
|
||||
}
|
||||
|
||||
|
@ -737,6 +742,22 @@ public:
|
|||
return items_[index]->grid;
|
||||
}
|
||||
|
||||
/** Inherited from tgenerator_. */
|
||||
tgrid& item_ordered(const unsigned index)
|
||||
{
|
||||
calculate_order();
|
||||
assert(index < items_.size());
|
||||
return items_[order_[index]]->grid;
|
||||
}
|
||||
|
||||
/** Inherited from tgenerator_. */
|
||||
const tgrid& item_ordered(const unsigned index) const
|
||||
{
|
||||
calculate_order();
|
||||
assert(index < items_.size());
|
||||
return items_[order_[index]]->grid;
|
||||
}
|
||||
|
||||
|
||||
/** Inherited from tgenerator_. */
|
||||
tgrid& create_item(const int index,
|
||||
|
@ -767,6 +788,7 @@ public:
|
|||
const unsigned item_index = index == -1 ? items_.size() : index;
|
||||
|
||||
items_.insert(items_.begin() + item_index, item);
|
||||
order_dirty_ = true;
|
||||
minimum_selection::create_item(item_index);
|
||||
placement::create_item(item_index);
|
||||
if(!is_selected(item_index)) {
|
||||
|
@ -855,8 +877,10 @@ public:
|
|||
{
|
||||
assert(this->get_visible() == twidget::tvisible::visible);
|
||||
|
||||
FOREACH(AUTO item, items_)
|
||||
calculate_order();
|
||||
FOREACH(AUTO index, order_)
|
||||
{
|
||||
titem* item = items_[index];
|
||||
if(item->grid.get_visible() == twidget::tvisible::visible
|
||||
&& item->shown) {
|
||||
|
||||
|
@ -871,9 +895,10 @@ public:
|
|||
int y_offset) OVERRIDE
|
||||
{
|
||||
assert(this->get_visible() == twidget::tvisible::visible);
|
||||
|
||||
FOREACH(AUTO item, items_)
|
||||
calculate_order();
|
||||
FOREACH(AUTO index, order_)
|
||||
{
|
||||
titem* item = items_[index];
|
||||
if(item->grid.get_visible() == twidget::tvisible::visible
|
||||
&& item->shown) {
|
||||
|
||||
|
@ -980,7 +1005,7 @@ private:
|
|||
struct titem
|
||||
{
|
||||
|
||||
titem() : grid(), selected(false), shown(true)
|
||||
titem() : grid(), selected(false), shown(true), ordered_index(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -1000,6 +1025,8 @@ private:
|
|||
* polishing.
|
||||
*/
|
||||
bool shown;
|
||||
|
||||
size_t ordered_index;
|
||||
};
|
||||
|
||||
/** The number of selected items. */
|
||||
|
@ -1009,8 +1036,78 @@ private:
|
|||
int last_selected_item_;
|
||||
|
||||
/** The items in the generator. */
|
||||
std::vector<titem*> items_;
|
||||
typedef std::vector<titem*> titems;
|
||||
titems items_;
|
||||
|
||||
/** the elements of order_ are indexes to items_ */
|
||||
mutable std::vector<size_t> order_;
|
||||
/** whether need to recalculate order_dirty_ */
|
||||
mutable bool order_dirty_;
|
||||
|
||||
typedef boost::function<bool (unsigned, unsigned)> torder_func;
|
||||
torder_func order_func_;
|
||||
|
||||
|
||||
virtual void set_order(const torder_func& order) OVERRIDE
|
||||
{
|
||||
order_func_ = order;
|
||||
order_dirty_ = true;
|
||||
this->set_is_dirty(true);
|
||||
}
|
||||
|
||||
struct calculate_order_helper
|
||||
{
|
||||
const torder_func& order_func_;
|
||||
const titems& items_;
|
||||
|
||||
calculate_order_helper(const torder_func& order_func, const titems& items)
|
||||
: order_func_(order_func)
|
||||
, items_(items)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator()(size_t a, size_t b)
|
||||
{
|
||||
return order_func_(a, b);
|
||||
}
|
||||
};
|
||||
|
||||
virtual unsigned get_ordered_index(unsigned index) const
|
||||
{
|
||||
assert(index < items_.size());
|
||||
calculate_order();
|
||||
return items_[index]->ordered_index;
|
||||
}
|
||||
|
||||
virtual unsigned get_item_at_ordered(unsigned index_ordered) const
|
||||
{
|
||||
assert(index_ordered < items_.size());
|
||||
calculate_order();
|
||||
return order_[index_ordered];
|
||||
}
|
||||
|
||||
void calculate_order() const
|
||||
{
|
||||
if(order_dirty_) {
|
||||
if(order_.size() != items_.size()) {
|
||||
order_.resize(items_.size());
|
||||
for(size_t i = 0; i < items_.size(); ++i) {
|
||||
order_[i] = i;
|
||||
}
|
||||
}
|
||||
if(!order_func_.empty()) {
|
||||
std::stable_sort(order_.begin(), order_.end(), calculate_order_helper(order_func_, items_));
|
||||
}
|
||||
for(size_t i = 0; i < order_.size(); ++i) {
|
||||
items_[order_[i]]->ordered_index = i;
|
||||
}
|
||||
|
||||
order_dirty_ = false;
|
||||
}
|
||||
else {
|
||||
assert(order_.size() == items_.size());
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Sets the selected state of an item.
|
||||
*
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "gui/auxiliary/window_builder/horizontal_listbox.hpp"
|
||||
#include "gui/widgets/detail/register.tpp"
|
||||
#include "gui/widgets/settings.hpp"
|
||||
#include "gui/widgets/selectable.hpp"
|
||||
#include "gui/widgets/window.hpp"
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
|
@ -60,6 +61,7 @@ tlistbox::tlistbox(const bool has_minimum,
|
|||
, list_builder_(NULL)
|
||||
, callback_value_changed_(NULL)
|
||||
, need_layout_(false)
|
||||
, orders_()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -515,7 +517,16 @@ void tlistbox::finalize(tbuilder_grid_const_ptr header,
|
|||
if(header) {
|
||||
swap_grid(&grid(), content_grid(), header->build(), "_header_grid");
|
||||
}
|
||||
|
||||
tgrid& p = find_widget<tgrid>(this, "_header_grid", false);
|
||||
for(unsigned i = 0, max = std::max(p.get_cols(), p.get_rows()); i < max; ++i) {
|
||||
if(tselectable_* selectable = find_widget<tselectable_>(p.widget(0,i), "sort_" + lexical_cast<std::string>(i), false, false)) {
|
||||
selectable->set_callback_state_change(boost::bind(&tlistbox::order_by_column, this, i, _1));
|
||||
if(orders_.size() < max ) {
|
||||
orders_.resize(max);
|
||||
}
|
||||
orders_[i].first = selectable;
|
||||
}
|
||||
}
|
||||
if(footer) {
|
||||
swap_grid(&grid(), content_grid(), footer->build(), "_footer_grid");
|
||||
}
|
||||
|
@ -524,6 +535,51 @@ void tlistbox::finalize(tbuilder_grid_const_ptr header,
|
|||
-1, list_builder_, list_data, callback_list_item_clicked);
|
||||
swap_grid(NULL, content_grid(), generator_, "_list_grid");
|
||||
}
|
||||
namespace {
|
||||
bool default_sort(unsigned i1, unsigned i2)
|
||||
{
|
||||
return i1 < i2;
|
||||
}
|
||||
}
|
||||
|
||||
void tlistbox::order_by_column(unsigned column, twidget& widget)
|
||||
{
|
||||
tselectable_& selectable = dynamic_cast<tselectable_&>(widget);
|
||||
if(column >= orders_.size()) {
|
||||
return;
|
||||
}
|
||||
FOREACH(AUTO& pair, orders_)
|
||||
{
|
||||
if(pair.first != NULL && pair.first != &selectable) {
|
||||
pair.first->set_value(0);
|
||||
}
|
||||
}
|
||||
if(selectable.get_value() > orders_[column].second.size()) {
|
||||
return;
|
||||
}
|
||||
if(selectable.get_value() == 0) {
|
||||
order_by(tgenerator_::torder_func(&default_sort));
|
||||
}
|
||||
else {
|
||||
order_by(orders_[column].second[selectable.get_value() - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
void tlistbox::order_by(const tgenerator_::torder_func& func)
|
||||
{
|
||||
generator_->set_order(func);
|
||||
|
||||
set_is_dirty(true);
|
||||
need_layout_ = true;
|
||||
}
|
||||
|
||||
void tlistbox::set_column_order(unsigned col, const std::vector<tgenerator_::torder_func>& func)
|
||||
{
|
||||
if(col >= orders_.size()) {
|
||||
orders_.resize(col + 1);
|
||||
}
|
||||
orders_[col].second = func;
|
||||
}
|
||||
|
||||
void tlistbox::set_content_size(const tpoint& origin, const tpoint& size)
|
||||
{
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
namespace gui2
|
||||
{
|
||||
|
||||
class tselectable_;
|
||||
namespace implementation
|
||||
{
|
||||
struct tbuilder_listbox;
|
||||
|
@ -226,6 +226,9 @@ public:
|
|||
list_builder_ = list_builder;
|
||||
}
|
||||
|
||||
void order_by(const tgenerator_::torder_func& func);
|
||||
|
||||
void set_column_order(unsigned col, const std::vector<tgenerator_::torder_func>& func);
|
||||
protected:
|
||||
/***** ***** ***** ***** keyboard functions ***** ***** ***** *****/
|
||||
|
||||
|
@ -297,6 +300,8 @@ private:
|
|||
|
||||
bool need_layout_;
|
||||
|
||||
typedef std::vector<std::pair<tselectable_*, std::vector<tgenerator_::torder_func> > > torder_list;
|
||||
torder_list orders_;
|
||||
/**
|
||||
* Resizes the content.
|
||||
*
|
||||
|
@ -332,6 +337,8 @@ private:
|
|||
|
||||
/** See @ref tcontrol::get_control_type. */
|
||||
virtual const std::string& get_control_type() const OVERRIDE;
|
||||
|
||||
void order_by_column(unsigned column, twidget& widget);
|
||||
};
|
||||
|
||||
} // namespace gui2
|
||||
|
|
Loading…
Add table
Reference in a new issue