The Battle for Wesnoth  1.13.11+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
lua_gui2.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2018 by Chris Beck <render787@gmail.com>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #include "scripting/lua_gui2.hpp"
16 
18 #include "gui/core/canvas.hpp"
27 #include "gui/dialogs/message.hpp"
34 #include "gui/widgets/slider.hpp"
36 #include "gui/widgets/text_box.hpp"
40 #include "gui/widgets/widget.hpp"
41 #include "gui/widgets/window.hpp"
42 
43 #ifdef GUI2_EXPERIMENTAL_LISTBOX
44 #include "gui/widgets/list.hpp"
45 #else
46 #include "gui/widgets/listbox.hpp"
47 #endif
48 
49 #include "config.hpp"
50 #include "log.hpp"
51 #include "scripting/lua_common.hpp"
53 #include "scripting/lua_unit.hpp"
55 #include "scripting/push_check.hpp"
57 #include "tstring.hpp"
58 #include "game_data.hpp"
59 #include "game_state.hpp"
60 
61 #include "utils/functional.hpp"
62 
63 #include <map>
64 #include <utility>
65 #include <vector>
66 #include <boost/optional.hpp>
67 
68 #include "lua/lauxlib.h" // for luaL_checkinteger, etc
69 #include "lua/lua.h" // for lua_setfield, etc
70 
71 static lg::log_domain log_scripting_lua("scripting/lua");
72 #define ERR_LUA LOG_STREAM(err, log_scripting_lua)
73 
74 static const char dlgclbkKey[] = "dialog callback";
75 
76 namespace {
77  struct scoped_dialog
78  {
79  lua_State* L;
80  scoped_dialog* prev;
81  static scoped_dialog* current;
82  std::unique_ptr<gui2::window> window;
83  typedef std::map<gui2::widget*, int> callback_map;
84  callback_map callbacks;
85 
86  scoped_dialog(lua_State* l, gui2::window* w);
87  ~scoped_dialog();
88  private:
89  scoped_dialog(const scoped_dialog&) = delete;
90  };
91 
92  scoped_dialog* scoped_dialog::current = nullptr;
93 
94  scoped_dialog::scoped_dialog(lua_State* l, gui2::window* w)
95  : L(l), prev(current), window(w), callbacks()
96  {
98  lua_createtable(L, 1, 0);
99  lua_pushvalue(L, -2);
101  lua_rawseti(L, -2, 1);
103  current = this;
104  }
105 
106  scoped_dialog::~scoped_dialog()
107  {
108  current = prev;
110  lua_pushvalue(L, -1);
112  lua_rawgeti(L, -1, 1);
113  lua_remove(L, -2);
115  }
116 }//unnamed namespace for scoped_dialog
117 
118 static gui2::widget* find_widget(lua_State* L, int i, bool readonly)
119 {
120  if(!scoped_dialog::current) {
121  luaL_error(L, "no visible dialog");
122  error_oob_call_dtors:
123  luaL_argerror(L, i, "out of bounds");
124  error_not_str_call_dtors:
125  luaW_type_error(L, i, "string");
126  error_no_wgt_call_dtors:
127  luaL_argerror(L, i, "widget not found");
128  return nullptr;
129  }
130 
131  gui2::widget* w = scoped_dialog::current->window.get();
132  for(; !lua_isnoneornil(L, i); ++i)
133  {
134 #ifdef GUI2_EXPERIMENTAL_LISTBOX
135  if(gui2::list_view* list = dynamic_cast<gui2::list_view*>(w))
136 #else
137  if(gui2::listbox* list = dynamic_cast<gui2::listbox*>(w))
138 #endif
139  {
140  int v = lua_tointeger(L, i);
141  if(v < 1) {
142  goto error_oob_call_dtors;
143  }
144  int n = list->get_item_count();
145  if(v > n) {
146  if(readonly) {
147  goto error_oob_call_dtors;
148  }
150  for(; n < v; ++n) {
151  list->add_row(dummy);
152  }
153  }
154  w = list->get_row_grid(v - 1);
155  } else if(gui2::multi_page* multi_page = dynamic_cast<gui2::multi_page*>(w)) {
156  int v = lua_tointeger(L, i);
157  if(v < 1) {
158  goto error_oob_call_dtors;
159  }
160  int n = multi_page->get_page_count();
161  if(v > n) {
162  if(readonly) {
163  goto error_oob_call_dtors;
164  }
166  for(; n < v; ++n) {
167  multi_page->add_page(dummy);
168  }
169  }
170  w = &multi_page->page_grid(v - 1);
171  } else if(gui2::tree_view* tree_view = dynamic_cast<gui2::tree_view*>(w)) {
172  gui2::tree_view_node& tvn = tree_view->get_root_node();
173  if(lua_isnumber(L, i)) {
174  int v = lua_tointeger(L, i);
175  if(v < 1) {
176  goto error_oob_call_dtors;
177  }
178  int n = tvn.count_children();
179  if(v > n) {
180  goto error_oob_call_dtors;
181  }
182  w = &tvn.get_child_at(v - 1);
183 
184  } else {
185  std::string m = luaL_checkstring(L, i);
186  w = tvn.find(m, false);
187  }
188  } else if(gui2::tree_view_node* tree_view_node = dynamic_cast<gui2::tree_view_node*>(w)) {
189  if(lua_isnumber(L, i)) {
190  int v = lua_tointeger(L, i);
191  if(v < 1) {
192  goto error_oob_call_dtors;
193  }
194  int n = tree_view_node->count_children();
195  if(v > n) {
196  goto error_oob_call_dtors;
197  }
198  w = &tree_view_node->get_child_at(v - 1);
199 
200  } else {
201  std::string m = luaL_checkstring(L, i);
202  w = tree_view_node->find(m, false);
203  }
204  } else if(gui2::stacked_widget* stacked_widget = dynamic_cast<gui2::stacked_widget*>(w)) {
205  if(lua_isnumber(L, i)) {
206  int v = lua_tointeger(L, i);
207  if(v < 1) {
208  goto error_oob_call_dtors;
209  }
210  int n = stacked_widget->get_layer_count();
211  if(v > n) {
212  goto error_oob_call_dtors;
213  }
214  w = stacked_widget->get_layer_grid(v - 1);
215  } else {
216  std::string m = luaL_checkstring(L, i);
217  w = stacked_widget->find(m, false);
218  }
219  } else {
220  char const *m = lua_tostring(L, i);
221  if(!m) {
222  goto error_not_str_call_dtors;
223  }
224  w = w->find(m, false);
225  }
226  if(!w) {
227  goto error_no_wgt_call_dtors;
228  }
229  }
230 
231  return w;
232 }
233 
234 namespace lua_gui2 {
235 
236 /**
237  * Displays a window.
238  * - Arg 1: WML table describing the window.
239  * - Arg 2: function called at pre-show.
240  * - Arg 3: function called at post-show.
241  * - Ret 1: integer.
242  */
244 {
245  config def_cfg = luaW_checkconfig(L, 1);
246 
248  scoped_dialog w(L, gui2::build(&def));
249 
250  if(!lua_isnoneornil(L, 2)) {
251  lua_pushvalue(L, 2);
252  lua_call(L, 0, 0);
253  }
254 
255  int v = scoped_dialog::current->window->show(true, 0);
256 
257  if (!lua_isnoneornil(L, 3)) {
258  lua_pushvalue(L, 3);
259  lua_call(L, 0, 0);
260  }
261 
262  lua_pushinteger(L, v);
263  return 1;
264 }
265 
266 /**
267  * Displays a message window
268  * - Arg 1: Table describing the window
269  * - Arg 2: List of options (nil or empty table - no options)
270  * - Arg 3: Text input specifications (nil or empty table - no text input)
271  * - Ret 1: option chosen (if no options: 0 if there's text input, -2 if escape pressed, else -1)
272  * - Ret 2: string entered (empty if none, nil if no text input)
273  */
275 {
276  config txt_cfg;
277  const bool has_input = !lua_isnoneornil(L, 3) && luaW_toconfig(L, 3, txt_cfg) && !txt_cfg.empty();
278 
280  input.caption = txt_cfg["label"].str();
281  input.text = txt_cfg["text"].str();
282  input.maximum_length = txt_cfg["max_length"].to_int(256);
283  input.text_input_was_specified = has_input;
284 
286  if(!lua_isnoneornil(L, 2)) {
287  luaL_checktype(L, 2, LUA_TTABLE);
288  size_t n = lua_rawlen(L, 2);
289  for(size_t i = 1; i <= n; i++) {
290  lua_rawgeti(L, 2, i);
291  t_string short_opt;
292  config opt;
293  if(luaW_totstring(L, -1, short_opt)) {
294  // Note: Although this currently uses the legacy_menu_item class
295  // for the deprecated syntax, this branch should still be retained
296  // when the deprecated syntax is removed, as a simpler method
297  // of specifying options when only a single string is needed.
298  const std::string& opt_str = short_opt;
299  gui2::legacy_menu_item item(opt_str);
300  opt["image"] = item.icon();
301  opt["label"] = item.label();
302  opt["description"] = item.description();
303  opt["default"] = item.is_default();
304  if(!opt["image"].blank() || !opt["description"].blank() || !opt["default"].blank()) {
305  // The exact error message depends on whether & or = was actually present
306  if(opt_str.find_first_of('=') == std::string::npos) {
307  // They just used a simple message, so the other error would be misleading
308  ERR_LUA << "[option]message= is deprecated, use label= instead.\n";
309  } else {
310  ERR_LUA << "The &image=col1=col2 syntax is deprecated, use new DescriptionWML instead.\n";
311  }
312  }
313  } else if(!luaW_toconfig(L, -1, opt)) {
314  std::ostringstream error;
315  error << "expected array of config and/or translatable strings, but index ";
316  error << i << " was a " << lua_typename(L, lua_type(L, -1));
317  return luaL_argerror(L, 2, error.str().c_str());
318  }
319  gui2::dialogs::wml_message_option option(opt["label"], opt["description"], opt["image"]);
320  if(opt["default"].to_bool(false)) {
321  options.chosen_option = i - 1;
322  }
323  options.option_list.push_back(option);
324  lua_pop(L, 1);
325  }
326  lua_getfield(L, 2, "default");
327  if(lua_isnumber(L, -1)) {
328  int i = lua_tointeger(L, -1);
329  if(i < 1 || size_t(i) > n) {
330  std::ostringstream error;
331  error << "default= key in options list is not a valid option index (1-" << n << ")";
332  return luaL_argerror(L, 2, error.str().c_str());
333  }
334  options.chosen_option = i - 1;
335  }
336  lua_pop(L, 1);
337  }
338 
339  const config& def_cfg = luaW_checkconfig(L, 1);
340  const std::string& title = def_cfg["title"];
341  const std::string& message = def_cfg["message"];
342 
343  using portrait = gui2::dialogs::wml_message_portrait;
344  std::unique_ptr<portrait> left;
345  std::unique_ptr<portrait> right;
346  const bool is_double = def_cfg.has_attribute("second_portrait");
347  const bool left_side = def_cfg["left_side"].to_bool(true);
348  if(is_double || left_side) {
349  left.reset(new portrait {def_cfg["portrait"], def_cfg["mirror"].to_bool(false)});
350  } else {
351  // This means right side only.
352  right.reset(new portrait {def_cfg["portrait"], def_cfg["mirror"].to_bool(false)});
353  }
354  if(is_double) {
355  right.reset(new portrait {def_cfg["second_portrait"], def_cfg["second_mirror"].to_bool(false)});
356  }
357 
358  int dlg_result = gui2::dialogs::show_wml_message(title, message, left.get(), right.get(), options, input);
359 
360  if(!has_input && options.option_list.empty()) {
361  lua_pushinteger(L, dlg_result);
362  } else {
363  lua_pushinteger(L, options.chosen_option + 1);
364  }
365 
366  if(has_input) {
367  lua_pushlstring(L, input.text.c_str(), input.text.length());
368  } else {
369  lua_pushnil(L);
370  }
371 
372  return 2;
373 }
374 
375 /**
376  * Displays a popup message
377  * - Arg 1: Title (allows Pango markup)
378  * - Arg 2: Message (allows Pango markup)
379  * - Arg 3: Image (optional)
380  */
382  std::string title = luaL_checkstring(L, 1);
384  std::string image = lua_isnoneornil(L, 3) ? "" : luaL_checkstring(L, 3);
385 
386  gui2::show_transient_message(title, msg, image, true, true);
387  return 0;
388 }
389 
390 /**
391  * Displays a story screen
392  * - Arg 1: The story config
393  * - Arg 2: The default title
394  */
396  config story = luaW_checkconfig(L, 1);
397  t_string title = luaW_checktstring(L, 2);
399  return 0;
400 }
401 
402 /**
403  * Displays a popup menu at the current mouse position
404  * Best used from a [set_menu_item], to show a submenu
405  * - Arg 1: Configs defining each item, with keys icon, image/label, second_label, tooltip
406  * - Args 2, 3: Initial selection (integer); whether to parse markup (boolean)
407  */
409  std::vector<config> items = lua_check<std::vector<config>>(L, 1);
410  SDL_Rect pos {1,1,1,1};
411  SDL_GetMouseState(&pos.x, &pos.y);
412 
413  int initial = -1;
414  bool markup = false;
415  if(lua_isnumber(L, 2)) {
416  initial = lua_tointeger(L, 2) - 1;
417  markup = luaW_toboolean(L, 3);
418  } else if(lua_isnumber(L, 3)) {
419  initial = lua_tointeger(L, 3) - 1;
420  markup = luaW_toboolean(L, 2);
421  }
422 
423  gui2::dialogs::drop_down_menu menu(pos, items, initial, markup, false);
424  menu.show();
425  lua_pushinteger(L, menu.selected_item() + 1);
426  return 1;
427 }
428 
429 /**
430  * Displays a simple message box.
431  */
433  const std::string title = luaL_checkstring(L, 1), message = luaL_checkstring(L, 2);
434  std::string button = luaL_optstring(L, 3, "ok");
435  std::transform(button.begin(), button.end(), button.begin(), [](char c) { return std::tolower(c); });
436  bool markup = lua_isnoneornil(L, 3) ? luaW_toboolean(L, 3) : luaW_toboolean(L, 4);
437  using button_style = gui2::dialogs::message::button_style;
438  boost::optional<button_style> style;
439  if(button.empty()) {
440  style = button_style::auto_close;
441  } else if(button == "ok") {
442  style = button_style::ok_button;
443  } else if(button == "close") {
444  style = button_style::close_button;
445  } else if(button == "ok_cancel") {
446  style = button_style::ok_cancel_buttons;
447  } else if(button == "cancel") {
448  style = button_style::cancel_button;
449  } else if(button == "yes_no") {
450  style = button_style::yes_no_buttons;
451  }
452  if(style) {
453  int result = gui2::show_message(title, message, *style, markup, markup);
454  if(style == button_style::ok_cancel_buttons || style == button_style::yes_no_buttons) {
455  lua_pushboolean(L, result == gui2::window::OK);
456  return 1;
457  }
458  } else {
459  gui2::show_message(title, message, button, false, markup, markup);
460  }
461  return 0;
462 }
463 
464 /**
465  * Sets the value of a widget on the current dialog.
466  * - Arg 1: scalar.
467  * - Args 2..n: path of strings and integers.
468  */
470 {
471  gui2::widget *w = find_widget(L, 2, false);
472 
473 #ifdef GUI2_EXPERIMENTAL_LISTBOX
474  if(gui2::list_view* list = dynamic_cast<gui2::list_view*>(w))
475 #else
476  if(gui2::listbox* list = dynamic_cast<gui2::listbox*>(w))
477 #endif
478  {
479  if(lua_istable(L, 1)) {
480  // Do two passes in case has_minimum is true
481  // Probably not the best way to do it, but should work in the majority of cases.
482  std::vector<int> selected_vec = lua_check<std::vector<int>>(L, 1);
483  std::set<int> selected(selected_vec.begin(), selected_vec.end());
484  for(unsigned i = 0; i < list->get_item_count(); i++) {
485  list->select_row(i, selected.count(i + 1) > 0);
486  }
487  for(unsigned i = 0; i < list->get_item_count(); i++) {
488  list->select_row(i, selected.count(i + 1) > 0);
489  }
490  } else {
491  int v = luaL_checkinteger(L, 1);
492  int n = list->get_item_count();
493  if(1 <= v && v <= n) {
494  list->select_row(v - 1);
495  } else {
496  return luaL_argerror(L, 1, "out of bounds");
497  }
498  }
499  } else if(gui2::multi_page* multi_page = dynamic_cast<gui2::multi_page*>(w)) {
500  int v = luaL_checkinteger(L, 1);
501  int n = multi_page->get_page_count();
502  if(1 <= v && v <= n) {
503  multi_page->select_page(v - 1);
504  } else {
505  return luaL_argerror(L, 1, "out of bounds");
506  }
507  } else if (gui2::selectable_item* selectable = dynamic_cast<gui2::selectable_item*>(w)) {
508  if(selectable->num_states() == 2) {
509  selectable->set_value_bool(luaW_toboolean(L, 1));
510  } else {
511  selectable->set_value(luaL_checkinteger(L, 1) -1);
512  }
513  } else if (gui2::text_box* text_box = dynamic_cast<gui2::text_box*>(w)) {
514  const t_string& text = luaW_checktstring(L, 1);
515  text_box->set_value(text.str());
516  } else if (gui2::slider* slider = dynamic_cast<gui2::slider*>(w)) {
517  const int v = luaL_checkinteger(L, 1);
518  const int m = slider->get_minimum_value();
519  const int n = slider->get_maximum_value();
520  if(m <= v && v <= n) {
521  slider->set_value(v);
522  } else {
523  return luaL_argerror(L, 1, "out of bounds");
524  }
525  } else if(gui2::progress_bar* progress_bar = dynamic_cast<gui2::progress_bar*>(w)) {
526  const int v = luaL_checkinteger(L, 1);
527  if(0 <= v && v <= 100) {
528  progress_bar->set_percentage(v);
529  } else {
530  return luaL_argerror(L, 1, "out of bounds");
531  }
532  } else if(gui2::stacked_widget* stacked_widget = dynamic_cast<gui2::stacked_widget*>(w)) {
533  if(lua_istable(L, 1)) {
534  boost::dynamic_bitset<> states;
535  states.resize(stacked_widget->get_layer_count());
536  for(unsigned i : lua_check<std::vector<unsigned>>(L, 1)) {
537  if(i > 0 && i <= stacked_widget->get_layer_count()) {
538  states[i] = true;
539  }
540  }
541  stacked_widget->select_layers(states);
542  } else {
543  const int v = luaL_checkinteger(L, 1);
544  const int n = stacked_widget->get_layer_count();
545  if(v >= 0 && v <= n) {
546  stacked_widget->select_layer(v - 1);
547  }
548  }
549  } else if(gui2::unit_preview_pane* unit_preview_pane = dynamic_cast<gui2::unit_preview_pane*>(w)) {
550  if(const unit_type* ut = luaW_tounittype(L, 1)) {
551  unit_preview_pane->set_displayed_type(*ut);
552  } else if(unit* u = luaW_tounit(L, 1)) {
553  unit_preview_pane->set_displayed_unit(*u);
554  } else {
555  return luaW_type_error(L, 1, "unit or unit type");
556  }
557  } else if(gui2::tree_view_node* node = dynamic_cast<gui2::tree_view_node*>(w)) {
558  const bool unfolded = luaW_toboolean(L, 1);
559  if(unfolded) {
560  node->unfold();
561  } else {
562  node->fold();
563  }
564  } else if(gui2::multimenu_button* menu = dynamic_cast<gui2::multimenu_button*>(w)) {
565  if(lua_istable(L, 1)) {
566  boost::dynamic_bitset<> states;
567  states.resize(menu->num_options());
568  for(unsigned i : lua_check<std::vector<unsigned>>(L, 1)) {
569  if(i > 0 && i <= menu->num_options()) {
570  states[i] = true;
571  }
572  }
573  menu->select_options(states);
574  } else {
575  int v = luaL_checkinteger(L, 1);
576  int n = menu->num_options();
577  if(1 <= v && v <= n) {
578  menu->select_option(v - 1);
579  } else {
580  return luaL_argerror(L, 1, "out of bounds");
581  }
582  }
583  } else {
584  t_string v = luaW_checktstring(L, 1);
585  gui2::styled_widget* c = dynamic_cast<gui2::styled_widget*>(w);
586  if(!c) {
587  return luaL_argerror(L, lua_gettop(L), "unsupported widget");
588  }
589  c->set_label(v);
590  }
591 
592  return 0;
593 }
594 
595 /**
596  * Gets the value of a widget on the current dialog.
597  * - Args 1..n: path of strings and integers.
598  * - Ret 1: scalar.
599  */
601 {
602  gui2::widget *w = find_widget(L, 1, true);
603  int num_rets = 1;
604 
605 #ifdef GUI2_EXPERIMENTAL_LISTBOX
606  if(gui2::list_view* list = dynamic_cast<gui2::list_view*>(w))
607 #else
608  if(gui2::listbox* list = dynamic_cast<gui2::listbox*>(w))
609 #endif
610  {
611  lua_pushinteger(L, list->get_selected_row() + 1);
612  num_rets = 2;
613  lua_newtable(L);
614  int count = 0;
615  for(unsigned i = 0; i < list->get_item_count(); i++) {
616  if(list->row_selected(i)) {
617  count++;
618  lua_pushnumber(L, i + 1);
619  lua_rawseti(L, -2, count);
620  }
621  }
622  } else if(gui2::multi_page* multi_page = dynamic_cast<gui2::multi_page*>(w)) {
623  lua_pushinteger(L, multi_page->get_selected_page() + 1);
624  } else if(gui2::selectable_item* selectable = dynamic_cast<gui2::selectable_item*>(w)) {
625  if(selectable->num_states() == 2) {
626  lua_pushboolean(L, selectable->get_value_bool());
627  } else {
628  lua_pushinteger(L, selectable->get_value() + 1);
629  }
630  } else if(gui2::text_box* text_box = dynamic_cast<gui2::text_box*>(w)) {
631  lua_pushstring(L, text_box->get_value().c_str());
632  } else if(gui2::slider* slider = dynamic_cast<gui2::slider*>(w)) {
633  lua_pushinteger(L, slider->get_value());
634  } else if(gui2::progress_bar* progress_bar = dynamic_cast<gui2::progress_bar*>(w)) {
635  lua_pushinteger(L, progress_bar->get_percentage());
636  } else if(gui2::tree_view* tree_view = dynamic_cast<gui2::tree_view*>(w)) {
637  std::vector<int> path = tree_view->selected_item()->describe_path();
638  lua_createtable(L, path.size(), 0);
639  for(size_t i = 0; i < path.size(); ++i) {
640  lua_pushinteger(L, path[i] + 1);
641  lua_rawseti(L, -2, i + 1);
642  }
643  } else if(gui2::stacked_widget* stacked_widget = dynamic_cast<gui2::stacked_widget*>(w)) {
644  lua_pushinteger(L, stacked_widget->current_layer());
645  num_rets = 2;
646  lua_newtable(L);
647  int count = 0;
648  for(unsigned i = 0; i < stacked_widget->get_layer_count(); i++) {
649  if(stacked_widget->layer_selected(i)) {
650  count++;
651  lua_pushnumber(L, i + 1);
652  lua_rawseti(L, -2, count);
653  }
654  }
655  } else if(gui2::multimenu_button* menu = dynamic_cast<gui2::multimenu_button*>(w)) {
656  auto selected = menu->get_toggle_states();
657  int count = 0;
658  lua_newtable(L);
659  for(unsigned i = 0; i < selected.size(); i++) {
660  if(selected[i]) {
661  count++;
662  lua_pushnumber(L, i + 1);
663  lua_rawseti(L, -2, count);
664  }
665  }
666  } else {
667  return luaL_argerror(L, lua_gettop(L), "unsupported widget");
668  }
669 
670  return num_rets;
671 }
672 namespace
673 {
674  void remove_treeview_node(gui2::tree_view_node& node, size_t pos, int number)
675  {
676  //Not tested yet.
677  gui2::tree_view& tv = node.get_tree_view();
678  if(pos >= node.count_children()) {
679  return;
680  }
681  if(number <= 0 || number + pos > node.count_children()) {
682  number = node.count_children() - pos;
683  }
684  for(int i = 0; i < number; ++i) {
685  tv.remove_node(&node.get_child_at(pos));
686  }
687  }
688 }
689 /**
690  * Removes an entry from a list.
691  * - Arg 1: number, index of the element to delete.
692  * - Arg 2: number, number of the elements to delete. (0 to delete all elements after index)
693  * - Args 2..n: path of strings and integers.
694  */
696 {
697  int pos = luaL_checkinteger(L, 1) - 1;
698  int number = luaL_checkinteger(L, 2);
699  gui2::widget* w = find_widget(L, 3, true);
700 
701 #ifdef GUI2_EXPERIMENTAL_LISTBOX
702  if(gui2::list_view* list = dynamic_cast<gui2::list_view*>(w))
703 #else
704  if(gui2::listbox* list = dynamic_cast<gui2::listbox*>(w))
705 #endif
706  {
707  list->remove_row(pos, number);
708  } else if(gui2::multi_page* multi_page = dynamic_cast<gui2::multi_page*>(w)) {
709  multi_page->remove_page(pos, number);
710  } else if(gui2::tree_view* tree_view = dynamic_cast<gui2::tree_view*>(w)) {
711  remove_treeview_node(tree_view->get_root_node(), pos, number);
712  } else if(gui2::tree_view_node* tree_view_node = dynamic_cast<gui2::tree_view_node*>(w)) {
713  remove_treeview_node(*tree_view_node, pos, number);
714  } else {
715  return luaL_argerror(L, lua_gettop(L), "unsupported widget");
716  }
717 
718  return 1;
719 }
720 
721 namespace { // helpers of intf_set_dialog_callback()
723  {
724  int cb;
725  {
726  scoped_dialog::callback_map &m = scoped_dialog::current->callbacks;
727  scoped_dialog::callback_map::const_iterator i = m.find(&w);
728  if(i == m.end()) {
729  return;
730  }
731  cb = i->second;
732  }
733  lua_State *L = scoped_dialog::current->L;
736  lua_rawgeti(L, -1, cb);
737  lua_remove(L, -2);
738  lua_call(L, 0, 0);
739  }
740 
741  /** Helper struct for intf_set_dialog_callback. */
742  struct dialog_callback_wrapper
743  {
744  void forward(gui2::widget* widget)
745  {
746  assert(widget);
747  dialog_callback(*widget);
748  }
749  };
750 }//unnamed namespace for helpers of intf_set_dialog_callback()
751 
752 /**
753  * Sets a callback on a widget of the current dialog.
754  * - Arg 1: function.
755  * - Args 2..n: path of strings and integers.
756  */
758 {
759  gui2::widget* w = find_widget(L, 2, true);
760 
761  scoped_dialog::callback_map &m = scoped_dialog::current->callbacks;
763  if(i != m.end()) {
766  lua_pushnil(L);
767  lua_rawseti(L, -2, i->second);
768  lua_pop(L, 1);
769  m.erase(i);
770  }
771 
772  if(lua_isnil(L, 1)) {
773  return 0;
774  }
775 
776  if(gui2::clickable_item* c = dynamic_cast<gui2::clickable_item*>(w)) {
777  static dialog_callback_wrapper wrapper;
778  c->connect_click_handler(std::bind(&dialog_callback_wrapper::forward, wrapper, w));
779  } else if(gui2::selectable_item* s = dynamic_cast<gui2::selectable_item*>(w)) {
780  connect_signal_notify_modified(dynamic_cast<gui2::widget&>(*s), std::bind(dialog_callback, _1));
781  } else if(gui2::integer_selector* s = dynamic_cast<gui2::integer_selector*>(w)) {
782  connect_signal_notify_modified(dynamic_cast<gui2::widget&>(*s), std::bind(dialog_callback, _1));
783  }
784 #ifdef GUI2_EXPERIMENTAL_LISTBOX
785  else if(gui2::list_view* l = dynamic_cast<gui2::list_view*>(w)) {
786  static dialog_callback_wrapper wrapper;
788  , std::bind(
789  &dialog_callback_wrapper::forward
790  , wrapper
791  , w));
792  }
793 #else
794  else if(gui2::listbox* l = dynamic_cast<gui2::listbox*>(w)) {
795  static dialog_callback_wrapper wrapper;
796  connect_signal_notify_modified(*l, std::bind(&dialog_callback_wrapper::forward, wrapper, w));
797  }
798 #endif
799  else if(gui2::tree_view* tv = dynamic_cast<gui2::tree_view*>(w)) {
801  } else {
802  return luaL_argerror(L, lua_gettop(L), "unsupported widget");
803  }
804 
807  int n = lua_rawlen(L, -1) + 1;
808  m[w] = n;
809  lua_pushvalue(L, 1);
810  lua_rawseti(L, -2, n);
811  lua_pop(L, 1);
812 
813  return 0;
814 }
815 
816 /**
817  * Enables/disables Pango markup on the label of a widget of the current dialog.
818  * - Arg 1: boolean.
819  * - Args 2..n: path of strings and integers.
820  */
822 {
823  bool b = luaW_toboolean(L, 1);
824  gui2::widget* w = find_widget(L, 2, true);
825  gui2::styled_widget* c = dynamic_cast<gui2::styled_widget*>(w);
826  if(!c) {
827  return luaL_argerror(L, lua_gettop(L), "unsupported widget");
828  }
829 
830  c->set_use_markup(b);
831  return 0;
832 }
833 
834 /**
835  * Sets a canvas on a widget of the current dialog.
836  * - Arg 1: integer.
837  * - Arg 2: WML table.
838  * - Args 3..n: path of strings and integers.
839  */
841 {
842  int i = luaL_checkinteger(L, 1);
843  gui2::widget* w = find_widget(L, 3, true);
844  gui2::styled_widget* c = dynamic_cast<gui2::styled_widget*>(w);
845  if(!c) {
846  return luaL_argerror(L, lua_gettop(L), "unsupported widget");
847  }
848 
849  std::vector<gui2::canvas> &cv = c->get_canvases();
850  if(i < 1 || unsigned(i) > cv.size()) {
851  return luaL_argerror(L, 1, "out of bounds");
852  }
853 
854  config cfg = luaW_checkconfig(L, 2);
855  cv[i - 1].set_cfg(cfg);
856  c->set_is_dirty(true);
857  return 0;
858 }
859 
860 /**
861  * Sets a widget to have the focus
862  * - Args 1..n: path of strings and integers.
863  */
865 {
866  gui2::widget* w = find_widget(L, 1, true);
867  scoped_dialog::current->window->keyboard_capture(w);
868  return 0;
869 }
870 
871 /**
872  * Sets a widget's state to active or inactive
873  * - Arg 1: boolean.
874  * - Args 2..n: path of strings and integers.
875  */
877 {
878  const bool b = luaW_toboolean(L, 1);
879  gui2::widget* w = find_widget(L, 2, true);
880  gui2::styled_widget* c = dynamic_cast<gui2::styled_widget*>(w);
881  if(!c) {
882  return luaL_argerror(L, lua_gettop(L), "unsupported widget");
883  }
884 
885  c->set_active(b);
886  return 0;
887 }
888 
889 /**
890  * Sets the visiblity of a widget in the current dialog.
891  * - Arg 1: boolean.
892  * - Args 2..n: path of strings and integers.
893  */
895 {
896  typedef gui2::styled_widget::visibility visibility;
897 
898  visibility flag = visibility::visible;
899 
900  switch(lua_type(L, 1)) {
901  case LUA_TBOOLEAN:
902  flag = luaW_toboolean(L, 1)
903  ? visibility::visible
904  : visibility::invisible;
905  break;
906  case LUA_TSTRING:
907  {
908  const std::string& str = lua_tostring(L, 1);
909  if(str == "visible") {
910  flag = visibility::visible;
911  } else if(str == "hidden") {
912  flag = visibility::hidden;
913  } else if(str == "invisible") {
914  flag = visibility::invisible;
915  } else {
916  return luaL_argerror(L, 1, "string must be one of: visible, hidden, invisible");
917  }
918  }
919  break;
920  default:
921  return luaW_type_error(L, 1, "boolean or string");
922  }
923 
924  gui2::widget* w = find_widget(L, 2, true);
925  gui2::styled_widget* c = dynamic_cast<gui2::styled_widget*>(w);
926  if(!c) {
927  return luaL_argerror(L, lua_gettop(L), "unsupported widget");
928  }
929 
930  c->set_visible(flag);
931 
932  if(flag == visibility::hidden) {
933  // HACK: this is needed to force the widget to be repainted immediately
934  // to get rid of its ghost image.
935  scoped_dialog::current->window->invalidate_layout();
936  }
937 
938  return 0;
939 }
940 
942 {
944  return 0;
945 }
946 
947 int show_gamestate_inspector(const vconfig& cfg, const game_data& data, const game_state& state)
948 {
949  gui2::dialogs::gamestate_inspector inspect_dialog(data.get_variables(), *state.events_manager_, state.board_, cfg["name"]);
950  inspect_dialog.show();
951  return 0;
952 }
953 
954 /**
955  * Sets a widget's state to active or inactive
956  * - Arg 1: string, the type (id of [node_definition]) of the new node.
957  * - Arg 3: integer, where to insert the new node.
958  * - Args 3..n: path of strings and integers.
959  */
960 
962 {
963  const std::string node_type = luaL_checkstring(L, 1);
964  const int insert_pos = luaL_checkinteger(L, 2);
965  static const std::map<std::string, string_map> data;
966  gui2::widget* w = find_widget(L, 3, false);
967 
968  if(gui2::tree_view_node* twn = dynamic_cast<gui2::tree_view_node*>(w)) {
969  twn->add_child(node_type, data, insert_pos);
970  } else if(gui2::tree_view* tw = dynamic_cast<gui2::tree_view*>(w)) {
971  tw->get_root_node().add_child(node_type, data, insert_pos);
972  } else if(gui2::multi_page* mp = dynamic_cast<gui2::multi_page*>(w)) {
973  mp->add_page(node_type, insert_pos, data);
974  } else {
975  return luaL_argerror(L, lua_gettop(L), "unsupported widget");
976  }
977  return 0;
978 }
979 
980 /**
981  * - Arg 1: string, widget type
982  * - Arg 3: string, id
983  * - Arg 3: config,
984  */
985 
987 {
989  std::string id = luaL_checkstring(L, 2);
990  try {
992  lua_kernel_base::get_lua_kernel<lua_kernel_base>(L).add_widget_definition(type, id);
993  }
994  } catch(const std::invalid_argument& e) {
995  return luaL_argerror(L, 1, e.what());
996  }
997  return 0;
998 }
999 } // end namespace lua_gui2
int intf_set_dialog_active(lua_State *L)
Sets a widget's state to active or inactive.
Definition: lua_gui2.cpp:876
#define lua_isnoneornil(L, n)
Definition: lua.h:359
Parameter pack for message portrait.
Small abstract helper class.
LUA_API void lua_createtable(lua_State *L, int narray, int nrec)
Definition: lapi.cpp:683
void show_message(const std::string &title, const std::string &msg, const std::string &button_caption, const bool auto_close, const bool message_use_markup, const bool title_use_markup)
Shows a message to the user.
Definition: message.cpp:152
Dialog is closed with ok button.
Definition: window.hpp:101
int show_lua_console(lua_State *, lua_kernel_base *lk)
Definition: lua_gui2.cpp:941
bool text_input_was_specified
True when [text_input] appeared in [message].
int intf_add_dialog_tree_node(lua_State *L)
Sets a widget's state to active or inactive.
Definition: lua_gui2.cpp:961
static void display(const std::string &scenario_name, const config &story)
const std::string & description() const
Definition: old_markup.hpp:54
std::vector< char_t > string
int chosen_option
The initially chosen option.
std::map< std::string, t_string > string_map
int show_story(lua_State *L)
Displays a story screen.
Definition: lua_gui2.cpp:395
void set_selection_change_callback(std::function< void(widget &)> callback)
Definition: tree_view.hpp:96
LUA_API int lua_type(lua_State *L, int idx)
Definition: lapi.cpp:251
This class represents a single unit of a specific type.
Definition: unit.hpp:100
int dummy
Definition: lstrlib.cpp:1125
virtual widget * find(const std::string &id, const bool must_be_active)
Returns a widget with the wanted id.
Definition: widget.cpp:587
int luaW_type_error(lua_State *L, int narg, const char *tname)
std::string caption
The caption for the optional input text box.
LUA_API void lua_pushboolean(lua_State *L, int b)
Definition: lapi.cpp:556
LUA_API int lua_rawgeti(lua_State *L, int idx, lua_Integer n)
Definition: lapi.cpp:657
std::string text
The initial text value.
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:39
int show_dialog(lua_State *L)
Displays a window.
Definition: lua_gui2.cpp:243
LUA_API void lua_rawseti(lua_State *L, int idx, lua_Integer n)
Definition: lapi.cpp:816
LUA_API int lua_gettop(lua_State *L)
Definition: lapi.cpp:167
This file contains the window object, this object is a top level container which has the event manage...
void show_transient_message(const std::string &title, const std::string &message, const std::string &image, const bool message_use_markup, const bool title_use_markup, const bool restore_background)
Shows a transient message to the user.
Base class for all widgets.
Definition: widget.hpp:48
#define lua_remove(L, idx)
Definition: lua.h:371
void dialog_callback(widget &caller)
Template for dialog callbacks.
Definition: helper.hpp:32
button_style
Selects the style of the buttons to be shown.
Definition: message.hpp:68
static gui2::widget * find_widget(lua_State *L, int i, bool readonly)
Definition: lua_gui2.cpp:118
#define lua_tointeger(L, i)
Definition: lua.h:342
Parameter pack for message list input options.
LUA_API int lua_rawget(lua_State *L, int idx)
Definition: lapi.cpp:646
unsigned maximum_length
The maximum length of the text.
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
const std::vector< std::string > items
int show_message_dialog(lua_State *L)
Displays a message window.
Definition: lua_gui2.cpp:274
int intf_remove_dialog_item(lua_State *L)
Removes an entry from a list.
Definition: lua_gui2.cpp:695
Simple push button.
widget * find(const std::string &id, const bool must_be_active) override
See widget::find.
int intf_set_dialog_markup(lua_State *L)
Enables/disables Pango markup on the label of a widget of the current dialog.
Definition: lua_gui2.cpp:821
bool empty() const
Definition: config.cpp:830
bool show(const unsigned auto_close_time=0)
Shows the window.
Small abstract helper class.
Definitions for the interface to Wesnoth Markup Language (WML).
int intf_add_widget_definition(lua_State *L)
Definition: lua_gui2.cpp:986
A single unit type that the player may recruit.
Definition: types.hpp:42
const std::unique_ptr< game_events::manager > events_manager_
Definition: game_state.hpp:52
Class for a single line text area.
Definition: text_box.hpp:121
#define lua_pop(L, n)
Definition: lua.h:344
int intf_set_dialog_focus(lua_State *L)
Sets a widget to have the focus.
Definition: lua_gui2.cpp:864
const config & get_variables() const
Definition: game_data.hpp:35
static void display(lua_kernel_base *lk)
Display a new console, using given video and lua kernel.
#define b
Pubic entry points for the MP workflow.
Definition: lobby_data.cpp:47
virtual void set_label(const t_string &label)
The listbox class.
Definition: listbox.hpp:42
#define ERR_LUA
Definition: lua_gui2.cpp:72
const config & options()
Definition: game.cpp:570
#define LUA_TSTRING
Definition: lua.h:68
int show_gamestate_inspector(const vconfig &cfg, const game_data &data, const game_state &state)
Definition: lua_gui2.cpp:947
LUALIB_API int luaL_argerror(lua_State *L, int arg, const char *extramsg)
Definition: lauxlib.cpp:164
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification_function &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.cpp:233
bool luaW_toboolean(lua_State *L, int n)
Definition: lua_common.cpp:858
unit * luaW_tounit(lua_State *L, int index, bool only_on_map)
Converts a Lua value to a unit pointer.
Definition: lua_unit.cpp:143
void set_visible(const visibility visible)
Definition: widget.cpp:479
void set_is_dirty(const bool is_dirty)
Definition: widget.cpp:469
t_string luaW_checktstring(lua_State *L, int index)
Converts a scalar to a translatable string.
Definition: lua_common.cpp:590
Used by the menu_button widget.
std::string selected
This file contains the canvas object which is the part where the widgets draw (temporally) images on...
LUA_API const char * lua_pushlstring(lua_State *L, const char *s, size_t len)
Definition: lapi.cpp:479
std::string path
Definition: game_config.cpp:56
int show_menu(lua_State *L)
Displays a popup menu at the current mouse position Best used from a [set_menu_item], to show a submenu.
Definition: lua_gui2.cpp:408
std::vector< canvas > & get_canvases()
virtual void set_use_markup(bool use_markup)
int intf_set_dialog_canvas(lua_State *L)
Sets a canvas on a widget of the current dialog.
Definition: lua_gui2.cpp:840
#define lua_newtable(L)
Definition: lua.h:346
LUA_API void lua_pushnil(lua_State *L)
Definition: lapi.cpp:450
LUA_API void lua_pushnumber(lua_State *L, lua_Number n)
Definition: lapi.cpp:458
LUALIB_API void luaL_checktype(lua_State *L, int arg, int t)
Definition: lauxlib.cpp:378
lu_byte right
Definition: lparser.cpp:1027
const unit_type * luaW_tounittype(lua_State *L, int idx)
Test if a stack element is a unit type, and return it if so.
int remove_node(tree_view_node *tree_view_node)
Definition: tree_view.cpp:70
bool has_attribute(config_key_type key) const
Definition: config.cpp:213
static lg::log_domain log_scripting_lua("scripting/lua")
bool luaW_toconfig(lua_State *L, int index, config &cfg)
Converts an optional table or vconfig to a config object.
Definition: lua_common.cpp:712
int show_popup_dialog(lua_State *L)
Displays a popup message.
Definition: lua_gui2.cpp:381
bool luaW_totstring(lua_State *L, int index, t_string &str)
Converts a scalar to a translatable string.
Definition: lua_common.cpp:565
size_t count_children() const
The number of children in this widget.
#define lua_isnil(L, n)
Definition: lua.h:355
int intf_set_dialog_value(lua_State *L)
Sets the value of a widget on the current dialog.
Definition: lua_gui2.cpp:469
LUALIB_API lua_Integer luaL_checkinteger(lua_State *L, int arg)
Definition: lauxlib.cpp:430
int show_wml_message(const std::string &title, const std::string &message, const wml_message_portrait *left, const wml_message_portrait *right, const wml_message_options &options, const wml_message_input &input)
Helper function to show a portrait.
LUA_API void lua_rawset(lua_State *L, int idx)
Definition: lapi.cpp:800
tree_view_node & get_child_at(int index)
config luaW_checkconfig(lua_State *L, int index)
Converts an optional table or vconfig to a config object.
Definition: lua_common.cpp:787
static map_location::DIRECTION s
window * build(const builder_window::window_resolution *definition)
Builds a window.
Helper class for message options.
Definition: wml_message.hpp:27
lua_check_impl::remove_constref< T > lua_check(lua_State *L, int n)
Definition: push_check.hpp:288
size_t i
Definition: function.cpp:933
#define lua_tostring(L, i)
Definition: lua.h:366
visibility
Visibility settings done by the user.
Definition: widget.hpp:58
int w
LUA_API void lua_pushvalue(lua_State *L, int idx)
Definition: lapi.cpp:237
bool is_default() const
Definition: old_markup.hpp:59
Base class for all visible items.
LUA_API int lua_isnumber(lua_State *L, int idx)
Definition: lapi.cpp:276
#define lua_call(L, n, r)
Definition: lua.h:274
LUALIB_API int luaL_error(lua_State *L, const char *fmt,...)
Definition: lauxlib.cpp:223
static const char dlgclbkKey[]
Definition: lua_gui2.cpp:74
int intf_set_dialog_callback(lua_State *L)
Sets a callback on a widget of the current dialog.
Definition: lua_gui2.cpp:757
const std::string & icon() const
Definition: old_markup.hpp:44
tree_view & get_tree_view()
LUA_API size_t lua_rawlen(lua_State *L, int idx)
Definition: lapi.cpp:392
#define LUA_REGISTRYINDEX
Definition: lua.h:42
std::vector< wml_message_option > option_list
A list of options to select in the dialog.
The multi page class.
Definition: multi_page.hpp:35
#define lua_istable(L, n)
Definition: lua.h:353
virtual void set_active(const bool active)=0
Sets the styled_widget's state.
map_location prev
Definition: astarsearch.cpp:65
lu_byte left
Definition: lparser.cpp:1026
A slider.
Definition: slider.hpp:33
game_board board_
Definition: game_state.hpp:46
A variable-expanding proxy for the config class.
Definition: variable.hpp:42
this module manages the cache of images.
Definition: image.cpp:103
Standard logging facilities (interface).
Implements simple parsing of legacy GUI1 item markup.
Definition: old_markup.hpp:25
bool add_single_widget_definition(const std::string &widget_type, const std::string &definition_id, const config &cfg)
Adds a widget definition to the default GUI.
Small concept class.
#define e
int intf_get_dialog_value(lua_State *L)
Gets the value of a widget on the current dialog.
Definition: lua_gui2.cpp:600
const std::string & str() const
Definition: tstring.hpp:186
int show_message_box(lua_State *L)
Displays a simple message box.
Definition: lua_gui2.cpp:432
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:93
mock_char c
LUA_API int lua_getfield(lua_State *L, int idx, const char *k)
Definition: lapi.cpp:621
#define LUA_TTABLE
Definition: lua.h:69
static map_location::DIRECTION n
LUA_API void lua_pushinteger(lua_State *L, lua_Integer n)
Definition: lapi.cpp:466
base class of top level items, the only item which needs to store the final canvases to draw on ...
Definition: window.hpp:62
const std::string & label() const
Definition: old_markup.hpp:49
#define luaL_optstring(L, n, d)
Definition: lauxlib.h:125
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
LUA_API const char * lua_pushstring(lua_State *L, const char *s)
Definition: lapi.cpp:491
#define LUA_TBOOLEAN
Definition: lua.h:65
int intf_set_dialog_visible(lua_State *L)
Sets the visiblity of a widget in the current dialog.
Definition: lua_gui2.cpp:894
LUA_API const char * lua_typename(lua_State *L, int t)
Definition: lapi.cpp:257
Parameter pack for message text input options.
#define luaL_checkstring(L, n)
Definition: lauxlib.h:124