The Battle for Wesnoth  1.19.18+dev
preferences_dialog.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2016 - 2025
3  by Charles Dang <exodia339gmail.com>
4  Copyright (C) 2011, 2015 by Iris Morelle <shadowm2006@gmail.com>
5  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
6 
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY.
13 
14  See the COPYING file for more details.
15 */
16 
17 #define GETTEXT_DOMAIN "wesnoth-lib"
18 
20 
21 #include "display.hpp"
22 #include "events.hpp"
23 #include "filesystem.hpp"
24 #include "formatter.hpp"
25 #include "formula/string_utils.hpp"
26 #include "game_data.hpp"
27 #include "gettext.hpp"
29 #include "hotkey/hotkey_item.hpp"
30 #include "lexical_cast.hpp"
31 #include "resources.hpp"
32 #include "theme.hpp"
33 #include "video.hpp"
34 
35 // Sub-dialog includes
43 
45 #include "gui/dialogs/message.hpp"
47 #include "gui/widgets/button.hpp"
50 #include "gui/widgets/grid.hpp"
51 #include "gui/widgets/image.hpp"
52 #include "gui/widgets/label.hpp"
53 #include "gui/widgets/slider.hpp"
57 #include "gui/widgets/text_box.hpp"
59 #include "gui/widgets/window.hpp"
60 
61 #include "serialization/markup.hpp"
62 #include "wml_exception.hpp"
63 
64 #include <functional>
65 #include <numeric>
66 
67 namespace gui2::dialogs
68 {
69 namespace
70 {
71 template<typename W>
72 void disable_widget_on_toggle(window& window, widget& w, const std::string& id)
73 {
74  window.find_widget<W>(id).set_active(dynamic_cast<selectable_item&>(w).get_value_bool());
75 }
76 
77 template<typename W>
78 void disable_widget_on_toggle_inverted(window& window, widget& w, const std::string& id)
79 {
80  window.find_widget<W>(id).set_active(!dynamic_cast<selectable_item&>(w).get_value_bool());
81 }
82 
83 // Helper to make it easier to immediately apply sound toggles immediately.
84 template<bool(*fptr)(bool)>
85 void sound_toggle_on_change(window& window, const std::string& id_to_toggle, widget& w)
86 {
87  std::invoke(fptr, dynamic_cast<selectable_item&>(w).get_value_bool());
88 
89  // Toggle the corresponding slider.
90  disable_widget_on_toggle<slider>(window, w, id_to_toggle);
91 }
92 
93 // Helper to make it easier to immediately apply volume (music, etc) setings on change.
94 template<void(*fptr)(int)>
95 void volume_setter_on_change(widget& w)
96 {
97  std::invoke(fptr, dynamic_cast<integer_selector&>(w).get_value());
98 }
99 
100 } // end anon namespace
101 
102 REGISTER_DIALOG(preferences_dialog)
103 
105  : modal_dialog(window_id())
106  , resolutions_() // should be populated by set_resolution_list before use
107  , themes_() // populated by set_theme_list
108  , gui2_themes_() // populated by set_gui2_theme_list
109  , last_selected_item_(0)
110  , current_gui_theme_(0)
111  , is_reload_needed_(false)
112  , accl_speeds_({0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 3, 4, 8, 16})
113  , visible_hotkeys_()
114  , visible_categories_()
115  , initial_index_(pef_view_map[initial_view])
116 {
117  initialize_callbacks();
118 }
119 
120 // Helper function to refresh resolution list
122 {
124 
125  std::vector<config> options;
126  for(const point& res : resolutions_) {
127  config option;
128  option["label"] = formatter() << res.x << font::unicode_multiplication_sign << res.y;
129 
130  const int div = std::gcd(res.x, res.y);
131  const int x_ratio = res.x / div;
132  const int y_ratio = res.y / div;
133 
134  if(x_ratio <= 10 || y_ratio <= 10) {
135  option["details"] = markup::span_color("#777777", "(", x_ratio, ':', y_ratio, ")");
136  }
137 
138  options.push_back(std::move(option));
139  }
140 
141  const unsigned current_res = std::distance(resolutions_.begin(), std::find(
143 
144  res_list.set_values(options, current_res);
145 }
146 
148 {
150 
151  std::vector<config> options;
152  std::size_t current_theme = 0;
153  for(std::size_t i = 0; i < themes_.size(); ++i) {
154  options.emplace_back("label", themes_[i].name, "tooltip", themes_[i].description);
155  if(themes_[i].id == prefs::get().theme()) {
156  current_theme = i;
157  }
158  }
159 
160  theme_list.set_values(options, current_theme);
161 }
162 
164 {
165  std::string current_gui_theme_name = prefs::get().gui2_theme();
166 
167  std::vector<config> options;
168  bool theme_found = false;
169  unsigned i = 0;
170  for(auto& gui : guis) {
171  gui2_themes_.emplace_back(gui.first);
172  options.emplace_back("label", gui.second.description());
173  if (current_gui_theme_name == gui.first) {
175  theme_found = true;
176  }
177  if (!theme_found) {
178  i++;
179  }
180  }
181 
182  theme_list.set_values(options);
183  theme_list.set_selected(current_gui_theme_);
184 }
185 
187 {
189  widget_item item;
190 
191  std::string image = "friend.png";
192  std::string descriptor = _("friend");
193  std::string notes;
194 
195  if(entry.get_status() == "ignore") {
196  image = "ignore.png";
197  descriptor = _("ignored");
198  }
199 
200  if(!entry.get_notes().empty()) {
201  notes = " " + markup::tag("small", "(", entry.get_notes(), ")");
202  }
203 
204  item["use_markup"] = "true";
205 
206  item["label"] = "misc/status-" + image;
207  data.emplace("friend_icon", item);
208 
209  item["label"] = entry.get_nick() + notes;
210  data.emplace("friend_name", item);
211 
212  item["label"] = markup::tag("small", descriptor);
213  data.emplace("friend_status", item);
214 
215  return data;
216 }
217 
219 {
220  const int num_friends = prefs::get().get_acquaintances().size();
221  const int sel = list.get_selected_row();
222 
223  if(sel < 0 || sel >= num_friends) {
224  return;
225  }
226 
227  std::map<std::string, preferences::acquaintance>::const_iterator who = prefs::get().get_acquaintances().begin();
228  std::advance(who, sel);
229 
230  textbox.set_value(who->second.get_nick() + " " + who->second.get_notes());
231 }
232 
234 {
235  const bool list_empty = list.get_item_count() == 0;
236 
237  if(!list_empty) {
238  list.select_row(std::min(static_cast<int>(list.get_item_count()) - 1, list.get_selected_row()));
239  }
240 
241  find_widget<button>("remove").set_active(!list_empty);
242 
243  find_widget<label>("no_friends_notice").set_visible(list_empty);
244 }
245 
246 void preferences_dialog::add_friend_list_entry(const bool is_friend, text_box& textbox)
247 {
248  std::string username = textbox.text();
249  if(username.empty()) {
250  gui2::show_transient_message("", _("No username specified"));
251  return;
252  }
253 
254  std::string reason;
255 
256  std::size_t pos = username.find_first_of(' ');
257  if(pos != std::string::npos) {
258  reason = username.substr(pos + 1);
259  username = username.substr(0, pos);
260  }
261 
262  auto [entry, added_new] = prefs::get().add_acquaintance(username, (is_friend ? "friend": "ignore"), reason);
263 
264  if(!entry) {
265  gui2::show_transient_message(_("Error"), _("Invalid username"));
266  return;
267  }
268 
269  textbox.clear();
270 
271  listbox& list = find_widget<listbox>("friends_list");
272 
273  //
274  // If this is a new entry, just add a new row. If it's not, we find the relevant
275  // row, remove it, and add a new row with the updated data. Should probably come
276  // up with a more elegant way to do .. the only reason I'm using the remove
277  // -and-replace method is to prevent any issues with the widgets' layout sizes.
278  //
279  if(added_new) {
280  list.add_row(get_friends_list_row_data(*entry));
281  } else {
282  for(unsigned i = 0; i < list.get_item_count(); ++i) {
283  grid* row_grid = list.get_row_grid(i);
284 
285  if(row_grid->find_widget<label>("friend_name").get_label() == entry->get_nick()) {
286  list.remove_row(i);
287  list.add_row(get_friends_list_row_data(*entry), i);
288 
289  break;
290  }
291  }
292  }
293 
295 }
296 
298 {
299  const int selected_row = std::max(0, friends_list.get_selected_row());
300 
301  std::map<std::string, preferences::acquaintance>::const_iterator who = prefs::get().get_acquaintances().begin();
302  std::advance(who, selected_row);
303 
304  const std::string to_remove = !textbox.text().empty() ? textbox.text() : who->second.get_nick();
305 
306  if(to_remove.empty()) {
307  gui2::show_transient_message("", _("No username specified"));
308  return;
309  }
310 
311  if(!prefs::get().remove_acquaintance(to_remove)) {
312  gui2::show_transient_error_message(_("Not on friends or ignore lists"));
313  return;
314  }
315 
316  textbox.clear();
317 
318  listbox& list = find_widget<listbox>("friends_list");
319  list.remove_row(selected_row);
320 
322 }
323 
325 {
326  // Update pixel scale preference.
327  slider& ps_slider = find_widget<slider>("pixel_scale_slider");
328  prefs::get().set_pixel_scale(ps_slider.get_value());
329 
330  // Update auto pixel scale preference.
331  toggle_button& auto_ps_toggle =
332  find_widget<toggle_button>("auto_pixel_scale");
333  prefs::get().set_auto_pixel_scale(auto_ps_toggle.get_value_bool());
334 
335  // Update draw buffers, taking these into account.
337 
338  // Update game display, if active
339  if(::display* disp = display::get_singleton()) {
340  disp->queue_rerender();
341  }
342 
343  // Raise a window resize event so we can react to the change
345 }
346 
347 template<bool(*toggle_getter)(), bool(*toggle_setter)(bool), int(*vol_getter)(), void(*vol_setter)(int)>
348 void preferences_dialog::initialize_sound_option_group(const std::string& id_suffix)
349 {
350  const std::string toggle_widget_id = "sound_toggle_" + id_suffix;
351  const std::string volume_widget_id = "sound_volume_" + id_suffix;
352 
353  const auto on_changed = [this, volume_widget_id](widget& w) {
354  sound_toggle_on_change<toggle_setter>(*this, volume_widget_id, w);
355  };
356 
357  // Set up the toggle. We utilize field_bool's callback-on-changed mechanism instead
358  // of manually registering the callback. Since we want the effects to apply immediately,
359  // the callback the setter callback is duplicated in the on-change callback. The field
360  // class could possibly use some reworking to make this less redundant, but for now it
361  // works well enough.
362  register_bool(toggle_widget_id, true, toggle_getter, toggle_setter, on_changed, true);
363 
364  // Set up the volume slider. integer_field doesn't have a callback-on-changed mechanism.
365  // To add one would either mean adding it to the base field class or make it a proper
366  // class of is own.
367  register_integer(volume_widget_id, true, vol_getter, vol_setter);
368 
369  // Callback to actually immediately apply the volume effect.
370  connect_signal_notify_modified(find_widget<slider>(volume_widget_id),
371  [](widget& w, auto&&...) { volume_setter_on_change<vol_setter>(w); });
372 }
373 
374 /* SOUND FX wrappers for template */
375 static bool sound(){return prefs::get().sound();}
376 static bool set_sound(bool v){return prefs::get().set_sound(v);}
377 static int sound_volume(){return prefs::get().sound_volume();}
379 
380 /* MUSIC wrappers for template */
381 static bool music_on(){return prefs::get().music_on();}
382 static bool set_music(bool v){return prefs::get().set_music(v);}
383 static int music_volume(){return prefs::get().music_volume();}
385 
386 /* TURN BELL wrappers for template */
387 static bool turn_bell(){return prefs::get().turn_bell();}
388 static bool set_turn_bell(bool v){return prefs::get().set_turn_bell(v);}
389 static int bell_volume(){return prefs::get().bell_volume();}
390 static void set_bell_volume(int v){prefs::get().set_bell_volume(v);}
391 
392 /* UI FX wrappers for template */
393 static bool ui_sound_on(){return prefs::get().ui_sound_on();}
394 static bool set_ui_sound(bool v){return prefs::get().set_ui_sound(v);}
395 static int ui_volume(){return prefs::get().ui_volume();}
396 static void set_ui_volume(int v){prefs::get().set_ui_volume(v);}
397 
398 /**
399  * Sets up states and callbacks for each of the widgets
400  */
402 {
403  //
404  // GENERAL PANEL
405  //
406 
407  /* SCROLL SPEED */
408  register_integer("scroll_speed", true,
409  []() {return prefs::get().scroll_speed();},
410  [](int v) {prefs::get().set_scroll_speed(v);});
411 
412  /* ACCELERATED SPEED */
413  register_bool("turbo_toggle", true,
414  []() {return prefs::get().turbo();},
415  [](bool v) {prefs::get().set_turbo(v);});
416 
417  const auto accl_load = [this]()->int {
418  return std::distance(accl_speeds_.begin(), std::find(accl_speeds_.begin(), accl_speeds_.end(), prefs::get().turbo_speed()));
419  };
420 
421  const auto accl_save = [this](int i) {
422  prefs::get().set_turbo_speed(accl_speeds_[i]);
423  };
424 
425  register_integer("turbo_slider", true,
426  accl_load, accl_save);
427 
428  // Set the value label transform function.
429  find_widget<slider>("turbo_slider").set_value_labels(
430  // TODO: this should probably be a locale dependent string (use comma/dot depending on language)
431  [this](int pos, int /*max*/)->t_string { return lexical_cast<std::string>(accl_speeds_[pos]); }
432  );
433 
434  /* SKIP AI MOVES */
435  register_bool("skip_ai_moves", true,
436  []() {return prefs::get().skip_ai_moves();},
437  [](bool v) {prefs::get().set_skip_ai_moves(v);});
438 
439  /* DISABLE AUTO MOVES */
440  register_bool("disable_auto_moves", true,
441  []() {return prefs::get().disable_auto_moves();},
442  [](bool v) {prefs::get().set_disable_auto_moves(v);});
443 
444  /* TURN DIALOG */
445  register_bool("show_turn_dialog", true,
446  []() {return prefs::get().turn_dialog();},
447  [](bool v) {prefs::get().set_turn_dialog(v);});
448 
449  /* ENABLE PLANNING MODE */
450  register_bool("whiteboard_on_start", true,
451  []() {return prefs::get().enable_planning_mode_on_start();},
452  [](bool v) {prefs::get().set_enable_planning_mode_on_start(v);});
453 
454  /* HIDE ALLY PLANS */
455  register_bool("whiteboard_hide_allies", true,
456  []() {return prefs::get().hide_whiteboard();},
457  [](bool v) {prefs::get().set_hide_whiteboard(v);});
458 
459  /* INTERRUPT ON SIGHTING */
460  register_bool("interrupt_move_when_ally_sighted", true,
461  []() {return prefs::get().ally_sighted_interrupts();},
462  [](bool v) {prefs::get().set_ally_sighted_interrupts(v);});
463 
464  /* SAVE REPLAYS */
465  register_bool("save_replays", true,
466  []() {return prefs::get().save_replays();},
467  [](bool v) {prefs::get().set_save_replays(v);});
468 
469  /* DELETE AUTOSAVES */
470  register_bool("delete_saves", true,
471  []() {return prefs::get().delete_saves();},
472  [](bool v) {prefs::get().set_delete_saves(v);});
473 
474  /* MAX AUTO SAVES */
475  register_integer("max_saves_slider", true,
476  []() {return prefs::get().auto_save_max();},
477  [](int v) {prefs::get().set_auto_save_max(v);});
478 
479  /* CACHE MANAGE */
480  connect_signal_mouse_left_click(find_widget<button>("cachemg"),
481  [](auto&&...) { dialogs::game_cache_options::display(); });
482 
483  //
484  // DISPLAY PANEL
485  //
486 
487  /* FULLSCREEN TOGGLE */
488  toggle_button& toggle_fullscreen = find_widget<toggle_button>("fullscreen");
489  toggle_fullscreen.set_value(prefs::get().fullscreen());
490 #ifdef __ANDROID__
491  toggle_fullscreen.set_active(false);
492 #endif
493 
494  // We bind a special callback function, so setup_single_toggle() is not used
496  [this](auto&&...) { fullscreen_toggle_callback(); });
497 
498  /* SET RESOLUTION */
499  menu_button& res_list = find_widget<menu_button>("resolution_set");
500 
501  res_list.set_use_markup(true);
502 #ifdef __ANDROID__
503  res_list.set_active(false);
504 #else
505  res_list.set_active(!prefs::get().fullscreen());
506 #endif
507 
508  set_resolution_list(res_list);
509 
511  [this](auto&&...) { handle_res_select(); });
512 
513  connect_signal<event::SDL_VIDEO_RESIZE>(
514  [this, &res_list](auto&&...) { set_resolution_list(res_list); });
515 
516  if (video::get_max_pixel_scale() > 1) {
517  /* PIXEL SCALE */
518  register_integer("pixel_scale_slider", true,
519  []() {return prefs::get().pixel_scale();},
520  [](int v) {prefs::get().set_pixel_scale(v);});
521 
522  slider& ps_slider = find_widget<slider>("pixel_scale_slider");
523  ps_slider.set_value_range(1, video::get_max_pixel_scale());
525  [this](auto&&...) { apply_pixel_scale(); });
526 
527  /* AUTOMATIC PIXEL SCALE */
528  register_bool("auto_pixel_scale", true,
529  []() {return prefs::get().auto_pixel_scale();},
530  [](bool v) {prefs::get().set_auto_pixel_scale(v);},
531  [&](widget& w) { disable_widget_on_toggle_inverted<slider>(*this, w, "pixel_scale_slider"); }, true);
532  } else {
533  find_widget<slider>("pixel_scale_slider").set_active(false);
534  find_widget<toggle_button>("auto_pixel_scale").set_active(false);
535  }
536 
537  toggle_button& auto_ps_toggle =
538  find_widget<toggle_button>("auto_pixel_scale");
539  connect_signal_mouse_left_click(auto_ps_toggle,
540  [this](auto&&...) { apply_pixel_scale(); });
541 
542  /* SHOW TIPS PANEL ON TITLESCREEN */
543  register_bool("show_tips", true,
544  []() {return prefs::get().show_tips();},
545  [&](bool v) {
546  // if changed once: different value, reload
547  // if changed twice: double toggle, same value, don't reload
549  prefs::get().set_show_tips(v);
550  });
551 
552  /* SHOW FLOATING LABELS */
553  register_bool("show_floating_labels", true,
554  []() {return prefs::get().floating_labels();},
555  [](bool v) {prefs::get().set_floating_labels(v);});
556 
557  /* SHOW TEAM COLORS */
558  register_bool("show_ellipses", true,
559  []() {return prefs::get().show_side_colors();},
560  [](bool v) {prefs::get().set_show_side_colors(v);});
561 
562  /* SHOW GRID */
563  register_bool("show_grid", true,
564  []() {return prefs::get().grid();},
565  [](bool v) {prefs::get().set_grid(v);});
566 
567  /* ANIMATE MAP */
568  register_bool("animate_terrains", true,
569  []() {return prefs::get().animate_map();},
570  [](bool v) {prefs::get().set_animate_map(v);},
571  [&](widget& w) { disable_widget_on_toggle<toggle_button>(*this, w, "animate_water"); }, true);
572 
573  /* ANIMATE WATER */
574  register_bool("animate_water", true,
575  []() {return prefs::get().animate_water();},
576  [](bool v) {prefs::get().set_animate_water(v);});
577 
578  /* SHOW UNIT STANDING ANIMS */
579  register_bool("animate_units_standing", true,
580  []() {return prefs::get().show_standing_animations();},
581  [](bool v) {prefs::get().set_show_standing_animations(v);});
582 
583  /* SHOW UNIT IDLE ANIMS */
584  register_bool("animate_units_idle", true,
585  []() {return prefs::get().idle_anim();},
586  [](bool v) {prefs::get().set_idle_anim(v);},
587  [&](widget& w) { disable_widget_on_toggle<slider>(*this, w, "idle_anim_frequency"); }, true);
588 
589  register_integer("idle_anim_frequency", true,
590  []() {return prefs::get().idle_anim_rate();},
591  [](int v) {prefs::get().set_idle_anim_rate(v);});
592 
593  /* FONT SCALING */
594  //register_integer("scaling_slider", true,
595  // font_scaling, set_font_scaling);
596 
597  /* VSYNC */
598  register_bool("vsync", true,
599  []() {return prefs::get().vsync();},
600  [](bool v) {prefs::get().set_vsync(v);});
601 
602  /* SELECT THEME */
603  menu_button& theme_list = find_widget<menu_button>("choose_theme");
606  [this](auto&&...) { handle_theme_select(); });
607 
608  /* SELECT GUI2 THEME */
609  menu_button& gui2_theme_list = find_widget<menu_button>("choose_gui2_theme");
610  button& apply_btn = find_widget<button>("apply");
611  set_gui2_theme_list(gui2_theme_list);
612  connect_signal_notify_modified(gui2_theme_list, [&](auto&&...) { apply_btn.set_active(true); });
613  apply_btn.set_active(false);
615  [this](auto&&...) { handle_gui2_theme_select(); });
616 
617  //
618  // SOUND PANEL
619  //
620 
621  /* SOUND FX */
622  initialize_sound_option_group<sound, set_sound, sound_volume, set_sound_volume>("sfx");
623 
624  /* MUSIC */
625  initialize_sound_option_group<music_on, set_music, music_volume, set_music_volume>("music");
626 
627  register_bool("sound_toggle_stop_music_in_background", true,
628  []() {return prefs::get().stop_music_in_background();},
629  [](bool v) {prefs::get().set_stop_music_in_background(v);});
630 
631  /* TURN BELL */
632  initialize_sound_option_group<turn_bell, set_turn_bell, bell_volume, set_bell_volume>("bell");
633 
634  /* UI FX */
635  initialize_sound_option_group<ui_sound_on, set_ui_sound, ui_volume, set_ui_volume>("uisfx");
636 
637  //
638  // MULTIPLAYER PANEL
639  //
640 
641  /* GENERAL TAB */
642 
643  /* CHAT LINES */
644  register_integer("chat_lines", true,
645  []() {return prefs::get().chat_lines();},
646  [](int v) {prefs::get().set_chat_lines(v);});
647 
648  /* CHAT TIMESTAMPPING */
649  register_bool("chat_timestamps", true,
650  []() {return prefs::get().chat_timestamp();},
651  [](bool v) {prefs::get().set_chat_timestamp(v);});
652 
653  /* SAVE PASSWORD */
654  register_bool("remember_password", true,
655  []() {return prefs::get().remember_password();},
656  [](bool v) {prefs::get().set_remember_password(v);});
657 
658  /* WHISPERS FROM FRIENDS ONLY */
659  register_bool("lobby_whisper_friends_only", true,
660  []() {return prefs::get().lobby_whisper_friends_only();},
661  [](bool v) {prefs::get().set_lobby_whisper_friends_only(v);});
662 
663  /* LOBBY JOIN NOTIFICATIONS */
664  lobby_joins_group.add_member(find_widget<toggle_button>("lobby_joins_none", false, true), pref_constants::lobby_joins::show_none);
665  lobby_joins_group.add_member(find_widget<toggle_button>("lobby_joins_friends", false, true), pref_constants::lobby_joins::show_friends);
666  lobby_joins_group.add_member(find_widget<toggle_button>("lobby_joins_all", false, true), pref_constants::lobby_joins::show_all);
667 
669 
672  });
673 
674  /* ALERTS */
675  connect_signal_mouse_left_click(find_widget<button>("mp_alerts"),
676  [](auto&&...) { mp_alerts_options::display(); });
677 
678  /* SET WESNOTHD PATH */
679  connect_signal_mouse_left_click(find_widget<button>("mp_wesnothd"),
680  [](auto&&...) { prefs::get().show_wesnothd_server_search(); });
681 
682 
683  /* FRIENDS TAB */
684 
685  listbox& friends_list = find_widget<listbox>("friends_list");
686  friends_list.clear();
687 
688  for(const auto& entry : prefs::get().get_acquaintances()) {
689  friends_list.add_row(get_friends_list_row_data(entry.second));
690  }
691 
692  update_friends_list_controls(friends_list);
693 
694  text_box& textbox = find_widget<text_box>("friend_name_box");
695 
696  connect_signal_mouse_left_click(find_widget<button>("add_friend"),
697  [&, this](auto&&...) { add_friend_list_entry(true, textbox); });
698 
699  connect_signal_mouse_left_click(find_widget<button>("add_ignored"),
700  [&, this](auto&&...) { add_friend_list_entry(false, textbox); });
701 
702  connect_signal_mouse_left_click(find_widget<button>("remove"),
703  [&, this](auto&&...) { remove_friend_list_entry(friends_list, textbox); });
704 
705  connect_signal_notify_modified(friends_list,
706  [&, this](auto&&...) { on_friends_list_select(friends_list, textbox); });
707 
708  //
709  // ADVANCED PANEL
710  //
711 
712  listbox& advanced = find_widget<listbox>("advanced_prefs");
713 
714  widget_data row_data;
715 
716  for(const auto& option : prefs::get().get_advanced_preferences()) {
717  const std::string pref_name = option.field;
718 
719  row_data["pref_name"]["label"] = option.name;
720  grid* main_grid = &advanced.add_row(row_data);
721 
722  grid& details_grid = main_grid->find_widget<grid>("prefs_setter_grid");
724 
725  // The toggle widget for toggle-type options (hidden for other types)
726  toggle_button& toggle_box = main_grid->find_widget<toggle_button>("value_toggle");
728 
729  if(!option.description.empty()) {
730  main_grid->find_widget<styled_widget>("description").set_label(option.description);
731  }
732 
733  switch(option.type) {
735 
737  toggle_box.set_value(preferences_dialog_friend::get(pref_name, option.cfg["default"].to_bool()));
738 
740  [&, pref_name](auto&&...) { preferences_dialog_friend::set(pref_name, toggle_box.get_value_bool()); });
741 
742  gui2::bind_default_status_label(toggle_box, "value");
743  break;
744  }
745 
747  auto setter_widget = build_single_widget_instance<slider>(config {"definition", "minimal"});
748  setter_widget->set_id("setter");
749  // Maximum must be set first or this will assert
750  setter_widget->set_value_range(option.cfg["min"].to_int(), option.cfg["max"].to_int());
751  setter_widget->set_step_size(option.cfg["step"].to_int(1));
752 
753  details_grid.swap_child("setter", std::move(setter_widget), true);
754 
755  slider& slide = details_grid.find_widget<slider>("setter");
756 
757  slide.set_value(preferences_dialog_friend::get(pref_name, option.cfg["default"].to_int()));
758 
760  [&, pref_name](auto&&...) { preferences_dialog_friend::set(pref_name, slide.get_value()); });
761 
762  gui2::bind_default_status_label(slide, "value");
763  break;
764  }
765 
767  std::vector<config> menu_data;
768  std::vector<std::string> option_ids;
769 
770  for(const config& choice : option.cfg.child_range("option")) {
771  config menu_item;
772  menu_item["label"] = choice["name"];
773  if(choice.has_attribute("description")) {
774  menu_item["details"] = markup::span_color("#777", choice["description"]);
775  }
776  menu_data.push_back(menu_item);
777  option_ids.push_back(choice["id"]);
778  }
779 
780  // Attempt to find an initial selection
781  int selected = std::distance(option_ids.begin(), std::find(option_ids.begin(), option_ids.end(),
782  preferences_dialog_friend::get(pref_name, option.cfg["default"].str())
783  ));
784 
785  // If the saved option value was invalid, reset selection to 0.
786  if(selected < 0 || selected >= static_cast<int>(option_ids.size())) {
787  selected = 0;
788  }
789 
790  auto setter_widget = build_single_widget_instance<menu_button>();
791  setter_widget->set_id("setter");
792 
793  details_grid.swap_child("setter", std::move(setter_widget), true);
794 
795  menu_button& menu = details_grid.find_widget<menu_button>("setter");
796 
797  menu.set_use_markup(true);
798  menu.set_values(menu_data, selected);
799 
801  [=](widget& w, auto&&...) { preferences_dialog_friend::set(pref_name, option_ids[dynamic_cast<menu_button&>(w).get_value()]); });
802 
803  gui2::bind_default_status_label(menu, "value");
804  break;
805  }
806 
808  //main_grid->remove_child("setter");
809 
810  auto value_widget = build_single_widget_instance<image>();
811  value_widget->set_label("icons/arrows/arrows_blank_right_25.png~CROP(3,3,18,18)");
812 
813  main_grid->swap_child("value", std::move(value_widget), true);
814  break;
815  }
816  }
817  }
818 
820  [this, &advanced](auto&&...) { on_advanced_prefs_list_select(advanced); });
821 
823 
824  //
825  // HOTKEYS PANEL
826  //
827 
828  multimenu_button& hotkey_menu = find_widget<multimenu_button>("hotkey_category_menu");
829  connect_signal_notify_modified(hotkey_menu,
830  [this](auto&&...) { hotkey_filter_callback(); });
831 
833 
834  text_box& filter = find_widget<text_box>("filter");
835  filter.on_modified([this](const auto&) { hotkey_filter_callback(); });
836 
837  hotkey_list.set_sorters(
838  // Action column
839  [this](const std::size_t i) { return visible_hotkeys_[i]->description; },
840 
841  // Hotkey column
842  [this](const std::size_t i) { return hotkey::get_names(visible_hotkeys_[i]->id); },
843 
844  // Scope columns
845  [this](const std::size_t i) { return !visible_hotkeys_[i]->scope[hotkey::SCOPE_GAME]; },
846  [this](const std::size_t i) { return !visible_hotkeys_[i]->scope[hotkey::SCOPE_EDITOR]; },
847  [this](const std::size_t i) { return !visible_hotkeys_[i]->scope[hotkey::SCOPE_MAIN_MENU]; }
848  );
849 
850  hotkey_list.set_active_sorter("sort_0", sort_order::type::ascending, true);
851 
852  connect_signal_mouse_left_click(find_widget<button>("btn_add_hotkey"),
853  [this, &hotkey_list](auto&&...) { add_hotkey_callback(hotkey_list); });
854 
855  connect_signal_mouse_left_click(find_widget<button>("btn_clear_hotkey"),
856  [this, &hotkey_list](auto&&...) { remove_hotkey_callback(hotkey_list); });
857 
858  connect_signal_mouse_left_click(find_widget<button>("btn_reset_hotkeys"),
859  [this](auto&&...) { default_hotkey_callback(); });
860 }
861 
863 {
864  widget_data row_data;
865 
866  t_string& row_icon = row_data["img_icon"]["label"];
867  t_string& row_action = row_data["lbl_desc"]["label"];
868  t_string& row_hotkey = row_data["lbl_hotkey"]["label"];
869 
870  t_string& row_is_g = row_data["lbl_is_game"]["label"];
871  t_string& row_is_e = row_data["lbl_is_editor"]["label"];
872  t_string& row_is_m = row_data["lbl_is_mainmenu"]["label"];
873 
874  listbox& hotkey_list = find_widget<listbox>("list_hotkeys");
875 
876  hotkey_list.clear();
877  visible_hotkeys_.clear();
878  visible_categories_.clear();
879 
880  //
881  // Main hotkeys list
882  //
883 
884  // These translated initials should match those used in data/gui/window/preferences/02_hotkeys.cfg
885  const std::string gh = markup::span_color("#0f0", _("game_hotkeys^G"));
886  const std::string eh = markup::span_color("#0f0", _("editor_hotkeys^E"));
887  const std::string mh = markup::span_color("#0f0", _("mainmenu_hotkeys^M"));
888 
889  for(const auto& [id, hotkey_item] : hotkey::get_hotkey_commands()) {
890  if(hotkey_item.hidden) {
891  continue;
892  }
893 
894  visible_hotkeys_.push_back(&hotkey_item);
895  visible_categories_.insert(hotkey_item.category);
896 
897  if(filesystem::file_exists(game_config::path + "/images/icons/action/" + hotkey_item.id + "_25.png")) {
898  row_icon = "icons/action/" + hotkey_item.id + "_25.png~CROP(3,3,18,18)";
899  } else {
900  row_icon = "";
901  }
902 
903  row_action = hotkey_item.description;
904  row_hotkey = hotkey::get_names(hotkey_item.id);
905 
906  row_is_g = hotkey_item.scope[hotkey::SCOPE_GAME] ? gh : "";
907  row_is_e = hotkey_item.scope[hotkey::SCOPE_EDITOR] ? eh : "";
908  row_is_m = hotkey_item.scope[hotkey::SCOPE_MAIN_MENU] ? mh : "";
909 
910  hotkey_list.add_row(row_data);
911  }
912 
913  //
914  // Filter options
915  //
916 
917  std::vector<config> filter_ops;
918  for(const hotkey::HOTKEY_CATEGORY& cat : visible_categories_) {
919  filter_ops.emplace_back("label", hotkey::get_translatable_category_name(cat), "checkbox", false);
920  }
921 
922  find_widget<multimenu_button>("hotkey_category_menu").set_values(filter_ops);
923 
924  return hotkey_list;
925 }
926 
928 {
929  int row_number = hotkeys.get_selected_row();
930  if(row_number < 0) {
931  gui2::show_transient_message("", _("No hotkey selected"));
932  return;
933  }
934 
935  const hotkey::hotkey_command& hotkey_item = *visible_hotkeys_[row_number];
936 
937  gui2::dialogs::hotkey_bind bind_dlg(hotkey_item.id);
938  bind_dlg.show();
939 
940  hotkey::hotkey_ptr newhk = bind_dlg.get_new_binding();
941  hotkey::hotkey_ptr oldhk;
942 
943  // only if not cancelled.
944  if(newhk.get() == nullptr) {
945  return;
946  }
947 
948  for(const hotkey::hotkey_ptr& hk : hotkey::get_hotkeys()) {
949  if(!hk->is_disabled() && newhk->bindings_equal(hk)) {
950  oldhk = hk;
951  }
952  }
953 
954  if(oldhk && oldhk->get_command() == hotkey_item.id) {
955  return;
956  }
957 
958  if(oldhk && oldhk->get_command() != "null") {
959  const std::string text = VGETTEXT("“<b>$hotkey_sequence|</b>” is in use by “<b>$old_hotkey_action|</b>”.\nDo you wish to reassign it to “<b>$new_hotkey_action|</b>”?", {
960  {"hotkey_sequence", oldhk->get_name()},
961  {"old_hotkey_action", hotkey::get_hotkey_command(oldhk->get_command()).description},
962  {"new_hotkey_action", hotkey::get_hotkey_command(newhk->get_command()).description}
963  });
964 
965  const int res = gui2::show_message(_("Reassign Hotkey"), text, gui2::dialogs::message::yes_no_buttons, true);
966  if(res != gui2::retval::OK) {
967  return;
968  }
969  }
970 
971  hotkey::add_hotkey(newhk);
972 
973  // We need to recalculate all hotkey names in because we might have removed a hotkey from another command.
974  for(std::size_t i = 0; i < hotkeys.get_item_count(); ++i) {
975  const hotkey::hotkey_command& hotkey_item_row = *visible_hotkeys_[i];
976  hotkeys.get_row_grid(i)->find_widget<label>("lbl_hotkey").set_label(hotkey::get_names(hotkey_item_row.id));
977  }
978 }
979 
981 {
982  gui2::show_transient_message(_("Hotkeys Reset"), _("All hotkeys have been reset to their default values."));
983 
985 
986  // Set up the list again and reselect the default sorting option.
988  hotkey_list.set_active_sorter("sort_0", sort_order::type::ascending, true);
989 }
990 
992 {
993  int row_number = hotkeys.get_selected_row();
994  if(row_number < 0) {
995  gui2::show_transient_message("", _("No hotkey selected"));
996  return;
997  }
998 
999  const hotkey::hotkey_command& hotkey_item = *visible_hotkeys_[row_number];
1000  hotkey::clear_hotkeys(hotkey_item.id);
1001  hotkeys.get_row_grid(row_number)->find_widget<label>("lbl_hotkey").set_label(hotkey::get_names(hotkey_item.id));
1002 }
1003 
1005 {
1006  const multimenu_button& hotkey_menu = find_widget<const multimenu_button>("hotkey_category_menu");
1007  const text_box& name_filter = find_widget<const text_box>("filter");
1008 
1009  boost::dynamic_bitset<> toggle_states = hotkey_menu.get_toggle_states();
1010  boost::dynamic_bitset<> res(visible_hotkeys_.size());
1011 
1012  std::string text = name_filter.get_value();
1013 
1014  // Nothing selected. It means that *all* categories are shown.
1015  if(toggle_states.none()) {
1016  toggle_states = ~toggle_states;
1017  }
1018 
1019  for(std::size_t h = 0; h < visible_hotkeys_.size(); ++h) {
1020  // Default to true if there is no filter text
1021  bool found = true;
1022 
1023  if(!text.empty()) {
1024  const std::string description = visible_hotkeys_[h]->description.str();
1025 
1026  for(const auto& word : utils::split(text, ' ')) {
1027  found = translation::ci_search(description, word);
1028 
1029  // No match, we're excluding this hotkey
1030  if(!found) {
1031  break;
1032  }
1033  }
1034  }
1035 
1036  unsigned cat_index = 0;
1037 
1038  // Filter categories
1039  for(const hotkey::HOTKEY_CATEGORY& cat : visible_categories_) {
1040  if(visible_hotkeys_[h]->category == cat) {
1041  break;
1042  } else {
1043  ++cat_index;
1044  }
1045  }
1046 
1047  if(cat_index < toggle_states.size() && found) {
1048  res[h] = toggle_states[cat_index];
1049  } else {
1050  res[h] = false;
1051  }
1052  }
1053 
1054  find_widget<listbox>("list_hotkeys").set_row_shown(res);
1055 }
1056 
1058 {
1059  const int selected_row = list.get_selected_row();
1060  const auto& pref = prefs::get().get_advanced_preferences()[selected_row];
1061 
1062  if(pref.type == preferences::option::avd_type::SPECIAL) {
1063  if(pref.field == "logging") {
1064  gui2::dialogs::log_settings::display();
1065  } else if(pref.field == "orb_color") {
1066  gui2::dialogs::select_orb_colors::display();
1067  } else if(pref.field == "reach_map") {
1068  gui2::dialogs::reachmap_options::display();
1069  } else {
1070  WRN_GUI_L << "Invalid or unimplemented custom advanced prefs option: " << pref.field;
1071  }
1072 
1073  // Add more options here as needed
1074  }
1075 
1076  const bool has_description = !pref.description.empty();
1077 
1078  if(has_description || (pref.type != preferences::option::avd_type::SPECIAL && pref.type != preferences::option::avd_type::TOGGLE)) {
1079  list.get_row_grid(selected_row)->find_widget<widget>("prefs_setter_grid")
1081  }
1082 
1083  if(last_selected_item_ != selected_row) {
1084  list.get_row_grid(last_selected_item_)->find_widget<widget>("prefs_setter_grid")
1086 
1087  last_selected_item_ = selected_row;
1088  }
1089 }
1090 
1092 {
1093  set_always_save_fields(true);
1094 
1095  connect_signal_mouse_left_click(find_widget<button>("about"),
1096  [](auto&&...) { game_version::display(); });
1097 
1098  //
1099  // Status labels
1100  // These need to be set here in pre_show, once the fields are initialized. For some reason, this
1101  // is not the case for those in Advanced
1102  //
1103  gui2::bind_default_status_label(find_widget<slider>("max_saves_slider"));
1104  gui2::bind_default_status_label(find_widget<slider>("turbo_slider"));
1105  gui2::bind_default_status_label(find_widget<slider>("pixel_scale_slider"));
1106 }
1107 
1108 // Special fullsceen callback
1110 {
1111  const bool ison = find_widget<toggle_button>("fullscreen").get_value_bool();
1112  video::set_fullscreen(ison);
1113 
1114  menu_button& res_list = find_widget<menu_button>("resolution_set");
1115 
1116  set_resolution_list(res_list);
1117  res_list.set_active(!ison);
1118 }
1119 
1121 {
1122  menu_button& res_list = find_widget<menu_button>("resolution_set");
1123 
1124  if(video::set_resolution(resolutions_[res_list.get_value()])) {
1125  set_resolution_list(res_list);
1126  }
1127 }
1128 
1130 {
1131  menu_button& theme_list = find_widget<menu_button>("choose_theme");
1132 
1133  const auto selection = theme_list.get_value();
1134  const auto& theme = themes_.at(selection);
1135  auto* display = display::get_singleton();
1136 
1137  prefs::get().set_theme(theme.id);
1138  if(display && resources::gamedata && resources::gamedata->get_theme().empty()) {
1139  display->set_theme(theme.id);
1140  }
1141 
1142 }
1143 
1145 {
1146  menu_button& gui2_theme_list = find_widget<menu_button>("choose_gui2_theme");
1147  unsigned selected_theme = gui2_theme_list.get_value();
1148  if (selected_theme != current_gui_theme_) {
1149  current_gui_theme_ = selected_theme;
1150  prefs::get().set_gui2_theme(gui2_themes_.at(selected_theme));
1152  }
1153 }
1154 
1156 {
1158 
1159  // Save new prefs to disk. This also happens on app close, but doing
1160  // it here too ensures nothing is lost in case of, say, a crash.
1162 
1163  // Needed for applying changes to tip panel visiblity on dialog close
1164  if (is_reload_needed_) {
1166  }
1167 }
1168 
1169 } // namespace dialogs
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:157
child_itors child_range(std::string_view key)
Definition: config.cpp:268
Sort-of-Singleton that many classes, both GUI and non-GUI, use to access the game data.
Definition: display.hpp:88
void set_theme(const std::string &new_theme)
Definition: display.cpp:241
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:102
std::ostringstream wrapper.
Definition: formatter.hpp:40
Simple push button.
Definition: button.hpp:36
virtual void set_active(const bool active) override
See styled_widget::set_active.
Definition: button.cpp:64
hotkey::hotkey_ptr get_new_binding() const
Definition: hotkey_bind.hpp:31
@ yes_no_buttons
Shows a yes and no button.
Definition: message.hpp:81
Abstract base class for all modal dialogs.
field_bool * register_bool(const std::string &id, const bool mandatory, const std::function< bool()> &callback_load_value=nullptr, const std::function< void(bool)> &callback_save_value=nullptr, const std::function< void(widget &)> &callback_change=nullptr, const bool initial_fire=false)
Creates a new boolean field.
void set_always_save_fields(const bool always_save_fields)
field_integer * register_integer(const std::string &id, const bool mandatory, const std::function< int()> &callback_load_value=nullptr, const std::function< void(int)> &callback_save_value=nullptr)
Creates a new integer field.
bool show(const unsigned auto_close_time=0)
Shows the window.
void set_theme_list(menu_button &theme_list)
void remove_hotkey_callback(listbox &hotkeys)
void set_resolution_list(menu_button &res_list)
void on_advanced_prefs_list_select(listbox &tree)
Callback for selection changes.
void on_friends_list_select(listbox &list, text_box &textbox)
void add_friend_list_entry(const bool is_friend, text_box &textbox)
std::vector< const hotkey::hotkey_command * > visible_hotkeys_
widget_data get_friends_list_row_data(const preferences::acquaintance &entry)
virtual void post_show() override
Actions to be taken after the window has been shown.
std::set< hotkey::HOTKEY_CATEGORY > visible_categories_
void update_friends_list_controls(listbox &list)
void handle_res_select()
Special callback functions.
std::vector< std::string > gui2_themes_
virtual void pre_show() override
Actions to be taken before showing the window.
void remove_friend_list_entry(listbox &friends_list, text_box &textbox)
void initialize_sound_option_group(const std::string &id_suffix)
void add_hotkey_callback(listbox &hotkeys)
group< pref_constants::lobby_joins > lobby_joins_group
void set_gui2_theme_list(menu_button &theme_list)
Base container class.
Definition: grid.hpp:32
std::unique_ptr< widget > swap_child(const std::string &id, std::unique_ptr< widget > w, const bool recurse, widget *new_parent=nullptr)
Exchanges a child in the grid.
Definition: grid.cpp:101
unsigned add_row(const unsigned count=1)
Adds a row to end of the grid.
Definition: grid.cpp:60
void on_modified(const Func &func)
Sets a common callback function for all members.
Definition: group.hpp:121
void add_member(selectable_item *w, const T &value)
Adds a widget/value pair to the group map.
Definition: group.hpp:41
void set_member_states(const T &value)
Sets the toggle values for all widgets besides the one associated with the specified value to false.
Definition: group.hpp:110
The listbox class.
Definition: listbox.hpp:41
grid & add_row(const widget_item &item, const int index=-1)
When an item in the list is selected by the user we need to update the state.
Definition: listbox.cpp:92
const grid * get_row_grid(const unsigned row) const
Returns the grid of the wanted row.
Definition: listbox.cpp:256
bool select_row(const unsigned row, const bool select=true)
Selects a row.
Definition: listbox.cpp:267
void remove_row(const unsigned row, unsigned count=1)
Removes a row in the listbox.
Definition: listbox.cpp:108
int get_selected_row() const
Returns the first selected row.
Definition: listbox.cpp:290
unsigned get_item_count() const
Returns the number of items in the listbox.
Definition: listbox.cpp:153
void set_values(const std::vector<::config > &values, unsigned selected=0)
virtual unsigned get_value() const override
Inherited from selectable_item.
Definition: menu_button.hpp:55
virtual void set_active(const bool active) override
See styled_widget::set_active.
Definition: menu_button.cpp:74
boost::dynamic_bitset get_toggle_states() const
Get the current state of the menu options.
virtual void set_value(int value) override
Inherited from integer_selector.
Definition: slider.cpp:81
virtual int get_value() const override
Inherited from integer_selector.
Definition: slider.hpp:52
const t_string & get_label() const
virtual void set_label(const t_string &text)
virtual void set_use_markup(bool use_markup)
std::string get_value() const
const std::string & text() const
virtual void set_value(const std::string &text)
The set_value is virtual for the password_box class.
A widget that allows the user to input text in single line.
Definition: text_box.hpp:125
virtual void set_value(unsigned selected, bool fire_event=false) override
Inherited from selectable_item.
Base class for all widgets.
Definition: widget.hpp:55
void set_visible(const visibility visible)
Definition: widget.cpp:479
@ visible
The user sets the widget visible, that means:
@ invisible
The user set the widget invisible, that means:
@ hidden
The user sets the widget hidden, that means:
T * find_widget(const std::string_view id, const bool must_be_active, const bool must_exist)
Gets a widget with the wanted id.
Definition: widget.hpp:747
void set_retval(const int retval, const bool close_window=true)
Sets there return value of the window.
Definition: window.hpp:387
const std::string & get_status() const
Definition: preferences.hpp:93
const std::string & get_nick() const
Definition: preferences.hpp:92
const std::string & get_notes() const
Definition: preferences.hpp:94
void set_lobby_joins(pref_constants::lobby_joins show)
bool set_music(bool ison)
void set_sound_volume(int vol)
void set_remember_password(bool remember)
const std::map< std::string, preferences::acquaintance > & get_acquaintances()
void set_turbo(bool ison)
void save_hotkeys()
bool turn_bell()
void write_preferences()
bool set_ui_sound(bool ison)
static prefs & get()
int scroll_speed()
void set_theme(const std::string &theme)
bool sound()
void show_wesnothd_server_search()
int bell_volume()
bool set_turn_bell(bool ison)
void set_music_volume(int vol)
void set_show_standing_animations(bool value)
void set_pixel_scale(const int scale)
pref_constants::lobby_joins get_lobby_joins()
bool set_sound(bool ison)
bool music_on()
void set_scroll_speed(const int scroll)
std::pair< preferences::acquaintance *, bool > add_acquaintance(const std::string &nick, const std::string &mode, const std::string &notes)
bool ui_sound_on()
int music_volume()
int pixel_scale()
void set_ui_volume(int vol)
bool turbo()
void set_bell_volume(int vol)
const std::vector< preferences::option > & get_advanced_preferences()
bool remember_password()
int ui_volume()
int sound_volume()
void clear_hotkeys()
bool show_standing_animations()
Definition: theme.hpp:43
static std::vector< theme_info > get_basic_theme_info(bool include_hidden=false)
Returns minimal info about saved themes, optionally including hidden ones.
Definition: theme.cpp:1001
map_display and display: classes which take care of displaying the map and game-data on the screen.
Declarations for File-IO.
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
std::size_t i
Definition: function.cpp:1032
static std::string _(const char *str)
Definition: gettext.hpp:97
#define WRN_GUI_L
Definition: log.hpp:57
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:199
This file contains the window object, this object is a top level container which has the event manage...
New lexcical_cast header.
void raise_resize_event()
Definition: events.cpp:732
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:341
const std::string unicode_multiplication_sign
Definition: constants.cpp:46
std::string path
Definition: filesystem.cpp:106
static void set_sound_volume(int v)
static int bell_volume()
static bool set_sound(bool v)
static bool set_music(bool v)
static bool set_ui_sound(bool v)
static bool turn_bell()
static int music_volume()
static void set_bell_volume(int v)
static void set_ui_volume(int v)
static void set_music_volume(int v)
REGISTER_DIALOG(editor_edit_unit)
static bool set_turn_bell(bool v)
static bool sound()
static int ui_volume()
static bool ui_sound_on()
static int sound_volume()
static bool music_on()
void connect_signal_mouse_left_release(dispatcher &dispatcher, const signal &signal)
Connects a signal handler for a left mouse button release.
Definition: dispatcher.cpp:173
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.cpp:189
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal &signal)
Connects a signal handler for a left mouse button click.
Definition: dispatcher.cpp:163
void bind_default_status_label(W &source)
Binds a status label using the default value getter and default target ID.
gui_theme_map_t guis
Map of all known GUIs.
std::map< std::string, widget_item > widget_data
Definition: widget.hpp:36
void show_transient_error_message(const std::string &message, const std::string &image, const bool message_use_markup)
Shows a transient error message to the user.
std::map< std::string, t_string > widget_item
Definition: widget.hpp:33
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)
Shows a transient message to the user.
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:148
@ OK
Dialog was closed with the OK button.
Definition: retval.hpp:35
General purpose widgets.
const hotkey_list & get_hotkeys()
Returns the list of hotkeys.
std::string get_names(const std::string &id)
Returns a comma-separated string of hotkey names.
std::shared_ptr< class hotkey_base > hotkey_ptr
Definition: hotkey_item.hpp:27
void clear_hotkeys(const std::string &command)
Unset the command bindings for all hotkeys matching the command.
const std::map< std::string_view, hotkey::hotkey_command > & get_hotkey_commands()
returns a container that contains all currently active hotkey_commands.
const hotkey_command & get_hotkey_command(std::string_view command)
Returns the hotkey_command with the given id.
std::vector< hotkey::hotkey_ptr > hotkey_list
Definition: hotkey_item.hpp:31
void add_hotkey(hotkey_ptr item)
Add a hotkey to the list of hotkeys.
t_string get_translatable_category_name(HOTKEY_CATEGORY category)
Gets the display name for a given hotkey category.
Functions to load and save images from/to disk.
std::string span_color(const color_t &color, Args &&... data)
Applies Pango markup to the input specifying its display color.
Definition: markup.hpp:110
std::string tag(std::string_view tag, Args &&... data)
Wraps the given data in the specified tag.
Definition: markup.hpp:45
game_data * gamedata
Definition: resources.cpp:22
bool ci_search(const std::string &s1, const std::string &s2)
Case-insensitive search.
Definition: gettext.cpp:559
constexpr auto filter
Definition: ranges.hpp:38
std::vector< std::string > split(const config_attribute_value &val)
auto * find(Container &container, const Value &value)
Convenience wrapper for using find on a container without needing to comare to end()
Definition: general.hpp:141
std::vector< point > get_available_resolutions(const bool include_current)
Returns the list of available screen resolutions.
Definition: video.cpp:741
int get_max_pixel_scale()
Definition: video.cpp:503
point current_resolution()
The current window size in desktop coordinates.
Definition: video.cpp:793
void set_fullscreen(bool fullscreen)
Set the fullscreen state.
Definition: video.cpp:809
bool set_resolution(const point &resolution)
Set the window resolution.
Definition: video.cpp:838
void toggle_fullscreen()
Toggle fullscreen mode.
Definition: video.cpp:833
void update_buffers(bool autoupdate)
Update buffers to match current resolution and pixel scale settings.
Definition: video.cpp:868
int w
Definition: pathfind.cpp:188
std::string_view data
Definition: picture.cpp:188
Stores all information related to functions that can be bound to hotkeys.
std::string id
The unique ID.
Holds a 2D point.
Definition: point.hpp:25
static bool get(const std::string &pref, bool def)
static void set(const std::string &pref, bool value)
Definitions related to theme-support.
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
#define h