rich label: support setting table bgcolors from definition

This commit is contained in:
Subhraman Sarkar 2025-03-17 16:58:52 +05:30
parent 00b6d14379
commit 877ac69366
5 changed files with 101 additions and 44 deletions

View File

@ -28,6 +28,12 @@
link_color = "255, 225, 0"
[colors]
table_header = "51, 55, 79, 255"
table_row1 = "26, 31, 51, 255"
table_row2 = "35, 40, 66, 255"
[/colors]
[state_enabled]
[draw][/draw]

View File

@ -227,6 +227,12 @@
min="0"
max="infinite"
super="$generic/widget_definition/resolution"
[tag]
name="colors"
min=0
max=1
{SIMPLE_KEY * color}
[/tag]
[tag]
name="state_disabled"
min="0"

View File

@ -67,6 +67,7 @@ rich_label::rich_label(const implementation::builder_rich_label& builder)
, can_wrap_(true)
, link_aware_(builder.link_aware)
, link_color_(font::YELLOW_COLOR)
, predef_colors_()
, font_size_(font::SIZE_NORMAL)
, can_shrink_(true)
, text_alpha_(ALPHA_OPAQUE)
@ -75,9 +76,27 @@ rich_label::rich_label(const implementation::builder_rich_label& builder)
, size_(0, 0)
, padding_(builder.padding)
{
const auto conf = cast_config_to<rich_label_definition>();
assert(conf);
text_color_enabled_ = conf->text_color_enabled;
text_color_disabled_ = conf->text_color_disabled;
font_family_ = conf->font_family;
font_size_ = conf->font_size;
font_style_ = conf->font_style;
link_color_ = conf->link_color;
predef_colors_.insert(conf->colors.begin(), conf->colors.end());
set_text_alignment(builder.text_alignment);
set_label(get_label());
}
wfl::map_formula_callable rich_label::setup_text_renderer(config text_cfg, unsigned width) const {
color_t rich_label::get_color(const std::string& color)
{
const auto iter = predef_colors_.find(color);
return (iter != predef_colors_.end()) ? iter->second : font::string_to_color(color);
}
wfl::map_formula_callable rich_label::setup_text_renderer(config text_cfg, unsigned width) const
{
// Set up fake render to calculate text position
static wfl::action_function_symbol_table functions;
wfl::map_formula_callable variables;
@ -89,7 +108,8 @@ wfl::map_formula_callable rich_label::setup_text_renderer(config text_cfg, unsig
return variables;
}
point rich_label::get_text_size(config& text_cfg, unsigned width) const {
point rich_label::get_text_size(config& text_cfg, unsigned width) const
{
wfl::map_formula_callable variables = setup_text_renderer(text_cfg, width);
return {
variables.query_value("text_width").as_int(),
@ -97,7 +117,8 @@ point rich_label::get_text_size(config& text_cfg, unsigned width) const {
};
}
point rich_label::get_image_size(config& img_cfg) const {
point rich_label::get_image_size(config& img_cfg) const
{
static wfl::action_function_symbol_table functions;
wfl::map_formula_callable variables;
variables.add("fake_draw", wfl::variant(true));
@ -108,7 +129,8 @@ point rich_label::get_image_size(config& img_cfg) const {
};
}
std::pair<size_t, size_t> rich_label::add_text(config& curr_item, const std::string& text) {
std::pair<size_t, size_t> rich_label::add_text(config& curr_item, const std::string& text)
{
auto& attr = curr_item["text"];
size_t start = attr.str().size();
attr = attr.str() + text;
@ -116,7 +138,13 @@ std::pair<size_t, size_t> rich_label::add_text(config& curr_item, const std::str
return { start, end };
}
void rich_label::add_attribute(config& curr_item, const std::string& attr_name, size_t start, size_t end, const std::string& extra_data) {
void rich_label::add_attribute(
config& curr_item,
const std::string& attr_name,
size_t start,
size_t end,
const std::string& extra_data)
{
curr_item.add_child("attribute", config{
"name" , attr_name,
"start" , start,
@ -125,13 +153,24 @@ void rich_label::add_attribute(config& curr_item, const std::string& attr_name,
});
}
std::pair<size_t, size_t> rich_label::add_text_with_attribute(config& curr_item, const std::string& text, const std::string& attr_name, const std::string& extra_data) {
std::pair<size_t, size_t> rich_label::add_text_with_attribute(
config& curr_item,
const std::string& text,
const std::string& attr_name,
const std::string& extra_data)
{
const auto [start, end] = add_text(curr_item, text);
add_attribute(curr_item, attr_name, start, end, extra_data);
return { start, end };
}
void rich_label::add_link(config& curr_item, const std::string& name, const std::string& dest, const point& origin, int img_width) {
void rich_label::add_link(
config& curr_item,
const std::string& name,
const std::string& dest,
const point& origin,
int img_width)
{
// TODO algorithm needs to be text_alignment independent
DBG_GUI_RL << "add_link: " << name << "->" << dest;
@ -177,8 +216,8 @@ void rich_label::add_link(config& curr_item, const std::string& name, const std:
}
}
size_t rich_label::get_split_location(std::string_view text, const point& pos) {
size_t rich_label::get_split_location(std::string_view text, const point& pos)
{
size_t len = get_offset_from_xy(pos);
len = (len > text.size()-1) ? text.size()-1 : len;
@ -194,7 +233,11 @@ size_t rich_label::get_split_location(std::string_view text, const point& pos) {
return len;
}
std::vector<std::string> rich_label::split_in_width(const std::string &s, const int font_size, const unsigned width) {
std::vector<std::string> rich_label::split_in_width(
const std::string &s,
const int font_size,
const unsigned width)
{
std::vector<std::string> res;
try {
const std::string& first_line = font::pango_word_wrap(s, font_size, width, -1, 1, true);
@ -445,7 +488,7 @@ std::pair<config, point> rich_label::get_parsed_text(
bgbox["y"] = pos.y;
bgbox["w"] = std::accumulate(col_widths.begin(), col_widths.end(), 0) + 2*(row_paddings[0] + row_paddings[1])*columns;
bgbox["h"] = row_paddings[0] + row_heights[row_idx] + row_paddings[1];
bgbox["fill_color"] = font::string_to_color(row["bgcolor"]).to_rgba_string();
bgbox["fill_color"] = get_color(row["bgcolor"].str()).to_rgba_string();
text_dom.append(std::move(bg_base));
}
@ -793,7 +836,12 @@ std::pair<config, point> rich_label::get_parsed_text(
return { text_dom, point(w, h - origin.y) };
} // function ends
void rich_label::default_text_config(config* txt_ptr, const point& pos, const int max_width, const t_string& text) {
void rich_label::default_text_config(
config* txt_ptr,
const point& pos,
const int max_width,
const t_string& text)
{
if (txt_ptr != nullptr) {
(*txt_ptr)["text"] = text;
(*txt_ptr)["color"] = text_color_enabled_.to_rgba_string();
@ -977,7 +1025,14 @@ rich_label_definition::resolution::resolution(const config& cfg)
, font_family(cfg["text_font_family"].str())
, font_size(cfg["text_font_size"].to_int(font::SIZE_NORMAL))
, font_style(cfg["text_font_style"].str("normal"))
, colors()
{
if(auto colors_cfg = cfg.optional_child("colors")) {
for (const auto& [name, value] : colors_cfg->attribute_range()) {
colors.try_emplace(name, color_t::from_rgba_string(value.str()));
}
}
// Note the order should be the same as the enum state_t is rich_label.hpp.
state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_enabled", missing_mandatory_wml_tag("rich_label_definition][resolution", "state_enabled")));
state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_disabled", missing_mandatory_wml_tag("rich_label_definition][resolution", "state_disabled")));
@ -999,24 +1054,10 @@ builder_rich_label::builder_rich_label(const config& cfg)
std::unique_ptr<widget> builder_rich_label::build() const
{
auto lbl = std::make_unique<rich_label>(*this);
const auto conf = lbl->cast_config_to<rich_label_definition>();
assert(conf);
lbl->set_text_alignment(text_alignment);
lbl->set_text_color(conf->text_color_enabled, true);
lbl->set_text_color(conf->text_color_disabled, false);
lbl->set_link_color(conf->link_color);
lbl->set_font_family(conf->font_family);
lbl->set_font_size(conf->font_size);
lbl->set_font_style(conf->font_style);
lbl->set_label(lbl->get_label());
DBG_GUI_G << "Window builder: placed rich_label '" << id << "' with definition '"
<< definition << "'.";
return lbl;
return std::make_unique<rich_label>(*this);
}
} // namespace implementation

View File

@ -93,11 +93,6 @@ public:
can_wrap_ = wrap;
}
void set_characters_per_line(const unsigned characters_per_line)
{
characters_per_line_ = characters_per_line;
}
void set_link_aware(bool l);
void set_link_color(const color_t& color);
@ -124,15 +119,6 @@ public:
void set_text_alpha(unsigned short alpha);
void set_text_color(const color_t& color, bool enabled)
{
if (enabled) {
text_color_enabled_ = color;
} else {
text_color_disabled_ = color;
}
}
const t_string& get_label() const
{
return unparsed_text_.empty() ? styled_widget::get_label() : unparsed_text_;
@ -207,6 +193,11 @@ private:
*/
color_t link_color_;
/**
* Color variables that can be used in place of colors strings, like `<row bgcolor=color1>`
*/
std::map<std::string, color_t> predef_colors_;
/**
* Base font family
*/
@ -245,6 +236,9 @@ private:
/** Padding */
int padding_;
/** If color is a predefined color set in resolution, return it, otherwise decode using `font::string_to_color`. */
color_t get_color(const std::string& color);
/** Create template for text config that can be shown in canvas */
void default_text_config(config* txt_ptr, const point& pos, const int max_width, const t_string& text = "");
@ -341,6 +335,7 @@ struct rich_label_definition : public styled_widget_definition
std::string font_family;
int font_size;
std::string font_style;
std::map<std::string, color_t> colors;
};
};

View File

@ -676,6 +676,7 @@ std::string unit_topic_generator::operator()() const {
// Print headers for the table.
table_ss << markup::tag("row",
{ {"bgcolor", "table_header"} },
//FIXME space/tab does not work, but nbsp does
//empty tags will be skipped by rich_label
markup::tag("col", font::nbsp),
@ -742,7 +743,7 @@ std::string unit_topic_generator::operator()() const {
}
}
table_ss << markup::tag("row", {{"valign", "center"}}, attack_ss.str());
table_ss << markup::tag("row", { {"valign", "center"} }, attack_ss.str());
}
ss << markup::tag("table", table_ss.str());
@ -777,10 +778,12 @@ std::string unit_topic_generator::operator()() const {
std::stringstream().swap(table_ss);
table_ss << markup::tag("row",
{ {"bgcolor", "table_header"} },
markup::tag("col", markup::bold(_("Attack Type"))),
markup::tag("col", markup::bold(_("Resistance"))));
utils::string_map_res dam_tab = movement_type.damage_table();
bool odd_row = true;
for(std::pair<std::string, std::string> dam_it : dam_tab) {
int resistance = 100;
try {
@ -795,8 +798,11 @@ std::string unit_topic_generator::operator()() const {
const std::string lang_type = string_table["type_" + dam_it.first];
const std::string type_icon = "icons/profiles/" + dam_it.first + ".png~SCALE_INTO(16,16)";
table_ss << markup::tag("row",
{ {"bgcolor", (odd_row ? "table_row1" : "table_row2")} },
markup::tag("col", markup::img(type_icon), lang_type),
markup::tag("col", markup::span_color(color, resist)));
odd_row = !odd_row;
}
ss << markup::tag("table", table_ss.str());
@ -816,7 +822,7 @@ std::string unit_topic_generator::operator()() const {
if (has_terrain_defense_caps) { row_ss << markup::tag("col", markup::bold(_("Defense Cap"))); }
if (has_vision) { row_ss << markup::tag("col", markup::bold(_("Vision Cost"))); }
if (has_jamming) { row_ss << markup::tag("col", markup::bold(_("Jamming Cost"))); }
table_ss << markup::tag("row", row_ss.str());
table_ss << markup::tag("row", { {"bgcolor", "table_header"} }, row_ss.str());
// Organize terrain movetype data
std::set<terrain_movement_info> terrain_moves;
@ -848,6 +854,7 @@ std::string unit_topic_generator::operator()() const {
}
// Add movement table rows
odd_row = true;
for(const terrain_movement_info& m : terrain_moves)
{
std::stringstream().swap(row_ss);
@ -887,7 +894,9 @@ std::string unit_topic_generator::operator()() const {
row_ss << markup::tag("col", format_mp_entry(type_.jamming(), m.jamming_cost));
}
table_ss << markup::tag("row", row_ss.str());
table_ss << markup::tag("row", { {"bgcolor", (odd_row ? "table_row1" : "table_row2")} }, row_ss.str());
odd_row = !odd_row;
}
ss << markup::tag("table", table_ss.str());