1 /*
2 * Copyright (C) 2006-2020 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 */
19
20 #include "scripting/lua_ui.h"
21
22 #include <SDL_mouse.h>
23
24 #include "base/macros.h"
25 #include "logic/game_controller.h"
26 #include "logic/player.h"
27 #include "scripting/globals.h"
28 #include "scripting/lua_map.h"
29 #include "scripting/luna.h"
30 #include "wui/interactive_player.h"
31
32 namespace LuaUi {
33
34 /* RST
35 :mod:`wl.ui`
36 =============
37
38 .. module:: wl.ui
39 :synopsis: Provides access on user interface. Mainly for tutorials and
40 debugging.
41
42 .. moduleauthor:: The Widelands development team
43
44 .. currentmodule:: wl.ui
45
46 .. Note::
47
48 The objects inside this module can not be persisted. That is if the player
49 tries to save the game while any of these objects are assigned to variables,
50 the game will crash. So when using these, make sure that you only create
51 objects for a short amount of time where the user can't take control to do
52 something else.
53
54 */
55
56 /*
57 * ========================================================================
58 * MODULE CLASSES
59 * ========================================================================
60 */
61
62 /* RST
63 Module Classes
64 ^^^^^^^^^^^^^^^^
65
66 */
67
68 /* RST
69 Panel
70 -----
71
72 .. class:: Panel
73
74 The Panel is the most basic ui class. Each ui element is a panel.
75 */
76 const char LuaPanel::className[] = "Panel";
77 const PropertyType<LuaPanel> LuaPanel::Properties[] = {
78 PROP_RO(LuaPanel, buttons), PROP_RO(LuaPanel, dropdowns), PROP_RO(LuaPanel, tabs),
79 PROP_RO(LuaPanel, windows), PROP_RW(LuaPanel, position_x), PROP_RW(LuaPanel, position_y),
80 PROP_RW(LuaPanel, width), PROP_RW(LuaPanel, height), {nullptr, nullptr, nullptr},
81 };
82 const MethodType<LuaPanel> LuaPanel::Methods[] = {
83 METHOD(LuaPanel, get_descendant_position), {nullptr, nullptr},
84 };
85
86 // Look for all descendant panels of class P and add the corresponding Lua version to the currently
87 // active Lua table. Class P needs to be a NamedPanel.
88 template <class P, class LuaP>
put_all_visible_panels_into_table(lua_State * L,UI::Panel * g)89 static void put_all_visible_panels_into_table(lua_State* L, UI::Panel* g) {
90 if (g == nullptr) {
91 return;
92 }
93
94 for (UI::Panel* child = g->get_first_child(); child; child = child->get_next_sibling()) {
95 put_all_visible_panels_into_table<P, LuaP>(L, child);
96
97 if (upcast(P, specific_panel, child)) {
98 if (specific_panel->is_visible()) {
99 lua_pushstring(L, specific_panel->get_name());
100 to_lua<LuaP>(L, new LuaP(specific_panel));
101 lua_rawset(L, -3);
102 }
103 }
104 }
105 }
106
107 /*
108 * Properties
109 */
110
111 /* RST
112 .. attribute:: name
113
114 (RO) The name of this panel
115 */
116
117 /* RST
118 .. attribute:: buttons
119
120 (RO) An :class:`array` of all visible buttons inside this Panel.
121 */
get_buttons(lua_State * L)122 int LuaPanel::get_buttons(lua_State* L) {
123 assert(panel_);
124
125 lua_newtable(L);
126 put_all_visible_panels_into_table<UI::Button, LuaButton>(L, panel_);
127
128 return 1;
129 }
130
131 /* RST
132 .. attribute:: dropdowns
133
134 (RO) An :class:`array` of all visible dropdowns inside this Panel.
135 */
get_dropdowns(lua_State * L)136 int LuaPanel::get_dropdowns(lua_State* L) {
137 assert(panel_);
138
139 lua_newtable(L);
140 put_all_visible_panels_into_table<UI::BaseDropdown, LuaDropdown>(L, panel_);
141
142 return 1;
143 }
144
145 /* RST
146 .. attribute:: tabs
147
148 (RO) An :class:`array` of all visible tabs inside this Panel.
149 */
put_all_tabs_into_table(lua_State * L,UI::Panel * g)150 static void put_all_tabs_into_table(lua_State* L, UI::Panel* g) {
151 if (!g) {
152 return;
153 }
154
155 for (UI::Panel* f = g->get_first_child(); f; f = f->get_next_sibling()) {
156 put_all_tabs_into_table(L, f);
157
158 if (upcast(UI::TabPanel, t, f)) {
159 for (UI::Tab* tab : t->tabs()) {
160 lua_pushstring(L, tab->get_name());
161 to_lua<LuaTab>(L, new LuaTab(tab));
162 lua_rawset(L, -3);
163 }
164 }
165 }
166 }
get_tabs(lua_State * L)167 int LuaPanel::get_tabs(lua_State* L) {
168 assert(panel_);
169
170 lua_newtable(L);
171 put_all_tabs_into_table(L, panel_);
172
173 return 1;
174 }
175
176 /* RST
177 .. attribute:: windows
178
179 (RO) A :class:`array` of all currently open windows that are
180 children of this Panel.
181 */
get_windows(lua_State * L)182 int LuaPanel::get_windows(lua_State* L) {
183 assert(panel_);
184
185 lua_newtable(L);
186 put_all_visible_panels_into_table<UI::Window, LuaWindow>(L, panel_);
187
188 return 1;
189 }
190
191 /* RST
192 .. attribute:: width, height
193
194 (RW) The dimensions of this panel in pixels
195 */
get_width(lua_State * L)196 int LuaPanel::get_width(lua_State* L) {
197 assert(panel_);
198 lua_pushint32(L, panel_->get_w());
199 return 1;
200 }
set_width(lua_State * L)201 int LuaPanel::set_width(lua_State* L) {
202 assert(panel_);
203 panel_->set_size(luaL_checkint32(L, -1), panel_->get_h());
204 return 1;
205 }
get_height(lua_State * L)206 int LuaPanel::get_height(lua_State* L) {
207 assert(panel_);
208 lua_pushint32(L, panel_->get_h());
209 return 1;
210 }
set_height(lua_State * L)211 int LuaPanel::set_height(lua_State* L) {
212 assert(panel_);
213 panel_->set_size(panel_->get_w(), luaL_checkint32(L, -1));
214 return 1;
215 }
216
217 /* RST
218 .. attribute:: position_x, position_y
219
220 (RO) The top left pixel of the our inner canvas relative to the
221 parent's element inner canvas.
222 */
get_position_x(lua_State * L)223 int LuaPanel::get_position_x(lua_State* L) {
224 assert(panel_);
225 Vector2i p = panel_->to_parent(Vector2i::zero());
226
227 lua_pushint32(L, p.x);
228 return 1;
229 }
set_position_x(lua_State * L)230 int LuaPanel::set_position_x(lua_State* L) {
231 assert(panel_);
232 Vector2i p(luaL_checkint32(L, -1) - panel_->get_lborder(), panel_->get_y());
233 panel_->set_pos(p);
234 return 1;
235 }
get_position_y(lua_State * L)236 int LuaPanel::get_position_y(lua_State* L) {
237 assert(panel_);
238 Vector2i p = panel_->to_parent(Vector2i::zero());
239
240 lua_pushint32(L, p.y);
241 return 1;
242 }
set_position_y(lua_State * L)243 int LuaPanel::set_position_y(lua_State* L) {
244 assert(panel_);
245 Vector2i p(panel_->get_x(), luaL_checkint32(L, -1) - panel_->get_tborder());
246 panel_->set_pos(p);
247 return 1;
248 }
249
250 /*
251 * Lua Functions
252 */
253 /* RST
254 .. method:: get_descendant_position(child)
255
256 Get the child position relative to the inner canvas of this Panel in
257 pixels. Throws an error if child is not really a child.
258
259 :arg child: children to get position for
260 :type child: :class:`Panel`
261
262 :returns: x, y
263 :rtype: both are :class:`integers`
264 */
get_descendant_position(lua_State * L)265 int LuaPanel::get_descendant_position(lua_State* L) {
266 assert(panel_);
267
268 UI::Panel* cur = (*get_base_user_class<LuaPanel>(L, 2))->panel_;
269
270 Vector2i cp = Vector2i::zero();
271 while (cur && cur != panel_) {
272 cp += cur->to_parent(Vector2i::zero());
273 cur = cur->get_parent();
274 }
275
276 if (!cur) {
277 report_error(L, "Widget is not a descendant!");
278 }
279
280 lua_pushint32(L, cp.x);
281 lua_pushint32(L, cp.y);
282 return 2;
283 }
284
285 /*
286 * C Functions
287 */
288
289 /* RST
290 Button
291 ------
292
293 .. class:: Button
294
295 Child of: :class:`Panel`
296
297 This represents a simple push button.
298 */
299 const char LuaButton::className[] = "Button";
300 const MethodType<LuaButton> LuaButton::Methods[] = {
301 METHOD(LuaButton, press), METHOD(LuaButton, click), {nullptr, nullptr},
302 };
303 const PropertyType<LuaButton> LuaButton::Properties[] = {
304 PROP_RO(LuaButton, name), {nullptr, nullptr, nullptr},
305 };
306
307 /*
308 * Properties
309 */
310
311 // Documented in parent Class
get_name(lua_State * L)312 int LuaButton::get_name(lua_State* L) {
313 lua_pushstring(L, get()->get_name());
314 return 1;
315 }
316
317 /*
318 * Lua Functions
319 */
320 /* RST
321 .. method:: press
322
323 Press and hold this button. This is mainly to visualize a pressing
324 event in tutorials
325 */
press(lua_State *)326 int LuaButton::press(lua_State* /* L */) {
327 log("Pressing button '%s'\n", get()->get_name().c_str());
328 get()->handle_mousein(true);
329 get()->handle_mousepress(SDL_BUTTON_LEFT, 1, 1);
330 return 0;
331 }
332 /* RST
333 .. method:: click
334
335 Click this button just as if the user would have moused over and clicked
336 it.
337 */
click(lua_State *)338 int LuaButton::click(lua_State* /* L */) {
339 log("Clicking button '%s'\n", get()->get_name().c_str());
340 get()->handle_mousein(true);
341 get()->handle_mousepress(SDL_BUTTON_LEFT, 1, 1);
342 get()->handle_mouserelease(SDL_BUTTON_LEFT, 1, 1);
343 return 0;
344 }
345
346 /*
347 * C Functions
348 */
349
350 /* RST
351 Dropdown
352 --------
353
354 .. class:: Dropdown
355
356 Child of: :class:`Panel`
357
358 This represents a dropdown menu.
359 */
360 const char LuaDropdown::className[] = "Dropdown";
361 const MethodType<LuaDropdown> LuaDropdown::Methods[] = {
362 METHOD(LuaDropdown, open),
363 METHOD(LuaDropdown, highlight_item),
364 METHOD(LuaDropdown, select),
365 {nullptr, nullptr},
366 };
367 const PropertyType<LuaDropdown> LuaDropdown::Properties[] = {
368 PROP_RO(LuaDropdown, name), PROP_RO(LuaDropdown, no_of_items), {nullptr, nullptr, nullptr},
369 };
370
371 /*
372 * Properties
373 */
374
375 // Documented in parent Class
get_name(lua_State * L)376 int LuaDropdown::get_name(lua_State* L) {
377 lua_pushstring(L, get()->get_name());
378 return 1;
379 }
380
381 /* RST
382 .. attribute:: no_of_items
383
384 (RO) The number of items his dropdown has.
385 */
get_no_of_items(lua_State * L)386 int LuaDropdown::get_no_of_items(lua_State* L) {
387 lua_pushinteger(L, get()->size());
388 return 1;
389 }
390
391 /*
392 * Lua Functions
393 */
394 /* RST
395 .. method:: open
396
397 Open this dropdown menu.
398 */
open(lua_State *)399 int LuaDropdown::open(lua_State* /* L */) {
400 log("Opening dropdown '%s'\n", get()->get_name().c_str());
401 get()->set_list_visibility(true);
402 return 0;
403 }
404
405 /* RST
406 .. method:: highlight_item(index)
407
408 :arg index: the index of the item to highlight, starting from ``1``
409 :type index: :class:`integer`
410
411 Highlights an item in this dropdown without triggering a selection.
412 */
highlight_item(lua_State * L)413 int LuaDropdown::highlight_item(lua_State* L) {
414 unsigned int desired_item = luaL_checkuint32(L, -1);
415 if (desired_item < 1 || desired_item > get()->size()) {
416 report_error(L, "Attempted to highlight item %d on dropdown '%s'. Avaliable range for this "
417 "dropdown is 1-%d.",
418 desired_item, get()->get_name().c_str(), get()->size());
419 }
420 log("Highlighting item %d in dropdown '%s'\n", desired_item, get()->get_name().c_str());
421 // Open the dropdown
422 get()->set_list_visibility(true);
423
424 SDL_Keysym code;
425 // Ensure that we're at the top
426 code.sym = SDLK_UP;
427 for (size_t i = 1; i < get()->size(); ++i) {
428 get()->handle_key(true, code);
429 }
430 // Press arrow down until the desired item is highlighted
431 code.sym = SDLK_DOWN;
432 for (size_t i = 1; i < desired_item; ++i) {
433 get()->handle_key(true, code);
434 }
435 return 0;
436 }
437
438 /* RST
439 .. method:: select()
440
441 Selects the currently highlighted item in this dropdown.
442 */
select(lua_State *)443 int LuaDropdown::select(lua_State* /* L */) {
444 log("Selecting current item in dropdown '%s'\n", get()->get_name().c_str());
445 SDL_Keysym code;
446 code.sym = SDLK_RETURN;
447 get()->handle_key(true, code);
448 return 0;
449 }
450
451 /*
452 * C Functions
453 */
454
455 /* RST
456 Tab
457 ------
458
459 .. class:: Tab
460
461 Child of: :class:`Panel`
462
463 A tab button.
464 */
465 const char LuaTab::className[] = "Tab";
466 const MethodType<LuaTab> LuaTab::Methods[] = {
467 METHOD(LuaTab, click), {nullptr, nullptr},
468 };
469 const PropertyType<LuaTab> LuaTab::Properties[] = {
470 PROP_RO(LuaTab, name), PROP_RO(LuaTab, active), {nullptr, nullptr, nullptr},
471 };
472
473 /*
474 * Properties
475 */
476
477 // Documented in parent Class
get_name(lua_State * L)478 int LuaTab::get_name(lua_State* L) {
479 lua_pushstring(L, get()->get_name());
480 return 1;
481 }
482
483 /* RST
484 .. attribute:: active
485
486 (RO) Is this the currently active tab in this window?
487 */
get_active(lua_State * L)488 int LuaTab::get_active(lua_State* L) {
489 lua_pushboolean(L, get()->active());
490 return 1;
491 }
492
493 /*
494 * Lua Functions
495 */
496 /* RST
497 .. method:: click
498
499 Click this tab making it the active one.
500 */
click(lua_State *)501 int LuaTab::click(lua_State* /* L */) {
502 log("Clicking tab '%s'\n", get()->get_name().c_str());
503 get()->activate();
504 return 0;
505 }
506
507 /*
508 * C Functions
509 */
510
511 /* RST
512 Window
513 ------
514
515 .. class:: Window
516
517 Child of: :class:`Panel`
518
519 This represents a Window.
520 */
521 const char LuaWindow::className[] = "Window";
522 const MethodType<LuaWindow> LuaWindow::Methods[] = {
523 METHOD(LuaWindow, close), {nullptr, nullptr},
524 };
525 const PropertyType<LuaWindow> LuaWindow::Properties[] = {
526 PROP_RO(LuaWindow, name), {nullptr, nullptr, nullptr},
527 };
528
529 /*
530 * Properties
531 */
532
533 // Documented in parent Class
get_name(lua_State * L)534 int LuaWindow::get_name(lua_State* L) {
535 lua_pushstring(L, get()->get_name());
536 return 1;
537 }
538
539 /*
540 * Lua Functions
541 */
542
543 /* RST
544 .. method:: close
545
546 Closes this window. This invalidates this Object, do
547 not use it any longer.
548 */
close(lua_State *)549 int LuaWindow::close(lua_State* /* L */) {
550 log("Closing window '%s'\n", get()->get_name().c_str());
551 delete panel_;
552 panel_ = nullptr;
553 return 0;
554 }
555
556 /*
557 * C Functions
558 */
559
560 /* RST
561 MapView
562 -------
563
564 .. class:: MapView
565
566 Child of :class:`Panel`
567
568 The map view is the main widget and the root of all panels. It is the big
569 view of the map that is visible at all times while playing.
570 */
571 const char LuaMapView::className[] = "MapView";
572 const MethodType<LuaMapView> LuaMapView::Methods[] = {
573 METHOD(LuaMapView, click),
574 METHOD(LuaMapView, start_road_building),
575 METHOD(LuaMapView, abort_road_building),
576 METHOD(LuaMapView, close),
577 METHOD(LuaMapView, scroll_to_field),
578 METHOD(LuaMapView, scroll_to_map_pixel),
579 METHOD(LuaMapView, is_visible),
580 METHOD(LuaMapView, mouse_to_field),
581 METHOD(LuaMapView, mouse_to_pixel),
582 {nullptr, nullptr},
583 };
584 const PropertyType<LuaMapView> LuaMapView::Properties[] = {
585 PROP_RO(LuaMapView, average_fps), PROP_RO(LuaMapView, center_map_pixel),
586 PROP_RW(LuaMapView, buildhelp), PROP_RW(LuaMapView, census),
587 PROP_RW(LuaMapView, statistics), PROP_RO(LuaMapView, is_building_road),
588 PROP_RO(LuaMapView, is_animating), {nullptr, nullptr, nullptr},
589 };
590
LuaMapView(lua_State * L)591 LuaMapView::LuaMapView(lua_State* L) : LuaPanel(get_egbase(L).get_ibase()) {
592 }
593
__unpersist(lua_State * L)594 void LuaMapView::__unpersist(lua_State* L) {
595 Widelands::Game& game = get_game(L);
596 panel_ = game.get_ibase();
597 }
598
599 /*
600 * Properties
601 */
602 /* RST
603 .. attribute:: average_fps
604
605 (RO) The average frames per second that the user interface is being drawn at.
606 */
get_average_fps(lua_State * L)607 int LuaMapView::get_average_fps(lua_State* L) {
608 lua_pushdouble(L, get()->average_fps());
609 return 1;
610 }
611 /* RST
612 .. attribute:: center_map_pixel
613
614 (RO) The map position (in pixels) that the center pixel of this map view
615 currently sees. This is a table containing 'x', 'y'.
616 */
get_center_map_pixel(lua_State * L)617 int LuaMapView::get_center_map_pixel(lua_State* L) {
618 const Vector2f center = get()->map_view()->view_area().rect().center();
619 lua_newtable(L);
620
621 lua_pushstring(L, "x");
622 lua_pushdouble(L, center.x);
623 lua_rawset(L, -3);
624
625 lua_pushstring(L, "y");
626 lua_pushdouble(L, center.y);
627 lua_rawset(L, -3);
628 return 1;
629 }
630
631 /* RST
632 .. attribute:: buildhelp
633
634 (RW) True if the buildhelp is show, false otherwise.
635 */
get_buildhelp(lua_State * L)636 int LuaMapView::get_buildhelp(lua_State* L) {
637 lua_pushboolean(L, get()->buildhelp());
638 return 1;
639 }
set_buildhelp(lua_State * L)640 int LuaMapView::set_buildhelp(lua_State* L) {
641 get()->show_buildhelp(luaL_checkboolean(L, -1));
642 return 0;
643 }
644
645 /* RST
646 .. attribute:: census
647
648 (RW) True if the census strings are shown on buildings, false otherwise
649 */
get_census(lua_State * L)650 int LuaMapView::get_census(lua_State* L) {
651 lua_pushboolean(L, get()->get_display_flag(InteractiveBase::dfShowCensus));
652 return 1;
653 }
set_census(lua_State * L)654 int LuaMapView::set_census(lua_State* L) {
655 get()->set_display_flag(InteractiveBase::dfShowCensus, luaL_checkboolean(L, -1));
656 return 0;
657 }
658
659 /* RST
660 .. attribute:: statistics
661
662 (RW) True if the statistics strings are shown on buildings, false
663 otherwise
664 */
get_statistics(lua_State * L)665 int LuaMapView::get_statistics(lua_State* L) {
666 lua_pushboolean(L, get()->get_display_flag(InteractiveBase::dfShowStatistics));
667 return 1;
668 }
set_statistics(lua_State * L)669 int LuaMapView::set_statistics(lua_State* L) {
670 get()->set_display_flag(InteractiveBase::dfShowStatistics, luaL_checkboolean(L, -1));
671 return 0;
672 }
673
674 /* RST
675 .. attribute:: is_building_road
676
677 (RO) Is the player currently in road/waterway building mode?
678 */
get_is_building_road(lua_State * L)679 int LuaMapView::get_is_building_road(lua_State* L) {
680 lua_pushboolean(L, get()->in_road_building_mode());
681 return 1;
682 }
683
684 /* RST
685 .. attribute:: is_animating
686
687 (RO) True if this MapView is currently panning or zooming.
688 */
get_is_animating(lua_State * L)689 int LuaMapView::get_is_animating(lua_State* L) {
690 lua_pushboolean(L, get()->map_view()->is_animating());
691 return 1;
692 }
693 /*
694 * Lua Functions
695 */
696
697 /* RST
698 .. method:: click(field)
699
700 Jumps the mouse onto a field and clicks it just like the user would
701 have.
702
703 :arg field: the field to click on
704 :type field: :class:`wl.map.Field`
705 */
click(lua_State * L)706 int LuaMapView::click(lua_State* L) {
707 const auto field = *get_user_class<LuaMaps::LuaField>(L, 2);
708 get()->map_view()->mouse_to_field(field->coords(), MapView::Transition::Jump);
709
710 // We fake the triangle here, since we only support clicking on Nodes from
711 // Lua.
712 Widelands::NodeAndTriangle<> node_and_triangle{
713 field->coords(), Widelands::TCoords<>(field->coords(), Widelands::TriangleIndex::D)};
714 get()->map_view()->field_clicked(node_and_triangle);
715 return 0;
716 }
717
718 /* RST
719 .. method:: start_road_building(flag[, waterway = false])
720
721 Enters the road building mode as if the player has clicked
722 the flag and chosen build road. It will also warp the mouse
723 to the given starting node. Throws an error if we are already in road
724 building mode.
725
726 :arg flag: :class:`wl.map.Flag` object to start building from.
727 :arg waterway: if `true`, start building a waterway rather than a road
728 */
729 // UNTESTED
start_road_building(lua_State * L)730 int LuaMapView::start_road_building(lua_State* L) {
731 InteractiveBase* me = get();
732 if (me->in_road_building_mode()) {
733 report_error(L, "Already building road!");
734 }
735
736 Widelands::Coords starting_field =
737 (*get_user_class<LuaMaps::LuaFlag>(L, 2))->get(L, get_egbase(L))->get_position();
738
739 me->map_view()->mouse_to_field(starting_field, MapView::Transition::Jump);
740 me->start_build_road(starting_field, me->get_player()->player_number(),
741 lua_gettop(L) > 2 && luaL_checkboolean(L, 3) ? RoadBuildingType::kWaterway :
742 RoadBuildingType::kRoad);
743
744 return 0;
745 }
746
747 /* RST
748 .. method:: abort_road_building
749
750 If the player is currently in road building mode, this will cancel it.
751 If he wasn't, this will do nothing.
752 */
753 // UNTESTED
abort_road_building(lua_State *)754 int LuaMapView::abort_road_building(lua_State* /* L */) {
755 InteractiveBase* me = get();
756 if (me->in_road_building_mode()) {
757 me->abort_build_road();
758 }
759 return 0;
760 }
761
762 /* RST
763 .. method:: close
764
765 Closes the MapView. Note that this is the equivalent as clicking on
766 the exit button in the game; that is the game will be exited.
767
768 This is especially useful for automated testing of features and is for
769 example used in the widelands Lua test suite.
770 */
close(lua_State *)771 int LuaMapView::close(lua_State* /* l */) {
772 get()->end_modal<UI::Panel::Returncodes>(UI::Panel::Returncodes::kBack);
773 return 0;
774 }
775
776 /* RST
777 .. method:: scroll_to_map_pixel(x, y)
778
779 Starts an animation to center the view on top of the pixel (x, y) in map
780 pixel space. Use `is_animating` to check if the animation is still going
781 on.
782
783 :arg x: x coordinate of the pixel
784 :type x: number
785 :arg y: y coordinate of the pixel
786 :type y: number
787 */
scroll_to_map_pixel(lua_State * L)788 int LuaMapView::scroll_to_map_pixel(lua_State* L) {
789 Widelands::Game& game = get_game(L);
790 // don't move view in replays
791 if (game.game_controller()->get_game_type() == GameController::GameType::kReplay) {
792 return 0;
793 }
794
795 const Vector2f center(luaL_checkdouble(L, 2), luaL_checkdouble(L, 3));
796 get()->map_view()->scroll_to_map_pixel(center, MapView::Transition::Smooth);
797 return 0;
798 }
799
800 /* RST
801 .. method:: scroll_to_field(field)
802
803 Starts an animation to center the view on top of the 'field'. Use
804 `is_animating` to check if the animation is still going on.
805
806 :arg field: the field to center on
807 :type field: :class:`wl.map.Field`
808 */
scroll_to_field(lua_State * L)809 int LuaMapView::scroll_to_field(lua_State* L) {
810 get()->map_view()->scroll_to_field(
811 (*get_user_class<LuaMaps::LuaField>(L, 2))->coords(), MapView::Transition::Smooth);
812 return 0;
813 }
814
815 /* RST
816 .. method:: is_visible(field)
817
818 Returns `true` if `field` is currently visible in the map view.
819
820 :arg field: the field
821 :type field: :class:`wl.map.Field`
822 */
is_visible(lua_State * L)823 int LuaMapView::is_visible(lua_State* L) {
824 lua_pushboolean(L, get()->map_view()->view_area().contains(
825 (*get_user_class<LuaMaps::LuaField>(L, 2))->coords()));
826 return 1;
827 }
828
829 /* RST
830 .. method:: mouse_to_pixel(x, y)
831
832 Starts an animation to move the mouse onto the pixel (x, y) of this panel.
833 Use `is_animating` to check if the animation is still going on.
834
835 :arg x: x coordinate of the pixel
836 :type x: number
837 :arg y: y coordinate of the pixel
838 :type y: number
839 */
mouse_to_pixel(lua_State * L)840 int LuaMapView::mouse_to_pixel(lua_State* L) {
841 int x = luaL_checkint32(L, 2);
842 int y = luaL_checkint32(L, 3);
843 get()->map_view()->mouse_to_pixel(Vector2i(x, y), MapView::Transition::Smooth);
844 return 0;
845 }
846
847 /* RST
848 .. method:: mouse_to_field(field)
849
850 Starts an animation to move the mouse onto the 'field'. If 'field' is not
851 visible on the screen currently, does nothing. Use `is_animating` to
852 check if the animation is still going on.
853
854 :arg field: the field
855 :type field: :class:`wl.map.Field`
856 */
mouse_to_field(lua_State * L)857 int LuaMapView::mouse_to_field(lua_State* L) {
858 get()->map_view()->mouse_to_field(
859 (*get_user_class<LuaMaps::LuaField>(L, 2))->coords(), MapView::Transition::Smooth);
860 return 0;
861 }
862
863 /*
864 * C Functions
865 */
866
867 /*
868 * ========================================================================
869 * MODULE FUNCTIONS
870 * ========================================================================
871 */
872
873 /* RST
874 .. function:: set_user_input_allowed(b)
875
876 Allow or disallow user input. Be warned, setting this will make that
877 mouse movements and keyboard presses are completely ignored. Only
878 scripted stuff will still happen.
879
880 :arg b: :const:`true` or :const:`false`
881 :type b: :class:`boolean`
882 */
L_set_user_input_allowed(lua_State * L)883 static int L_set_user_input_allowed(lua_State* L) {
884 UI::Panel::set_allow_user_input(luaL_checkboolean(L, -1));
885 return 0;
886 }
887 /* RST
888 .. method:: get_user_input_allowed
889
890 Return the current state of this flag.
891
892 :returns: :const:`true` or :const:`false`
893 :rtype: :class:`boolean`
894 */
L_get_user_input_allowed(lua_State * L)895 static int L_get_user_input_allowed(lua_State* L) {
896 lua_pushboolean(L, UI::Panel::allow_user_input());
897 return 1;
898 }
899
900 const static struct luaL_Reg wlui[] = {{"set_user_input_allowed", &L_set_user_input_allowed},
901 {"get_user_input_allowed", &L_get_user_input_allowed},
902 {nullptr, nullptr}};
903
luaopen_wlui(lua_State * L)904 void luaopen_wlui(lua_State* L) {
905 lua_getglobal(L, "wl"); // S: wl_table
906 lua_pushstring(L, "ui"); // S: wl_table "ui"
907 luaL_newlib(L, wlui); // S: wl_table "ui" wl.ui_table
908 lua_settable(L, -3); // S: wl_table
909 lua_pop(L, 1); // S:
910
911 register_class<LuaPanel>(L, "ui");
912
913 register_class<LuaButton>(L, "ui", true);
914 add_parent<LuaButton, LuaPanel>(L);
915 lua_pop(L, 1); // Pop the meta table
916
917 register_class<LuaDropdown>(L, "ui", true);
918 add_parent<LuaDropdown, LuaPanel>(L);
919 lua_pop(L, 1); // Pop the meta table
920
921 register_class<LuaTab>(L, "ui", true);
922 add_parent<LuaTab, LuaPanel>(L);
923 lua_pop(L, 1); // Pop the meta table
924
925 register_class<LuaWindow>(L, "ui", true);
926 add_parent<LuaWindow, LuaPanel>(L);
927 lua_pop(L, 1); // Pop the meta table
928
929 register_class<LuaMapView>(L, "ui", true);
930 add_parent<LuaMapView, LuaPanel>(L);
931 lua_pop(L, 1); // Pop the meta table
932 }
933 } // namespace LuaUi
934