The Battle for Wesnoth  1.19.7+dev
lua_widget_attributes.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2024
3  by Chris Beck <render787@gmail.com>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
18 #include "gui/widgets/helper.hpp"
20 #include "gui/widgets/combobox.hpp"
21 #include "gui/widgets/label.hpp"
22 #include "gui/widgets/listbox.hpp"
29 #include "gui/widgets/slider.hpp"
31 #include "gui/widgets/text_box.hpp"
35 #include "gui/widgets/widget.hpp"
36 #include "gui/widgets/window.hpp"
37 #include "log.hpp"
38 #include "scripting/lua_common.hpp"
40 #include "scripting/lua_unit.hpp"
42 #include "scripting/push_check.hpp"
43 #include "scripting/lua_widget.hpp"
47 
48 #include <functional>
49 
50 #include <boost/preprocessor/cat.hpp>
51 
52 #include <map>
53 #include <utility>
54 #include <vector>
55 
56 static lg::log_domain log_scripting_lua("scripting/lua");
57 #define ERR_LUA LOG_STREAM(err, log_scripting_lua)
58 
60 {
61  if(auto window = w.get_window()) {
62  window->invalidate_layout();
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(gui2::widget_item{});
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(gui2::widget_item{});
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 
108 static gui2::widget* find_child_by_name(gui2::widget& w, const std::string& m)
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&, bool)>>>;
115 
116 using tsetters = std::map<std::string, std::vector<std::function<bool(lua_State*, int, gui2::widget&, bool)>>>;
118 
119 template<typename widget_type, typename value_type, typename action_type, bool setter>
120 void register_widget_attribute(const char* name)
121 {
122  utils::split_foreach(name, ',', 0, [](std::string_view name_part) {
123  using map_type = std::conditional_t<setter, tsetters, tgetters>;
124  using list_type = typename map_type::mapped_type;
125  using callback_type = typename list_type::value_type;
126  map_type* map;
127  callback_type fcn;
128  if constexpr(setter) {
129  map = &setters;
130  fcn = [action = action_type()](lua_State* L, int idx, gui2::widget& w, bool nop) {
131  if(widget_type* pw = dynamic_cast<widget_type*>(&w)) {
132  if(!nop) action.set(L, *pw, lua_check<value_type>(L, idx));
133  return true;
134  }
135  return false;
136  };
137  } else {
138  map = &getters;
139  fcn = [action = action_type()](lua_State* L, gui2::widget& w, bool nop) {
140  if(widget_type* pw = dynamic_cast<widget_type*>(&w)) {
141  if(!nop) lua_push(L, action.get(L, *pw));
142  return true;
143  }
144  return false;
145  };
146  }
147  list_type& list = (*map)[std::string(name_part)];
148  list.push_back(fcn);
149  });
150 }
151 
152 #define WIDGET_GETTER4(name, value_type, widgt_type, id) \
153 struct BOOST_PP_CAT(getter_, id) : public lua_getter<widgt_type, value_type> { \
154  value_type get(lua_State* L, const widgt_type& w) const override; \
155 }; \
156 struct BOOST_PP_CAT(getter_adder_, id) { \
157  BOOST_PP_CAT(getter_adder_, id) () \
158  { \
159  register_widget_attribute<widgt_type, value_type, BOOST_PP_CAT(getter_, id), false>(name); \
160  } \
161 }; \
162 static BOOST_PP_CAT(getter_adder_, id) BOOST_PP_CAT(getter_adder_instance_, id) ; \
163 value_type BOOST_PP_CAT(getter_, id)::get([[maybe_unused]] lua_State* L, const widgt_type& w) const
164 
165 
166 #define WIDGET_SETTER4(name, value_type, widgt_type, id) \
167 struct BOOST_PP_CAT(setter_, id) : public lua_setter<widgt_type, value_type> { \
168  void set(lua_State* L, widgt_type& w, const value_type& value) const override; \
169 }; \
170 struct BOOST_PP_CAT(setter_adder_, id) { \
171  BOOST_PP_CAT(setter_adder_, id) ()\
172  { \
173  register_widget_attribute<widgt_type, value_type, BOOST_PP_CAT(setter_, id), true>(name); \
174  } \
175 }; \
176 static BOOST_PP_CAT(setter_adder_, id) BOOST_PP_CAT(setter_adder_instance_, id); \
177 void BOOST_PP_CAT(setter_, id)::set([[maybe_unused]] lua_State* L, widgt_type& w, const value_type& value) const
178 
179 
180 /**
181  * @param name: string comma seperated list
182  * @param value_type: the type of the attribute, for example int or std::string
183  * @param widgt_type: the type of the widget, for example gui2::listbox
184  */
185 #define WIDGET_GETTER(name, value_type, widgt_type) WIDGET_GETTER4(name, value_type, widgt_type, __LINE__)
186 
187 #define WIDGET_SETTER(name, value_type, widgt_type) WIDGET_SETTER4(name, value_type, widgt_type, __LINE__)
188 
189 
190 /// CLASSIC
191 
192 WIDGET_GETTER("value_compat,selected_index", int, gui2::listbox)
193 {
194  return w.get_selected_row() + 1;
195 }
196 
197 WIDGET_SETTER("value_compat,selected_index", int, gui2::listbox)
198 {
199  w.select_row(value - 1);
200 }
201 
202 WIDGET_GETTER("value_compat,selected_index", int, gui2::multi_page)
203 {
204  return w.get_selected_page() + 1;
205 }
206 
207 WIDGET_SETTER("value_compat,selected_index", int, gui2::multi_page)
208 {
209  w.select_page(value -1);
210 }
211 
212 WIDGET_GETTER("value_compat,selected_index", int, gui2::stacked_widget)
213 {
214  return w.current_layer() + 1;
215 }
216 
217 WIDGET_SETTER("value_compat,selected_index", int, gui2::stacked_widget)
218 {
219  w.select_layer(value - 1);
220 }
221 
222 WIDGET_GETTER("selected_index", int, gui2::selectable_item)
223 {
224  return w.get_value() + 1;
225 }
226 
227 WIDGET_SETTER("selected_index", int, gui2::selectable_item)
228 {
229  if(value > int(w.num_states())) {
230  throw std::invalid_argument("invalid index");
231  }
232  w.set_value(value - 1);
233 }
234 
235 WIDGET_GETTER("value_compat,selected", bool, gui2::selectable_item)
236 {
237  if(w.num_states() == 2) {
238  return w.get_value_bool();
239  }
240  throw std::invalid_argument("invalid widget");
241 }
242 
243 WIDGET_SETTER("value_compat,selected", bool, gui2::selectable_item)
244 {
245  w.set_value_bool(value);
246 }
247 
248 WIDGET_GETTER("value_compat,text", std::string, gui2::text_box)
249 {
250  return w.get_value();
251 }
252 
253 WIDGET_SETTER("value_compat,text", std::string, gui2::text_box)
254 {
255  w.set_value(value);
256 }
257 
258 WIDGET_GETTER("value_compat,value", int, gui2::slider)
259 {
260  return w.get_value();
261 }
262 
263 WIDGET_SETTER("value_compat,value", int, gui2::slider)
264 {
265  w.set_value(value);
266 }
267 
268 WIDGET_GETTER("best_slider_length", int, gui2::slider)
269 {
270  return w.get_best_slider_length();
271 }
272 
273 WIDGET_SETTER("best_slider_length", int, gui2::slider)
274 {
275  if(value < 0) {
276  throw std::invalid_argument("best_slider_length must be >= 0");
277  }
278  w.set_best_slider_length(value);
279 }
280 
281 WIDGET_GETTER("max_value", int, gui2::slider)
282 {
283  return w.get_maximum_value();
284 }
285 
286 WIDGET_SETTER("max_value", int, gui2::slider)
287 {
288  w.set_value_range(w.get_minimum_value(), value);
289 }
290 
291 WIDGET_GETTER("maximum_value_label", t_string, gui2::slider)
292 {
293  return w.get_maximum_value_label();
294 }
295 
296 WIDGET_SETTER("maximum_value_label", t_string, gui2::slider)
297 {
298  w.set_maximum_value_label(value);
300 }
301 
302 WIDGET_GETTER("min_value", int, gui2::slider)
303 {
304  return w.get_minimum_value();
305 }
306 
307 WIDGET_SETTER("min_value", int, gui2::slider)
308 {
309  w.set_value_range(value, w.get_maximum_value());
310 }
311 
312 WIDGET_GETTER("minimum_value_label", t_string, gui2::slider)
313 {
314  return w.get_minimum_value_label();
315 }
316 
317 WIDGET_SETTER("minimum_value_label", t_string, gui2::slider)
318 {
319  w.set_minimum_value_label(value);
321 }
322 
323 WIDGET_GETTER("value_compat,percentage", int, gui2::progress_bar)
324 {
325  return w.get_percentage();
326 }
327 
328 WIDGET_SETTER("value_compat,percentage", int, gui2::progress_bar)
329 {
330  w.set_percentage(value);
331 }
332 
333 WIDGET_GETTER("value_compat,selected_item_path", std::vector<int>, gui2::tree_view)
334 {
335  auto res = w.selected_item()->describe_path();
336  for(int& a : res) { ++a;}
337  return res;
338 }
339 
340 WIDGET_GETTER("path", std::vector<int>, gui2::tree_view_node)
341 {
342  auto res = w.describe_path();
343  for(int& a : res) { ++a;}
344  return res;
345 }
346 
347 WIDGET_GETTER("unfolded", bool, gui2::tree_view)
348 {
349  return !w.get_root_node().is_folded();
350 }
351 
352 WIDGET_SETTER("value_compat,unfolded", bool, gui2::tree_view)
353 {
354  if(value) {
355  w.get_root_node().unfold();
356  } else {
357  w.get_root_node().fold();
358  }
359 }
360 
362 {
363  return !w.is_folded();
364 }
365 
366 WIDGET_SETTER("value_compat,unfolded", bool, gui2::tree_view_node)
367 {
368  if(value) {
369  w.unfold();
370  } else {
371  w.fold();
372  }
373 }
374 
376 {
377  if(const unit_type* ut = luaW_tounittype(L, value.index)) {
378  w.set_display_data(*ut);
379  } else if(unit* u = luaW_tounit(L, value.index)) {
380  w.set_display_data(*u);
381  } else {
382  luaW_type_error(L, value.index, "unit or unit type");
383  }
384 }
385 
386 WIDGET_GETTER("item_count", int, gui2::combobox)
387 {
388  return w.get_item_count();
389 }
390 
391 WIDGET_GETTER("item_count", int, gui2::listbox)
392 {
393  return w.get_item_count();
394 }
395 
396 WIDGET_GETTER("item_count", int, gui2::multi_page)
397 {
398  return w.get_page_count();
399 }
400 
402 {
403  return w.get_layer_count();
404 }
405 
406 WIDGET_GETTER("item_count", int, gui2::tree_view)
407 {
408  const gui2::tree_view_node& tvn = w.get_root_node();
409  return tvn.count_children();
410 }
411 
413 {
414  return w.count_children();
415 }
416 
417 WIDGET_GETTER("use_markup", bool, gui2::styled_widget)
418 {
419  return w.get_use_markup();
420 }
421 
422 WIDGET_SETTER("use_markup", bool, gui2::styled_widget)
423 {
424  w.set_use_markup(value);
425 }
426 
427 //TODO: while i think this shortcut is useful, i'm not that happy about
428 // the name since it changes 'label' and not 'text', the first one
429 // is the label that is part of most widgets (like checkboxes), the
430 // later is specific to input textboxes.
432 {
433  w.set_use_markup(true);
434  w.set_label(value);
435 }
436 
437 WIDGET_GETTER("characters_per_line", int, gui2::label)
438 {
439  return w.get_characters_per_line();
440 }
441 
442 WIDGET_SETTER("characters_per_line", int, gui2::label)
443 {
444  if(value < 0) {
445  throw std::invalid_argument("characters_per_line must be >= 0");
446  }
447  w.set_characters_per_line(value);
449 }
450 
451 WIDGET_GETTER("editable", bool, gui2::text_box)
452 {
453  return w.is_editable();
454 }
455 
456 WIDGET_SETTER("editable", bool, gui2::text_box)
457 {
458  w.set_editable(value);
459 }
460 
461 WIDGET_GETTER("ellipsize_mode", std::string, gui2::styled_widget)
462 {
463  return gui2::encode_ellipsize_mode(w.get_text_ellipse_mode());
464 }
465 
466 WIDGET_SETTER("ellipsize_mode", std::string, gui2::styled_widget)
467 {
468  w.set_text_ellipse_mode(gui2::decode_ellipsize_mode(value));
470 }
471 
473 {
474  return w.get_active();
475 }
476 
478 {
479  w.set_active(value);
480 }
481 
483 {
484  return w.help_message();
485 }
486 
488 {
489  w.set_help_message(value);
490 }
491 
492 WIDGET_GETTER("hint_image", std::string, gui2::combobox)
493 {
494  return w.get_hint_image();
495 }
496 
497 WIDGET_SETTER("hint_image", std::string, gui2::combobox)
498 {
499  w.set_hint_image(value);
501 }
502 
504 {
505  return w.get_hint_text();
506 }
507 
509 {
510  w.set_hint_text(value);
512 }
513 
514 WIDGET_GETTER("hint_image", std::string, gui2::text_box)
515 {
516  return w.get_hint_image();
517 }
518 
519 WIDGET_SETTER("hint_image", std::string, gui2::text_box)
520 {
521  w.set_hint_image(value);
523 }
524 
526 {
527  return w.get_hint_text();
528 }
529 
531 {
532  w.set_hint_text(value);
534 }
535 
536 WIDGET_SETTER("history", std::string, gui2::text_box)
537 {
538  w.set_history(value);
539 }
540 
541 WIDGET_GETTER("indentation_step_size", int, gui2::tree_view)
542 {
543  return w.get_indentation_step_size();
544 }
545 
546 WIDGET_SETTER("indentation_step_size", int, gui2::tree_view)
547 {
548  if(value < 0) {
549  throw std::invalid_argument("indentation_step_size must be >= 0");
550  }
551  w.set_indentation_step_size(value);
553 }
554 
555 WIDGET_GETTER("link_aware", bool, gui2::label)
556 {
557  return w.get_link_aware();
558 }
559 
560 WIDGET_SETTER("link_aware", bool, gui2::label)
561 {
562  w.set_link_aware(value);
564 }
565 
566 WIDGET_GETTER("link_aware", bool, gui2::rich_label)
567 {
568  return w.get_link_aware();
569 }
570 
571 WIDGET_SETTER("link_aware", bool, gui2::rich_label)
572 {
573  w.set_link_aware(value);
575 }
576 
577 WIDGET_GETTER("link_aware", bool, gui2::scroll_label)
578 {
579  return w.get_link_aware();
580 }
581 
582 WIDGET_SETTER("link_aware", bool, gui2::scroll_label)
583 {
584  w.set_link_aware(value);
586 }
587 
588 WIDGET_GETTER("link_aware", bool, gui2::scroll_text)
589 {
590  return w.get_link_aware();
591 }
592 
593 WIDGET_SETTER("link_aware", bool, gui2::scroll_text)
594 {
595  w.set_link_aware(value);
597 }
598 
599 WIDGET_GETTER("link_color", std::string, gui2::label)
600 {
601  return w.get_link_color().to_hex_string();
602 }
603 
604 WIDGET_GETTER("link_color", std::string, gui2::rich_label)
605 {
606  return w.get_link_color().to_hex_string();
607 }
608 
609 WIDGET_GETTER("max_input_length", int, gui2::combobox)
610 {
611  return w.get_max_input_length();
612 }
613 
614 WIDGET_SETTER("max_input_length", int, gui2::combobox)
615 {
616  if(value < 0) {
617  throw std::invalid_argument("max_input_length must be >= 0");
618  }
619  w.set_max_input_length(value);
621 }
622 
623 WIDGET_GETTER("max_input_length", int, gui2::text_box)
624 {
625  return w.get_max_input_length();
626 }
627 
628 WIDGET_SETTER("max_input_length", int, gui2::text_box)
629 {
630  if(value < 0) {
631  throw std::invalid_argument("max_input_length must be >= 0");
632  }
633  w.set_max_input_length(value);
635 }
636 
637 WIDGET_GETTER("step_size", int, gui2::slider)
638 {
639  return w.get_step_size();
640 }
641 
642 WIDGET_SETTER("step_size", int, gui2::slider)
643 {
644  if(value < 0) {
645  throw std::invalid_argument("step_size must be >= 0");
646  }
647  w.set_step_size(value);
648 }
649 
650 WIDGET_GETTER("text_alignment", std::string, gui2::styled_widget)
651 {
652  return gui2::encode_text_alignment(w.get_text_alignment());
653 }
654 
655 WIDGET_SETTER("text_alignment", std::string, gui2::styled_widget)
656 {
657  w.set_text_alignment(gui2::decode_text_alignment(value));
658 }
659 
661 {
662  return w.tooltip();
663 }
664 
666 {
667  w.set_tooltip(value);
668 }
669 
670 WIDGET_GETTER("overflow_to_tooltip", bool, gui2::styled_widget)
671 {
672  return w.get_use_tooltip_on_label_overflow();
673 }
674 
675 WIDGET_SETTER("overflow_to_tooltip", bool, gui2::styled_widget)
676 {
677  w.set_use_tooltip_on_label_overflow(value);
679 }
680 
682 {
683  return w.can_wrap();
684 }
685 
687 {
688  w.set_can_wrap(value);
689 }
690 
692 {
693  return w.can_wrap();
694 }
695 
697 {
698  w.set_can_wrap(value);
699 }
700 
702 {
703  if(!luaW_getglobal(L, "gui", "widget", "set_callback")) {
704  ERR_LUA << "gui.widget.set_callback didn't exist";
705  }
706  luaW_pushwidget(L, w);
707  lua_pushvalue(L, value.index);
708  lua_call(L, 2, 0);
709 }
710 
711 WIDGET_GETTER("visible", std::string, gui2::styled_widget)
712 {
713  std::string s;
714  switch(w.get_visible()) {
716  s = "visible";
717  break;
719  s = "hidden";
720  break;
722  s = "invisible";
723  }
724 
725  return s;
726 }
727 
728 
730 {
731 
732  typedef gui2::styled_widget::visibility visibility;
733 
734  visibility flag = visibility::visible;
735 
736  switch(lua_type(L, value.index)) {
737  case LUA_TBOOLEAN:
738  flag = luaW_toboolean(L, value.index)
739  ? visibility::visible
740  : visibility::invisible;
741  break;
742  case LUA_TSTRING:
743  {
744  const std::string& str = lua_tostring(L, value.index);
745  if(str == "visible") {
746  flag = visibility::visible;
747  } else if(str == "hidden") {
748  flag = visibility::hidden;
749  } else if(str == "invisible") {
750  flag = visibility::invisible;
751  } else {
752  luaL_argerror(L, value.index, "string must be one of: visible, hidden, invisible");
753  }
754  }
755  break;
756  default:
757  luaW_type_error(L, value.index, "boolean or string");
758  }
759 
760  w.set_visible(flag);
761 
762  if(flag == visibility::hidden) {
763  // HACK: this is needed to force the widget to be repainted immediately
764  // to get rid of its ghost image.
765  gui2::window* window = w.get_window();
766  if(window) {
767  window->invalidate_layout();
768  }
769  }
770 }
771 
773 {
774  return w.get_label();
775 }
776 
777 //must be last
779 {
780  gui2::window* window = w.get_window();
781  if(window) {
782  window->invalidate_layout();
783  }
784  w.set_label(value);
785 }
786 
787 WIDGET_GETTER("type", std::string, gui2::widget)
788 {
789  if(const gui2::styled_widget* sw = dynamic_cast<const gui2::styled_widget*>(&w)) {
790  return sw->get_control_type();
791  }
792  else if(dynamic_cast<const gui2::tree_view_node*>(&w)) {
793  return "tree_view_node";
794  }
795  else if(dynamic_cast<const gui2::grid*>(&w)) {
796  return "grid";
797  }
798  else {
799  return "";
800  }
801 }
802 
803 ///////////////////////////////////////////////////////
804 ////////////////////// CALLBACKS //////////////////////
805 ///////////////////////////////////////////////////////
806 namespace {
807 
808 void dialog_callback(lua_State* L, lua_ptr<gui2::widget>& wp, const std::string& id)
809 {
810  gui2::widget* w = wp.get_ptr();
811  if(!w) {
812  ERR_LUA << "widget was deleted";
813  return;
814  }
815  gui2::window* wd = w->get_window();
816  if(!wd) {
817  ERR_LUA << "cannot find window in widget callback";
818  return;
819  }
820  luaW_callwidgetcallback(L, w, wd, id);
821 }
822 
824 {
825  gui2::window* wd = w.get_window();
826  if(!wd) {
827  throw std::invalid_argument("the widget has no window assigned");
828  }
829  lua_pushvalue(L, value.index);
830  if (!luaW_setwidgetcallback(L, &w, wd, "on_modified")) {
831  connect_signal_notify_modified(w, std::bind(&dialog_callback, L, lua_ptr<gui2::widget>(w), "on_modified"));
832  }
833 }
834 
835 WIDGET_SETTER("on_left_click", lua_index_raw, gui2::widget)
836 {
837  gui2::window* wd = w.get_window();
838  if(!wd) {
839  throw std::invalid_argument("the widget has no window assigned");
840  }
841  lua_pushvalue(L, value.index);
842  if (!luaW_setwidgetcallback(L, &w, wd, "on_left_click")) {
843  connect_signal_mouse_left_click(w, std::bind(&dialog_callback, L, lua_ptr<gui2::widget>(w), "on_left_click"));
844  }
845 }
846 
847 WIDGET_SETTER("on_button_click", lua_index_raw, gui2::widget)
848 {
849  gui2::window* wd = w.get_window();
850  gui2::clickable_item* cl = dynamic_cast<gui2::clickable_item*>(&w);
851 
852  if(!wd) {
853  throw std::invalid_argument("the widget has no window assigned");
854  }
855  if(!cl) {
856  throw std::invalid_argument("unsupported widget");
857  }
858  lua_pushvalue(L, value.index);
859  if (!luaW_setwidgetcallback(L, &w, wd, "on_button_click")) {
860  cl->connect_click_handler(std::bind(&dialog_callback, L, lua_ptr<gui2::widget>(w), "on_button_click"));
861  }
862 }
863 
864 }
865 
866 namespace lua_widget {
867 
868 int impl_widget_get(lua_State* L)
869 {
871  if(lua_isinteger(L, 2)) {
872 
873  if(auto pwidget = find_child_by_index(w, luaL_checkinteger(L, 2))) {
874  luaW_pushwidget(L, *pwidget);
875  return 1;
876  }
877 
878  }
879  std::string_view str = lua_check<std::string_view>(L, 2);
880 
881  tgetters::iterator it = getters.find(std::string(str));
882  if(it != getters.end()) {
883  for(const auto& func : it->second) {
884  if(func(L, w, false)) {
885  return 1;
886  }
887  }
888  }
889  if(luaW_getglobal(L, "gui", "widget", std::string(str).c_str())) {
890  return 1;
891  }
892  if(auto pwidget = find_child_by_name(w, std::string(str))) {
893  luaW_pushwidget(L, *pwidget);
894  return 1;
895  }
896  ERR_LUA << "invalid property of '" << typeid(w).name()<< "' widget :" << str;
897  std::string err = "invalid property of widget: ";
898  err += str;
899  return luaL_argerror(L, 2, err.c_str());
900 }
901 
902 int impl_widget_set(lua_State* L)
903 {
905  std::string_view str = lua_check<std::string_view>(L, 2);
906 
907 
908  tsetters::iterator it = setters.find(std::string(str));
909  if(it != setters.end()) {
910  for(const auto& func : it->second) {
911  if(func(L, 3, w, false)) {
912  return 0;
913  }
914  }
915  ERR_LUA << "none of "<< it->second.size() << " setters matched";
916  }
917  else {
918  ERR_LUA << "unknown property id : " << str << " #known properties=" << setters.size();
919 
920  }
921  ERR_LUA << "invalid modifiable property of '" << typeid(w).name()<< "' widget:" << str;
922  std::string err = "invalid modifiable property of widget: ";
923  err += str;
924  return luaL_argerror(L, 2, err.c_str());
925 }
926 
927 int impl_widget_dir(lua_State* L)
928 {
930  std::vector<std::string> keys;
931  // Add any readable keys
932  for(const auto& [key, funcs] : getters) {
933  if(key == "value_compat") continue;
934  for(const auto& func : funcs) {
935  if(func(L, w, true)){
936  keys.push_back(key);
937  break;
938  }
939  }
940  }
941  // Add any writable keys
942  for(const auto& [key, funcs] : setters) {
943  if(key == "value_compat") continue;
944  if(key == "callback") continue;
945  for(const auto& func : funcs) {
946  if(func(L, 0, w, true)){
947  keys.push_back(key);
948  break;
949  }
950  }
951  }
952  // Add any nested widget IDs
954  for(auto child = iter_t(w); !child.at_end(); child.next()) {
955  const auto& key = child->id();
956  if(!key.empty() && key != w.id()) {
957  keys.push_back(key);
958  }
959  }
960  // Add the gui.widget methods
961  luaW_getglobal(L, "gui", "widget");
962  auto methods = luaW_get_attributes(L, -1);
963  keys.insert(keys.end(), methods.begin(), methods.end());
964  lua_push(L, keys);
965  return 1;
966 }
967 }
Small concept class.
virtual void connect_click_handler(const event::signal &signal)=0
Connects a signal handler for a 'click' event.
Class for a combobox.
Definition: combobox.hpp:37
Base container class.
Definition: grid.hpp:32
The iterator class.
Definition: iterator.hpp:37
The listbox class.
Definition: listbox.hpp:41
A rich_label takes marked up text and shows it correctly formatted and wrapped but no scrollbars are ...
Definition: rich_label.hpp:41
Small abstract helper class.
A widget that allows the user to input text in single line.
Definition: text_box.hpp:125
tree_view_node & get_child_at(int index)
std::size_t count_children() const
The number of children in this widget.
Base class for all widgets.
Definition: widget.hpp:55
const std::string & id() const
Definition: widget.cpp:110
visibility
Visibility settings done by the user.
Definition: widget.hpp:65
@ visible
The user sets the widget visible, that means:
@ invisible
The user set the widget invisible, that means:
@ hidden
The user sets the widget hidden, that means:
base class of top level items, the only item which needs to store the final canvases to draw on.
Definition: window.hpp:61
void invalidate_layout()
Updates the size of the window.
Definition: window.cpp:761
Tmust inherit enable_lua_ptr<T>
Definition: lua_ptr.hpp:34
T * get_ptr()
Definition: lua_ptr.hpp:37
A single unit type that the player may recruit.
Definition: types.hpp:43
This class represents a single unit of a specific type.
Definition: unit.hpp:133
std::size_t i
Definition: function.cpp:1029
int w
This file contains the window object, this object is a top level container which has the event manage...
Contains the base iterator class for the gui2 widgets.
Standard logging facilities (interface).
bool luaW_toboolean(lua_State *L, int n)
Definition: lua_common.cpp:998
int luaW_type_error(lua_State *L, int narg, const char *tname)
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:979
std::vector< std::string > luaW_get_attributes(lua_State *L, int idx)
This function does the actual work of grabbing all the attribute names.
unit * luaW_tounit(lua_State *L, int index, bool only_on_map)
Converts a Lua value to a unit pointer.
Definition: lua_unit.cpp:142
const unit_type * luaW_tounittype(lua_State *L, int idx)
Test if a stack element is a unit type, and return it if so.
void luaW_pushwidget(lua_State *L, gui2::widget &w)
Definition: lua_widget.cpp:35
gui2::widget & luaW_checkwidget(lua_State *L, int n)
Definition: lua_widget.cpp:41
bool luaW_setwidgetcallback(lua_State *L, gui2::widget *wg, gui2::window *owner, std::string_view name)
returns true if a callback already existed.
Definition: lua_widget.cpp:140
void luaW_callwidgetcallback(lua_State *L, gui2::widget *wg, gui2::window *owner, std::string_view name)
callas a widgets callback [-0, +0, e]
Definition: lua_widget.cpp:174
std::map< std::string, std::vector< std::function< bool(lua_State *, int, gui2::widget &, bool)> >> tsetters
#define WIDGET_GETTER(name, value_type, widgt_type)
#define ERR_LUA
static lg::log_domain log_scripting_lua("scripting/lua")
static gui2::widget * find_child_by_index(gui2::widget &w, int i)
#define WIDGET_SETTER(name, value_type, widgt_type)
std::map< std::string, std::vector< std::function< bool(lua_State *, gui2::widget &, bool)> >> tgetters
static tsetters setters
static gui2::widget * find_child_by_name(gui2::widget &w, const std::string &m)
void register_widget_attribute(const char *name)
static void try_invalidate_layout(gui2::widget &w)
static tgetters getters
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.cpp:203
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal &signal)
Connects a signal handler for a left mouse button click.
Definition: dispatcher.cpp:177
std::string encode_ellipsize_mode(const PangoEllipsizeMode ellipsize_mode)
Converts a PangoEllipsizeMode to its string representation.
Definition: helper.cpp:121
std::map< std::string, t_string > widget_item
Definition: widget.hpp:33
PangoAlignment decode_text_alignment(const std::string &alignment)
Converts a text alignment string to a text alignment.
Definition: helper.cpp:89
std::string encode_text_alignment(const PangoAlignment alignment)
Converts a PangoAlignment to its string representation.
Definition: helper.cpp:135
PangoEllipsizeMode decode_ellipsize_mode(const std::string &ellipsize_mode)
Converts a text ellipsize mode string to a PangoEllipsizeMode.
Definition: helper.cpp:104
logger & err()
Definition: log.cpp:307
int impl_widget_set(lua_State *L)
int impl_widget_get(lua_State *L)
int impl_widget_dir(lua_State *L)
void split_foreach(std::string_view s, char sep, const int flags, const F &f)
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
void lua_push(lua_State *L, const T &val)
Definition: push_check.hpp:425
static map_location::direction n
static map_location::direction sw
static map_location::direction s