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