The Battle for Wesnoth  1.19.27+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 "sound.hpp"
33 #include "theme.hpp"
34 #include "video.hpp"
35 
36 // Sub-dialog includes
44 
46 #include "gui/dialogs/message.hpp"
48 #include "gui/widgets/button.hpp"
51 #include "gui/widgets/grid.hpp"
52 #include "gui/widgets/image.hpp"
53 #include "gui/widgets/label.hpp"
54 #include "gui/widgets/slider.hpp"
58 #include "gui/widgets/text_box.hpp"
60 #include "gui/widgets/window.hpp"
61 
62 #include "serialization/markup.hpp"
63 #include "wml_exception.hpp"
64 
65 #include <functional>
66 #include <numeric>
67 
68 namespace gui2::dialogs
69 {
70 namespace
71 {
72 template<typename W>
73 void disable_widget_on_toggle(window& window, widget& w, const std::string& id)
74 {
75  window.find_widget<W>(id).set_active(dynamic_cast<selectable_item&>(w).get_value_bool());
76 }
77 
78 template<typename W>
79 void disable_widget_on_toggle_inverted(window& window, widget& w, const std::string& id)
80 {
81  window.find_widget<W>(id).set_active(!dynamic_cast<selectable_item&>(w).get_value_bool());
82 }
83 
84 } // end anon namespace
85 
86 REGISTER_DIALOG(preferences_dialog)
87 
88 preferences_dialog::preferences_dialog(const pref_constants::PREFERENCE_VIEW initial_view, bool allow_gui2_theme_change)
89  : modal_dialog(window_id())
90  , resolutions_() // should be populated by set_resolution_list before use
91  , themes_() // populated by set_theme_list
92  , gui2_themes_() // populated by set_gui2_theme_list
93  , last_selected_item_(0)
94  , current_gui_theme_(0)
95  , is_reload_needed_(false)
96  , allow_gui2_theme_change_(allow_gui2_theme_change)
97  , accl_speeds_({0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 3, 4, 8, 16})
98  , visible_hotkeys_()
99  , visible_categories_()
100  , initial_index_(pef_view_map[initial_view])
101 {
102  initialize_callbacks();
103 }
104 
105 // Helper function to refresh resolution list
107 {
109 
110  std::vector<config> options;
111  for(const point& res : resolutions_) {
112  config option;
113  option["label"] = formatter() << res.x << font::unicode_multiplication_sign << res.y;
114 
115  const int div = std::gcd(res.x, res.y);
116  const int x_ratio = res.x / div;
117  const int y_ratio = res.y / div;
118 
119  if(x_ratio <= 10 || y_ratio <= 10) {
120  option["details"] = markup::span_color("#777777", "(", x_ratio, ':', y_ratio, ")");
121  }
122 
123  options.push_back(std::move(option));
124  }
125 
126  const unsigned current_res = std::distance(resolutions_.begin(), std::find(
128 
129  res_list.set_values(options, current_res);
130 }
131 
133 {
135 
136  std::vector<config> options;
137  std::size_t current_theme = 0;
138  for(std::size_t i = 0; i < themes_.size(); ++i) {
139  options.emplace_back("label", themes_[i].name, "tooltip", themes_[i].description);
140  if(themes_[i].id == prefs::get().theme()) {
141  current_theme = i;
142  }
143  }
144 
145  theme_list.set_values(options, current_theme);
146 }
147 
149 {
150  std::string current_gui_theme_name = prefs::get().gui2_theme();
151 
152  std::vector<config> options;
153  bool theme_found = false;
154  unsigned i = 0;
155  for(auto& gui : guis) {
156  gui2_themes_.emplace_back(gui.first);
157  options.emplace_back("label", gui.second.description());
158  if (current_gui_theme_name == gui.first) {
160  theme_found = true;
161  }
162  if (!theme_found) {
163  i++;
164  }
165  }
166 
167  theme_list.set_values(options);
168  theme_list.set_selected(current_gui_theme_);
169 }
170 
172 {
174  widget_item item;
175 
176  std::string image = "friend.png";
177  std::string descriptor = _("friend");
178  std::string notes;
179 
180  if(entry.get_status() == "ignore") {
181  image = "ignore.png";
182  descriptor = _("ignored");
183  }
184 
185  if(!entry.get_notes().empty()) {
186  notes = " " + markup::tag("small", "(", entry.get_notes(), ")");
187  }
188 
189  item["use_markup"] = "true";
190 
191  item["label"] = "misc/status-" + image;
192  data.emplace("friend_icon", item);
193 
194  item["label"] = entry.get_nick() + notes;
195  data.emplace("friend_name", item);
196 
197  item["label"] = markup::tag("small", descriptor);
198  data.emplace("friend_status", item);
199 
200  return data;
201 }
202 
204 {
205  const int num_friends = prefs::get().get_acquaintances().size();
206  const int sel = list.get_selected_row();
207 
208  if(sel < 0 || sel >= num_friends) {
209  return;
210  }
211 
212  std::map<std::string, preferences::acquaintance>::const_iterator who = prefs::get().get_acquaintances().begin();
213  std::advance(who, sel);
214 
215  textbox.set_value(who->second.get_nick() + " " + who->second.get_notes());
216 }
217 
219 {
220  const bool list_empty = list.get_item_count() == 0;
221 
222  if(!list_empty) {
223  list.select_row(std::min(static_cast<int>(list.get_item_count()) - 1, list.get_selected_row()));
224  }
225 
226  find_widget<button>("remove").set_active(!list_empty);
227 
228  find_widget<label>("no_friends_notice").set_visible(list_empty);
229 }
230 
231 void preferences_dialog::add_friend_list_entry(const bool is_friend, text_box& textbox)
232 {
233  std::string username = textbox.text();
234  if(username.empty()) {
235  gui2::show_transient_message("", _("No username specified"));
236  return;
237  }
238 
239  std::string reason;
240 
241  std::size_t pos = username.find_first_of(' ');
242  if(pos != std::string::npos) {
243  reason = username.substr(pos + 1);
244  username = username.substr(0, pos);
245  }
246 
247  auto [entry, added_new] = prefs::get().add_acquaintance(username, (is_friend ? "friend": "ignore"), reason);
248 
249  if(!entry) {
250  gui2::show_transient_message(_("Error"), _("Invalid username"));
251  return;
252  }
253 
254  textbox.clear();
255 
256  listbox& list = find_widget<listbox>("friends_list");
257 
258  //
259  // If this is a new entry, just add a new row. If it's not, we find the relevant
260  // row, remove it, and add a new row with the updated data. Should probably come
261  // up with a more elegant way to do .. the only reason I'm using the remove
262  // -and-replace method is to prevent any issues with the widgets' layout sizes.
263  //
264  if(added_new) {
265  list.add_row(get_friends_list_row_data(*entry));
266  } else {
267  for(unsigned i = 0; i < list.get_item_count(); ++i) {
268  grid* row_grid = list.get_row_grid(i);
269 
270  if(row_grid->find_widget<label>("friend_name").get_label() == entry->get_nick()) {
271  list.remove_row(i);
272  list.add_row(get_friends_list_row_data(*entry), i);
273 
274  break;
275  }
276  }
277  }
278 
280 }
281 
283 {
284  const int selected_row = std::max(0, friends_list.get_selected_row());
285 
286  std::map<std::string, preferences::acquaintance>::const_iterator who = prefs::get().get_acquaintances().begin();
287  std::advance(who, selected_row);
288 
289  const std::string to_remove = !textbox.text().empty() ? textbox.text() : who->second.get_nick();
290 
291  if(to_remove.empty()) {
292  gui2::show_transient_message("", _("No username specified"));
293  return;
294  }
295 
296  if(!prefs::get().remove_acquaintance(to_remove)) {
297  gui2::show_transient_error_message(_("Not on friends or ignore lists"));
298  return;
299  }
300 
301  textbox.clear();
302 
303  listbox& list = find_widget<listbox>("friends_list");
304  list.remove_row(selected_row);
305 
307 }
308 
310 {
311  // Update pixel scale preference.
312  slider& ps_slider = find_widget<slider>("pixel_scale_slider");
313  prefs::get().set_pixel_scale(ps_slider.get_value());
314 
315  // Update auto pixel scale preference.
316  toggle_button& auto_ps_toggle =
317  find_widget<toggle_button>("auto_pixel_scale");
318  prefs::get().set_auto_pixel_scale(auto_ps_toggle.get_value_bool());
319 
320  // Update draw buffers, taking these into account.
322 
323  // Update game display, if active
324  if(::display* disp = display::get_singleton()) {
325  disp->queue_rerender();
326  }
327 
328  // Raise a window resize event so we can react to the change
330 }
331 
332 template<typename ToggleGet, typename ToggleSet, typename VolumeGet, typename VolumeSet>
334  const std::string& id_suffix,
335  ToggleGet&& toggle_getter,
336  ToggleSet&& toggle_setter,
337  VolumeGet&& volume_getter,
338  VolumeSet&& volume_setter)
339 {
340  const std::string toggle_widget_id = "sound_toggle_" + id_suffix;
341  const std::string volume_widget_id = "sound_volume_" + id_suffix;
342 
343  // Set up the volume slider.
344  register_integer(volume_widget_id, true, volume_getter, volume_setter);
345  auto& volume_slider = find_widget<slider>(volume_widget_id);
346 
347  // Callback to actually immediately apply the volume effect. field_integer doesn't
348  // have a callback-on-changed mechanism. To add one would either mean adding it to
349  // the base field class or make it a proper class of is own.
350  connect_signal_notify_modified(volume_slider,
351  [=](widget& w, auto&&...) {
352  std::invoke(volume_setter, dynamic_cast<integer_selector&>(w).get_value());
353  });
354 
355  const auto toggle_changed =
356  [=, &volume_slider](widget& w) {
357  const bool sound_enabled = dynamic_cast<selectable_item&>(w).get_value_bool();
358  std::invoke(toggle_setter, sound_enabled); // FIXME: we should use this return value
359  volume_slider.set_active(sound_enabled);
360  };
361 
362  // Set up the toggle. We utilize field_bool's callback-on-changed mechanism instead
363  // of manually registering the callback. Since we want the effects to apply immediately,
364  // the callback the setter callback is duplicated in the on-change callback. The field
365  // class could possibly use some reworking to make this less redundant, but for now it
366  // works well enough.
367  register_bool(toggle_widget_id, true, toggle_getter, toggle_setter, toggle_changed, true);
368 }
369 
370 /**
371  * Sets up states and callbacks for each of the widgets
372  */
374 {
375  //
376  // GENERAL PANEL
377  //
378 
379  /* SCROLL SPEED */
380  register_integer("scroll_speed", true,
381  []() {return prefs::get().scroll_speed();},
382  [](int v) {prefs::get().set_scroll_speed(v);});
383 
384  /* ACCELERATED SPEED */
385  register_bool("turbo_toggle", true,
386  []() {return prefs::get().turbo();},
387  [](bool v) {prefs::get().set_turbo(v);});
388 
389  const auto accl_load = [this]()->int {
390  return std::distance(accl_speeds_.begin(), std::find(accl_speeds_.begin(), accl_speeds_.end(), prefs::get().turbo_speed()));
391  };
392 
393  const auto accl_save = [this](int i) {
394  prefs::get().set_turbo_speed(accl_speeds_[i]);
395  };
396 
397  register_integer("turbo_slider", true,
398  accl_load, accl_save);
399 
400  // Set the value label transform function.
401  find_widget<slider>("turbo_slider").set_value_labels(
402  // TODO: this should probably be a locale dependent string (use comma/dot depending on language)
403  [this](int pos, int /*max*/)->t_string { return lexical_cast<std::string>(accl_speeds_[pos]); }
404  );
405 
406  /* SKIP AI MOVES */
407  register_bool("skip_ai_moves", true,
408  []() {return prefs::get().skip_ai_moves();},
409  [](bool v) {prefs::get().set_skip_ai_moves(v);});
410 
411  /* DISABLE AUTO MOVES */
412  register_bool("disable_auto_moves", true,
413  []() {return prefs::get().disable_auto_moves();},
414  [](bool v) {prefs::get().set_disable_auto_moves(v);});
415 
416  /* TURN DIALOG */
417  register_bool("show_turn_dialog", true,
418  []() {return prefs::get().turn_dialog();},
419  [](bool v) {prefs::get().set_turn_dialog(v);});
420 
421  /* ENABLE PLANNING MODE */
422  register_bool("whiteboard_on_start", true,
423  []() {return prefs::get().enable_planning_mode_on_start();},
424  [](bool v) {prefs::get().set_enable_planning_mode_on_start(v);});
425 
426  /* HIDE ALLY PLANS */
427  register_bool("whiteboard_hide_allies", true,
428  []() {return prefs::get().hide_whiteboard();},
429  [](bool v) {prefs::get().set_hide_whiteboard(v);});
430 
431  /* INTERRUPT ON SIGHTING */
432  register_bool("interrupt_move_when_ally_sighted", true,
433  []() {return prefs::get().ally_sighted_interrupts();},
434  [](bool v) {prefs::get().set_ally_sighted_interrupts(v);});
435 
436  /* SAVE REPLAYS */
437  register_bool("save_replays", true,
438  []() {return prefs::get().save_replays();},
439  [](bool v) {prefs::get().set_save_replays(v);});
440 
441  /* DELETE AUTOSAVES */
442  register_bool("delete_saves", true,
443  []() {return prefs::get().delete_saves();},
444  [](bool v) {prefs::get().set_delete_saves(v);});
445 
446  /* MAX AUTO SAVES */
447  register_integer("max_saves_slider", true,
448  []() {return prefs::get().auto_save_max();},
449  [](int v) {prefs::get().set_auto_save_max(v);});
450 
451  /* CACHE MANAGE */
452  connect_signal_mouse_left_click(find_widget<button>("cachemg"),
453  [](auto&&...) { dialogs::game_cache_options::display(); });
454 
455  /* DISPLAY->ACCESSIBILITY */
456  connect_signal_mouse_left_click(find_widget<button>("custom_orb_color"),
457  [](auto&&...) { dialogs::select_orb_colors::display(); });
458  connect_signal_mouse_left_click(find_widget<button>("custom_reach_map"),
459  [](auto&&...) { dialogs::reachmap_options::display(); });
460 
461  //
462  // DISPLAY PANEL
463  //
464 
465  /* FULLSCREEN TOGGLE */
466  toggle_button& toggle_fullscreen = find_widget<toggle_button>("fullscreen");
467  toggle_fullscreen.set_value(prefs::get().fullscreen());
468 #ifdef __ANDROID__
469  toggle_fullscreen.set_active(false);
470 #endif
471 
472  // We bind a special callback function, so setup_single_toggle() is not used
474  [this](auto&&...) { fullscreen_toggle_callback(); });
475 
476  /* SET RESOLUTION */
477  menu_button& res_list = find_widget<menu_button>("resolution_set");
478 
479  res_list.set_use_markup(true);
480 #ifdef __ANDROID__
481  res_list.set_active(false);
482 #else
483  res_list.set_active(!prefs::get().fullscreen());
484 #endif
485 
486  set_resolution_list(res_list);
487 
489  [this](auto&&...) { handle_res_select(); });
490 
491  connect_signal<event::SDL_VIDEO_RESIZE>(
492  [this, &res_list](auto&&...) { set_resolution_list(res_list); });
493 
494  // Toggling full-screen via hot-key by-passes fullscreen_toggle_callback()
495  // So watch for the window actually entering/leaving full-screen to refresh
496  // resolution list enablement and full-screen status
497  connect_signal<event::SDL_WINDOW_ENTER_FULLSCREEN>(
498  [this](auto&&...) { fullscreen_entered_callback(); });
499 
500  connect_signal<event::SDL_WINDOW_LEAVE_FULLSCREEN>(
501  [this](auto&&...) { fullscreen_left_callback(); });
502 
503  /* PIXEL SCALE */
504  register_integer("pixel_scale_slider", true,
505  []() {return prefs::get().pixel_scale();},
506  [](int v) {prefs::get().set_pixel_scale(v);});
507 
508  slider& ps_slider = find_widget<slider>("pixel_scale_slider");
509  ps_slider.set_value_range(1, video::get_max_pixel_scale());
511  [this](auto&&...) { apply_pixel_scale(); });
512 
513  /* AUTOMATIC PIXEL SCALE */
514  register_bool("auto_pixel_scale", true,
515  []() {return prefs::get().auto_pixel_scale();},
516  [](bool v) {prefs::get().set_auto_pixel_scale(v);},
517  [&](widget& w) { disable_widget_on_toggle_inverted<slider>(*this, w, "pixel_scale_slider"); }, true);
518 
519  if (video::get_max_pixel_scale() <= 1) {
520  find_widget<slider>("pixel_scale_slider").set_active(false);
521  find_widget<toggle_button>("auto_pixel_scale").set_active(false);
522  }
523 
524  toggle_button& auto_ps_toggle =
525  find_widget<toggle_button>("auto_pixel_scale");
526  connect_signal_mouse_left_click(auto_ps_toggle,
527  [this](auto&&...) { apply_pixel_scale(); });
528 
529  /* SHOW TIPS PANEL ON TITLESCREEN */
530  register_bool("show_tips", true,
531  []() {return prefs::get().show_tips();},
532  [&](bool v) {
533  // if changed once: different value, reload
534  // if changed twice: double toggle, same value, don't reload
536  prefs::get().set_show_tips(v);
537  });
538 
539  /* SHOW FLOATING LABELS */
540  register_bool("show_floating_labels", true,
541  []() {return prefs::get().floating_labels();},
542  [](bool v) {prefs::get().set_floating_labels(v);});
543 
544  /* SHOW TEAM COLORS */
545  register_bool("show_ellipses", true,
546  []() {return prefs::get().show_side_colors();},
547  [](bool v) {prefs::get().set_show_side_colors(v);});
548 
549  /* SHOW GRID */
550  register_bool("show_grid", true,
551  []() {return prefs::get().grid();},
552  [](bool v) {prefs::get().set_grid(v);});
553 
554  /* ANIMATE MAP */
555  register_bool("animate_terrains", true,
556  []() {return prefs::get().animate_map();},
557  [](bool v) {prefs::get().set_animate_map(v);},
558  [&](widget& w) { disable_widget_on_toggle<toggle_button>(*this, w, "animate_water"); }, true);
559 
560  /* ANIMATE WATER */
561  register_bool("animate_water", true,
562  []() {return prefs::get().animate_water();},
563  [](bool v) {prefs::get().set_animate_water(v);});
564 
565  /* SHOW UNIT STANDING ANIMS */
566  register_bool("animate_units_standing", true,
567  []() {return prefs::get().show_standing_animations();},
568  [](bool v) {prefs::get().set_show_standing_animations(v);});
569 
570  /* SHOW UNIT IDLE ANIMS */
571  register_bool("animate_units_idle", true,
572  []() {return prefs::get().idle_anim();},
573  [](bool v) {prefs::get().set_idle_anim(v);},
574  [&](widget& w) { disable_widget_on_toggle<slider>(*this, w, "idle_anim_frequency"); }, true);
575 
576  register_integer("idle_anim_frequency", true,
577  []() {return prefs::get().idle_anim_rate();},
578  [](int v) {prefs::get().set_idle_anim_rate(v);});
579 
580  /* FONT SCALING */
581  //register_integer("scaling_slider", true,
582  // font_scaling, set_font_scaling);
583 
584  /* VSYNC */
585  register_bool("vsync", true,
586  []() {return prefs::get().vsync();},
587  [](bool v) {prefs::get().set_vsync(v);});
588 
589  /* SELECT THEME */
590  menu_button& theme_list = find_widget<menu_button>("choose_theme");
593  [this](auto&&...) { handle_theme_select(); });
594 
595  /* SELECT GUI2 THEME */
596  menu_button& gui2_theme_list = find_widget<menu_button>("choose_gui2_theme");
597  button& apply_btn = find_widget<button>("apply");
598  set_gui2_theme_list(gui2_theme_list);
599  connect_signal_notify_modified(gui2_theme_list, [&](auto&&...) { apply_btn.set_active(true); });
600  apply_btn.set_active(false);
602  [this](auto&&...) { handle_gui2_theme_select(); });
603 
605  // Switching the GUI2 theme only affects windows built afterwards, so it's only
606  // useful from the title screen, which rebuilds itself on RELOAD_UI. Elsewhere,
607  // leave the control visible but non-interactive rather than have it silently
608  // do nothing until the next restart.
609  gui2_theme_list.set_active(false);
610  }
611 
612  //
613  // SOUND PANEL
614  //
615 
616  /* SOUND FX */
618  [] { return prefs::get().sound(); },
619  [](bool v) { return prefs::get().set_sound(v); },
620  [] { return prefs::get().sound_volume().as_percent(); },
622 
623  /* MUSIC */
625  [] { return prefs::get().music_on(); },
626  [](bool v) { return prefs::get().set_music(v); },
627  [] { return prefs::get().music_volume().as_percent(); },
629 
630  register_bool("sound_toggle_stop_music_in_background", true,
631  []() {return prefs::get().stop_music_in_background();},
632  [](bool v) {prefs::get().set_stop_music_in_background(v);});
633 
634  /* TURN BELL */
636  [] { return prefs::get().turn_bell(); },
637  [](bool v) { return prefs::get().set_turn_bell(v); },
638  [] { return prefs::get().bell_volume().as_percent(); },
640 
641  /* UI FX */
643  [] { return prefs::get().ui_sound_on(); },
644  [](bool v) { return prefs::get().set_ui_sound(v); },
645  [] { return prefs::get().ui_volume().as_percent(); },
647 
648  //
649  // MULTIPLAYER PANEL
650  //
651 
652  /* GENERAL TAB */
653 
654  /* CHAT LINES */
655  register_integer("chat_lines", true,
656  []() {return prefs::get().chat_lines();},
657  [](int v) {prefs::get().set_chat_lines(v);});
658 
659  /* CHAT TIMESTAMPPING */
660  register_bool("chat_timestamps", true,
661  []() {return prefs::get().chat_timestamp();},
662  [](bool v) {prefs::get().set_chat_timestamp(v);});
663 
664  /* SAVE PASSWORD */
665  register_bool("remember_password", true,
666  []() {return prefs::get().remember_password();},
667  [](bool v) {prefs::get().set_remember_password(v);});
668 
669  /* WHISPERS FROM FRIENDS ONLY */
670  register_bool("lobby_whisper_friends_only", true,
671  []() {return prefs::get().lobby_whisper_friends_only();},
672  [](bool v) {prefs::get().set_lobby_whisper_friends_only(v);});
673 
674  /* LOBBY JOIN NOTIFICATIONS */
675  lobby_joins_group.add_member(find_widget<toggle_button>("lobby_joins_none", false, true), pref_constants::lobby_joins::show_none);
676  lobby_joins_group.add_member(find_widget<toggle_button>("lobby_joins_friends", false, true), pref_constants::lobby_joins::show_friends);
677  lobby_joins_group.add_member(find_widget<toggle_button>("lobby_joins_all", false, true), pref_constants::lobby_joins::show_all);
678 
680 
683  });
684 
685  /* ALERTS */
686  connect_signal_mouse_left_click(find_widget<button>("mp_alerts"),
687  [](auto&&...) { mp_alerts_options::display(); });
688 
689  /* SET WESNOTHD PATH */
690  connect_signal_mouse_left_click(find_widget<button>("mp_wesnothd"),
691  [](auto&&...) { prefs::get().show_wesnothd_server_search(); });
692 
693 
694  /* FRIENDS TAB */
695 
696  listbox& friends_list = find_widget<listbox>("friends_list");
697  friends_list.clear();
698 
699  for(const auto& entry : prefs::get().get_acquaintances()) {
700  friends_list.add_row(get_friends_list_row_data(entry.second));
701  }
702 
703  update_friends_list_controls(friends_list);
704 
705  text_box& textbox = find_widget<text_box>("friend_name_box");
706 
707  connect_signal_mouse_left_click(find_widget<button>("add_friend"),
708  [&, this](auto&&...) { add_friend_list_entry(true, textbox); });
709 
710  connect_signal_mouse_left_click(find_widget<button>("add_ignored"),
711  [&, this](auto&&...) { add_friend_list_entry(false, textbox); });
712 
713  connect_signal_mouse_left_click(find_widget<button>("remove"),
714  [&, this](auto&&...) { remove_friend_list_entry(friends_list, textbox); });
715 
716  connect_signal_notify_modified(friends_list,
717  [&, this](auto&&...) { on_friends_list_select(friends_list, textbox); });
718 
719  //
720  // ADVANCED PANEL
721  //
722 
723  listbox& advanced = find_widget<listbox>("advanced_prefs");
724 
725  widget_data row_data;
726 
727  for(const auto& option : prefs::get().get_advanced_preferences()) {
728  const std::string pref_name = option.field;
729 
730  row_data["pref_name"]["label"] = option.name;
731  grid* main_grid = &advanced.add_row(row_data);
732 
733  grid& details_grid = main_grid->find_widget<grid>("prefs_setter_grid");
735 
736  // The toggle widget for toggle-type options (hidden for other types)
737  toggle_button& toggle_box = main_grid->find_widget<toggle_button>("value_toggle");
739 
740  if(!option.description.empty()) {
741  main_grid->find_widget<styled_widget>("description").set_label(option.description);
742  }
743 
744  switch(option.type) {
746 
748  toggle_box.set_value(preferences_dialog_friend::get(pref_name, option.cfg["default"].to_bool()));
749 
751  [&, pref_name](auto&&...) { preferences_dialog_friend::set(pref_name, toggle_box.get_value_bool()); });
752 
753  gui2::bind_default_status_label(toggle_box, "value");
754  break;
755  }
756 
758  auto setter_widget = build_single_widget_instance<slider>(config {"definition", "minimal"});
759  setter_widget->set_id("setter");
760  // Maximum must be set first or this will assert
761  setter_widget->set_value_range(option.cfg["min"].to_int(), option.cfg["max"].to_int());
762  setter_widget->set_step_size(option.cfg["step"].to_int(1));
763 
764  details_grid.swap_child("setter", std::move(setter_widget), true);
765 
766  slider& slide = details_grid.find_widget<slider>("setter");
767 
768  slide.set_value(preferences_dialog_friend::get(pref_name, option.cfg["default"].to_int()));
769 
771  [&, pref_name](auto&&...) { preferences_dialog_friend::set(pref_name, slide.get_value()); });
772 
773  gui2::bind_default_status_label(slide, "value");
774  break;
775  }
776 
778  std::vector<config> menu_data;
779  std::vector<std::string> option_ids;
780 
781  for(const config& choice : option.cfg.child_range("option")) {
782  config menu_item;
783  menu_item["label"] = choice["name"];
784  if(choice.has_attribute("description")) {
785  menu_item["details"] = markup::span_color("#777", choice["description"]);
786  }
787  menu_data.push_back(menu_item);
788  option_ids.push_back(choice["id"]);
789  }
790 
791  // Attempt to find an initial selection
792  int selected = std::distance(option_ids.begin(), std::find(option_ids.begin(), option_ids.end(),
793  preferences_dialog_friend::get(pref_name, option.cfg["default"].str())
794  ));
795 
796  // If the saved option value was invalid, reset selection to 0.
797  if(selected < 0 || selected >= static_cast<int>(option_ids.size())) {
798  selected = 0;
799  }
800 
801  auto setter_widget = build_single_widget_instance<menu_button>();
802  setter_widget->set_id("setter");
803 
804  details_grid.swap_child("setter", std::move(setter_widget), true);
805 
806  menu_button& menu = details_grid.find_widget<menu_button>("setter");
807 
808  menu.set_use_markup(true);
809  menu.set_values(menu_data, selected);
810 
812  [=](widget& w, auto&&...) { preferences_dialog_friend::set(pref_name, option_ids[dynamic_cast<menu_button&>(w).get_value()]); });
813 
814  gui2::bind_default_status_label(menu, "value");
815  break;
816  }
817 
819  //main_grid->remove_child("setter");
820 
821  auto value_widget = build_single_widget_instance<image>();
822  value_widget->set_label("icons/arrows/arrows_blank_right_25.png~CROP(3,3,18,18)");
823 
824  main_grid->swap_child("value", std::move(value_widget), true);
825  break;
826  }
827  }
828  }
829 
831  [this, &advanced](auto&&...) { on_advanced_prefs_list_select(advanced); });
832 
833  on_advanced_prefs_list_select(advanced, true);
834 
835  //
836  // HOTKEYS PANEL
837  //
838 
839  multimenu_button& hotkey_menu = find_widget<multimenu_button>("hotkey_category_menu");
840  connect_signal_notify_modified(hotkey_menu,
841  [this](auto&&...) { hotkey_filter_callback(); });
842 
844 
845  text_box& filter = find_widget<text_box>("filter");
846  filter.on_modified([this](const auto&) { hotkey_filter_callback(); });
847 
848  hotkey_list.set_sorters(
849  // Action column
850  [this](const std::size_t i) { return visible_hotkeys_[i]->description; },
851 
852  // Hotkey column
853  [this](const std::size_t i) { return hotkey::get_names(visible_hotkeys_[i]->id); },
854 
855  // Scope columns
856  [this](const std::size_t i) { return !visible_hotkeys_[i]->scope[hotkey::SCOPE_GAME]; },
857  [this](const std::size_t i) { return !visible_hotkeys_[i]->scope[hotkey::SCOPE_EDITOR]; },
858  [this](const std::size_t i) { return !visible_hotkeys_[i]->scope[hotkey::SCOPE_MAIN_MENU]; }
859  );
860 
861  hotkey_list.set_active_sorter("sort_0", sort_order::type::ascending, true);
862 
863  connect_signal_mouse_left_click(find_widget<button>("btn_add_hotkey"),
864  [this, &hotkey_list](auto&&...) { add_hotkey_callback(hotkey_list); });
865 
866  connect_signal_mouse_left_click(find_widget<button>("btn_clear_hotkey"),
867  [this, &hotkey_list](auto&&...) { remove_hotkey_callback(hotkey_list); });
868 
869  connect_signal_mouse_left_click(find_widget<button>("btn_reset_hotkeys"),
870  [this](auto&&...) { default_hotkey_callback(); });
871 }
872 
874 {
875  widget_data row_data;
876 
877  t_string& row_icon = row_data["img_icon"]["label"];
878  t_string& row_action = row_data["lbl_desc"]["label"];
879  t_string& row_hotkey = row_data["lbl_hotkey"]["label"];
880 
881  t_string& row_is_g = row_data["lbl_is_game"]["label"];
882  t_string& row_is_e = row_data["lbl_is_editor"]["label"];
883  t_string& row_is_m = row_data["lbl_is_mainmenu"]["label"];
884 
885  listbox& hotkey_list = find_widget<listbox>("list_hotkeys");
886 
887  hotkey_list.clear();
888  visible_hotkeys_.clear();
889  visible_categories_.clear();
890 
891  //
892  // Main hotkeys list
893  //
894 
895  // These translated initials should match those used in data/gui/window/preferences/02_hotkeys.cfg
896  const std::string gh = markup::span_color("#0f0", _("game_hotkeys^G"));
897  const std::string eh = markup::span_color("#0f0", _("editor_hotkeys^E"));
898  const std::string mh = markup::span_color("#0f0", _("mainmenu_hotkeys^M"));
899 
900  for(const auto& [id, hotkey_item] : hotkey::get_hotkey_commands()) {
901  if(hotkey_item.hidden) {
902  continue;
903  }
904 
905  visible_hotkeys_.push_back(&hotkey_item);
906  visible_categories_.insert(hotkey_item.category);
907 
908  if(filesystem::file_exists(game_config::path + "/images/icons/action/" + hotkey_item.id + "_25.png")) {
909  row_icon = "icons/action/" + hotkey_item.id + "_25.png~CROP(3,3,18,18)";
910  } else {
911  row_icon = "";
912  }
913 
914  row_action = hotkey_item.description;
915  row_hotkey = hotkey::get_names(hotkey_item.id);
916 
917  row_is_g = hotkey_item.scope[hotkey::SCOPE_GAME] ? gh : "";
918  row_is_e = hotkey_item.scope[hotkey::SCOPE_EDITOR] ? eh : "";
919  row_is_m = hotkey_item.scope[hotkey::SCOPE_MAIN_MENU] ? mh : "";
920 
921  hotkey_list.add_row(row_data);
922  }
923 
924  //
925  // Filter options
926  //
927 
928  std::vector<config> filter_ops;
929  for(const hotkey::HOTKEY_CATEGORY& cat : visible_categories_) {
930  filter_ops.emplace_back("label", hotkey::get_translatable_category_name(cat), "checkbox", false);
931  }
932 
933  find_widget<multimenu_button>("hotkey_category_menu").set_values(filter_ops);
934 
935  return hotkey_list;
936 }
937 
939 {
940  int row_number = hotkeys.get_selected_row();
941  if(row_number < 0) {
942  gui2::show_transient_message("", _("No hotkey selected"));
943  return;
944  }
945 
946  const hotkey::hotkey_command& hotkey_item = *visible_hotkeys_[row_number];
947 
948  gui2::dialogs::hotkey_bind bind_dlg(hotkey_item.id);
949  bind_dlg.show();
950 
951  hotkey::hotkey_ptr newhk = bind_dlg.get_new_binding();
952  hotkey::hotkey_ptr oldhk;
953 
954  // only if not cancelled.
955  if(!newhk) {
956  return;
957  }
958 
959  for(const hotkey::hotkey_ptr& hk : hotkey::get_hotkeys()) {
960  if(!hk->is_disabled() && newhk->bindings_equal(hk)) {
961  oldhk = hk;
962  }
963  }
964 
965  if(oldhk && oldhk->get_command() == hotkey_item.id) {
966  return;
967  }
968 
969  if(oldhk && oldhk->get_command() != "null") {
970  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>”?", {
971  {"hotkey_sequence", oldhk->get_name()},
972  {"old_hotkey_action", hotkey::get_hotkey_command(oldhk->get_command()).description},
973  {"new_hotkey_action", hotkey::get_hotkey_command(newhk->get_command()).description}
974  });
975 
976  const int res = gui2::show_message(_("Reassign Hotkey"), text, gui2::dialogs::message::yes_no_buttons, true);
977  if(res != gui2::retval::OK) {
978  return;
979  }
980  }
981 
982  hotkey::add_hotkey(newhk);
983 
984  // We need to recalculate all hotkey names in because we might have removed a hotkey from another command.
985  for(std::size_t i = 0; i < hotkeys.get_item_count(); ++i) {
986  const hotkey::hotkey_command& hotkey_item_row = *visible_hotkeys_[i];
987  hotkeys.get_row_grid(i)->find_widget<label>("lbl_hotkey").set_label(hotkey::get_names(hotkey_item_row.id));
988  }
989 }
990 
992 {
993  gui2::show_transient_message(_("Hotkeys Reset"), _("All hotkeys have been reset to their default values."));
994 
996 
997  find_widget<text_box>("filter").set_value("");
998 
999  // Set up the list again and reselect the default sorting option.
1001  hotkey_list.set_active_sorter("sort_0", sort_order::type::ascending, true);
1002 }
1003 
1005 {
1006  int row_number = hotkeys.get_selected_row();
1007  if(row_number < 0) {
1008  gui2::show_transient_message("", _("No hotkey selected"));
1009  return;
1010  }
1011 
1012  const hotkey::hotkey_command& hotkey_item = *visible_hotkeys_[row_number];
1013  hotkey::clear_hotkeys(hotkey_item.id);
1014  hotkeys.get_row_grid(row_number)->find_widget<label>("lbl_hotkey").set_label(hotkey::get_names(hotkey_item.id));
1015 }
1016 
1018 {
1019  const multimenu_button& hotkey_menu = find_widget<const multimenu_button>("hotkey_category_menu");
1020  const text_box& name_filter = find_widget<const text_box>("filter");
1021 
1022  boost::dynamic_bitset<> toggle_states = hotkey_menu.get_toggle_states();
1023  boost::dynamic_bitset<> res(visible_hotkeys_.size());
1024 
1025  std::string text = name_filter.get_value();
1026 
1027  // Nothing selected. It means that *all* categories are shown.
1028  if(toggle_states.none()) {
1029  toggle_states = ~toggle_states;
1030  }
1031 
1032  for(std::size_t h = 0; h < visible_hotkeys_.size(); ++h) {
1033  // Default to true if there is no filter text
1034  bool found = true;
1035 
1036  if(!text.empty()) {
1037  const std::string description = visible_hotkeys_[h]->description.str();
1038 
1039  for(const auto& word : utils::split(text, ' ')) {
1040  found = translation::ci_search(description, word);
1041 
1042  // No match, we're excluding this hotkey
1043  if(!found) {
1044  break;
1045  }
1046  }
1047  }
1048 
1049  unsigned cat_index = 0;
1050 
1051  // Filter categories
1052  for(const hotkey::HOTKEY_CATEGORY& cat : visible_categories_) {
1053  if(visible_hotkeys_[h]->category == cat) {
1054  break;
1055  } else {
1056  ++cat_index;
1057  }
1058  }
1059 
1060  if(cat_index < toggle_states.size() && found) {
1061  res[h] = toggle_states[cat_index];
1062  } else {
1063  res[h] = false;
1064  }
1065  }
1066 
1067  find_widget<listbox>("list_hotkeys").set_row_shown(res);
1068 }
1069 
1071 {
1072  const int selected_row = list.get_selected_row();
1073  const auto& pref = prefs::get().get_advanced_preferences()[selected_row];
1074 
1075  // In Japanese language sorting, Reach Map Options becomes the first item and is selected
1076  // automatically before the user has made any choice. Skip SPECIAL preferences if
1077  // Preferences is still being built (initial == true). GitHub #11136.
1078  if(!initial && pref.type == preferences::option::avd_type::SPECIAL) {
1079  if(pref.field == "logging") {
1080  gui2::dialogs::log_settings::display();
1081  } else {
1082  WRN_GUI_L << "Invalid or unimplemented custom advanced prefs option: " << pref.field;
1083  }
1084 
1085  // Add more options here as needed
1086  }
1087 
1088  const bool has_description = !pref.description.empty();
1089 
1090  if(has_description || (pref.type != preferences::option::avd_type::SPECIAL && pref.type != preferences::option::avd_type::TOGGLE)) {
1091  list.get_row_grid(selected_row)->find_widget<widget>("prefs_setter_grid")
1093  }
1094 
1095  if(last_selected_item_ != selected_row) {
1096  list.get_row_grid(last_selected_item_)->find_widget<widget>("prefs_setter_grid")
1098 
1099  last_selected_item_ = selected_row;
1100  }
1101 }
1102 
1104 {
1105  set_always_save_fields(true);
1106 
1107  connect_signal_mouse_left_click(find_widget<button>("about"),
1108  [](auto&&...) { game_version::display(); });
1109 
1110  //
1111  // Status labels
1112  // These need to be set here in pre_show, once the fields are initialized. For some reason, this
1113  // is not the case for those in Advanced
1114  //
1115  gui2::bind_default_status_label(find_widget<slider>("max_saves_slider"));
1116  gui2::bind_default_status_label(find_widget<slider>("turbo_slider"));
1117  gui2::bind_default_status_label(find_widget<slider>("pixel_scale_slider"));
1118 }
1119 
1120 // Special fullscreen callback
1122 {
1123  const bool ison = find_widget<toggle_button>("fullscreen").get_value_bool();
1124  video::set_fullscreen(ison);
1125 
1126  menu_button& res_list = find_widget<menu_button>("resolution_set");
1127 
1128  set_resolution_list(res_list);
1129  res_list.set_active(!ison);
1130 }
1131 
1133 {
1134  find_widget<toggle_button>("fullscreen").set_value(true);
1135 
1136  menu_button& res_list = find_widget<menu_button>("resolution_set");
1137 
1138  set_resolution_list(res_list);
1139  res_list.set_active(false);
1140 }
1141 
1143 {
1144  find_widget<toggle_button>("fullscreen").set_value(false);
1145 
1146  menu_button& res_list = find_widget<menu_button>("resolution_set");
1147 
1148  set_resolution_list(res_list);
1149  res_list.set_active(true);
1150 }
1151 
1153 {
1154  menu_button& res_list = find_widget<menu_button>("resolution_set");
1155 
1156  if(video::set_resolution(resolutions_[res_list.get_value()])) {
1157  set_resolution_list(res_list);
1158  }
1159 }
1160 
1162 {
1163  menu_button& theme_list = find_widget<menu_button>("choose_theme");
1164 
1165  const auto selection = theme_list.get_value();
1166  const auto& theme = themes_.at(selection);
1167  auto* display = display::get_singleton();
1168 
1169  prefs::get().set_theme(theme.id);
1170  if(display && resources::gamedata && resources::gamedata->get_theme().empty()) {
1171  display->set_theme(theme.id);
1172  }
1173 
1174 }
1175 
1177 {
1178  menu_button& gui2_theme_list = find_widget<menu_button>("choose_gui2_theme");
1179  unsigned selected_theme = gui2_theme_list.get_value();
1180  if (selected_theme != current_gui_theme_) {
1181  current_gui_theme_ = selected_theme;
1182  prefs::get().set_gui2_theme(gui2_themes_.at(selected_theme));
1184  }
1185 }
1186 
1188 {
1190 
1191  // Save new prefs to disk. This also happens on app close, but doing
1192  // it here too ensures nothing is lost in case of, say, a crash.
1194 
1195  // Needed for applying changes to tip panel visiblity on dialog close
1196  if (is_reload_needed_) {
1198  }
1199 }
1200 
1201 } // 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:246
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 on_advanced_prefs_list_select(listbox &tree, const bool initial=false)
Callback for selection changes.
void remove_hotkey_callback(listbox &hotkeys)
void set_resolution_list(menu_button &res_list)
void initialize_sound_option_group(const std::string &id_suffix, ToggleGet &&toggle_getter, ToggleSet &&toggle_setter, VolumeGet &&volume_getter, VolumeSet &&volume_setter)
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 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
Small abstract helper class.
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.
Small abstract helper class.
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:94
const std::string & get_nick() const
Definition: preferences.hpp:93
const std::string & get_notes() const
Definition: preferences.hpp:95
void set_lobby_joins(pref_constants::lobby_joins show)
bool set_music(bool ison)
void set_remember_password(bool remember)
const std::map< std::string, preferences::acquaintance > & get_acquaintances()
void set_turbo(bool ison)
sound::volume music_volume()
void set_sound_volume(sound::volume vol)
void save_hotkeys()
bool turn_bell()
void write_preferences()
sound::volume sound_volume()
bool set_ui_sound(bool ison)
static prefs & get()
sound::volume bell_volume()
int scroll_speed()
void set_theme(const std::string &theme)
bool sound()
void show_wesnothd_server_search()
bool set_turn_bell(bool ison)
sound::volume ui_volume()
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)
void set_music_volume(sound::volume vol)
std::pair< preferences::acquaintance *, bool > add_acquaintance(const std::string &nick, const std::string &mode, const std::string &notes)
bool ui_sound_on()
int pixel_scale()
bool turbo()
const std::vector< preferences::option > & get_advanced_preferences()
void set_bell_volume(sound::volume vol)
bool remember_password()
void clear_hotkeys()
bool show_standing_animations()
void set_ui_volume(sound::volume vol)
constexpr float as_percent() const
Definition: sound.hpp:124
constexpr static volume from_percent(float percentage)
Definition: sound.hpp:121
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:1031
static std::string _(const char *str)
Definition: gettext.hpp:100
#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:728
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:344
const std::string unicode_multiplication_sign
Definition: constants.cpp:46
std::string path
Definition: filesystem.cpp:106
REGISTER_DIALOG(editor_edit_unit)
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:42
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:142
std::vector< point > get_available_resolutions(const bool include_current)
Returns the list of available screen resolutions.
Definition: video.cpp:743
int get_max_pixel_scale()
Definition: video.cpp:485
point current_resolution()
The current window size in desktop coordinates.
Definition: video.cpp:797
void set_fullscreen(bool fullscreen)
Set the fullscreen state.
Definition: video.cpp:805
bool set_resolution(const point &resolution)
Set the window resolution.
Definition: video.cpp:830
void toggle_fullscreen()
Toggle fullscreen mode.
Definition: video.cpp:825
void update_buffers(bool autoupdate)
Update buffers to match current resolution and pixel scale settings.
Definition: video.cpp:860
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