The Battle for Wesnoth  1.15.2+dev
title_screen.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2018 by Mark de Wever <koraq@xs4all.nl>
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 #define GETTEXT_DOMAIN "wesnoth-lib"
16 
18 
19 #include "addon/manager_ui.hpp"
20 #include "filesystem.hpp"
21 #include "formula/string_utils.hpp"
22 #include "game_config.hpp"
23 #include "game_config_manager.hpp"
24 #include "game_launcher.hpp"
26 #include "gui/auxiliary/tips.hpp"
27 #include "gui/core/timer.hpp"
34 #include "gui/dialogs/message.hpp"
39 #include "log.hpp"
40 #include "preferences/game.hpp"
41 //#define DEBUG_TOOLTIP
42 #ifdef DEBUG_TOOLTIP
43 #include "gui/dialogs/tooltip.hpp"
44 #endif
45 #include "gui/widgets/button.hpp"
46 #include "gui/widgets/image.hpp"
47 #include "gui/widgets/label.hpp"
49 #include "gui/widgets/settings.hpp"
50 #include "gui/widgets/window.hpp"
51 #include "help/help.hpp"
53 #include "sdl/surface.hpp"
54 #include "sdl/utils.hpp"
55 #include "utils/functional.hpp"
56 #include "video.hpp"
57 
58 #include <algorithm>
59 
60 static lg::log_domain log_config("config");
61 #define ERR_CF LOG_STREAM(err, log_config)
62 #define WRN_CF LOG_STREAM(warn, log_config)
63 
64 namespace gui2
65 {
66 namespace dialogs
67 {
68 
69 /*WIKI
70  * @page = GUIWindowDefinitionWML
71  * @order = 2_title_screen
72  *
73  * == Title screen ==
74  *
75  * This shows the title screen.
76  *
77  * @begin{table}{dialog_widgets}
78  * tutorial & & button & o &
79  * The button to start the tutorial. $
80  *
81  * campaign & & button & o &
82  * The button to start a campaign. $
83  *
84  * multiplayer & & button & o &
85  * The button to start multiplayer mode. $
86  *
87  * load & & button & o &
88  * The button to load a saved game. $
89  *
90  * editor & & button & o &
91  * The button to start the editor. $
92  *
93  * addons & & button & o &
94  * The button to start managing the addons. $
95  *
96  * language & & button & o &
97  * The button to select the game language. $
98  *
99  * credits & & button & o &
100  * The button to show Wesnoth's contributors. $
101  *
102  * quit & & button & m &
103  * The button to quit Wesnoth. $
104  *
105  * tips & & multi_page & o &
106  * A multi_page to hold all tips, when this widget is used the area of
107  * the tips doesn't need to be resized when the next or previous button
108  * is pressed. $
109  *
110  * -tip & & label & o &
111  * Shows the text of the current tip. $
112  *
113  * -source & & label & o &
114  * The source (the one who's quoted or the book referenced) of the
115  * current tip. $
116  *
117  * next_tip & & button & o &
118  * The button show the next tip of the day. $
119  *
120  * previous_tip & & button & o &
121  * The button show the previous tip of the day. $
122  *
123  * logo & & image & o &
124  * The Wesnoth logo. $
125  *
126  * revision_number & & styled_widget & o &
127  * A widget to show the version number when the version number is
128  * known. $
129  *
130  * @end{table}
131  */
132 
133 REGISTER_DIALOG(title_screen)
134 
136 
138  : debug_clock_()
139  , game_(game)
140 {
141  set_restore(false);
142 
143  // Need to set this in the constructor, pre_show() / post_build() is too late
144  set_allow_plugin_skip(false);
145 }
146 
148 {
149 }
150 
151 using btn_callback = std::function<void()>;
152 
153 static void register_button(window& win, const std::string& id, hotkey::HOTKEY_COMMAND hk, btn_callback callback)
154 {
155  if(hk != hotkey::HOTKEY_NULL) {
156  win.register_hotkey(hk, std::bind(callback));
157  }
158 
159  auto b = find_widget<button>(&win, id, false, false);
160  if(b != nullptr)
161  {
162  connect_signal_mouse_left_click(*b, std::bind(callback));
163  }
164 }
165 
166 static void launch_lua_console()
167 {
169 }
170 
171 static void make_screenshot(window& win)
172 {
173  surface screenshot = win.video().getSurface().clone();
174  if(screenshot) {
175  std::string filename = filesystem::get_screenshot_dir() + "/" + _("Screenshot") + "_";
176  filename = filesystem::get_next_filename(filename, ".png");
177  gui2::dialogs::screenshot_notification::display(filename, screenshot);
178  }
179 }
180 
181 #ifdef DEBUG_TOOLTIP
182 /*
183  * This function is used to test the tooltip placement algorithms as
184  * described in the »Tooltip placement« section in the GUI2 design
185  * document.
186  *
187  * Use a 1024 x 768 screen size, set the maximum loop iteration to:
188  * - 0 to test with a normal tooltip placement.
189  * - 30 to test with a larger normal tooltip placement.
190  * - 60 to test with a huge tooltip placement.
191  * - 150 to test with a borderline to insanely huge tooltip placement.
192  * - 180 to test with an insanely huge tooltip placement.
193  */
194 static void debug_tooltip(window& /*window*/, bool& handled, const point& coordinate)
195 {
196  std::string message = "Hello world.";
197 
198  for(int i = 0; i < 0; ++i) {
199  message += " More greetings.";
200  }
201 
203  gui2::tip::show("tooltip", message, coordinate);
204 
205  handled = true;
206 }
207 #endif
208 
210 {
211  win.set_click_dismiss(false);
212  win.set_enter_disabled(true);
213  win.set_escape_disabled(true);
214 
215 #ifdef DEBUG_TOOLTIP
217  std::bind(debug_tooltip, std::ref(win), _3, _5),
219 #endif
220 
221  win.connect_signal<event::SDL_VIDEO_RESIZE>(std::bind(&title_screen::on_resize, this, std::ref(win)));
222 
223  //
224  // General hotkeys
225  //
227  std::bind(&gui2::window::set_retval, std::ref(win), RELOAD_GAME_DATA, true));
228 
230  std::bind(&title_screen::hotkey_callback_select_tests, this, std::ref(win)));
231 
232  // A wrapper is needed here since the relevant display function is overloaded, and
233  // since the wrapper's signature doesn't exactly match what register_hotkey expects.
235 
236  win.register_hotkey(hotkey::HOTKEY_SCREENSHOT, std::bind(&make_screenshot, std::ref(win)));
237 
238  //
239  // Background and logo images
240  //
241  if(game_config::images::game_title.empty()) {
242  ERR_CF << "No title image defined" << std::endl;
243  }
244 
246 
248  ERR_CF << "No title background image defined" << std::endl;
249  }
250 
252 
253  find_widget<image>(&win, "logo-bg", false).set_image(game_config::images::game_logo_background);
254  find_widget<image>(&win, "logo", false).set_image(game_config::images::game_logo);
255 
256  //
257  // Version string
258  //
259  const std::string& version_string = VGETTEXT("Version $version", {{ "version", game_config::revision }});
260 
261  if(label* version_label = find_widget<label>(&win, "revision_number", false, false)) {
262  version_label->set_label(version_string);
263  }
264 
265  win.get_canvas(0).set_variable("revision_number", wfl::variant(version_string));
266 
267  //
268  // Tip-of-the-day browser
269  //
270  multi_page* tip_pages = find_widget<multi_page>(&win, "tips", false, false);
271 
272  if(tip_pages != nullptr) {
273  std::vector<game_tip> tips = tip_of_the_day::shuffle(settings::tips);
274  if(tips.empty()) {
275  WRN_CF << "There are no tips of day available." << std::endl;
276  }
277  for(const auto& tip : tips) {
279  std::map<std::string, string_map> page;
280 
281  widget["use_markup"] = "true";
282 
283  widget["label"] = tip.text();
284  page.emplace("tip", widget);
285 
286  widget["label"] = tip.source();
287  page.emplace("source", widget);
288 
289  tip_pages->add_page(page);
290  }
291 
292  update_tip(win, true);
293  }
294 
296  std::bind(&title_screen::update_tip, this, std::ref(win), true));
297 
299  std::bind(&title_screen::update_tip, this, std::ref(win), false));
300 
301  //
302  // Help
303  //
304  register_button(win, "help", hotkey::HOTKEY_HELP, []() {
305  if(gui2::new_widgets) {
306  gui2::dialogs::help_browser::display();
307  }
308 
310  help::show_help();
311  });
312 
313  //
314  // About
315  //
316  register_button(win, "about", hotkey::HOTKEY_NULL, std::bind(&game_version::display<>));
317 
318  //
319  // Tutorial
320  //
321  register_button(win, "tutorial", hotkey::TITLE_SCREEN__TUTORIAL, [this, &win]() {
323  win.set_retval(LAUNCH_GAME);
324  });
325 
326  //
327  // Campaign
328  //
329  register_button(win, "campaign", hotkey::TITLE_SCREEN__CAMPAIGN, [this, &win]() {
330  try{
331  if(game_.new_campaign()) {
332  win.set_retval(LAUNCH_GAME);
333  }
334  } catch (const config::error& e) {
336  }
337  });
338 
339  //
340  // Multiplayer
341  //
343  std::bind(&title_screen::button_callback_multiplayer, this, std::ref(win)));
344 
345  //
346  // Load game
347  //
348  register_button(win, "load", hotkey::HOTKEY_LOAD_GAME, [this, &win]() {
349  if(game_.load_game()) {
350  win.set_retval(LAUNCH_GAME);
351  } else {
353  }
354  });
355 
356  //
357  // Addons
358  //
359  register_button(win, "addons", hotkey::TITLE_SCREEN__ADDONS, []() {
360  // NOTE: we need the help_manager to get access to the Add-ons section in the game help!
362 
363  if(manage_addons()) {
365  }
366  });
367 
368  //
369  // Editor
370  //
371  register_button(win, "editor", hotkey::TITLE_SCREEN__EDITOR, [&win]() { win.set_retval(MAP_EDITOR); });
372 
373  //
374  // Cores
375  //
376  win.register_hotkey(hotkey::TITLE_SCREEN__CORES,
377  std::bind(&title_screen::button_callback_cores, this));
378 
379  //
380  // Language
381  //
382  register_button(win, "language", hotkey::HOTKEY_LANGUAGE, [this, &win]() {
383  try {
384  if(game_.change_language()) {
388  on_resize(win);
389  }
390  } catch(const std::runtime_error& e) {
391  gui2::show_error_message(e.what());
392  }
393  });
394 
395  //
396  // Preferences
397  //
398  register_button(win, "preferences", hotkey::HOTKEY_PREFERENCES, [this]() { game_.show_preferences(); });
399 
400  //
401  // Credits
402  //
403  register_button(win, "credits", hotkey::TITLE_SCREEN__CREDITS, [&win]() { win.set_retval(SHOW_ABOUT); });
404 
405  //
406  // Quit
407  //
408  register_button(win, "quit", hotkey::HOTKEY_QUIT_TO_DESKTOP, [&win]() { win.set_retval(QUIT_GAME); });
409  // A sanity check, exit immediately if the .cfg file didn't have a "quit" button.
410  find_widget<button>(&win, "quit", false, true);
411 
412  //
413  // Debug clock
414  //
415  register_button(win, "clock", hotkey::HOTKEY_NULL,
416  std::bind(&title_screen::show_debug_clock_window, this));
417 
418  auto clock = find_widget<button>(&win, "clock", false, false);
419  if(clock) {
421  }
422 }
423 
425 {
427 }
428 
429 void title_screen::update_tip(window& win, const bool previous)
430 {
431  multi_page* tip_pages = find_widget<multi_page>(&win, "tips", false, false);
432  if(tip_pages == nullptr) {
433  return;
434  }
435  if(tip_pages->get_page_count() == 0) {
436  return;
437  }
438 
439  int page = tip_pages->get_selected_page();
440  if(previous) {
441  if(page <= 0) {
442  page = tip_pages->get_page_count();
443  }
444  --page;
445  } else {
446  ++page;
447  if(static_cast<unsigned>(page) >= tip_pages->get_page_count()) {
448  page = 0;
449  }
450  }
451 
452  tip_pages->select_page(page);
453 
454  /**
455  * @todo Look for a proper fix.
456  *
457  * This dirtying is required to avoid the blurring to be rendered wrong.
458  * Not entirely sure why, but since we plan to move to SDL2 that change
459  * will probably fix this issue automatically.
460  */
461  win.set_is_dirty(true);
462 }
463 
465 {
466  assert(show_debug_clock_button);
467 
468  if(debug_clock_) {
469  debug_clock_.reset(nullptr);
470  } else {
471  debug_clock_.reset(new debug_clock());
472  debug_clock_->show(true);
473  }
474 }
475 
477 {
479 
480  std::vector<std::string> options;
481  for(const config &sc : game_config_manager::get()->game_config().child_range("test")) {
482  if(!sc["is_unit_test"].to_bool(false)) {
483  options.emplace_back(sc["id"]);
484  }
485  }
486 
487  std::sort(options.begin(), options.end());
488 
489  gui2::dialogs::simple_item_selector dlg(_("Choose Test"), "", options);
490  dlg.show();
491 
492  int choice = dlg.selected_index();
493  if(choice >= 0) {
494  game_.set_test(options[choice]);
495  window.set_retval(LAUNCH_GAME);
496  }
497 }
498 
500 {
501  while(true) {
503  dlg.show();
504 
505  if(dlg.get_retval() != gui2::retval::OK) {
506  return;
507  }
508 
509  const int res = dlg.get_choice();
510 
511  if(res == 2 && preferences::mp_server_warning_disabled() < 2) {
512  if(!gui2::dialogs::mp_host_game_prompt::execute()) {
513  continue;
514  }
515  }
516 
517  switch(res) {
518  case 0:
520  window.set_retval(MP_CONNECT);
521  break;
522  case 1:
524  window.set_retval(MP_CONNECT);
525  break;
526  case 2:
527  game_.select_mp_server("localhost");
528  window.set_retval(MP_HOST);
529  break;
530  case 3:
531  window.set_retval(MP_LOCAL);
532  break;
533  }
534 
535  return;
536  }
537 }
538 
540 {
541  int current = 0;
542 
543  std::vector<config> cores;
544  for(const config& core : game_config_manager::get()->game_config().child_range("core")) {
545  cores.push_back(core);
546 
547  if(core["id"] == preferences::core_id()) {
548  current = cores.size() - 1;
549  }
550  }
551 
552  gui2::dialogs::core_selection core_dlg(cores, current);
553  if(core_dlg.show()) {
554  const std::string& core_id = cores[core_dlg.get_choice()]["id"];
555 
556  preferences::set_core_id(core_id);
558  }
559 }
560 
561 } // namespace dialogs
562 } // namespace gui2
bool new_widgets
Do we wish to use the new library or not.
Definition: settings.cpp:21
void remove()
Removes a tip.
Definition: tooltip.cpp:189
void update_tip(window &window, const bool previous)
Updates the tip of day widget.
void show_debug_clock_window()
Shows the debug clock.
static void reset_translations()
Definition: tstring.cpp:654
void show_help(const std::string &show_topic, int xloc, int yloc)
Open the help browser, show topic with id show_topic.
Definition: help.cpp:115
std::string get_next_filename(const std::string &name, const std::string &extension)
Get the next free filename using "name + number (3 digits) + extension" maximum 1000 files then start...
Definition: filesystem.cpp:493
std::vector< game_tip > tips
Definition: settings.cpp:46
Main class to show messages to the user.
Definition: message.hpp:34
void register_hotkey(const hotkey::HOTKEY_COMMAND id, const hotkey_function &function)
Registers a hotkey.
Definition: dispatcher.cpp:203
grid & add_page(const string_map &item)
Adds single page to the grid.
Definition: multi_page.cpp:43
void show_preferences()
#define WRN_CF
void set_variable(const std::string &key, const wfl::variant &value)
Definition: canvas.hpp:171
std::string game_title
An SDL resize request, coordinate is the new window size.
Definition: handler.hpp:59
This file contains the window object, this object is a top level container which has the event manage...
Base class for all widgets.
Definition: widget.hpp:47
This class implements the title screen.
void set_click_dismiss(const bool click_dismiss)
Definition: window.hpp:380
std::string get_screenshot_dir()
void set_escape_disabled(const bool escape_disabled)
Disable the escape key.
Definition: window.hpp:299
bool change_language()
Label showing a text.
Definition: label.hpp:32
int get_selected_page() const
Returns the selected page.
Definition: multi_page.cpp:112
std::enable_if_t< has_key< set_event, E >::value > connect_signal(const signal_function &signal, const queue_position position=back_child)
Connect a signal for callback in set_event.
Definition: dispatcher.hpp:393
bool show(const unsigned auto_close_time=0)
Shows the window.
-file util.hpp
void hotkey_callback_select_tests(window &window)
static void launch_lua_console()
surface clone() const
Makes a copy of this surface.
Definition: surface.cpp:75
surface & getSurface()
Returns a reference to the framebuffer.
Definition: video.cpp:459
static void display(lua_kernel_base *lk)
Display a new console, using given video and lua kernel.
void flush_cache()
Definition: picture.cpp:225
Generic file dialog.
Definition: field-fwd.hpp:22
#define b
const wesnothd::game & game_
Definition: game.cpp:1692
const config & options()
Definition: game.cpp:593
static game_config_manager * get()
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:91
const std::vector< game_config::server_info > & builtin_servers_list()
Definition: game.cpp:383
This file contains the settings handling of the widget library.
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal_function &signal)
Connects a signal handler for a left mouse button click.
Definition: dispatcher.cpp:233
std::string game_title_background
void set_test(const std::string &id)
void set_core_id(const std::string &core_id)
Definition: general.cpp:319
void select_mp_server(const std::string &server)
static lg::log_domain log_config("config")
std::string game_logo_background
const char * what() const noexcept
Definition: exceptions.hpp:37
static tooltip & tip()
Definition: tooltip.cpp:144
bool manage_addons()
Shows the add-ons server connection dialog, for access to the various management front-ends.
Definition: manager_ui.cpp:230
Various uncategorised dialogs.
CVideo & video()
Definition: window.hpp:352
void flush_cache()
Definition: sound.cpp:194
int mp_server_warning_disabled()
Definition: game.cpp:526
std::size_t i
Definition: function.cpp:933
const std::string revision
void load_game_config_for_create(bool is_mp, bool is_test=false)
Clock to test the draw events.
Definition: debug_clock.hpp:33
Game configuration data as global variables.
Definition: build_info.cpp:49
The user set the widget invisible, that means:
void select_page(const unsigned page, const bool select=true)
Selectes a page.
Definition: multi_page.cpp:106
Contains the gui2 timer routines.
std::map< std::string, t_string > string_map
Definition: widget.hpp:24
Holds a 2D point.
Definition: point.hpp:23
void on_resize(window &window)
Declarations for File-IO.
void set_retval(const int retval, const bool close_window=true)
Sets there return value of the window.
Definition: window.hpp:363
static void make_screenshot(window &win)
static int sort(lua_State *L)
Definition: ltablib.cpp:411
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
std::string core_id()
Definition: general.cpp:313
unsigned get_page_count() const
Returns the number of pages.
Definition: multi_page.cpp:100
The user sets the widget visible, that means:
The multi page class.
Definition: multi_page.hpp:35
Standard logging facilities (interface).
void clear_loaded_game()
std::string game_logo
void button_callback_multiplayer(window &window)
std::unique_ptr< modeless_dialog > debug_clock_
Holds the debug clock dialog.
void show_error_message(const std::string &msg, bool message_use_markup)
Shows an error message to the user.
Definition: message.cpp:205
#define e
std::vector< game_tip > shuffle(const std::vector< game_tip > &tips)
Shuffles the tips.
Definition: tips.cpp:46
virtual void pre_show(window &window) override
Inherited from modal_dialog.
Dialog was closed with the OK button.
Definition: retval.hpp:34
map_location coordinate
Contains an x and y coordinate used for starting positions in maps.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
canvas & get_canvas(const unsigned index)
base class of top level items, the only item which needs to store the final canvases to draw on ...
Definition: window.hpp:62
std::function< void()> btn_callback
static void register_button(window &win, const std::string &id, hotkey::HOTKEY_COMMAND hk, btn_callback callback)
void show(const std::string &window_id, const t_string &message, const point &mouse, const SDL_Rect &source_rect)
Shows a tip.
Definition: tooltip.cpp:154
bool show_debug_clock_button
Do we wish to show the button for the debug clock.
An SDL mouse motion event.
Definition: handler.hpp:61
#define ERR_CF
void set_enter_disabled(const bool enter_disabled)
Disable the enter key.
Definition: window.hpp:286