The Battle for Wesnoth  1.19.9+dev
lua_widget_attributes.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2025
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 
823 void link_callback(lua_State* L, lua_ptr<gui2::widget>& wp, const std::string& id, const std::string& dest)
824 {
825  gui2::widget* w = wp.get_ptr();
826  if(!w) {
827  ERR_LUA << "widget was deleted";
828  return;
829  }
830  gui2::window* wd = w->get_window();
831  if(!wd) {
832  ERR_LUA << "cannot find window in widget callback";
833  return;
834  }
835  luaW_getwidgetcallback(L, w, wd, id);
836  assert(lua_isfunction(L, -1));
837  lua_pushstring(L, dest.c_str());
838  luaW_pushwidget(L, *w);
839  lua_call(L, 2, 0);
840 }
841 
843 {
844  gui2::window* wd = w.get_window();
845  if(!wd) {
846  throw std::invalid_argument("the widget has no window assigned");
847  }
848  lua_pushvalue(L, value.index);
849  if (!luaW_setwidgetcallback(L, &w, wd, "on_modified")) {
850  connect_signal_notify_modified(w, std::bind(&dialog_callback, L, lua_ptr<gui2::widget>(w), "on_modified"));
851  }
852 }
853 
855 {
856  gui2::window* wd = w.get_window();
857  if(!wd) {
858  throw std::invalid_argument("the widget has no window assigned");
859  }
860 
861  lua_pushvalue(L, value.index);
862  if (!luaW_setwidgetcallback(L, &w, wd, "on_link_click")) {
863  w.register_link_callback(
864  std::bind(&link_callback, L, lua_ptr<gui2::widget>(w), "on_link_click", std::placeholders::_1));
865  }
866 }
867 
868 WIDGET_SETTER("on_left_click", lua_index_raw, gui2::widget)
869 {
870  gui2::window* wd = w.get_window();
871  if(!wd) {
872  throw std::invalid_argument("the widget has no window assigned");
873  }
874  lua_pushvalue(L, value.index);
875  if (!luaW_setwidgetcallback(L, &w, wd, "on_left_click")) {
876  connect_signal_mouse_left_click(w, std::bind(&dialog_callback, L, lua_ptr<gui2::widget>(w), "on_left_click"));
877  }
878 }
879 
880 WIDGET_SETTER("on_button_click", lua_index_raw, gui2::widget)
881 {
882  gui2::window* wd = w.get_window();
883  gui2::clickable_item* cl = dynamic_cast<gui2::clickable_item*>(&w);
884 
885  if(!wd) {
886  throw std::invalid_argument("the widget has no window assigned");
887  }
888  if(!cl) {
889  throw std::invalid_argument("unsupported widget");
890  }
891  lua_pushvalue(L, value.index);
892  if (!luaW_setwidgetcallback(L, &w, wd, "on_button_click")) {
893  cl->connect_click_handler(std::bind(&dialog_callback, L, lua_ptr<gui2::widget>(w), "on_button_click"));
894  }
895 }
896 
897 }
898 
899 namespace lua_widget {
900 
901 int impl_widget_get(lua_State* L)
902 {
904  if(lua_isinteger(L, 2)) {
905 
906  if(auto pwidget = find_child_by_index(w, luaL_checkinteger(L, 2))) {
907  luaW_pushwidget(L, *pwidget);
908  return 1;
909  }
910 
911  }
912  std::string_view str = lua_check<std::string_view>(L, 2);
913 
914  tgetters::iterator it = getters.find(std::string(str));
915  if(it != getters.end()) {
916  for(const auto& func : it->second) {
917  if(func(L, w, false)) {
918  return 1;
919  }
920  }
921  }
922  if(luaW_getglobal(L, "gui", "widget", std::string(str).c_str())) {
923  return 1;
924  }
925  if(auto pwidget = find_child_by_name(w, std::string(str))) {
926  luaW_pushwidget(L, *pwidget);
927  return 1;
928  }
929  ERR_LUA << "invalid property of '" << typeid(w).name()<< "' widget :" << str;
930  std::string err = "invalid property of widget: ";
931  err += str;
932  return luaL_argerror(L, 2, err.c_str());
933 }
934 
935 int impl_widget_set(lua_State* L)
936 {
938  std::string_view str = lua_check<std::string_view>(L, 2);
939 
940 
941  tsetters::iterator it = setters.find(std::string(str));
942  if(it != setters.end()) {
943  for(const auto& func : it->second) {
944  if(func(L, 3, w, false)) {
945  return 0;
946  }
947  }
948  ERR_LUA << "none of "<< it->second.size() << " setters matched";
949  }
950  else {
951  ERR_LUA << "unknown property id : " << str << " #known properties=" << setters.size();
952 
953  }
954  ERR_LUA << "invalid modifiable property of '" << typeid(w).name()<< "' widget:" << str;
955  std::string err = "invalid modifiable property of widget: ";
956  err += str;
957  return luaL_argerror(L, 2, err.c_str());
958 }
959 
960 int impl_widget_dir(lua_State* L)
961 {
963  std::vector<std::string> keys;
964  // Add any readable keys
965  for(const auto& [key, funcs] : getters) {
966  if(key == "value_compat") continue;
967  for(const auto& func : funcs) {
968  if(func(L, w, true)){
969  keys.push_back(key);
970  break;
971  }
972  }
973  }
974  // Add any writable keys
975  for(const auto& [key, funcs] : setters) {
976  if(key == "value_compat") continue;
977  if(key == "callback") continue;
978  for(const auto& func : funcs) {
979  if(func(L, 0, w, true)){
980  keys.push_back(key);
981  break;
982  }
983  }
984  }
985  // Add any nested widget IDs
987  for(auto child = iter_t(w); !child.at_end(); child.next()) {
988  const auto& key = child->id();
989  if(!key.empty() && key != w.id()) {
990  keys.push_back(key);
991  }
992  }
993  // Add the gui.widget methods
994  luaW_getglobal(L, "gui", "widget");
995  auto methods = luaW_get_attributes(L, -1);
996  keys.insert(keys.end(), methods.begin(), methods.end());
997  lua_push(L, keys);
998  return 1;
999 }
1000 }
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:752
Tmust inherit enable_lua_ptr<T>
Definition: lua_ptr.hpp:45
T * get_ptr()
Definition: lua_ptr.hpp:48
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)
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.
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_getwidgetcallback(lua_State *L, gui2::widget *wg, gui2::window *owner, std::string_view name)
pushed ther callback function onoto the stack [-0, +1, -]
Definition: lua_widget.cpp:162
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)
constexpr auto keys
Definition: ranges.hpp:39
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