Lua GUI2: A few additions

- Support for multimenu buttons
- Support for selecting multiple items in listboxes and stacked widgets

GUI2 C++ API:
- Documentation fixups
- Function to test if a layer of a stacked_widget is selected
- Function to test if a row of a listbox is selected
- listbox::select_row now returns false if it failed to select/deselect anything
- Functions to count and select options of a multimenu_button
- multimenu_button now always has keep_open, so the function to set it is removed
- multimenu_button now assumes every option has a toggle
This commit is contained in:
Celtic Minstrel 2017-04-18 14:26:12 -04:00
parent 3c5a9aa57c
commit 1954bb67b4
9 changed files with 205 additions and 31 deletions

View file

@ -368,7 +368,6 @@ void addon_manager::pre_show(window& window)
}
type_filter.set_values(type_filter_entries);
type_filter.set_keep_open(true);
type_filter.set_callback_toggle_state_change(std::bind(&addon_manager::apply_filters, this, std::ref(window)));
button& url_go_button = find_widget<button>(&window, "url_go", false);

View file

@ -708,7 +708,6 @@ void preferences_dialog::post_build(window& window)
multimenu_button& hotkey_menu = find_widget<multimenu_button>(&window, "hotkey_category_menu", false);
hotkey_menu.set_values(hotkey_category_entries);
hotkey_menu.set_keep_open(true);
hotkey_menu.set_callback_toggle_state_change(std::bind(&preferences_dialog::hotkey_type_filter_callback, this, std::ref(window)));
listbox& hotkey_list = setup_hotkey_list(window);

View file

@ -254,9 +254,17 @@ bool listbox::select_row(const unsigned row, const bool select)
{
assert(generator_);
int before = generator_->get_selected_item_count();
generator_->select_item(row, select);
return true; // FIXME test what result should have been!!!
return before != generator_->get_selected_item_count();
}
bool listbox::row_selected(const unsigned row)
{
assert(generator_);
return generator_->is_selected(row);
}
int listbox::get_selected_row() const

View file

@ -177,18 +177,25 @@ public:
grid* get_row_grid(const unsigned row);
/**
* Selectes a row.
* Selects a row.
*
* @param row The row to select.
* @param select Select or deselect the row.
* @returns True if the operation succeeded.
*/
bool select_row(const unsigned row, const bool select = true);
/**
* Check if a row is selected
* @param row The row to test
* @returns True if it is selected.
*/
bool row_selected(const unsigned row);
/**
* Returns the first selected row
*
* @returns The first selected row.
* @retval -1 No row selected.
* @returns The first selected row, or -1 if no row is selected.
*/
int get_selected_row() const;

View file

