The Battle for Wesnoth  1.15.12+dev
lua_gui2.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2018 by Chris Beck <render787@gmail.com>
3  Part of the Battle for Wesnoth Project 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 "scripting/lua_gui2.hpp"
16 
25 #include "gui/dialogs/message.hpp"
26 #include "gui/widgets/retval.hpp"
28 #include "scripting/lua_widget_methods.hpp" //intf_show_dialog
29 
30 #include "config.hpp"
31 #include "log.hpp"
32 #include "scripting/lua_common.hpp"
36 #include "scripting/push_check.hpp"
38 #include "tstring.hpp"
39 #include "game_data.hpp"
40 #include "game_state.hpp"
41 
42 #include <functional>
43 #include <optional>
44 
45 #include <map>
46 #include <utility>
47 #include <vector>
48 
49 #include "lua/lauxlib.h" // for luaL_checkinteger, etc
50 #include "lua/lua.h" // for lua_setfield, etc
51 
52 static lg::log_domain log_scripting_lua("scripting/lua");
53 #define ERR_LUA LOG_STREAM(err, log_scripting_lua)
54 
55 namespace lua_gui2 {
56 
57 
58 /**
59  * Displays a message window
60  * - Arg 1: Table describing the window
61  * - Arg 2: List of options (nil or empty table - no options)
62  * - Arg 3: Text input specifications (nil or empty table - no text input)
63  * - Ret 1: option chosen (if no options: 0 if there's text input, -2 if escape pressed, else -1)
64  * - Ret 2: string entered (empty if none, nil if no text input)
65  */
67 {
68  config txt_cfg;
69  const bool has_input = !lua_isnoneornil(L, 3) && luaW_toconfig(L, 3, txt_cfg) && !txt_cfg.empty();
70 
72  input.caption = txt_cfg["label"].str();
73  input.text = txt_cfg["text"].str();
74  input.maximum_length = txt_cfg["max_length"].to_int(256);
75  input.text_input_was_specified = has_input;
76 
78  if(!lua_isnoneornil(L, 2)) {
80  std::size_t n = lua_rawlen(L, 2);
81  for(std::size_t i = 1; i <= n; i++) {
82  lua_rawgeti(L, 2, i);
83  t_string short_opt;
84  config opt;
85  if(luaW_totstring(L, -1, short_opt)) {
86  opt["description"] = short_opt;
87  } else if(!luaW_toconfig(L, -1, opt)) {
88  std::ostringstream error;
89  error << "expected array of config and/or translatable strings, but index ";
90  error << i << " was a " << lua_typename(L, lua_type(L, -1));
91  return luaL_argerror(L, 2, error.str().c_str());
92  }
93  gui2::dialogs::wml_message_option option(opt["label"], opt["description"], opt["image"]);
94  if(opt["default"].to_bool(false)) {
95  options.chosen_option = i - 1;
96  }
97  options.option_list.push_back(option);
98  lua_pop(L, 1);
99  }
100  lua_getfield(L, 2, "default");
101  if(lua_isnumber(L, -1)) {
102  int i = lua_tointeger(L, -1);
103  if(i < 1 || std::size_t(i) > n) {
104  std::ostringstream error;
105  error << "default= key in options list is not a valid option index (1-" << n << ")";
106  return luaL_argerror(L, 2, error.str().c_str());
107  }
108  options.chosen_option = i - 1;
109  }
110  lua_pop(L, 1);
111  }
112 
113  const config& def_cfg = luaW_checkconfig(L, 1);
114  const std::string& title = def_cfg["title"];
115  const std::string& message = def_cfg["message"];
116 
117  using portrait = gui2::dialogs::wml_message_portrait;
118  std::unique_ptr<portrait> left;
119  std::unique_ptr<portrait> right;
120  const bool is_double = def_cfg.has_attribute("second_portrait");
121  const bool left_side = def_cfg["left_side"].to_bool(true);
122  if(is_double || left_side) {
123  left.reset(new portrait {def_cfg["portrait"], def_cfg["mirror"].to_bool(false)});
124  } else {
125  // This means right side only.
126  right.reset(new portrait {def_cfg["portrait"], def_cfg["mirror"].to_bool(false)});
127  }
128  if(is_double) {
129  right.reset(new portrait {def_cfg["second_portrait"], def_cfg["second_mirror"].to_bool(false)});
130  }
131 
132  int dlg_result = gui2::dialogs::show_wml_message(title, message, left.get(), right.get(), options, input);
133 
134  if(!has_input && options.option_list.empty()) {
135  lua_pushinteger(L, dlg_result);
136  } else {
137  lua_pushinteger(L, options.chosen_option + 1);
138  }
139 
140  if(has_input) {
141  lua_pushlstring(L, input.text.c_str(), input.text.length());
142  } else {
143  lua_pushnil(L);
144  }
145 
146  return 2;
147 }
148 
149 /**
150  * Displays a popup message
151  * - Arg 1: Title (allows Pango markup)
152  * - Arg 2: Message (allows Pango markup)
153  * - Arg 3: Image (optional)
154  */
156  t_string title = luaW_checktstring(L, 1);
158  std::string image = lua_isnoneornil(L, 3) ? "" : luaL_checkstring(L, 3);
159 
160  gui2::show_transient_message(title, msg, image, true, true);
161  return 0;
162 }
163 
164 /**
165  * Displays a story screen
166  * - Arg 1: The story config
167  * - Arg 2: The default title
168  */
170  config story = luaW_checkconfig(L, 1);
171  t_string title = luaW_checktstring(L, 2);
173  return 0;
174 }
175 
176 /**
177  * Displays a popup menu at the current mouse position
178  * Best used from a [set_menu_item], to show a submenu
179  * - Arg 1: Configs defining each item, with keys icon, image/label, second_label, tooltip
180  * - Args 2, 3: Initial selection (integer); whether to parse markup (boolean)
181  */
183  std::vector<config> items = lua_check<std::vector<config>>(L, 1);
184  SDL_Rect pos {1,1,1,1};
185  SDL_GetMouseState(&pos.x, &pos.y);
186 
187  int initial = -1;
188  bool markup = false;
189  if(lua_isnumber(L, 2)) {
190  initial = lua_tointeger(L, 2) - 1;
191  markup = luaW_toboolean(L, 3);
192  } else if(lua_isnumber(L, 3)) {
193  initial = lua_tointeger(L, 3) - 1;
194  markup = luaW_toboolean(L, 2);
195  } else if(lua_isboolean(L, 2)) {
196  markup = luaW_toboolean(L, 2);
197  }
198 
199  gui2::dialogs::drop_down_menu menu(pos, items, initial, markup, false);
200  menu.show();
201  lua_pushinteger(L, menu.selected_item() + 1);
202  return 1;
203 }
204 
205 /**
206  * Displays a simple message box.
207  */
209  const t_string title = luaW_checktstring(L, 1), message = luaW_checktstring(L, 2);
210  std::string button = luaL_optstring(L, 3, "ok"), btn_style;
211  std::transform(button.begin(), button.end(), std::inserter(btn_style, btn_style.begin()), [](char c) { return std::tolower(c); });
212  bool markup = lua_isnoneornil(L, 3) ? luaW_toboolean(L, 3) : luaW_toboolean(L, 4);
213  using button_style = gui2::dialogs::message::button_style;
214  std::optional<button_style> style;
215  if(btn_style.empty()) {
216  style = button_style::auto_close;
217  } else if(btn_style == "ok") {
218  style = button_style::ok_button;
219  } else if(btn_style == "close") {
220  style = button_style::close_button;
221  } else if(btn_style == "ok_cancel") {
222  style = button_style::ok_cancel_buttons;
223  } else if(btn_style == "cancel") {
224  style = button_style::cancel_button;
225  } else if(btn_style == "yes_no") {
226  style = button_style::yes_no_buttons;
227  }
228  if(style) {
229  int result = gui2::show_message(title, message, *style, markup, markup);
230  if(style == button_style::ok_cancel_buttons || style == button_style::yes_no_buttons) {
231  lua_pushboolean(L, result == gui2::retval::OK);
232  return 1;
233  }
234  } else {
235  gui2::show_message(title, message, button, false, markup, markup);
236  }
237  return 0;
238 }
239 
241 {
243  return 0;
244 }
245 
246 int show_gamestate_inspector(const vconfig& cfg, const game_data& data, const game_state& state)
247 {
248  gui2::dialogs::gamestate_inspector::display(data.get_variables(), *state.events_manager_, state.board_, cfg["name"]);
249  return 0;
250 }
251 
252 
253 /**
254  * - Arg 1: string, widget type
255  * - Arg 3: string, id
256  * - Arg 3: config,
257  */
258 
260 {
261  std::string type = luaL_checkstring(L, 1);
262  std::string id = luaL_checkstring(L, 2);
263  try {
265  lua_kernel_base::get_lua_kernel<lua_kernel_base>(L).add_widget_definition(type, id);
266  }
267  } catch(const std::invalid_argument& e) {
268  return luaL_argerror(L, 1, e.what());
269  }
270  return 0;
271 }
272 
274 {
275  auto& lk = lua_kernel_base::get_lua_kernel<lua_kernel_base>(L);
276  lk.add_log("Adding gui module...\n");
277  static luaL_Reg const gui_callbacks[] = {
278  { "show_menu", &show_menu },
279  { "show_narration", &show_message_dialog },
280  { "show_popup", &show_popup_dialog },
281  { "show_story", &show_story },
282  { "show_prompt", &show_message_box },
283  { "add_widget_definition", &intf_add_widget_definition },
284  { "show_dialog", &intf_show_dialog },
285  { nullptr, nullptr },
286  };
287  std::vector<lua_cpp::Reg> const cpp_gui_callbacks {
288  {"show_lua_console", std::bind(&lua_kernel_base::intf_show_lua_console, &lk, std::placeholders::_1)},
289  {nullptr, nullptr}
290  };
291  lua_newtable(L);
292  luaL_setfuncs(L, gui_callbacks, 0);
293  lua_cpp::set_functions(L, cpp_gui_callbacks);
294 
295  lua_pushstring(L, "widget");
297  lua_rawset(L, -3);
298 
299  return 1;
300 }
301 
302 } // end namespace lua_gui2
#define lua_isnoneornil(L, n)
Definition: lua.h:379
Parameter pack for message portrait.
void show_message(const std::string &title, const std::string &msg, const std::string &button_caption, const bool auto_close, const bool message_use_markup, const bool title_use_markup)
Shows a message to the user.
Definition: message.cpp:152
int show_lua_console(lua_State *, lua_kernel_base *lk)
Definition: lua_gui2.cpp:240
bool text_input_was_specified
True when [text_input] appeared in [message].
static void display(const std::string &scenario_name, const config &story)
int chosen_option
The initially chosen option.
int show_story(lua_State *L)
Displays a story screen.
Definition: lua_gui2.cpp:169
LUA_API int lua_type(lua_State *L, int idx)
Definition: lapi.cpp:260
std::string caption
The caption for the optional input text box.
LUA_API void lua_pushboolean(lua_State *L, int b)
Definition: lapi.cpp:581
LUA_API int lua_rawgeti(lua_State *L, int idx, lua_Integer n)
Definition: lapi.cpp:710
std::string text
The initial text value.
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:40
bool has_attribute(config_key_type key) const
Definition: config.cpp:207
void show_transient_message(const std::string &title, const std::string &message, const std::string &image, const bool message_use_markup, const bool title_use_markup, const bool restore_background)
Shows a transient message to the user.
button_style
Selects the style of the buttons to be shown.
Definition: message.hpp:68
#define lua_tointeger(L, i)
Definition: lua.h:362
Parameter pack for message list input options.
unsigned maximum_length
The maximum length of the text.
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
const std::vector< std::string > items
const config & get_variables() const
Definition: game_data.hpp:33
int show_message_dialog(lua_State *L)
Displays a message window.
Definition: lua_gui2.cpp:66
bool show(const unsigned auto_close_time=0)
Shows the window.
Definitions for the interface to Wesnoth Markup Language (WML).
int luaW_open(lua_State *L)
Definition: lua_gui2.cpp:273
int intf_add_widget_definition(lua_State *L)
Definition: lua_gui2.cpp:259
const std::unique_ptr< game_events::manager > events_manager_
Definition: game_state.hpp:52
#define lua_pop(L, n)
Definition: lua.h:364
static void display(lua_kernel_base *lk)
Display a new console, using given video and lua kernel.
const config & options()
Definition: game.cpp:563
int show_gamestate_inspector(const vconfig &cfg, const game_data &data, const game_state &state)
Definition: lua_gui2.cpp:246
LUALIB_API int luaL_argerror(lua_State *L, int arg, const char *extramsg)
Definition: lauxlib.cpp:175
int intf_show_dialog(lua_State *L)
Displays a window.
bool luaW_toboolean(lua_State *L, int n)
Definition: lua_common.cpp:893
int intf_show_lua_console(lua_State *L)
t_string luaW_checktstring(lua_State *L, int index)
Converts a scalar to a translatable string.
Definition: lua_common.cpp:623
Used by the menu_button widget.
LUA_API const char * lua_pushlstring(lua_State *L, const char *s, size_t len)
Definition: lapi.cpp:502
int show_menu(lua_State *L)
Displays a popup menu at the current mouse position Best used from a [set_menu_item], to show a submenu.
Definition: lua_gui2.cpp:182
#define lua_newtable(L)
Definition: lua.h:366
LUA_API void lua_pushnil(lua_State *L)
Definition: lapi.cpp:473
LUALIB_API void luaL_checktype(lua_State *L, int arg, int t)
Definition: lauxlib.cpp:390
lu_byte right
Definition: lparser.cpp:1227
#define lua_isboolean(L, n)
Definition: lua.h:376
static lg::log_domain log_scripting_lua("scripting/lua")
bool luaW_toconfig(lua_State *L, int index, config &cfg)
Converts an optional table or vconfig to a config object.
Definition: lua_common.cpp:745
int show_popup_dialog(lua_State *L)
Displays a popup message.
Definition: lua_gui2.cpp:155
bool luaW_totstring(lua_State *L, int index, t_string &str)
Converts a scalar to a translatable string.
Definition: lua_common.cpp:598
std::size_t i
Definition: function.cpp:940
int show_wml_message(const std::string &title, const std::string &message, const wml_message_portrait *left, const wml_message_portrait *right, const wml_message_options &options, const wml_message_input &input)
Helper function to show a portrait.
LUA_API void lua_rawset(lua_State *L, int idx)
Definition: lapi.cpp:877
config luaW_checkconfig(lua_State *L, int index)
Converts an optional table or vconfig to a config object.
Definition: lua_common.cpp:822
Helper class for message options.
Definition: wml_message.hpp:25
int luaW_open(lua_State *L)
LUA_API int lua_isnumber(lua_State *L, int idx)
Definition: lapi.cpp:285
LUA_API lua_Unsigned lua_rawlen(lua_State *L, int idx)
Definition: lapi.cpp:402
std::vector< wml_message_option > option_list
A list of options to select in the dialog.
lu_byte left
Definition: lparser.cpp:1226
game_board board_
Definition: game_state.hpp:46
A variable-expanding proxy for the config class.
Definition: variable.hpp:44
Functions to load and save images from/to disk.
Standard logging facilities (interface).
bool add_single_widget_definition(const std::string &widget_type, const std::string &definition_id, const config &cfg)
Adds a widget definition to the default GUI.
#define e
Dialog was closed with the OK button.
Definition: retval.hpp:34
LUALIB_API void luaL_setfuncs(lua_State *L, const luaL_Reg *l, int nup)
Definition: lauxlib.cpp:904
int show_message_box(lua_State *L)
Displays a simple message box.
Definition: lua_gui2.cpp:208
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:59
mock_char c
LUA_API int lua_getfield(lua_State *L, int idx, const char *k)
Definition: lapi.cpp:655
#define LUA_TTABLE
Definition: lua.h:70
static map_location::DIRECTION n
LUA_API void lua_pushinteger(lua_State *L, lua_Integer n)
Definition: lapi.cpp:489
#define luaL_optstring(L, n, d)
Definition: lauxlib.h:139
LUA_API const char * lua_pushstring(lua_State *L, const char *s)
Definition: lapi.cpp:514
bool empty() const
Definition: config.cpp:916
LUA_API const char * lua_typename(lua_State *L, int t)
Definition: lapi.cpp:266
Parameter pack for message text input options.
void set_functions(lua_State *L, const std::vector< lua_cpp::Reg > &functions)
Analogous to lua_setfuncs, it registers a collection of function wrapper objects into a table...
#define luaL_checkstring(L, n)
Definition: lauxlib.h:138