The Battle for Wesnoth  1.15.9+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"
40 #include "log.hpp"
41 #include "preferences/game.hpp"
42 //#define DEBUG_TOOLTIP
43 #ifdef DEBUG_TOOLTIP
44 #include "gui/dialogs/tooltip.hpp"
45 #endif
46 #include "gui/widgets/button.hpp"
47 #include "gui/widgets/image.hpp"
48 #include "gui/widgets/label.hpp"
50 #include "gui/widgets/settings.hpp"
51 #include "gui/widgets/window.hpp"
52 #include "help/help.hpp"
54 #include "sdl/surface.hpp"
55 #include "sdl/utils.hpp"
56 #include "video.hpp"
57 
58 #include <algorithm>
59 #include <functional>
60 
61 static lg::log_domain log_config("config");
62 #define ERR_CF LOG_STREAM(err, log_config)
63 #define WRN_CF LOG_STREAM(warn, log_config)
64 
65 namespace gui2
66 {
67 namespace dialogs
68 {
69 
70 REGISTER_DIALOG(title_screen)
71 
73 
75  : debug_clock_()
76  , game_(game)
77 {
78  set_restore(false);
79 
80  // Need to set this in the constructor, pre_show() / post_build() is too late
81  set_allow_plugin_skip(false);
82 }
83 
85 {
86 }
87 
88 using btn_callback = std::function<void()>;
89 
90 static void register_button(window& win, const std::string& id, hotkey::HOTKEY_COMMAND hk, btn_callback callback)
91 {
92  if(hk != hotkey::HOTKEY_NULL) {
93  win.register_hotkey(hk, std::bind(callback));
94  }
95 
96  auto b = find_widget<button>(&win, id, false, false);
97  if(b != nullptr)
98  {
99  connect_signal_mouse_left_click(*b, std::bind(callback));
100  }
101 }
102 
103 static void launch_lua_console()
104 {
106 }
107 
108 static void make_screenshot()
109 {
110  surface screenshot = CVideo::get_singleton().getSurface().clone();
111  if(screenshot) {
112  std::string filename = filesystem::get_screenshot_dir() + "/" + _("Screenshot") + "_";
113  filename = filesystem::get_next_filename(filename, ".jpg");
114  gui2::dialogs::screenshot_notification::display(filename, screenshot);
115  }
116 }
117 
118 #ifdef DEBUG_TOOLTIP
119 /*
120  * This function is used to test the tooltip placement algorithms as
121  * described in the »Tooltip placement« section in the GUI2 design
122  * document.
123  *
124  * Use a 1024 x 768 screen size, set the maximum loop iteration to:
125  * - 0 to test with a normal tooltip placement.
126  * - 30 to test with a larger normal tooltip placement.
127  * - 60 to test with a huge tooltip placement.
128  * - 150 to test with a borderline to insanely huge tooltip placement.
129  * - 180 to test with an insanely huge tooltip placement.
130  */
131 static void debug_tooltip(window& /*window*/, bool& handled, const point& coordinate)
132 {
133  std::string message = "Hello world.";
134 
135  for(int i = 0; i < 0; ++i) {
136  message += " More greetings.";
137  }
138 
140  gui2::tip::show("tooltip", message, coordinate);
141 
142  handled = true;
143 }
144 #endif
145 
147 {
148  win.set_click_dismiss(false);
149  win.set_enter_disabled(true);
150  win.set_escape_disabled(true);
151 
152 #ifdef DEBUG_TOOLTIP
154  std::bind(debug_tooltip, std::ref(win), std::placeholders::_3, std::placeholders::_5),
156 #endif
157 
159 
160  //
161  // General hotkeys
162  //
164  std::bind(&gui2::window::set_retval, std::ref(win), RELOAD_GAME_DATA, true));
165 
168 
169  // A wrapper is needed here since the relevant display function is overloaded, and
170  // since the wrapper's signature doesn't exactly match what register_hotkey expects.
172 
174 
175  //
176  // Background and logo images
177  //
178  if(game_config::images::game_title.empty()) {
179  ERR_CF << "No title image defined" << std::endl;
180  }
181 
183 
185  ERR_CF << "No title background image defined" << std::endl;
186  }
187 
189 
190  find_widget<image>(&win, "logo-bg", false).set_image(game_config::images::game_logo_background);
191  find_widget<image>(&win, "logo", false).set_image(game_config::images::game_logo);
192 
193  //
194  // Version string
195  //
196  const std::string& version_string = VGETTEXT("Version $version", {{ "version", game_config::revision }});
197 
198  if(label* version_label = find_widget<label>(&win, "revision_number", false, false)) {
199  version_label->set_label(version_string);
200  }
201 
202  win.get_canvas(0).set_variable("revision_number", wfl::variant(version_string));
203 
204  //
205  // Tip-of-the-day browser
206  //
207  multi_page* tip_pages = find_widget<multi_page>(&win, "tips", false, false);
208 
209  if(tip_pages != nullptr) {
210  std::vector<game_tip> tips = tip_of_the_day::shuffle(settings::tips);
211  if(tips.empty()) {
212  WRN_CF << "There are no tips of day available." << std::endl;
213  }
214  for(const auto& tip : tips) {
216  std::map<std::string, string_map> page;
217 
218  widget["use_markup"] = "true";
219 
220  widget["label"] = tip.text();
221  page.emplace("tip", widget);
222 
223  widget["label"] = tip.source();
224  page.emplace("source", widget);
225 
226  tip_pages->add_page(page);
227  }
228 
229  update_tip(true);
230  }
231 
233  std::bind(&title_screen::update_tip, this, true));
234 
236  std::bind(&title_screen::update_tip, this, false));
237 
238  //
239  // Help
240  //
241  register_button(win, "help", hotkey::HOTKEY_HELP, []() {
242  if(gui2::new_widgets) {
243  gui2::dialogs::help_browser::display();
244  }
245 
247  help::show_help();
248  });
249 
250  //
251  // About
252  //
253  register_button(win, "about", hotkey::HOTKEY_NULL, std::bind(&game_version::display<>));
254 
255  //
256  // Campaign
257  //
258  register_button(win, "campaign", hotkey::TITLE_SCREEN__CAMPAIGN, [this, &win]() {
259  try{
260  if(game_.new_campaign()) {
261  win.set_retval(LAUNCH_GAME);
262  }
263  } catch (const config::error& e) {
265  }
266  });
267 
268  //
269  // Multiplayer
270  //
273 
274  //
275  // Load game
276  //
277  register_button(win, "load", hotkey::HOTKEY_LOAD_GAME, [this, &win]() {
278  if(game_.load_game()) {
279  win.set_retval(LAUNCH_GAME);
280  }
281  });
282 
283  //
284  // Addons
285  //
286  register_button(win, "addons", hotkey::TITLE_SCREEN__ADDONS, [&win]() {
287  // NOTE: we need the help_manager to get access to the Add-ons section in the game help!
289 
290  if(manage_addons()) {
291  win.set_retval(RELOAD_GAME_DATA);
292  }
293  });
294 
295  //
296  // Editor
297  //
298  register_button(win, "editor", hotkey::TITLE_SCREEN__EDITOR, [&win]() { win.set_retval(MAP_EDITOR); });
299 
300  //
301  // Cores
302  //
303  win.register_hotkey(hotkey::TITLE_SCREEN__CORES,
304  std::bind(&title_screen::button_callback_cores, this));
305 
306  //
307  // Language
308  //
309  register_button(win, "language", hotkey::HOTKEY_LANGUAGE, [this, &win]() {
310  try {
311  if(game_.change_language()) {
312  on_resize();
313  }
314  } catch(const std::runtime_error& e) {
315  gui2::show_error_message(e.what());
316  }
317  });
318 
319  //
320  // Preferences
321  //
322  register_button(win, "preferences", hotkey::HOTKEY_PREFERENCES, []() {
323  gui2::dialogs::preferences_dialog::display();
324  });
325 
326  //
327  // Credits
328  //
329  register_button(win, "credits", hotkey::TITLE_SCREEN__CREDITS, [&win]() { win.set_retval(SHOW_ABOUT); });
330 
331  //
332  // Quit
333  //
334  register_button(win, "quit", hotkey::HOTKEY_QUIT_TO_DESKTOP, [&win]() { win.set_retval(QUIT_GAME); });
335  // A sanity check, exit immediately if the .cfg file didn't have a "quit" button.
336  find_widget<button>(&win, "quit", false, true);
337 
338  //
339  // Debug clock
340  //
341  register_button(win, "clock", hotkey::HOTKEY_NULL,
342  std::bind(&title_screen::show_debug_clock_window, this));
343 
344  auto clock = find_widget<button>(&win, "clock", false, false);
345  if(clock) {
347  }
348 }
349 
351 {
353 }
354 
355 void title_screen::update_tip(const bool previous)
356 {
357  multi_page* tip_pages = find_widget<multi_page>(get_window(), "tips", false, false);
358  if(tip_pages == nullptr) {
359  return;
360  }
361  if(tip_pages->get_page_count() == 0) {
362  return;
363  }
364 
365  int page = tip_pages->get_selected_page();
366  if(previous) {
367  if(page <= 0) {
368  page = tip_pages->get_page_count();
369  }
370  --page;
371  } else {
372  ++page;
373  if(static_cast<unsigned>(page) >= tip_pages->get_page_count()) {
374  page = 0;
375  }
376  }
377 
378  tip_pages->select_page(page);
379 
380  /**
381  * @todo Look for a proper fix.
382  *
383  * This dirtying is required to avoid the blurring to be rendered wrong.
384  * Not entirely sure why, but since we plan to move to SDL2 that change
385  * will probably fix this issue automatically.
386  */
387  get_window()->set_is_dirty(true);
388 }
389 
391 {
392  assert(show_debug_clock_button);
393 
394  if(debug_clock_) {
395  debug_clock_.reset(nullptr);
396  } else {
397  debug_clock_.reset(new debug_clock());
398  debug_clock_->show(true);
399  }
400 }
401 
403 {
405 
406  std::vector<std::string> options;
407  for(const config &sc : game_config_manager::get()->game_config().child_range("test")) {
408  if(!sc["is_unit_test"].to_bool(false)) {
409  options.emplace_back(sc["id"]);
410  }
411  }
412 
413  std::sort(options.begin(), options.end());
414 
415  gui2::dialogs::simple_item_selector dlg(_("Choose Test"), "", options);
416  dlg.show();
417 
418  int choice = dlg.selected_index();
419  if(choice >= 0) {
420  game_.set_test(options[choice]);
422  }
423 }
424 
426 {
427  while(true) {
429  dlg.show();
430 
431  if(dlg.get_retval() != gui2::retval::OK) {
432  return;
433  }
434 
435  const auto res = dlg.get_choice();
436 
437  if(res == decltype(dlg)::choice::HOST && preferences::mp_server_warning_disabled() < 2) {
438  if(!gui2::dialogs::mp_host_game_prompt::execute()) {
439  continue;
440  }
441  }
442 
443  switch(res) {
444  case decltype(dlg)::choice::JOIN:
447  break;
448  case decltype(dlg)::choice::CONNECT:
451  break;
452  case decltype(dlg)::choice::HOST:
453  game_.select_mp_server("localhost");
455  break;
456  case decltype(dlg)::choice::LOCAL:
458  break;
459  }
460 
461  return;
462  }
463 }
464 
466 {
467  int current = 0;
468 
469  std::vector<config> cores;
470  for(const config& core : game_config_manager::get()->game_config().child_range("core")) {
471  cores.push_back(core);
472 
473  if(core["id"] == preferences::core_id()) {
474  current = cores.size() - 1;
475  }
476  }
477 
478  gui2::dialogs::core_selection core_dlg(cores, current);
479  if(core_dlg.show()) {
480  const std::string& core_id = cores[core_dlg.get_choice()]["id"];
481 
482  preferences::set_core_id(core_id);
484  }
485 }
486 
487 } // namespace dialogs
488 } // namespace gui2
bool new_widgets
Do we wish to use the new library or not.
Definition: settings.cpp:24
void remove()
Removes a tip.
Definition: tooltip.cpp:176
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:114
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:49
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:141
grid & add_page(const string_map &item)
Adds single page to the grid.
Definition: multi_page.cpp:43
#define WRN_CF
void set_variable(const std::string &key, const wfl::variant &value)
Definition: canvas.hpp:169
std::string game_title
An SDL resize request, coordinate is the new window size.
Definition: handler.hpp:51
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:374
std::string get_screenshot_dir()
void set_escape_disabled(const bool escape_disabled)
Disable the escape key.
Definition: window.hpp:298
static CVideo & get_singleton()
Definition: video.hpp:48
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:56
int get_selected_page() const
Returns the selected page.
Definition: multi_page.cpp:115
static std::string _(const char *str)
Definition: gettext.hpp:92
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:75
surface & getSurface()
Returns a reference to the framebuffer.
Definition: video.cpp:503
static void display(lua_kernel_base *lk)
Display a new console, using given video and lua kernel.
Generic file dialog.
Definition: field-fwd.hpp:22
#define b
const config & options()
Definition: game.cpp:563
static game_config_manager * get()
const std::vector< game_config::server_info > & builtin_servers_list()
Definition: game.cpp:364
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:171
void set_is_dirty(const bool is_dirty)
Definition: widget.cpp:465
std::string game_title_background
void set_test(const std::string &id)
void set_core_id(const std::string &core_id)
Definition: general.cpp:317
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:131
bool manage_addons()
Shows the add-ons server connection dialog, for access to the various management front-ends.
Definition: manager_ui.cpp:228
Various uncategorised dialogs.
int mp_server_warning_disabled()
Definition: game.cpp:499
std::size_t i
Definition: function.cpp:933
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:505
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:48
Game configuration data as global variables.
Definition: build_info.cpp:56
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:106
Contains the gui2 timer routines.
std::map< std::string, t_string > string_map
Definition: widget.hpp:26
Holds a 2D point.
Definition: point.hpp:23
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:357
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:311
unsigned get_page_count() const
Returns the number of pages.
Definition: multi_page.cpp:100
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:48
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:205
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: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:59
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:64
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:141
bool show_debug_clock_button
Do we wish to show the button for the debug clock.
An SDL mouse motion event.
Definition: handler.hpp:53
#define ERR_CF
void set_enter_disabled(const bool enter_disabled)
Disable the enter key.
Definition: window.hpp:285