@ -46,7 +46,6 @@ multimenu_button::multimenu_button()
, retval_(0)
, values_()
, toggle_states_()
, keep_open_(false)
, droplist_(nullptr)
{
values_.emplace_back(config_of("label", this->get_label()));
@ -140,7 +139,7 @@ void multimenu_button::signal_handler_left_button_click(const event::ui_event ev
sound::play_UI_sound(settings::sound_button_click);
// If a button has a retval do the default handling.
dialogs::drop_down_menu droplist(this->get_rectangle(), this->values_, -1, this->get_use_markup(), this->keep_open_,
dialogs::drop_down_menu droplist(this->get_rectangle(), this->values_, -1, this->get_use_markup(), true,
std::bind(&multimenu_button::toggle_state_changed, this));
droplist_ = &droplist;
@ -218,9 +217,7 @@ void multimenu_button::update_config_from_toggle_states()
for(unsigned i = 0; i < values_.size(); i++) {
::config& c = values_[i];
if(c.has_attribute("checkbox")) {
c["checkbox"] = toggle_states_[i];
}
c["checkbox"] = toggle_states_[i];
}
}
@ -244,6 +241,26 @@ void multimenu_button::toggle_state_changed()
}
}
void multimenu_button::select_option(const unsigned option, const bool selected)
{
assert(option < values_.size());
if(option < toggle_states_.size()) {
toggle_states_.resize(option + 1);
}
toggle_states_[option] = selected;
update_config_from_toggle_states();
update_label();
}
void multimenu_button::select_options(boost::dynamic_bitset<> states)
{
assert(states.size() == values_.size());
toggle_states_ = states;
update_config_from_toggle_states();
update_label();
}
void multimenu_button::set_values(const std::vector<::config>& values)
{
set_is_dirty(true);

View file

@ -69,16 +69,57 @@ public:
retval_ = retval;
}
/**
* Sets the maximum number of selected elements shown on the label.
* If more are selected, the label will say "and N others".
*
* @param max The maximum number of elements to show
*/
void set_max_shown(const int max)
{
max_shown_ = max;
}
/**
* Get the maximum number of selected elements shown on the label.
*
* @returns The maximum number of elements to show
*/
int get_max_shown()
{
return max_shown_;
}
/**
* Get the number of options available in the menu
*
* @returns The number of options in the menu
*/
unsigned num_options()
{
return values_.size();
}
/**
* Select an option in the menu
*
* @param option The option to select
* @param selected True to select it, or false to deselect it
*/
void select_option(const unsigned option, const bool selected = true);
/**
* Set the options selected in the menu.
*
* @param mask A mask specifying which options to select and deselect
*/
void select_options(boost::dynamic_bitset<> states);
/**
* Set the available menu options.
*
* @param values A list of options to show in the menu
*/
void set_values(const std::vector<::config>& values);
/**
@ -89,21 +130,21 @@ public:
callback_toggle_state_change_ = callback;
}
/** Returns the value of the selected row */
//std::string get_value_string() const;
/**
* Get the current state of the menu options.
*
* @returns A mask specifying which options are selected
*/
boost::dynamic_bitset<> get_toggle_states() const
{
return toggle_states_;
}
/**
* Deselect all the menu options.
*/
void reset_toggle_states();
void set_keep_open(const bool keep_open)
{
keep_open_ = keep_open;
}
private:
/**
* Possible states of the widget.
@ -145,8 +186,6 @@ private:
boost::dynamic_bitset<> toggle_states_;
bool keep_open_;
dialogs::drop_down_menu* droplist_;
std::function<void(boost::dynamic_bitset<>)> callback_toggle_state_change_;

View file

@ -158,6 +158,12 @@ void stacked_widget::update_selected_layer_index(const int i)
selected_layer_ = utils::clamp<int>(i, -1, get_layer_count() - 1);
}
bool stacked_widget::layer_selected(const unsigned layer)
{
assert(layer < get_layer_count());
return generator_->is_selected(layer);
}
void stacked_widget::select_layer(const int layer)
{
update_selected_layer_index(layer);

View file

@ -62,27 +62,50 @@ public:
*
* If more than one but not all layers are visible, this will be the number of
* the last one made visible.
*
* @returns The most recently shown layer
*/
int current_layer() const { return selected_layer_; }
/**
* Tests if the specified layer is selected (ie, visible).
*
* @param layer The layer to test
* @returns True if the specified layer is selected
*/
bool layer_selected(const unsigned layer);
/**
* Selects and displays a particular layer.
*
* If layer -1 is selected, all layers will be displayed but only the
* topmost (highest-numbered) layer will receive events.
*
* @param layer The layer to select
*/
void select_layer(const int layer);
/**
* Selects and displays multiple layers based on the state of the provided dynamic_bitset.
*
* @param mask A mask specifying which layers to select and deselect
*/
void select_layers(const boost::dynamic_bitset<>& mask);
/**
* Gets the total number of layers.
*
* @returns The total number of layers
*/
unsigned int get_layer_count() const;
/**
* Gets the grid for a specified layer.
* This can be used to search for widgets in a hidden layer.
*
* @param The layer to retrieve
* @returns The grid for the specified layer.
*/
grid* get_layer_grid(unsigned int i);
private:

View file

