The Battle for Wesnoth  1.15.5+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 
29 #include "config.hpp"
30 #include "log.hpp"
31 #include "scripting/lua_common.hpp"
35 #include "scripting/push_check.hpp"
37 #include "tstring.hpp"
38 #include "game_data.hpp"
39 #include "game_state.hpp"
40 
41 #include "utils/functional.hpp"
42 
43 #include <map>
44 #include <utility>
45 #include <vector>
46 #include <boost/optional.hpp>
47 
48 #include "lua/lauxlib.h" // for luaL_checkinteger, etc
49 #include "lua/lua.h" // for lua_setfield, etc
50 
51 static lg::log_domain log_scripting_lua("scripting/lua");
52 #define ERR_LUA LOG_STREAM(err, log_scripting_lua)
53 
54 namespace lua_gui2 {
55 
56 
57 /**
58  * Displays a message window
59  * - Arg 1: Table describing the window
60  * - Arg 2: List of options (nil or empty table - no options)
61  * - Arg 3: Text input specifications (nil or empty table - no text input)
62  * - Ret 1: option chosen (if no options: 0 if there's text input, -2 if escape pressed, else -1)
63  * - Ret 2: string entered (empty if none, nil if no text input)
64  */
66 {
67  config txt_cfg;
68  const bool has_input = !lua_isnoneornil(L, 3) && luaW_toconfig(L, 3, txt_cfg) && !txt_cfg.empty();
69 
71  input.caption = txt_cfg["label"].str();
72  input.text = txt_cfg["text"].str();
73  input.maximum_length = txt_cfg["max_length"].to_int(256);
74  input.text_input_was_specified = has_input;
75 
77  if(!lua_isnoneornil(L, 2)) {
79  std::size_t n = lua_rawlen(L, 2);
80  for(std::size_t i = 1; i <= n; i++) {
81  lua_rawgeti(L, 2, i);
82  t_string short_opt;
83  config opt;
84  if(luaW_totstring(L, -1, short_opt)) {
85  opt["description"] = short_opt;
86  } else if(!luaW_toconfig(L, -1, opt)) {
87  std::ostringstream error;
88  error << "expected array of config and/or translatable strings, but index ";
89  error << i << " was a " << lua_typename(L, lua_type(L, -1));
90  return luaL_argerror(L, 2, error.str().c_str());
91  }
92  gui2::dialogs::wml_message_option option(opt["label"], opt["description"], opt["image"]);
93  if(opt["default"].to_bool(false)) {
94  options.chosen_option = i - 1;
95  }
96  options.option_list.push_back(option);
97  lua_pop(L, 1);
98  }
99  lua_getfield(L, 2, "default");
100  if(lua_isnumber(L, -1)) {
101  int i = lua_tointeger(L, -1);
102  if(i < 1 || std::size_t(i) > n) {
103  std::ostringstream error;
104  error << "default= key in options list is not a valid option index (1-" << n << ")";
105  return luaL_argerror(L, 2, error.str().c_str());
106  }
107  options.chosen_option = i - 1;
108  }
109  lua_pop(L, 1);
110  }
111 
112  const config& def_cfg = luaW_checkconfig(L, 1);
113  const std::string& title = def_cfg["title"];
114  const std::string& message = def_cfg["message"];
115 
116  using portrait = gui2::dialogs::wml_message_portrait;
117  std::unique_ptr<portrait> left;
118  std::unique_ptr<portrait> right;
119  const bool is_double = def_cfg.has_attribute("second_portrait");
120  const bool left_side = def_cfg["left_side"].to_bool(true);
121  if(is_double || left_side) {
122  left.reset(new portrait {def_cfg["portrait"], def_cfg["mirror"].to_bool(false)});
123  } else {
124  // This means right side only.
125  right.reset(new portrait {def_cfg["portrait"], def_cfg["mirror"].to_bool(false)});
126  }
127  if(is_double) {
128  right.reset(new portrait {def_cfg["second_portrait"], def_cfg["second_mirror"].to_bool(false)});
129  }
130 
131  int dlg_result = gui2::dialogs::show_wml_message(title, message, left.get(), right.get(), options, input);
132 
133  if(!has_input && options.option_list.empty()) {
134  lua_pushinteger(L, dlg_result);
135  } else {
136  lua_pushinteger(L, options.chosen_option + 1);
137  }
138 
139  if(has_input) {
140  lua_pushlstring(L, input.text.c_str(), input.text.length());
141  } else {
142  lua_pushnil(L);
143  }
144 
145  return 2;
146 }
147 
148 /**
149  * Displays a popup message
150  * - Arg 1: Title (allows Pango markup)
151  * - Arg 2: Message (allows Pango markup)
152  * - Arg 3: Image (optional)
153  */
155  t_string title = luaW_checktstring(L, 1);
157  std::string image = lua_isnoneornil(L, 3) ? "" : luaL_checkstring(L, 3);
158 
159  gui2::show_transient_message(title, msg, image, true, true);
160  return 0;
161 }
162 
163 /**
164  * Displays a story screen
165  * - Arg 1: The story config
166  * - Arg 2: The default title
167  */
169  config story = luaW_checkconfig(L, 1);
170  t_string title = luaW_checktstring(L, 2);
172  return 0;
173 }
174 
175 /**
176  * Displays a popup menu at the current mouse position
177  * Best used from a [set_menu_item], to show a submenu
178  * - Arg 1: Configs defining each item, with keys icon, image/label, second_label, tooltip
179  * - Args 2, 3: Initial selection (integer); whether to parse markup (boolean)
180  */
182  std::vector<config> items = lua_check<std::vector<config>>(L, 1);
183  SDL_Rect pos {1,1,1,1};
184  SDL_GetMouseState(&pos.x, &pos.y);
185 
186  int initial = -1;
187  bool markup = false;
188  if(lua_isnumber(L, 2)) {
189  initial = lua_tointeger(L, 2) - 1;
190  markup = luaW_toboolean(L, 3);
191  } else if(lua_isnumber(L, 3)) {
192  initial = lua_tointeger(L, 3) - 1;
193  markup = luaW_toboolean(L, 2);
194  } else if(lua_isboolean(L, 2)) {
195  markup = luaW_toboolean(L, 2);
196  }
197 
198  gui2::dialogs::drop_down_menu menu(pos, items, initial, markup, false);
199  menu.show();
200  lua_pushinteger(L, menu.selected_item() + 1);
201  return 1;
202 }
203 
204 /**
205  * Displays a simple message box.
206  */
208  const t_string title = luaW_checktstring(L, 1), message = luaW_checktstring(L, 2);
209  std::string button = luaL_optstring(L, 3, "ok"), btn_style;
210  std::transform(button.begin(), button.end(), std::inserter(btn_style, btn_style.begin()), [](char c) { return std::tolower(c); });
211  bool markup = lua_isnoneornil(L, 3) ? luaW_toboolean(L, 3) : luaW_toboolean(L, 4);
212  using button_style = gui2::dialogs::message::button_style;
213  boost::optional<button_style> style;
214  if(btn_style.empty()) {
215  style = button_style::auto_close;
216  } else if(btn_style == "ok") {
217  style = button_style::ok_button;
218  } else if(btn_style == "close") {
219  style = button_style::close_button;
220  } else if(btn_style == "ok_cancel") {
221  style = button_style::ok_cancel_buttons;
222  } else if(btn_style == "cancel") {
223  style = button_style::cancel_button;
224  } else if(btn_style == "yes_no") {
225  style = button_style::yes_no_buttons;
226  }
227  if(style) {
228  int result = gui2::show_message(title, message, *style, markup, markup);
229  if(style == button_style::ok_cancel_buttons || style == button_style::yes_no_buttons) {
230  lua_pushboolean(L, result == gui2::retval::OK);
231  return 1;
232  }
233  } else {
234  gui2::show_message(title, message, button, false, markup, markup);
235  }
236  return 0;
237 }
238 
240 {
242  return 0;
243 }
244 
245 int show_gamestate_inspector(const vconfig& cfg, const game_data& data, const game_state& state)
246 {
247  gui2::dialogs::gamestate_inspector::display(data.get_variables(), *state.events_manager_, state.board_, cfg["name"]);
248  return 0;
249 }
250 
251 
252 /**
253  * - Arg 1: string, widget type
254  * - Arg 3: string, id
255  * - Arg 3: config,
256  */
257 
259 {
261  std::string id = luaL_checkstring(L, 2);
262  try {
264  lua_kernel_base::get_lua_kernel<lua_kernel_base>(L).add_widget_definition(type, id);
265  }
266  } catch(const std::invalid_argument& e) {
267  return luaL_argerror(L, 1, e.what());
268  }
269  return 0;
270 }
271 
273 {
274  auto& lk = lua_kernel_base::get_lua_kernel<lua_kernel_base>(L);
275  lk.add_log("Adding gui module...\n");
276  static luaL_Reg const gui_callbacks[] = {
277  { "show_menu", &show_menu },
278  { "show_narration", &show_message_dialog },
279  { "show_popup", &show_popup_dialog },
280  { "show_story", &show_story },
281  { "show_prompt", &show_message_box },
282  { "add_widget_definition", &intf_add_widget_definition },
283  { nullptr, nullptr },
284  };
285  std::vector<lua_cpp::Reg> const cpp_gui_callbacks {
286  {"show_lua_console", std::bind(&lua_kernel_base::intf_show_lua_console, &lk, _1)},
287  {nullptr, nullptr}
288  };
289  lua_newtable(L);
290  luaL_setfuncs(L, gui_callbacks, 0);
291  lua_cpp::set_functions(L, cpp_gui_callbacks);
292 
293  lua_pushstring(L, "widget");
295  lua_rawset(L, -3);
296 
297  return 1;
298 }
299 
300 } // end namespace lua_gui2
#define lua_isnoneornil(L, n)
Definition: lua.h:359
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:239
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:168
LUA_API int lua_type(lua_State *L, int idx)
Definition: lapi.cpp:251
std::string caption
The caption for the optional input text box.
LUA_API void lua_pushboolean(lua_State *L, int b)
Definition: lapi.cpp:557
LUA_API int lua_rawgeti(lua_State *L, int idx, lua_Integer n)
Definition: lapi.cpp:658
std::string text
The initial text value.
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:39
bool has_attribute(config_key_type key) const
Definition: config.cpp:213
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:342
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:35
int show_message_dialog(lua_State *L)
Displays a message window.
Definition: lua_gui2.cpp:65
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:272
int intf_add_widget_definition(lua_State *L)
Definition: lua_gui2.cpp:258
const std::unique_ptr< game_events::manager > events_manager_
Definition: game_state.hpp:52
#define lua_pop(L, n)
Definition: lua.h:344
static void display(lua_kernel_base *lk)
Display a new console, using given video and lua kernel.
const config & options()
Definition: game.cpp:592
int show_gamestate_inspector(const vconfig &cfg, const game_data &data, const game_state &state)
Definition: lua_gui2.cpp:245
LUALIB_API int luaL_argerror(lua_State *L, int arg, const char *extramsg)
Definition: lauxlib.cpp:164
bool luaW_toboolean(lua_State *L, int n)
Definition: lua_common.cpp:890
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:620
Used by the menu_button widget.
LUA_API const char * lua_pushlstring(lua_State *L, const char *s, size_t len)
Definition: lapi.cpp:479
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:181
#define lua_newtable(L)
Definition: lua.h:346
LUA_API void lua_pushnil(lua_State *L)
Definition: lapi.cpp:450
LUALIB_API void luaL_checktype(lua_State *L, int arg, int t)
Definition: lauxlib.cpp:378
lu_byte right
Definition: lparser.cpp:1027
#define lua_isboolean(L, n)
Definition: lua.h:356
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:742
int show_popup_dialog(lua_State *L)
Displays a popup message.
Definition: lua_gui2.cpp:154
bool luaW_totstring(lua_State *L, int index, t_string &str)
Converts a scalar to a translatable string.
Definition: lua_common.cpp:595
std::size_t i
Definition: function.cpp:933
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:801
config luaW_checkconfig(lua_State *L, int index)
Converts an optional table or vconfig to a config object.
Definition: lua_common.cpp:819
Helper class for message options.
Definition: wml_message.hpp:27
int luaW_open(lua_State *L)
LUA_API int lua_isnumber(lua_State *L, int idx)
Definition: lapi.cpp:276
LUA_API size_t lua_rawlen(lua_State *L, int idx)
Definition: lapi.cpp:392
std::vector< wml_message_option > option_list
A list of options to select in the dialog.
lu_byte left
Definition: lparser.cpp:1026
game_board board_
Definition: game_state.hpp:46
A variable-expanding proxy for the config class.
Definition: variable.hpp:44
this module manages the cache of images.
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:934
int show_message_box(lua_State *L)
Displays a simple message box.
Definition: lua_gui2.cpp:207
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
mock_char c
LUA_API int lua_getfield(lua_State *L, int idx, const char *k)
Definition: lapi.cpp:622
#define LUA_TTABLE
Definition: lua.h:69
static map_location::DIRECTION n
LUA_API void lua_pushinteger(lua_State *L, lua_Integer n)
Definition: lapi.cpp:466
#define luaL_optstring(L, n, d)
Definition: lauxlib.h:125
LUA_API const char * lua_pushstring(lua_State *L, const char *s)
Definition: lapi.cpp:491
bool empty() const
Definition: config.cpp:884
LUA_API const char * lua_typename(lua_State *L, int t)
Definition: lapi.cpp:257
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:124