The Battle for Wesnoth  1.17.0-dev
title_screen.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2021
3  by Mark de Wever <koraq@xs4all.nl>
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 
16 #define GETTEXT_DOMAIN "wesnoth-lib"
17 
19 
20 #include "addon/manager_ui.hpp"
21 #include "filesystem.hpp"
22 #include "formula/string_utils.hpp"
23 #include "game_config.hpp"
24 #include "game_config_manager.hpp"
25 #include "game_launcher.hpp"
27 #include "gui/auxiliary/tips.hpp"
28 #include "gui/core/timer.hpp"
35 #include "gui/dialogs/message.hpp"
41 #include "log.hpp"
42 #include "preferences/game.hpp"
43 //#define DEBUG_TOOLTIP
44 #ifdef DEBUG_TOOLTIP
45 #include "gui/dialogs/tooltip.hpp"
46 #endif
47 #include "gui/widgets/button.hpp"
48 #include "gui/widgets/image.hpp"
49 #include "gui/widgets/label.hpp"
51 #include "gui/widgets/settings.hpp"
52 #include "gui/widgets/window.hpp"
53 #include "help/help.hpp"
55 #include "sdl/surface.hpp"
56 #include "sdl/utils.hpp"
57 #include "video.hpp"
58 
59 #include <algorithm>
60 #include <functional>
61 
62 static lg::log_domain log_config("config");
63 #define ERR_CF LOG_STREAM(err, log_config)
64 #define WRN_CF LOG_STREAM(warn, log_config)
65 
66 namespace gui2::dialogs
67 {
68 
69 REGISTER_DIALOG(title_screen)
70 
72 
74  : debug_clock_()
75  , game_(game)
76 {
77  set_restore(false);
78 
79  // Need to set this in the constructor, pre_show() / post_build() is too late
80  set_allow_plugin_skip(false);
81 }
82 
84 {
85 }
86 
87 using btn_callback = std::function<void()>;
88 
89 static void register_button(window& win, const std::string& id, hotkey::HOTKEY_COMMAND hk, btn_callback callback)
90 {
91  if(hk != hotkey::HOTKEY_NULL) {
92  win.register_hotkey(hk, std::bind(callback));
93  }
94 
95  auto b = find_widget<button>(&win, id, false, false);
96  if(b != nullptr)
97  {
98  connect_signal_mouse_left_click(*b, std::bind(callback));
99  }
100 }
101 
102 static void launch_lua_console()
103 {
105 }
106 
107 static void make_screenshot()
108 {
109  surface screenshot = CVideo::get_singleton().getSurface().clone();
110  if(screenshot) {
111  std::string filename = filesystem::get_screenshot_dir() + "/" + _("Screenshot") + "_";
112  filename = filesystem::get_next_filename(filename, ".jpg");
113  gui2::dialogs::screenshot_notification::display(filename, screenshot);
114  }
115 }
116 
117 #ifdef DEBUG_TOOLTIP
118 /*
119  * This function is used to test the tooltip placement algorithms as
120  * described in the »Tooltip placement« section in the GUI2 design
121  * document.
122  *
123  * Use a 1024 x 768 screen size, set the maximum loop iteration to:
124  * - 0 to test with a normal tooltip placement.
125  * - 30 to test with a larger normal tooltip placement.
126  * - 60 to test with a huge tooltip placement.
127  * - 150 to test with a borderline to insanely huge tooltip placement.
128  * - 180 to test with an insanely huge tooltip placement.
129  */
130 static void debug_tooltip(window& /*window*/, bool& handled, const point& coordinate)
131 {
132  std::string message = "Hello world.";
133 
134  for(int i = 0; i < 0; ++i) {
135  message += " More greetings.";
136  }
137 
139  gui2::tip::show("tooltip", message, coordinate);
140 
141  handled = true;
142 }
143 #endif
144 
146 {
147  win.set_click_dismiss(false);
148  win.set_enter_disabled(true);
149  win.set_escape_disabled(true);
150 
151 #ifdef DEBUG_TOOLTIP
153  std::bind(debug_tooltip, std::ref(win), std::placeholders::_3, std::placeholders::_5),
155 #endif
156 
158 
159  //
160  // General hotkeys
161  //
163  std::bind(&gui2::window::set_retval, std::ref(win), RELOAD_GAME_DATA, true));
164 
167 
168  // A wrapper is needed here since the relevant display function is overloaded, and
169  // since the wrapper's signature doesn't exactly match what register_hotkey expects.
171 
173 
174  //
175  // Background and logo images
176  //
177  if(game_config::images::game_title.empty()) {
178  ERR_CF << "No title image defined" << std::endl;
179  }
180 
182 
184  ERR_CF << "No title background image defined" << std::endl;
185  }
186 
188 
189  find_widget<image>(&win, "logo-bg", false).set_image(game_config::images::game_logo_background);
190  find_widget<image>(&win, "logo", false).set_image(game_config::images::game_logo);
191 
192  //
193  // Version string
194  //
195  const std::string& version_string = VGETTEXT("Version $version", {{ "version", game_config::revision }});
196 
197  if(label* version_label = find_widget<label>(&win, "revision_number", false, false)) {
198  version_label->set_label(version_string);
199  }
200 
201  win.get_canvas(0).set_variable("revision_number", wfl::variant(version_string));
202 
203  //
204  // Tip-of-the-day browser
205  //
206  multi_page* tip_pages = find_widget<multi_page>(&win, "tips", false, false);
207 
208  if(tip_pages != nullptr) {
209  std::vector<game_tip> tips = tip_of_the_day::shuffle(settings::tips);
210  if(tips.empty()) {
211  WRN_CF << "There are no tips of day available." << std::endl;
212  }
213  for(const auto& tip : tips) {
215  std::map<std::string, string_map> page;
216 
217  widget["use_markup"] = "true";
218 
219  widget["label"] = tip.text();
220  page.emplace("tip", widget);
221 
222  widget["label"] = tip.source();
223  page.emplace("source", widget);
224 
225  tip_pages->add_page(page);
226  }
227 
228  update_tip(true);
229  }
230 
232  std::bind(&title_screen::update_tip, this, true));
233 
235  std::bind(&title_screen::update_tip, this, false));
236 
237  //
238  // Help
239  //
240  register_button(win, "help", hotkey::HOTKEY_HELP, []() {
241  if(gui2::new_widgets) {
242  gui2::dialogs::help_browser::display();
243  }
244 
245  help::show_help();
246  });
247 
248  //
249  // About
250  //
251  register_button(win, "about", hotkey::HOTKEY_NULL, std::bind(&game_version::display<>));
252 
253  //
254  // Campaign
255  //
256  register_button(win, "campaign", hotkey::TITLE_SCREEN__CAMPAIGN, [this, &win]() {
257  try{
258  if(game_.new_campaign()) {
259  win.set_retval(LAUNCH_GAME);
260  }
261  } catch (const config::error& e) {
263  }
264  });
265 
266  //
267  // Multiplayer
268  //
271 
272  //
273  // Load game
274  //
275  register_button(win, "load", hotkey::HOTKEY_LOAD_GAME, [this, &win]() {
276  if(game_.load_game()) {
277  win.set_retval(LAUNCH_GAME);
278  }
279  });
280 
281  //
282  // Addons
283  //
284  register_button(win, "addons", hotkey::TITLE_SCREEN__ADDONS, [&win]() {
285  if(manage_addons()) {
286  win.set_retval(RELOAD_GAME_DATA);
287  }
288  });
289 
290  //
291  // Editor
292  //
293  register_button(win, "editor", hotkey::TITLE_SCREEN__EDITOR, [&win]() { win.set_retval(MAP_EDITOR); });
294 
295  //
296  // Cores
297  //
298  win.register_hotkey(hotkey::TITLE_SCREEN__CORES,
299  std::bind(&title_screen::button_callback_cores, this));
300 
301  //
302  // Language
303  //
304  register_button(win, "language", hotkey::HOTKEY_LANGUAGE, [this]() {
305  try {
306  if(game_.change_language()) {
307  on_resize();
308  }
309  } catch(const std::runtime_error& e) {
310  gui2::show_error_message(e.what());
311  }
312  });
313 
314  //
315  // Preferences
316  //
317  register_button(win, "preferences", hotkey::HOTKEY_PREFERENCES, []() {
318  gui2::dialogs::preferences_dialog::display();
319  });
320 
321  //
322  // Credits
323  //
324  register_button(win, "credits", hotkey::TITLE_SCREEN__CREDITS, [&win]() { win.set_retval(SHOW_ABOUT); });
325 
326  //
327  // Quit
328  //
329  register_button(win, "quit", hotkey::HOTKEY_QUIT_TO_DESKTOP, [&win]() { win.set_retval(QUIT_GAME); });
330  // A sanity check, exit immediately if the .cfg file didn't have a "quit" button.
331  find_widget<button>(&win, "quit", false, true);
332 
333  //
334  // Debug clock
335  //
336  register_button(win, "clock", hotkey::HOTKEY_NULL,
337  std::bind(&title_screen::show_debug_clock_window, this));
338 
339  auto clock = find_widget<button>(&win, "clock", false, false);
340  if(clock) {
342  }
343 }
344 
346 {
348 }
349 
350 void title_screen::update_tip(const bool previous)
351 {
352  multi_page* tip_pages = find_widget<multi_page>(get_window(), "tips", false, false);
353  if(tip_pages == nullptr) {
354  return;
355  }
356  if(tip_pages->get_page_count() == 0) {
357  return;
358  }
359 
360  int page = tip_pages->get_selected_page();
361  if(previous) {
362  if(page <= 0) {
363  page = tip_pages->get_page_count();
364  }
365  --page;
366  } else {
367  ++page;
368  if(static_cast<unsigned>(page) >= tip_pages->get_page_count()) {
369  page = 0;
370  }
371  }
372 
373  tip_pages->select_page(page);
374 
375  /**
376  * @todo Look for a proper fix.
377  *
378  * This dirtying is required to avoid the blurring to be rendered wrong.
379  * Not entirely sure why, but since we plan to move to SDL2 that change
380  * will probably fix this issue automatically.
381  */
382  get_window()->set_is_dirty(true);
383 }
384 
386 {
387  assert(show_debug_clock_button);
388 
389  if(debug_clock_) {
390  debug_clock_.reset(nullptr);
391  } else {
392  debug_clock_.reset(new debug_clock());
393  debug_clock_->show(true);
394  }
395 }
396 
398 {
400 
401  std::vector<std::string> options;
402  for(const config &sc : game_config_manager::get()->game_config().child_range("test")) {
403  if(!sc["is_unit_test"].to_bool(false)) {
404  options.emplace_back(sc["id"]);
405  }
406  }
407 
408  std::sort(options.begin(), options.end());
409 
410  gui2::dialogs::simple_item_selector dlg(_("Choose Test"), "", options);
411  dlg.show();
412 
413  int choice = dlg.selected_index();
414  if(choice >= 0) {
415  game_.set_test(options[choice]);
417  }
418 }
419 
421 {
422  while(true) {
424  dlg.show();
425 
426  if(dlg.get_retval() != gui2::retval::OK) {
427  return;
428  }
429 
430  const auto res = dlg.get_choice();
431 
432  if(res == decltype(dlg)::choice::HOST && preferences::mp_server_warning_disabled() < 2) {
433  if(!gui2::dialogs::mp_host_game_prompt::execute()) {
434  continue;
435  }
436  }
437 
438  switch(res) {
439  case decltype(dlg)::choice::JOIN:
442  break;
443  case decltype(dlg)::choice::CONNECT:
446  break;
447  case decltype(dlg)::choice::HOST:
448  game_.select_mp_server("localhost");
450  break;
451  case decltype(dlg)::choice::LOCAL:
453  break;
454  }
455 
456  return;
457  }
458 }
459 
461 {
462  int current = 0;
463 
464  std::vector<config> cores;
465  for(const config& core : game_config_manager::get()->game_config().child_range("core")) {
466  cores.push_back(core);
467 
468  if(core["id"] == preferences::core_id()) {
469  current = cores.size() - 1;
470  }
471  }
472 
473  gui2::dialogs::core_selection core_dlg(cores, current);
474  if(core_dlg.show()) {
475  const std::string& core_id = cores[core_dlg.get_choice()]["id"];
476 
477  preferences::set_core_id(core_id);
479  }
480 }
481 
482 } // namespace dialogs
bool new_widgets
Do we wish to use the new library or not.
Definition: settings.cpp:25
void remove()
Removes a tip.
Definition: tooltip.cpp:175
void show_debug_clock_window()
Shows the debug clock.
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:144
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:490
std::vector< game_tip > tips
Definition: settings.cpp:50
void set_variable(const std::string &key, wfl::variant &&value)
Definition: canvas.hpp:168
Main class to show messages to the user.
Definition: message.hpp:35
void register_hotkey(const hotkey::HOTKEY_COMMAND id, const hotkey_function &function)
Registers a hotkey.
Definition: dispatcher.cpp:142
grid & add_page(const string_map &item)
Adds single page to the grid.
Definition: multi_page.cpp:44
#define WRN_CF
std::string game_title
An SDL resize request, coordinate is the new window size.
Definition: handler.hpp:52
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:49
This class implements the title screen.
void set_click_dismiss(const bool click_dismiss)
Definition: window.hpp:375
std::string get_screenshot_dir()
void set_escape_disabled(const bool escape_disabled)
Disable the escape key.
Definition: window.hpp:299
static CVideo & get_singleton()
Definition: video.hpp:49
window * get_window() const
Returns a pointer to the dialog&#39;s window.
bool change_language()
A label displays a text, the text can be wrapped but no scrollbars are provided.
Definition: label.hpp:57
int get_selected_page() const
Returns the selected page.
Definition: multi_page.cpp:116
static std::string _(const char *str)
Definition: gettext.hpp:93
bool show(const unsigned auto_close_time=0)
Shows the window.
static void launch_lua_console()
surface clone() const
Makes a copy of this surface.
Definition: surface.cpp:63
surface & getSurface()
Returns a reference to the framebuffer.
Definition: video.cpp:483
static void display(lua_kernel_base *lk)
Display a new console, using given video and lua kernel.
#define b
const config & options()
Definition: game.cpp:569
static game_config_manager * get()
const std::vector< game_config::server_info > & builtin_servers_list()
Definition: game.cpp:370
A simple one-column listbox with OK and Cancel buttons.
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:172
void set_is_dirty(const bool is_dirty)
Definition: widget.cpp:466
std::string game_title_background
void set_test(const std::string &id)
void set_core_id(const std::string &core_id)
Definition: general.cpp:329
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:36
static tooltip & tip()
Definition: tooltip.cpp:130
bool manage_addons()
Shows the add-ons server connection dialog, for access to the various management front-ends.
Definition: manager_ui.cpp:229
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:215
int mp_server_warning_disabled()
Definition: game.cpp:505
std::size_t i
Definition: function.cpp:967
std::enable_if_t< is_general_event(E)> connect_signal(const signal_function &signal, const queue_position position=back_child)
Connect a signal for callback in set_event.
Definition: dispatcher.hpp:506
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:49
Game configuration data as global variables.
Definition: build_info.cpp:59
The user set the widget invisible, that means:
void select_page(const unsigned page, const bool select=true)
Selects a page.
Definition: multi_page.cpp:107
Contains the gui2 timer routines.
std::map< std::string, t_string > string_map
Definition: widget.hpp:26
Holds a 2D point.
Definition: point.hpp:24
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:358
static int sort(lua_State *L)
Definition: ltablib.cpp:397
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
std::string core_id()
Definition: general.cpp:323
unsigned get_page_count() const
Returns the number of pages.
Definition: multi_page.cpp:101
int get_retval() const
Returns the cached window exit code.
The user sets the widget visible, that means:
This shows the dialog to select the kind of MP game the user wants to play.
A multi page is a control that contains several &#39;pages&#39; of which only one is visible.
Definition: multi_page.hpp:49
void update_tip(const bool previous)
Updates the tip of day widget.
Standard logging facilities (interface).
std::string game_logo
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:206
This shows the dialog which allows the user to choose which core to play.
#define e
std::vector< game_tip > shuffle(const std::vector< game_tip > &tips)
Shuffles the tips.
Definition: tips.cpp:47
virtual void pre_show(window &window) override
Actions to be taken before showing the window.
Dialog was closed with the OK button.
Definition: retval.hpp:35
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:61
canvas & get_canvas(const unsigned index)
static void make_screenshot()
void set_retval(int retval)
Convenience wrapper to set the window&#39;s exit code.
base class of top level items, the only item which needs to store the final canvases to draw on...
Definition: window.hpp:65
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:140
bool show_debug_clock_button
Do we wish to show the button for the debug clock.
An SDL mouse motion event.
Definition: handler.hpp:54
#define ERR_CF
void set_enter_disabled(const bool enter_disabled)
Disable the enter key.
Definition: window.hpp:286