@ -25,6 +25,7 @@
#include "gui/widgets/clickable_item.hpp"
#include "gui/widgets/styled_widget.hpp"
#include "gui/widgets/multi_page.hpp"
#include "gui/widgets/multimenu_button.hpp"
#include "gui/widgets/progress_bar.hpp"
#include "gui/widgets/selectable_item.hpp"
#include "gui/widgets/settings.hpp"
@ -428,12 +429,25 @@ int intf_set_dialog_value(lua_State* L)
if(gui2::listbox* list = dynamic_cast<gui2::listbox*>(w))
#endif
{
int v = luaL_checkinteger(L, 1);
int n = list->get_item_count();
if(1 <= v && v <= n) {
list->select_row(v - 1);
if(lua_istable(L, 1)) {
// Do two passes in case has_minimum is true
// Probably not the best way to do it, but should work in the majority of cases.
std::vector<int> selected_vec = lua_check<std::vector<int>>(L, 1);
std::set<int> selected(selected_vec.begin(), selected_vec.end());
for(unsigned i = 0; i < list->get_item_count(); i++) {
list->select_row(i, selected.count(i + 1));
}
for(unsigned i = 0; i < list->get_item_count(); i++) {
list->select_row(i, selected.count(i + 1));
}
} else {
return luaL_argerror(L, 1, "out of bounds");
int v = luaL_checkinteger(L, 1);
int n = list->get_item_count();
if(1 <= v && v <= n) {
list->select_row(v - 1);
} else {
return luaL_argerror(L, 1, "out of bounds");
}
}
} else if(gui2::multi_page* multi_page = dynamic_cast<gui2::multi_page*>(w)) {
int v = luaL_checkinteger(L, 1);
@ -469,10 +483,21 @@ int intf_set_dialog_value(lua_State* L)
return luaL_argerror(L, 1, "out of bounds");
}
} else if(gui2::stacked_widget* stacked_widget = dynamic_cast<gui2::stacked_widget*>(w)) {
const int v = luaL_checkinteger(L, 1);
const int n = stacked_widget->get_layer_count();
if(v >= 0 && v <= n) {
stacked_widget->select_layer(v - 1);
if(lua_istable(L, 1)) {
boost::dynamic_bitset<> states;
states.resize(stacked_widget->get_layer_count());
for(unsigned i : lua_check<std::vector<unsigned>>(L, 1)) {
if(i > 0 && i <= stacked_widget->get_layer_count()) {
states[i] = true;
}
}
stacked_widget->select_layers(states);
} else {
const int v = luaL_checkinteger(L, 1);
const int n = stacked_widget->get_layer_count();
if(v >= 0 && v <= n) {
stacked_widget->select_layer(v - 1);
}
}
} else if(gui2::unit_preview_pane* unit_preview_pane = dynamic_cast<gui2::unit_preview_pane*>(w)) {
if(const unit_type* ut = luaW_tounittype(L, 1)) {
@ -489,6 +514,25 @@ int intf_set_dialog_value(lua_State* L)
} else {
node->fold();
}
} else if(gui2::multimenu_button* menu = dynamic_cast<gui2::multimenu_button*>(w)) {
if(lua_istable(L, 1)) {
boost::dynamic_bitset<> states;
states.resize(menu->num_options());
for(unsigned i : lua_check<std::vector<unsigned>>(L, 1)) {
if(i > 0 && i <= menu->num_options()) {
states[i] = true;
}
}
menu->select_options(states);
} else {
int v = luaL_checkinteger(L, 1);
int n = menu->num_options();
if(1 <= v && v <= n) {
menu->select_option(v - 1);
} else {
return luaL_argerror(L, 1, "out of bounds");
}
}
} else {
t_string v = luaW_checktstring(L, 1);
gui2::styled_widget* c = dynamic_cast<gui2::styled_widget*>(w);
@ -509,6 +553,7 @@ int intf_set_dialog_value(lua_State* L)
int intf_get_dialog_value(lua_State* L)
{
gui2::widget *w = find_widget(L, 1, true);
int num_rets = 1;
#ifdef GUI2_EXPERIMENTAL_LISTBOX
if(gui2::list_view* list = dynamic_cast<gui2::list_view*>(w))
@ -517,6 +562,16 @@ int intf_get_dialog_value(lua_State* L)
#endif
{
lua_pushinteger(L, list->get_selected_row() + 1);
num_rets = 2;
lua_newtable(L);
int count = 0;
for(unsigned i = 0; i < list->get_item_count(); i++) {
if(list->row_selected(i)) {
count++;
lua_pushnumber(L, i + 1);
lua_rawseti(L, -1, count);
}
}
} else if(gui2::multi_page* multi_page = dynamic_cast<gui2::multi_page*>(w)) {
lua_pushinteger(L, multi_page->get_selected_page() + 1);
} else if(gui2::selectable_item* selectable = dynamic_cast<gui2::selectable_item*>(w)) {
@ -540,11 +595,32 @@ int intf_get_dialog_value(lua_State* L)
}
} else if(gui2::stacked_widget* stacked_widget = dynamic_cast<gui2::stacked_widget*>(w)) {
lua_pushinteger(L, stacked_widget->current_layer());
num_rets = 2;
lua_newtable(L);
int count = 0;
for(unsigned i = 0; i < stacked_widget->get_layer_count(); i++) {
if(stacked_widget->layer_selected(i)) {
count++;
lua_pushnumber(L, i + 1);
lua_rawseti(L, -1, count);
}
}
} else if(gui2::multimenu_button* menu = dynamic_cast<gui2::multimenu_button*>(w)) {
auto selected = menu->get_toggle_states();
int count = 0;
lua_newtable(L);
for(unsigned i = 0; i < selected.size(); i++) {
if(selected[i]) {
count++;
lua_pushnumber(L, i + 1);
lua_rawseti(L, -1, count);
}
}
} else {
return luaL_argerror(L, lua_gettop(L), "unsupported widget");
}
return 1;
return num_rets;
}
namespace
{