31 #define DBG_DP LOG_STREAM(debug, log_display)
32 #define LOG_DP LOG_STREAM(info, log_display)
33 #define ERR_DP LOG_STREAM(err, log_display)
39 const color_t DefaultFontRGB {200, 200, 200};
41 _rect ref_rect {0, 0, 0, 0};
44 static std::size_t
compute(std::string
expr, std::size_t ref1, std::size_t ref2 = 0)
50 }
else if((
expr[0] ==
'+') || (
expr[0] ==
'-')) {
54 return ref + atoi(
expr.c_str());
61 std::vector<std::string> items =
utils::split(cfg[
"rect"].str());
63 rect.x1 = atoi(items[0].c_str());
66 rect.y1 = atoi(items[1].c_str());
69 rect.x2 = atoi(items[2].c_str());
74 rect.y2 = atoi(items[3].c_str());
96 std::stringstream resolved;
97 const std::vector<std::string> items =
utils::split(rect_str.c_str());
98 if(items.size() >= 1) {
99 rect.x1 =
compute(items[0], ref_rect.x1, ref_rect.x2);
102 if(items.size() >= 2) {
103 rect.y1 =
compute(items[1], ref_rect.y1, ref_rect.y2);
104 resolved <<
"," <<
rect.y1;
106 if(items.size() >= 3) {
108 resolved <<
"," <<
rect.x2;
110 if(items.size() >= 4) {
112 resolved <<
"," <<
rect.y2;
118 return resolved.str();
123 static config empty_config;
128 if(
i->cfg[
"id"] ==
id) {
139 if(&
c != &empty_config) {
153 return find_ref(std::string(
id), cfg);
181 for(
const auto& resolution : resolutions.
child_range(
"resolution")) {
182 if(resolution[
"id"] ==
id) {
187 throw config::error(
"[partialresolution] refers to non-existent [resolution] " +
id);
205 for(
const auto& resolution :
theme.child_range(
"resolution")) {
206 result.
add_child(
"resolution", resolution);
210 for(
const auto& part :
theme.child_range(
"partialresolution")) {
214 for(
const auto&
remove : part.child_range(
"remove")) {
220 for(
const auto& change : part.child_range(
"change")) {
228 for(
const auto& add : part.child_range(
"add")) {
229 for(
const auto [key, cfg] : add.all_children_view()) {
234 result.
add_child(
"resolution", resolution);
245 do_resolve_rects(child_cfg, dest, child_key ==
"resolution" ? &dest : resol_cfg);
252 if(!cfg[
"ref"].empty()) {
253 if(resol_cfg ==
nullptr) {
254 ERR_DP <<
"Use of ref= outside a [resolution] block";
259 if(ref[
"id"].empty()) {
260 ERR_DP <<
"Reference to non-existent rect id \"" << cfg[
"ref"] <<
"\"";
261 }
else if(ref[
"rect"].empty()) {
262 ERR_DP <<
"Reference to id \"" << cfg[
"ref"] <<
"\" which does not have a \"rect\"";
269 if(!cfg[
"rect"].empty()) {
275 : location_modified_(false)
288 : location_modified_(false)
293 , xanchor_(read_anchor(cfg[
"xanchor"]))
294 , yanchor_(read_anchor(cfg[
"yanchor"]))
309 :
size(cfg[
"border_size"].to_double())
310 , background_image(cfg[
"background_image"])
311 , tile_image(cfg[
"tile_image"])
312 , show_border(cfg[
"show_border"].to_bool(true))
314 VALIDATE(
size >= 0.0 &&
size <= 0.5,
_(
"border_size should be between 0.0 and 0.5."));
319 if(last_screen_ == screen && !location_modified_)
320 return relative_loc_;
322 last_screen_ = screen;
326 relative_loc_.x =
loc_.
x;
327 relative_loc_.w =
loc_.w;
331 relative_loc_.w =
loc_.w + screen.w - std::min<std::size_t>(spec_width_,
loc_.w + screen.w);
333 case BOTTOM_ANCHORED:
334 relative_loc_.x =
loc_.
x + screen.w - std::min<std::size_t>(spec_width_,
loc_.
x + screen.w);
335 relative_loc_.w =
loc_.w;
338 relative_loc_.
x = (
loc_.
x * screen.w) / spec_width_;
339 relative_loc_.w = (
loc_.w * screen.w) / spec_width_;
347 relative_loc_.y =
loc_.
y;
348 relative_loc_.h =
loc_.h;
352 relative_loc_.h =
loc_.h + screen.h - std::min<std::size_t>(spec_height_,
loc_.h + screen.h);
354 case BOTTOM_ANCHORED:
355 relative_loc_.y =
loc_.
y + screen.h - std::min<std::size_t>(spec_height_,
loc_.
y + screen.h);
356 relative_loc_.h =
loc_.h;
359 relative_loc_.
y = (
loc_.
y * screen.h) / spec_height_;
360 relative_loc_.h = (
loc_.h * screen.h) / spec_height_;
366 relative_loc_.w = std::min<int>(relative_loc_.w, screen.w);
367 if(relative_loc_.x > screen.w) {
368 relative_loc_.x = std::min<int>(relative_loc_.x, screen.w - relative_loc_.w);
370 relative_loc_.w = std::min<int>(relative_loc_.w, screen.w - relative_loc_.x);
372 relative_loc_.h = std::min<int>(relative_loc_.h, screen.h);
373 if(relative_loc_.y > screen.h) {
374 relative_loc_.y = std::min<int>(relative_loc_.y, screen.h - relative_loc_.h);
376 relative_loc_.h = std::min<int>(relative_loc_.h, screen.h - relative_loc_.y);
378 return relative_loc_;
383 static const std::string top_anchor =
"top", left_anchor =
"left", bot_anchor =
"bottom", right_anchor =
"right",
384 proportional_anchor =
"proportional";
385 if(str == top_anchor || str == left_anchor)
387 else if(str == bot_anchor || str == right_anchor)
388 return BOTTOM_ANCHORED;
389 else if(str == proportional_anchor)
401 location_modified_ =
true;
407 const std::vector<std::string> items =
utils::split(rect_str.c_str());
408 if(items.size() >= 1) {
409 rect.x1 =
compute(items[0], location_ref_rect.x, location_ref_rect.x + location_ref_rect.w);
411 if(items.size() >= 2) {
412 rect.y1 =
compute(items[1], location_ref_rect.y, location_ref_rect.y + location_ref_rect.h);
414 if(items.size() >= 3) {
415 rect.x2 =
compute(items[2], location_ref_rect.x + location_ref_rect.w,
rect.x1);
417 if(items.size() >= 4) {
418 rect.y2 =
compute(items[3], location_ref_rect.y + location_ref_rect.h,
rect.y1);
420 modify_location(
rect);
427 , font_rgb_set_(false)
428 , font_rgb_(DefaultFontRGB)
434 , text_(cfg[
"prefix"].str() + cfg[
"prefix_literal"].str() + cfg[
"text"].str() + cfg[
"postfix_literal"].str() + cfg[
"postfix"].str())
436 , font_(cfg[
"font_size"].to_size_t())
437 , font_rgb_set_(false)
438 , font_rgb_(DefaultFontRGB)
441 font_ = DefaultFontSize;
451 , prefix_(cfg[
"prefix"].str() + cfg[
"prefix_literal"].str())
452 , postfix_(cfg[
"postfix_literal"].str() + cfg[
"postfix"].str())
454 , font_(cfg[
"font_size"].to_size_t())
455 , font_rgb_set_(false)
456 , font_rgb_(DefaultFontRGB)
459 font_ = DefaultFontSize;
473 , image_(cfg[
"image"])
488 , title_(cfg[
"title"].str() + cfg[
"title_literal"].str())
489 , tooltip_(cfg[
"tooltip"])
490 , image_(cfg[
"image"])
491 , overlay_(cfg[
"overlay"])
492 , black_line_(cfg[
"black_line"].to_bool(false))
510 , button_(cfg[
"button"].to_bool(true))
511 ,
context_(cfg[
"is_context_menu"].to_bool(false))
512 , title_(cfg[
"title"].str() + cfg[
"title_literal"].str())
513 , tooltip_(cfg[
"tooltip"])
514 , image_(cfg[
"image"])
515 , overlay_(cfg[
"overlay"])
519 items_.emplace_back(
"id", item);
523 if(cfg[
"auto_tooltip"].to_bool() &&
tooltip_.empty() &&
items_.size() == 1) {
525 }
else if(cfg[
"tooltip_name_prepend"].to_bool() &&
items_.size() == 1) {
533 , auto_tooltip_(false)
534 , tooltip_name_prepend_(false)
546 ,
context_(cfg[
"is_context_menu"].to_bool())
547 , auto_tooltip_(cfg[
"auto_tooltip"].to_bool(false))
548 , tooltip_name_prepend_(cfg[
"tooltip_name_prepend"].to_bool(false))
549 , title_(cfg[
"title"].str() + cfg[
"title_literal"].str())
550 , tooltip_(cfg[
"tooltip"])
551 , image_(cfg[
"image"])
552 , overlay_(cfg[
"overlay"])
561 std::stringstream result;
562 if(auto_tooltip_ && tooltip_.empty() && items_.size() >
index) {
563 result << cmd.description;
566 result <<
"\n" << cmd.tooltip;
567 }
else if(tooltip_name_prepend_ && items_.size() == 1) {
568 result << cmd.description;
571 result <<
"\n" << tooltip_;
610 int current_rating = 1000000;
611 const config* current =
nullptr;
613 int width =
i[
"width"].to_int();
614 int height =
i[
"height"].to_int();
615 LOG_DP <<
"comparing resolution " << screen.w <<
"," << screen.h <<
" to " << width <<
"," << height;
616 if(screen.w >= width && screen.h >= height) {
617 LOG_DP <<
"loading theme: " << width <<
"," << height;
623 const int rating = width * height;
624 if(rating < current_rating) {
626 current_rating = rating;
632 ERR_DP <<
"No valid resolution found";
639 std::map<std::string, std::string> title_stash_menus;
642 if(!m->title().empty() && !m->get_id().empty())
643 title_stash_menus[m->get_id()] = m->title();
646 std::map<std::string, std::string> title_stash_actions;
649 if(!a->title().empty() && !a->get_id().empty())
650 title_stash_actions[a->get_id()] = a->title();
663 if(title_stash_menus.find(m->get_id()) != title_stash_menus.end())
664 m->set_title(title_stash_menus[m->get_id()]);
668 if(title_stash_actions.find(a->get_id()) != title_stash_actions.end())
669 a->set_title(title_stash_actions[a->get_id()]);
692 for(
const auto [child_key, child_cfg] : status_cfg->all_children_view()) {
695 if(
const auto unit_image_cfg = status_cfg->optional_child(
"unit_image")) {
716 DBG_DP <<
"adding menu: " << (new_menu.
is_context() ?
"is context" :
"not context");
721 menus_.push_back(new_menu);
724 DBG_DP <<
"done adding menu...";
729 DBG_DP <<
"adding action: " << (new_action.
is_context() ?
"is context" :
"not context");
737 DBG_DP <<
"done adding action...";
742 DBG_DP <<
"adding slider";
746 DBG_DP <<
"done adding slider...";
755 static const int BATTERY_ICON_MIN_WIDTH = 1152;
770 if(
p->get_id() ==
id) {
776 if(l->get_id() ==
id) {
782 if(m->get_id() ==
id) {
788 if(a->get_id() ==
id) {
794 if(
s->get_id() ==
id) {
800 std::stringstream stream;
801 stream <<
"theme object " <<
id <<
" not found";
809 ref_id = element.
get_id();
813 if(ref_element.
get_id() == ref_id) {
814 SDL_Rect location_ref_rect = ref_element.
get_location();
821 std::map<std::string, std::string> title_stash;
824 if(!m->title().empty() && !m->get_id().empty())
825 title_stash[m->get_id()] = m->title();
830 if(!a->title().empty() && !a->get_id().empty())
831 title_stash[a->get_id()] = a->title();
836 std::string
id =
c[
"id"];
837 std::string ref_id =
c[
"ref"];
839 if(element.
get_id() ==
id)
854 if(title_stash.find(m->get_id()) != title_stash.end())
855 m->set_title(title_stash[m->get_id()]);
858 if(title_stash.find(a->get_id()) != title_stash.end())
859 a->set_title(title_stash[a->get_id()]);
868 auto status_item_it =
status_.find(
id);
869 if(status_item_it !=
status_.end()) {
870 res = status_item_it->second.get();
874 if(
p->get_id() ==
id) {
879 if(l->get_id() ==
id) {
884 if(m->get_id() ==
id) {
889 if(a->get_id() ==
id) {
893 if(
id ==
"main-map") {
896 if(
id ==
"mini-map") {
899 if(
id ==
"palette") {
902 if(
id ==
"unit-image") {
912 return i->second.get();
920 if(m.get_id() == key)
929 if(a.get_id() == key)
940 if(a->get_id() ==
id) {
942 a->set_title(new_title);
947 if(m->get_id() ==
id) {
949 m->set_title(new_title);
958 std::string new_title;
961 if(!cfg[title_tag].empty())
962 new_title = cfg[title_tag].str();
964 return refresh_title(
id, new_title + cfg[
"title_literal"].str());
971 LOG_DP <<
"Theme contains no label called '" <<
id <<
"'.";
989 std::vector<theme_info> res;
992 if(!cfg[
"hidden"].to_bool(
false) || include_hidden) {
993 auto&
info = res.emplace_back();
995 info.name = cfg[
"name"].t_str();
996 info.description = cfg[
"description"].t_str();
1007 return iter->second;
1011 ERR_DP <<
"Theme '" <<
id <<
"' not found."
1012 <<
" Falling back to default theme.";
1017 return iter->second;
1020 ERR_DP <<
"Default theme not found.";
A config object defines a single node in a WML file, with access to child nodes.
all_children_iterator erase(const all_children_iterator &i)
std::size_t child_count(config_key_type key) const
auto all_children_view() const
In-order iteration over all children.
void merge_attributes(const config &)
bool has_attribute(config_key_type key) const
const_all_children_itors all_children_range() const
In-order iteration over all children.
boost::iterator_range< all_children_iterator > all_children_itors
child_itors child_range(config_key_type key)
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
config & add_child(config_key_type key)
virtual void notify_observers()
A class grating read only view to a vector of config objects, viewed as one config with all children ...
config_array_view child_range(config_key_type key) const
const std::string tooltip(std::size_t index) const
void set_text(const std::string &text)
virtual rect & location(const SDL_Rect &screen) const
void modify_location(const _rect &rect)
const rect & get_location() const
static ANCHORING read_anchor(const std::string &str)
const std::string & get_id() const
panel(std::size_t sw, std::size_t sh, const config &cfg)
status_item(std::size_t sw, std::size_t sh, const config &cfg)
theme::object & find_element(const std::string &id)
std::vector< panel > panels_
void add_object(std::size_t sw, std::size_t sh, const config &cfg)
std::vector< action > actions_
void set_object_location(theme::object &element, const std::string &rect_str, std::string ref_id)
std::map< std::string, std::unique_ptr< status_item > > status_
static NOT_DANGLING const config & get_theme_config(const std::string &id)
Returns the saved config for the theme with the given ID.
const action * get_action_item(const std::string &key) const
object * refresh_title(const std::string &id, const std::string &new_title)
const menu * get_menu_item(const std::string &key) const
void remove_object(const std::string &id)
object * refresh_title2(const std::string &id, const std::string &title_tag)
void modify(const config &cfg)
theme & operator=(const theme &)=delete
static std::map< std::string, config > known_themes
std::vector< menu > menus_
std::size_t cur_spec_height_
std::vector< slider > sliders_
std::size_t cur_spec_width_
static std::vector< theme_info > get_basic_theme_info(bool include_hidden=false)
Returns minimal info about saved themes, optionally including hidden ones.
void modify_label(const std::string &id, const std::string &text)
std::vector< label > labels_
SDL_Rect screen_dimensions_
bool set_resolution(const SDL_Rect &screen)
static void set_known_themes(const game_config_view *cfg)
Copies the theme configs from the main game config.
events::generic_event theme_reset_event_
const status_item * get_status_item(const std::string &item) const
theme(const config &cfg, const SDL_Rect &screen)
static std::string _(const char *str)
std::string label
What to show in the filter's drop-down list.
std::string id
Text to match against addon_info.tags()
Standard logging facilities (interface).
bool does_device_have_battery()
void rect(const SDL_Rect &rect)
Draw a rectangle.
void remove()
Removes a tip.
const hotkey_command & get_hotkey_command(const std::string &command)
returns the hotkey_command with the given name
std::string get_names(const std::string &id)
Returns a comma-separated string of hotkey names.
constexpr const SDL_Rect empty_rect
std::size_t size(std::string_view str)
Length in characters of a UTF-8 string.
std::size_t index(std::string_view str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
std::vector< std::string > split(const config_attribute_value &val)
std::string::const_iterator iterator
Contains the SDL_Rect helper code.
The basic class for representing 8-bit RGB or RGBA colour values.
static color_t from_rgb_string(std::string_view c)
Creates a new opaque color_t object from a string variable in "R,G,B" format.
An abstract description of a rectangle with integer coordinates.
static map_location::direction sw
static map_location::direction s
static _rect read_rect(const config &cfg)
static config get_resolution(const config &resolutions, const std::string &id)
Returns a copy of the wanted resolution.
static config & find_ref(const std::string &id, config &cfg, bool remove=false)
static std::string resolve_rect(const std::string &rect_str)
static lg::log_domain log_display("display")
static std::size_t compute(std::string expr, std::size_t ref1, std::size_t ref2=0)
static config expand_partialresolution(const config &theme)
Returns a config with all partial resolutions of a theme expanded.
static SDL_Rect read_sdl_rect(const config &cfg)
static void do_resolve_rects(const config &cfg, config &resolved_config, config *resol_cfg=nullptr)
Definitions related to theme-support.
std::string missing_mandatory_wml_key(const std::string §ion, const std::string &key, const std::string &primary_key, const std::string &primary_value)
Returns a standard message for a missing wml key (attribute).
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
#define VALIDATE(cond, message)
The macro to use for the validation of WML.