32 #define DBG_DP LOG_STREAM(debug, log_display)
33 #define LOG_DP LOG_STREAM(info, log_display)
34 #define ERR_DP LOG_STREAM(err, log_display)
40 const color_t DefaultFontRGB {200, 200, 200};
42 _rect ref_rect {0, 0, 0, 0};
45 static std::size_t
compute(std::string
expr, std::size_t ref1, std::size_t ref2 = 0)
51 }
else if((
expr[0] ==
'+') || (
expr[0] ==
'-')) {
55 return ref + atoi(
expr.c_str());
97 std::stringstream resolved;
99 if(
items.size() >= 1) {
103 if(
items.size() >= 2) {
105 resolved <<
"," <<
rect.y1;
107 if(
items.size() >= 3) {
109 resolved <<
"," <<
rect.x2;
111 if(
items.size() >= 4) {
113 resolved <<
"," <<
rect.y2;
119 return resolved.str();
124 static config empty_config;
129 if(
i->cfg[
"id"] ==
id) {
140 if(&
c != &empty_config) {
154 return find_ref(std::string(
id), cfg);
188 throw config::error(
"[partialresolution] refers to non-existent [resolution] " +
id);
211 for(
const auto& part :
theme.child_range(
"partialresolution")) {
215 for(
const auto&
remove : part.child_range(
"remove")) {
221 for(
const auto& change : part.child_range(
"change")) {
229 for(
const auto& add : part.child_range(
"add")) {
230 for(
const auto child : add.all_children_range()) {
246 do_resolve_rects(value.cfg, childcfg, value.key ==
"resolution" ? &childcfg : resol_cfg);
253 if(!cfg[
"ref"].empty()) {
254 if(resol_cfg ==
nullptr) {
255 ERR_DP <<
"Use of ref= outside a [resolution] block";
260 if(ref[
"id"].empty()) {
261 ERR_DP <<
"Reference to non-existent rect id \"" << cfg[
"ref"] <<
"\"";
262 }
else if(ref[
"rect"].empty()) {
263 ERR_DP <<
"Reference to id \"" << cfg[
"ref"] <<
"\" which does not have a \"rect\"";
270 if(!cfg[
"rect"].empty()) {
276 : location_modified_(false)
289 : location_modified_(false)
294 , xanchor_(read_anchor(cfg[
"xanchor"]))
295 , yanchor_(read_anchor(cfg[
"yanchor"]))
310 :
size(cfg[
"border_size"].to_double())
311 , background_image(cfg[
"background_image"])
312 , tile_image(cfg[
"tile_image"])
313 , show_border(cfg[
"show_border"].to_bool(true))
315 VALIDATE(
size >= 0.0 &&
size <= 0.5,
_(
"border_size should be between 0.0 and 0.5."));
320 if(last_screen_ == screen && !location_modified_)
321 return relative_loc_;
323 last_screen_ = screen;
327 relative_loc_.x =
loc_.
x;
328 relative_loc_.w =
loc_.w;
332 relative_loc_.w =
loc_.w + screen.w - std::min<std::size_t>(spec_width_,
loc_.w + screen.w);
334 case BOTTOM_ANCHORED:
335 relative_loc_.x =
loc_.
x + screen.w - std::min<std::size_t>(spec_width_,
loc_.
x + screen.w);
336 relative_loc_.w =
loc_.w;
339 relative_loc_.
x = (
loc_.
x * screen.w) / spec_width_;
340 relative_loc_.w = (
loc_.w * screen.w) / spec_width_;
348 relative_loc_.y =
loc_.
y;
349 relative_loc_.h =
loc_.h;
353 relative_loc_.h =
loc_.h + screen.h - std::min<std::size_t>(spec_height_,
loc_.h + screen.h);
355 case BOTTOM_ANCHORED:
356 relative_loc_.y =
loc_.
y + screen.h - std::min<std::size_t>(spec_height_,
loc_.
y + screen.h);
357 relative_loc_.h =
loc_.h;
360 relative_loc_.
y = (
loc_.
y * screen.h) / spec_height_;
361 relative_loc_.h = (
loc_.h * screen.h) / spec_height_;
367 relative_loc_.w = std::min<int>(relative_loc_.w, screen.w);
368 if(relative_loc_.x > screen.w) {
369 relative_loc_.x = std::min<int>(relative_loc_.x, screen.w - relative_loc_.w);
371 relative_loc_.w = std::min<int>(relative_loc_.w, screen.w - relative_loc_.x);
373 relative_loc_.h = std::min<int>(relative_loc_.h, screen.h);
374 if(relative_loc_.y > screen.h) {
375 relative_loc_.y = std::min<int>(relative_loc_.y, screen.h - relative_loc_.h);
377 relative_loc_.h = std::min<int>(relative_loc_.h, screen.h - relative_loc_.y);
379 return relative_loc_;
384 static const std::string top_anchor =
"top", left_anchor =
"left", bot_anchor =
"bottom", right_anchor =
"right",
385 proportional_anchor =
"proportional";
386 if(str == top_anchor || str == left_anchor)
388 else if(str == bot_anchor || str == right_anchor)
389 return BOTTOM_ANCHORED;
390 else if(str == proportional_anchor)
402 location_modified_ =
true;
409 if(
items.size() >= 1) {
410 rect.x1 =
compute(
items[0], location_ref_rect.x, location_ref_rect.x + location_ref_rect.w);
412 if(
items.size() >= 2) {
413 rect.y1 =
compute(
items[1], location_ref_rect.y, location_ref_rect.y + location_ref_rect.h);
415 if(
items.size() >= 3) {
418 if(
items.size() >= 4) {
421 modify_location(
rect);
428 , font_rgb_set_(false)
429 , font_rgb_(DefaultFontRGB)
435 , text_(cfg[
"prefix"].str() + cfg[
"prefix_literal"].str() + cfg[
"text"].str() + cfg[
"postfix_literal"].str() + cfg[
"postfix"].str())
437 , font_(cfg[
"font_size"])
438 , font_rgb_set_(false)
439 , font_rgb_(DefaultFontRGB)
442 font_ = DefaultFontSize;
452 , prefix_(cfg[
"prefix"].str() + cfg[
"prefix_literal"].str())
453 , postfix_(cfg[
"postfix_literal"].str() + cfg[
"postfix"].str())
455 , font_(cfg[
"font_size"])
456 , font_rgb_set_(false)
457 , font_rgb_(DefaultFontRGB)
460 font_ = DefaultFontSize;
474 , image_(cfg[
"image"])
489 , title_(cfg[
"title"].str() + cfg[
"title_literal"].str())
490 , tooltip_(cfg[
"tooltip"])
491 , image_(cfg[
"image"])
492 , overlay_(cfg[
"overlay"])
493 , black_line_(cfg[
"black_line"].to_bool(false))
511 , button_(cfg[
"button"].to_bool(true))
512 ,
context_(cfg[
"is_context_menu"].to_bool(false))
513 , title_(cfg[
"title"].str() + cfg[
"title_literal"].str())
514 , tooltip_(cfg[
"tooltip"])
515 , image_(cfg[
"image"])
516 , overlay_(cfg[
"overlay"])
524 if(cfg[
"auto_tooltip"].to_bool() &&
tooltip_.empty() &&
items_.size() == 1) {
526 }
else if(cfg[
"tooltip_name_prepend"].to_bool() &&
items_.size() == 1) {
534 , auto_tooltip_(false)
535 , tooltip_name_prepend_(false)
547 ,
context_(cfg[
"is_context_menu"].to_bool())
548 , auto_tooltip_(cfg[
"auto_tooltip"].to_bool(false))
549 , tooltip_name_prepend_(cfg[
"tooltip_name_prepend"].to_bool(false))
550 , title_(cfg[
"title"].str() + cfg[
"title_literal"].str())
551 , tooltip_(cfg[
"tooltip"])
552 , image_(cfg[
"image"])
553 , overlay_(cfg[
"overlay"])
562 std::stringstream result;
563 if(auto_tooltip_ && tooltip_.empty() && items_.size() >
index) {
564 result << cmd.description;
567 result <<
"\n" << cmd.tooltip;
568 }
else if(tooltip_name_prepend_ && items_.size() == 1) {
569 result << cmd.description;
572 result <<
"\n" << tooltip_;
611 int current_rating = 1000000;
612 const config* current =
nullptr;
614 int width =
i[
"width"];
615 int height =
i[
"height"];
616 LOG_DP <<
"comparing resolution " << screen.w <<
"," << screen.h <<
" to " << width <<
"," << height;
617 if(screen.w >= width && screen.h >= height) {
618 LOG_DP <<
"loading theme: " << width <<
"," << height;
624 const int rating = width * height;
625 if(rating < current_rating) {
627 current_rating = rating;
633 ERR_DP <<
"No valid resolution found";
640 std::map<std::string, std::string> title_stash_menus;
643 if(!m->title().empty() && !m->get_id().empty())
644 title_stash_menus[m->get_id()] = m->title();
647 std::map<std::string, std::string> title_stash_actions;
650 if(!
a->title().empty() && !
a->get_id().empty())
651 title_stash_actions[
a->get_id()] =
a->title();
664 if(title_stash_menus.find(m->get_id()) != title_stash_menus.end())
665 m->set_title(title_stash_menus[m->get_id()]);
669 if(title_stash_actions.find(
a->get_id()) != title_stash_actions.end())
670 a->set_title(title_stash_actions[
a->get_id()]);
696 if(
const auto unit_image_cfg = status_cfg->optional_child(
"unit_image")) {
717 DBG_DP <<
"adding menu: " << (new_menu.
is_context() ?
"is context" :
"not context");
722 menus_.push_back(new_menu);
725 DBG_DP <<
"done adding menu...";
730 DBG_DP <<
"adding action: " << (new_action.
is_context() ?
"is context" :
"not context");
738 DBG_DP <<
"done adding action...";
743 DBG_DP <<
"adding slider";
747 DBG_DP <<
"done adding slider...";
756 static const int BATTERY_ICON_MIN_WIDTH = 1152;
771 if(
p->get_id() ==
id) {
777 if(l->get_id() ==
id) {
783 if(m->get_id() ==
id) {
789 if(
a->get_id() ==
id) {
795 if(
s->get_id() ==
id) {
801 std::stringstream stream;
802 stream <<
"theme object " <<
id <<
" not found";
810 ref_id = element.
get_id();
814 if(ref_element.
get_id() == ref_id) {
815 SDL_Rect location_ref_rect = ref_element.
get_location();
822 std::map<std::string, std::string> title_stash;
825 if(!m->title().empty() && !m->get_id().empty())
826 title_stash[m->get_id()] = m->title();
831 if(!
a->title().empty() && !
a->get_id().empty())
832 title_stash[
a->get_id()] =
a->title();
837 std::string
id =
c[
"id"];
838 std::string ref_id =
c[
"ref"];
840 if(element.
get_id() ==
id)
855 if(title_stash.find(m->get_id()) != title_stash.end())
856 m->set_title(title_stash[m->get_id()]);
859 if(title_stash.find(
a->get_id()) != title_stash.end())
860 a->set_title(title_stash[
a->get_id()]);
869 auto status_item_it =
status_.find(
id);
870 if(status_item_it !=
status_.end()) {
871 res = status_item_it->second.get();
875 if(
p->get_id() ==
id) {
880 if(l->get_id() ==
id) {
885 if(m->get_id() ==
id) {
890 if(
a->get_id() ==
id) {
894 if(
id ==
"main-map") {
897 if(
id ==
"mini-map") {
900 if(
id ==
"palette") {
903 if(
id ==
"unit-image") {
913 return i->second.get();
921 if(m.get_id() == key)
930 if(
a.get_id() == key)
941 if(
a->get_id() ==
id) {
943 a->set_title(new_title);
948 if(m->get_id() ==
id) {
950 m->set_title(new_title);
959 std::string new_title;
962 if(!cfg[title_tag].empty())
963 new_title = cfg[title_tag].str();
965 return refresh_title(
id, new_title + cfg[
"title_literal"].str());
972 LOG_DP <<
"Theme contains no label called '" <<
id <<
"'.";
990 std::vector<theme_info> res;
993 if(!cfg[
"hidden"].to_bool(
false) || include_hidden) {
994 auto&
info = res.emplace_back();
996 info.name = cfg[
"name"].t_str();
997 info.description = cfg[
"description"].t_str();
1008 return iter->second;
1012 ERR_DP <<
"Theme '" <<
id <<
"' not found."
1013 <<
" Falling back to default theme.";
1018 return iter->second;
1021 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
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)
Euivalent 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_
std::map< std::string, std::unique_ptr< status_item > > status_
static 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)
void set_object_location(theme::object &element, std::string rect_str, std::string ref_id)
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.
std::pair< std::string, unsigned > item
std::string get_names(const std::string &id)
Returns a comma-separated string of hotkey names.
const hotkey_command & get_hotkey_command(const std::string &command)
returns the hotkey_command with the given name
const std::vector< std::string > items
constexpr const SDL_Rect empty_rect
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
std::size_t size(const std::string &str)
Length in characters of 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(const std::string &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.
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.