The Battle for Wesnoth  1.15.5+dev
lua_widget_attributes.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 https://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 "gui/core/canvas.hpp"
19 #include "gui/widgets/listbox.hpp"
24 #include "gui/widgets/slider.hpp"
26 #include "gui/widgets/text_box.hpp"
30 #include "gui/widgets/widget.hpp"
31 #include "gui/widgets/window.hpp"
32 #include "config.hpp"
33 #include "log.hpp"
34 #include "scripting/lua_common.hpp"
37 #include "scripting/lua_unit.hpp"
39 #include "scripting/push_check.hpp"
40 #include "scripting/lua_widget.hpp"
43 #include "tstring.hpp"
44 #include "game_data.hpp"
45 #include "game_state.hpp"
46 
47 #include "utils/functional.hpp"
49 
50 #include <boost/preprocessor/cat.hpp>
51 
52 #include <map>
53 #include <utility>
54 #include <vector>
55 #include <boost/optional.hpp>
56 
57 #include "lua/lauxlib.h" // for luaL_checkinteger, etc
58 #include "lua/lua.h" // for lua_setfield, etc
59 
60 static lg::log_domain log_scripting_lua("scripting/lua");
61 #define ERR_LUA LOG_STREAM(err, log_scripting_lua)
62 
63 
64 
65 
67 {
68  assert(i > 0);
69  if(gui2::listbox* list = dynamic_cast<gui2::listbox*>(&w)) {
70  int n = list->get_item_count();
71  if(i > n) {
72  for(; n < i; ++n) {
73  list->add_row(utils::string_map());
74  }
75  }
76  return list->get_row_grid(i - 1);
77  } else if(gui2::multi_page* multi_page = dynamic_cast<gui2::multi_page*>(&w)) {
78  int n = multi_page->get_page_count();
79  if(i > n) {
80  for(; n < i; ++n) {
81  multi_page->add_page(utils::string_map());
82  }
83  }
84  return &multi_page->page_grid(i - 1);
85  } else if(gui2::tree_view* tree_view = dynamic_cast<gui2::tree_view*>(&w)) {
86  gui2::tree_view_node& tvn = tree_view->get_root_node();
87  int n = tvn.count_children();
88  if(i > n) {
89  throw std::invalid_argument("out of range");
90  }
91  return &tvn.get_child_at(i - 1);
92  } else if(gui2::tree_view_node* tree_view_node = dynamic_cast<gui2::tree_view_node*>(&w)) {
93  int n = tree_view_node->count_children();
94  if(i > n) {
95  throw std::invalid_argument("out of range");
96  }
97  return &tree_view_node->get_child_at(i - 1);
98  } else if(gui2::stacked_widget* stacked_widget = dynamic_cast<gui2::stacked_widget*>(&w)) {
99  int n = stacked_widget->get_layer_count();
100  if(i > n) {
101  throw std::invalid_argument("out of range");
102  }
103  return stacked_widget->get_layer_grid(i - 1);
104  }
105  return nullptr;
106 }
107 
109 {
110  return w.find(m, false);
111 }
112 
113 using tgetters = std::map<std::string, std::vector<std::function<bool(lua_State*, gui2::widget&)>>>;
115 
116 using tsetters = std::map<std::string, std::vector<std::function<bool(lua_State*, int, gui2::widget&)>>>;
118 
119 #define WIDGET_GETTER4(name, value_type, widgt_type, id) \
120 /* use a class memeber for L to supress unused praemter wanring */ \
121 struct BOOST_PP_CAT(getter_, id) { value_type do_it(widgt_type& w); lua_State* L; }; \
122 struct BOOST_PP_CAT(getter_adder_, id) { \
123  BOOST_PP_CAT(getter_adder_, id) () \
124  { \
125  utils::split_foreach(name, ',', 0, [](utils::string_view name_part){\
126  getters[std::string(name_part)].push_back([](lua_State* L, gui2::widget& w) { \
127  if(widgt_type* pw = dynamic_cast<widgt_type*>(&w)) { \
128  lua_push(L, BOOST_PP_CAT(getter_, id){L}.do_it(*pw)); \
129  return true; \
130  } \
131  return false; \
132  }); \
133  }); \
134  } \
135 }; \
136 static BOOST_PP_CAT(getter_adder_, id) BOOST_PP_CAT(getter_adder_instance_, id) ; \
137 value_type BOOST_PP_CAT(getter_, id)::do_it(widgt_type& w)
138 
139 
140 #define WIDGET_SETTER4(name, value_type, widgt_type, id) \
141 struct BOOST_PP_CAT(setter_, id) { void do_it(widgt_type& w, const value_type& value); lua_State* L; }; \
142 struct BOOST_PP_CAT(setter_adder_, id) { \
143  BOOST_PP_CAT(setter_adder_, id) ()\
144  { \
145  utils::split_foreach(name, ',', 0, [](utils::string_view name_part){\
146  setters[std::string(name_part)].push_back([](lua_State* L, int idx, gui2::widget& w) { \
147  if(widgt_type* pw = dynamic_cast<widgt_type*>(&w)) { \
148  BOOST_PP_CAT(setter_, id){L}.do_it(*pw, lua_check<value_type>(L, idx)); \
149  return true; \
150  } \
151  return false; \
152  }); \
153  }); \
154  } \
155 }; \
156 static BOOST_PP_CAT(setter_adder_, id) BOOST_PP_CAT(setter_adder_instance_, id); \
157 void BOOST_PP_CAT(setter_, id)::do_it(widgt_type& w, const value_type& value)
158 
159 
160 /**
161  * @param name: string comma seperated list
162  * @param type: the type of the attribute, for exmaple int or std::string
163  * @param widgt_type: the type of the widget, for exmaple gui2::listbox
164  */
165 
166 #define WIDGET_GETTER(name, value_type, widgt_type) WIDGET_GETTER4(name, value_type, widgt_type, __LINE__)
167 
168 #define WIDGET_SETTER(name, value_type, widgt_type) WIDGET_SETTER4(name, value_type, widgt_type, __LINE__)
169 
170 
171 /// CLASSIC
172 
173 WIDGET_GETTER("value_compat,selected_index", int, gui2::listbox)
174 {
175  return w.get_selected_row() + 1;
176 }
177 
178 /* idea
179 WIDGET_GETTER("selected_widget", gui2::widget, gui2::listbox)
180 {
181  if(w.get_selected_row() >= w.get_item_count()) {
182  throw std::invalid_argument("widget has no selected item");
183  }
184  return w.get_row_grid(w.get_selected_row());
185 }
186 */
187 
188 WIDGET_SETTER("value_compat,selected_index", int, gui2::listbox)
189 {
190  w.select_row(value - 1);
191 }
192 
193 WIDGET_GETTER("value_compat,selected_index", int, gui2::multi_page)
194 {
195  return w.get_selected_page() + 1;
196 }
197 
198 WIDGET_SETTER("value_compat,selected_index", int, gui2::multi_page)
199 {
200  w.select_page(value -1);
201 }
202 
203 WIDGET_GETTER("value_compat,selected_index", int, gui2::stacked_widget)
204 {
205  return w.current_layer() + 1;
206 }
207 
208 WIDGET_SETTER("value_compat,selected_index", int, gui2::stacked_widget)
209 {
210  w.select_layer(value - 1);
211 }
212 
213 WIDGET_GETTER("selected_index", int, gui2::selectable_item)
214 {
215  return w.get_value() + 1;
216 }
217 
218 WIDGET_SETTER("selected_index", int, gui2::selectable_item)
219 {
220  if(value > int(w.num_states())) {
221  throw std::invalid_argument("invalid index");
222  }
223  w.set_value(value + 1);
224 }
225 
226 WIDGET_GETTER("value_compat,selected", bool, gui2::selectable_item)
227 {
228  if(w.num_states() == 2) {
229  return w.get_value_bool();
230  }
231  throw std::invalid_argument("invalid widget");
232 }
233 
234 WIDGET_SETTER("value_compat,selected", bool, gui2::selectable_item)
235 {
236  w.set_value_bool(value);
237 }
238 
240 {
241  return w.get_value();
242 }
243 
245 {
246  w.set_value(value);
247 }
248 
249 WIDGET_GETTER("value_compat,value", int, gui2::slider)
250 {
251  return w.get_value();
252 }
253 
254 WIDGET_SETTER("value_compat,value", int, gui2::slider)
255 {
256  w.set_value(value);
257 }
258 
259 WIDGET_GETTER("value_compat,percentage", int, gui2::progress_bar)
260 {
261  return w.get_percentage();
262 }
263 
264 WIDGET_SETTER("value_compat,percentage", int, gui2::progress_bar)
265 {
266  w.set_percentage(value);
267 }
268 
269 WIDGET_GETTER("value_compat,selected_item_path", std::vector<int>, gui2::tree_view)
270 {
271  auto res = w.selected_item()->describe_path();
272  for(int& a : res) { ++a;}
273  return res;
274 }
275 
276 WIDGET_GETTER("path", std::vector<int>, gui2::tree_view_node)
277 {
278  auto res = w.describe_path();
279  for(int& a : res) { ++a;}
280  return res;
281 }
282 
283 WIDGET_SETTER("value_compat,unfolded", bool, gui2::tree_view_node)
284 {
285  if(value) {
286  w.unfold();
287  } else {
288  w.fold();
289  }
290 }
291 
293 {
294  if(const unit_type* ut = luaW_tounittype(L, value.index)) {
295  w.set_displayed_type(*ut);
296  } else if(unit* u = luaW_tounit(L, value.index)) {
297  w.set_displayed_unit(*u);
298  } else {
299  luaW_type_error(L, value.index, "unit or unit type");
300  }
301 }
302 
303 WIDGET_GETTER("item_count", int, gui2::multi_page)
304 {
305  return w.get_page_count();
306 }
307 
308 WIDGET_SETTER("use_markup", bool, gui2::styled_widget)
309 {
310  w.set_use_markup(value);
311 }
312 
313 //TODO: while i think this shortcut is useful, i'm not that happy about
314 // the name since it changes 'label' and not 'text', the first one
315 // is the label that is part of most widgets (like checkboxes), the
316 // later is sepcific to input textboxes.
318 {
319  w.set_use_markup(true);
320  w.set_label(value);
321 }
322 
324 {
325  w.set_active(value);
326 }
327 
329 {
330  w.set_tooltip(value);
331 }
332 
333 
335 {
336  if(!luaW_getglobal(L, "gui", "widget", "set_callback")) {
337  ERR_LUA << "gui.widget.set_callback didnt exist\n";
338  }
339  luaW_pushwidget(L, w);
340  lua_pushvalue(L, value.index);
341  lua_call(L, 2, 0);
342 }
343 
345 {
346 
347  typedef gui2::styled_widget::visibility visibility;
348 
349  visibility flag = visibility::visible;
350 
351  switch(lua_type(L, value.index)) {
352  case LUA_TBOOLEAN:
353  flag = luaW_toboolean(L, value.index)
354  ? visibility::visible
355  : visibility::invisible;
356  break;
357  case LUA_TSTRING:
358  {
359  const std::string& str = lua_tostring(L, value.index);
360  if(str == "visible") {
361  flag = visibility::visible;
362  } else if(str == "hidden") {
363  flag = visibility::hidden;
364  } else if(str == "invisible") {
365  flag = visibility::invisible;
366  } else {
367  luaL_argerror(L, value.index, "string must be one of: visible, hidden, invisible");
368  }
369  }
370  break;
371  default:
372  luaW_type_error(L, value.index, "boolean or string");
373  }
374 
375  w.set_visible(flag);
376 
377  //if(flag == visibility::hidden) {
378  // // HACK: this is needed to force the widget to be repainted immediately
379  // // to get rid of its ghost image.
380  // scoped_dialog::current->window->invalidate_layout();
381  //}
382 }
383 
384 //must be last
386 {
387  w.set_label(value);
388 }
389 
391 {
392  if(gui2::styled_widget* sw = dynamic_cast<gui2::styled_widget*>(&w)) {
393  return sw->get_control_type();
394  }
395  else if(dynamic_cast<gui2::tree_view_node*>(&w)) {
396  return "tree_view_node";
397  }
398  else if(dynamic_cast<gui2::grid*>(&w)) {
399  return "grid";
400  }
401  else {
402  return "";
403  }
404 }
405 
406 ///////////////////////////////////////////////////////
407 ////////////////////// CALLBACLS //////////////////////
408 ///////////////////////////////////////////////////////
409 namespace {
410 
411 void dialog_callback(lua_State* L, lua_ptr<gui2::widget>& wp, const std::string& id)
412 {
413  gui2::widget* w = wp.get_ptr();
414  if(!w) {
415  ERR_LUA << "widget was deleted\n";
416  return;
417  }
418  gui2::window* wd = w->get_window();
419  if(!wd) {
420  ERR_LUA << "canot find window in eidgte callback\n";
421  return;
422  }
423  luaW_callwidgetcallback(L, w, wd, id);
424 }
425 
427 {
428  gui2::window* wd = w.get_window();
429  if(!wd) {
430  throw std::invalid_argument("the widget has no window assigned");
431  }
432  lua_pushvalue(L, value.index);
433  if (!luaW_setwidgetcallback(L, &w, wd, "on_modified")) {
434  connect_signal_notify_modified(w, std::bind(&dialog_callback, L, lua_ptr<gui2::widget>(w), "callback"));
435  }
436 }
437 
438 WIDGET_SETTER("on_left_click", lua_index_raw, gui2::widget)
439 {
440  gui2::window* wd = w.get_window();
441  if(!wd) {
442  throw std::invalid_argument("the widget has no window assigned");
443  }
444  lua_pushvalue(L, value.index);
445  if (!luaW_setwidgetcallback(L, &w, wd, "on_left_click")) {
446  connect_signal_notify_modified(w, std::bind(&dialog_callback, L, lua_ptr<gui2::widget>(w), "callback"));
447  }
448 }
449 
450 WIDGET_SETTER("on_button_click", lua_index_raw, gui2::widget)
451 {
452  gui2::window* wd = w.get_window();
453  gui2::clickable_item* cl = dynamic_cast<gui2::clickable_item*>(&w);
454 
455  if(!wd) {
456  throw std::invalid_argument("the widget has no window assigned");
457  }
458  if(!cl) {
459  throw std::invalid_argument("unsupported widget");
460  }
461  lua_pushvalue(L, value.index);
462  if (!luaW_setwidgetcallback(L, &w, wd, "on_button_click")) {
463  cl->connect_click_handler(std::bind(&dialog_callback, L, lua_ptr<gui2::widget>(w), "on_button_click"));
464  }
465 }
466 
467 }
468 
469 
470 
471 namespace lua_widget {
472 
474 {
476  if(lua_isinteger(L, 2)) {
477 
478  if(auto pwidget = find_child_by_index(w, luaL_checkinteger(L, 2))) {
479  luaW_pushwidget(L, *pwidget);
480  return 1;
481  }
482 
483  }
484  utils::string_view str = lua_check<utils::string_view>(L, 2);
485 
486  tgetters::iterator it = getters.find(std::string(str));
487  if(it != getters.end()) {
488  for(const auto& func : it->second) {
489  if(func(L, w)) {
490  return 1;
491  }
492  }
493  }
494  if(luaW_getglobal(L, "gui", "widget", std::string(str).c_str())) {
495  return 1;
496  }
497  if(auto pwidget = find_child_by_name(w, std::string(str))) {
498  luaW_pushwidget(L, *pwidget);
499  return 1;
500  }
501  ERR_LUA << "invalid propertly of '" << typeid(w).name()<< "' widget :" << str << "\n";
502  return luaL_argerror(L, 2, "invalid propertly of widget");
503 }
504 
506 {
508  utils::string_view str = lua_check<utils::string_view>(L, 2);
509 
510 
511  tsetters::iterator it = setters.find(std::string(str));
512  if(it != setters.end()) {
513  for(const auto& func : it->second) {
514  if(func(L, 3, w)) {
515  return 0;
516  }
517  }
518  ERR_LUA << "none of "<< it->second.size() << " setters macthed\n";
519  }
520  else {
521  ERR_LUA << "unknown peopertly id : " << str << " #known properties=" << setters.size() << "\n";
522 
523  }
524  ERR_LUA << "invalid modifiable propertly of '" << typeid(w).name()<< "' widget:" << str << "\n";
525  return luaL_argerror(L, 2, "invalid modifiable propertly of widget");
526 }
527 }
int impl_widget_get(lua_State *L)
std::map< std::string, std::vector< std::function< bool(lua_State *, gui2::widget &)> >> tgetters
Small abstract helper class.
#define WIDGET_SETTER(name, value_type, widgt_type)
std::map< std::string, t_string > string_map
static gui2::widget * find_child_by_index(gui2::widget &w, int i)
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:129
virtual widget * find(const std::string &id, const bool must_be_active)
Returns a widget with the wanted id.
Definition: widget.cpp:583
int luaW_type_error(lua_State *L, int narg, const char *tname)
void luaW_callwidgetcallback(lua_State *L, gui2::widget *wg, gui2::window *owner, utils::string_view name)
callas a widgets callback [-0, +0, e]
Definition: lua_widget.cpp:174
#define a
Tmust inherit enable_lua_ptr<T>
Definition: lua_ptr.hpp:18
This file contains the window object, this object is a top level container which has the event manage...
Base class for all widgets.
Definition: widget.hpp:49
void luaW_pushwidget(lua_State *L, gui2::widget &w)
Definition: lua_widget.cpp:35
std::string str
Definition: statement.cpp:110
T * get_ptr()
Definition: lua_ptr.hpp:36
Definitions for the interface to Wesnoth Markup Language (WML).
A single unit type that the player may recruit.
Definition: types.hpp:44
Class for a single line text area.
Definition: text_box.hpp:121
std::size_t count_children() const
The number of children in this widget.
The listbox class.
Definition: listbox.hpp:40
#define LUA_TSTRING
Definition: lua.h:68
std::map< std::string, std::vector< std::function< bool(lua_State *, int, gui2::widget &)> >> tsetters
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:248
#define WIDGET_GETTER(name, value_type, widgt_type)
bool luaW_getglobal(lua_State *L, const std::vector< std::string > &path)
Pushes the value found by following the variadic names (char *), if the value is not nil...
Definition: lua_common.cpp:871
bool luaW_toboolean(lua_State *L, int n)
Definition: lua_common.cpp:890
unit * luaW_tounit(lua_State *L, int index, bool only_on_map)
Converts a Lua value to a unit pointer.
Definition: lua_unit.cpp:145
virtual void connect_click_handler(const event::signal_function &signal)=0
Connects a signal handler for a &#39;click&#39; event.
static lg::log_domain log_scripting_lua("scripting/lua")
bool luaW_setwidgetcallback(lua_State *L, gui2::widget *wg, gui2::window *owner, utils::string_view name)
returns true if a callback already existed. [-1, +0, -]
Definition: lua_widget.cpp:140
This file contains the canvas object which is the part where the widgets draw (temporally) images on...
static tsetters setters
const unit_type * luaW_tounittype(lua_State *L, int idx)
Test if a stack element is a unit type, and return it if so.
std::size_t i
Definition: function.cpp:933
LUALIB_API lua_Integer luaL_checkinteger(lua_State *L, int arg)
Definition: lauxlib.cpp:430
gui2::widget & luaW_checkwidget(lua_State *L, int n)
Definition: lua_widget.cpp:41
tree_view_node & get_child_at(int index)
window * get_window()
Get the parent window.
Definition: widget.cpp:116
#define lua_tostring(L, i)
Definition: lua.h:366
visibility
Visibility settings done by the user.
Definition: widget.hpp:59
#define ERR_LUA
int w
LUA_API void lua_pushvalue(lua_State *L, int idx)
Definition: lapi.cpp:237
Base class for all visible items.
LUA_API int lua_isinteger(lua_State *L, int idx)
Definition: lapi.cpp:270
#define lua_call(L, n, r)
Definition: lua.h:274
static map_location::DIRECTION sw
static tgetters getters
The multi page class.
Definition: multi_page.hpp:35
A slider.
Definition: slider.hpp:33
Standard logging facilities (interface).
static gui2::widget * find_child_by_name(gui2::widget &w, const std::string &m)
Small concept class.
int impl_widget_set(lua_State *L)
static map_location::DIRECTION n
base class of top level items, the only item which needs to store the final canvases to draw on ...
Definition: window.hpp:62
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
#define LUA_TBOOLEAN
Definition: lua.h:65