rich label: fix broken tables and reorganize code

This commit is contained in:
Subhraman Sarkar 2024-08-06 21:35:01 +05:30 committed by Celtic Minstrel
parent 62c03ad779
commit b1fc4701aa
2 changed files with 201 additions and 190 deletions

View file

@ -40,6 +40,8 @@
static lg::log_domain log_rich_label("gui/widget/rich_label");
#define DBG_GUI_RL LOG_STREAM(debug, log_rich_label)
#define DEBUG_LINES false
namespace gui2
{
@ -334,7 +336,7 @@ void rich_label::set_parsed_text(const config& parsed_text)
unsigned max_col_height = 0;
unsigned row_y = 0;
PLAIN_LOG << parsed_text.debug();
DBG_GUI_RL << parsed_text.debug();
for(config::any_child tag : parsed_text.all_children_range()) {
config& child = tag.cfg;
@ -364,11 +366,148 @@ void rich_label::set_parsed_text(const config& parsed_text)
is_float = is_curr_float;
new_text_block = true;
} else if(tag.key == "table") {
if (curr_item == nullptr) {
curr_item = &(text_dom_.add_child("text"));
default_text_config(curr_item);
new_text_block = false;
}
in_table = true;
col_idx = 0;
// table doesn't support floating images alongside
img_size = point(0,0);
float_size = point(0,0);
// setup column width
unsigned columns = child["col"].to_int();
unsigned width = child["width"].to_int();
width = width > 0 ? width : w_;
col_width = width/columns;
// start on a new line
(*curr_item)["actions"] = boost::str(boost::format("([set_var('pos_x', 0), set_var('pos_y', pos_y + %s), set_var('tw', width - pos_x - %d)])") % (is_image ? "image_height" : "text_height") % col_width);
x_ = 0;
prev_blk_height_ += txt_height_;
txt_height_ = 0;
row_y = prev_blk_height_;
// DEBUG: debug lines for table
#if DEBUG_LINES
config& link_rect_cfg = text_dom_.add_child("line");
link_rect_cfg["x1"] = 0;
link_rect_cfg["y1"] = prev_blk_height_;
link_rect_cfg["x2"] = w_;
link_rect_cfg["y2"] = prev_blk_height_;
link_rect_cfg["color"] = "255, 0, 0, 255";
#endif
new_text_block = true;
is_image = false;
DBG_GUI_RL << "start table : " << "col=" << columns;
DBG_GUI_RL << "col_width : " << col_width;
} else if(tag.key == "jump") {
if (curr_item == nullptr) {
curr_item = &(text_dom_.add_child("text"));
default_text_config(curr_item);
new_text_block = false;
}
if (col_width > 0) {
max_col_height = std::max(max_col_height, txt_height_);
max_col_height = std::max(max_col_height, static_cast<unsigned>(img_size.y));
txt_height_ = 0;
col_idx++;
x_ = col_idx * col_width;
DBG_GUI_RL << "jump to next column";
(*curr_item)["actions"] = boost::str(boost::format("([set_var('pos_x', %d), set_var('tw', width - %d - %d)])") % x_ % x_ % col_width);
prev_blk_height_ = row_y;
if (!is_image) {
new_text_block = true;
}
is_image = false;
}
} else if(tag.key == "break" || tag.key == "br") {
if (curr_item == nullptr) {
curr_item = &(text_dom_.add_child("text"));
default_text_config(curr_item);
new_text_block = false;
}
if (in_table) {
DBG_GUI_RL << "break";
DBG_GUI_RL << txt_height_;
DBG_GUI_RL << img_size;
max_col_height = std::max(max_col_height, txt_height_);
max_col_height = std::max(max_col_height, static_cast<unsigned>(img_size.y));
DBG_GUI_RL << max_col_height;
//linebreak
col_idx = 0;
x_ = 0;
prev_blk_height_ += max_col_height + padding_;
row_y = prev_blk_height_;
// DEBUG: debug lines for table
#if DEBUG_LINES
config& link_rect_cfg = text_dom_.add_child("line");
link_rect_cfg["x1"] = 0;
link_rect_cfg["y1"] = prev_blk_height_;
link_rect_cfg["x2"] = w_;
link_rect_cfg["y2"] = prev_blk_height_;
link_rect_cfg["color"] = "255, 0, 0, 255";
#endif
(*curr_item)["actions"] = boost::str(boost::format("([set_var('pos_x', 0), set_var('pos_y', pos_y + %d + %d), set_var('tw', width - pos_x - %d)])") % max_col_height % padding_ % col_width);
is_image = false;
max_col_height = 0;
txt_height_ = 0;
img_size = point(0,0);
}
DBG_GUI_RL << "linebreak";
if (!is_image) {
new_text_block = true;
}
} else if(tag.key == "endtable") {
if (curr_item == nullptr) {
curr_item = &(text_dom_.add_child("text"));
default_text_config(curr_item);
new_text_block = false;
}
DBG_GUI_RL << "end table: " << max_col_height;
col_width = 0;
in_table = false;
is_image = false;
row_y = 0;
} else {
std::string line = child["text"];
if (is_image && (!is_float)) {
x_ = 0;
(*curr_item)["actions"] = "([set_var('pos_x', 0), set_var('pos_y', pos_y + image_height + padding)])";
if (line.size() > 0 && line.at(0) == '\n') {
x_ = 0;
(*curr_item)["actions"] = "([set_var('pos_x', 0), set_var('pos_y', pos_y + image_height + padding)])";
line = line.substr(1);
} else {
prev_blk_height_ -= img_size.y + padding_;
}
}
if (curr_item == nullptr || new_text_block) {
@ -388,43 +527,42 @@ void rich_label::set_parsed_text(const config& parsed_text)
if(tag.key == "ref") {
add_link(*curr_item, child["text"], child["dst"], float_size.x);
add_link(*curr_item, line, child["dst"], float_size.x);
is_image = false;
DBG_GUI_RL << "ref: dst=" << child["dst"];
} else if(tag.key == "bold" || tag.key == "b") {
add_text_with_attribute(*curr_item, child["text"], "bold");
add_text_with_attribute(*curr_item, line, "bold");
is_image = false;
DBG_GUI_RL << "bold: text=" << child["text"];
DBG_GUI_RL << "bold: text=" << line;
} else if(tag.key == "italic" || tag.key == "i") {
add_text_with_attribute(*curr_item, child["text"], "italic");
add_text_with_attribute(*curr_item, line, "italic");
is_image = false;
DBG_GUI_RL << "italic: text=" << child["text"];
DBG_GUI_RL << "italic: text=" << line;
} else if(tag.key == "underline" || tag.key == "u") {
add_text_with_attribute(*curr_item, child["text"], "underline");
add_text_with_attribute(*curr_item, line, "underline");
is_image = false;
DBG_GUI_RL << "u: text=" << child["text"];
DBG_GUI_RL << "u: text=" << line;
} else if(tag.key == "header" || tag.key == "h") {
// Header starts in a new line
append_if_not_empty(&((*curr_item)["attr_name"]), ",");
append_if_not_empty(&((*curr_item)["attr_start"]), ",");
append_if_not_empty(&((*curr_item)["attr_end"]), ",");
append_if_not_empty(&((*curr_item)["attr_data"]), ",");
std::stringstream header_text;
header_text << child["text"].str();
// Header starts in a new line
header_text << "\n" + line + "\n";
std::vector<std::string> attrs = {"face", "color", "size"};
std::vector<std::string> attr_data;
attr_data.push_back("serif");
@ -435,14 +573,14 @@ void rich_label::set_parsed_text(const config& parsed_text)
is_image = false;
DBG_GUI_RL << "h: text=" << child["text"];
DBG_GUI_RL << "h: text=" << line;
} else if(tag.key == "span" || tag.key == "format") {
std::vector<std::string> attrs;
std::vector<std::string> attr_data;
DBG_GUI_RL << "span/format: text=" << child["text"];
DBG_GUI_RL << "span/format: text=" << line;
DBG_GUI_RL << "attributes:";
for (const auto& attr : child.attribute_range()) {
@ -453,105 +591,67 @@ void rich_label::set_parsed_text(const config& parsed_text)
}
}
add_text_with_attributes((*curr_item), child["text"], attrs, attr_data);
add_text_with_attributes((*curr_item), line, attrs, attr_data);
is_image = false;
// }---------- TABLE TAGS -----------{
} else if(tag.key == "table") {
in_table = true;
col_idx = 0;
} else if (tag.key == "text") {
DBG_GUI_RL << "text: text=" << ((line.size() > 20) ? line.substr(0,20) : line) << "...";
// table doesn't support floating images alongside
img_size = point(0,0);
float_size = point(0,0);
(*curr_item)["font_size"] = font::SIZE_NORMAL;
// setup column width
unsigned columns = child["col"].to_int();
unsigned width = child["width"].to_int();
width = width > 0 ? width : w_;
col_width = width/columns;
int tmp_h = get_text_size(*curr_item, w_ - (x_ == 0 ? float_size.x : x_)).y;
// start on a new line
(*curr_item)["actions"] = boost::str(boost::format("([set_var('pos_x', 0), set_var('pos_y', pos_y + %s), set_var('tw', width - pos_x - %d)])") % (is_image ? "image_height" : "text_height") % col_width);
x_ = 0;
prev_blk_height_ += txt_height_;
txt_height_ = 0;
(*curr_item)["text"] = (*curr_item)["text"].str() + line;
row_y = prev_blk_height_;
point text_size = get_text_size(*curr_item, w_ - (x_ == 0 ? float_size.x : x_));
text_size.x -= x_;
config& link_rect_cfg = text_dom_.add_child("line");
link_rect_cfg["x1"] = 0;
link_rect_cfg["y1"] = prev_blk_height_;
link_rect_cfg["x2"] = w_;
link_rect_cfg["y2"] = prev_blk_height_;
link_rect_cfg["color"] = "255, 0, 0, 255";
if (wrap_mode && (float_size.y > 0) && (text_size.y > float_size.y)) {
DBG_GUI_RL << "wrap start";
new_text_block = true;
size_t len = get_split_location((*curr_item)["text"].str(), point(w_ - float_size.x, float_size.y));
DBG_GUI_RL << "wrap around area: " << float_size;
DBG_GUI_RL << "start table : " << "col=" << columns;
DBG_GUI_RL << "col_width : " << col_width;
// first part of the text
t_string* removed_part = new t_string((*curr_item)["text"].str().substr(len+1));
(*curr_item)["text"] = (*curr_item)["text"].str().substr(0, len);
(*curr_item)["maximum_width"] = w_ - float_size.x;
(*curr_item)["actions"] = "([set_var('pos_x', 0), set_var('ww', 0), set_var('pos_y', pos_y + text_height + " + std::to_string(0.3*font::get_max_height(font::SIZE_NORMAL)) + ")])";
} else if(tag.key == "jump") {
if (col_width > 0) {
max_col_height = std::max(max_col_height, txt_height_);
max_col_height = std::max(max_col_height, static_cast<unsigned>(img_size.y));
txt_height_ = 0;
col_idx++;
x_ = col_idx * col_width;
DBG_GUI_RL << "jump to next column";
(*curr_item)["actions"] = boost::str(boost::format("([set_var('pos_x', %d), set_var('tw', width - %d - %d)])") % x_ % x_ % col_width);
prev_blk_height_ = row_y;
if (!is_image) {
new_text_block = true;
// Height update
int ah = get_text_size(*curr_item, w_ - float_size.x).y; // no x_?
if (tmp_h > ah) {
tmp_h = 0;
}
}
} else if(tag.key == "break" || tag.key == "br") {
if (in_table) {
DBG_GUI_RL << "break";
DBG_GUI_RL << txt_height_;
DBG_GUI_RL << img_size;
max_col_height = std::max(max_col_height, txt_height_);
max_col_height = std::max(max_col_height, static_cast<unsigned>(img_size.y));
DBG_GUI_RL << max_col_height;
//linebreak
col_idx = 0;
x_ = 0;
prev_blk_height_ += max_col_height + padding_;
row_y = prev_blk_height_;
(*curr_item)["actions"] = boost::str(boost::format("([set_var('pos_x', 0), set_var('pos_y', pos_y + %d + %d), set_var('tw', width - pos_x - %d)])") % max_col_height % padding_ % col_width);
max_col_height = 0;
txt_height_ += ah - tmp_h;
prev_blk_height_ += txt_height_ + 0.3*font::get_max_height(font::SIZE_NORMAL);
DBG_GUI_RL << "wrap: " << prev_blk_height_ << "," << txt_height_;
txt_height_ = 0;
img_size = point(0,0);
// New text block
x_ = 0;
wrap_mode = false;
// rest of the text
curr_item = &(text_dom_.add_child("text"));
default_text_config(curr_item);
tmp_h = get_text_size(*curr_item, w_).y;
add_text_with_attribute(*curr_item, *removed_part);
} else if ((float_size.y > 0) && (text_size.y < float_size.y)) {
// text height less than floating image's height, don't split
DBG_GUI_RL << "no wrap";
(*curr_item)["actions"] = "([set_var('pos_y', pos_y + text_height)])";
}
DBG_GUI_RL << "linebreak";
if (!is_image) {
new_text_block = true;
if (!wrap_mode) {
float_size = point(0,0);
}
} else if(tag.key == "endtable") {
DBG_GUI_RL << "end table: " << max_col_height;
col_width = 0;
in_table = false;
row_y = 0;
is_image = false;
}
int ah = get_text_size(*curr_item, w_ - (x_ == 0 ? float_size.x : x_)).y;
@ -563,94 +663,6 @@ void rich_label::set_parsed_text(const config& parsed_text)
txt_height_ += ah - tmp_h;
}
if (tag.key == "text") {
std::string line = child["text"];
DBG_GUI_RL << "text: text=" << line << "...";
// Start the text in a new paragraph if a newline follows after an inline image
if (is_image && (!is_float)) {
if ((line[0] == '\n')) {
x_ = 0;
(*curr_item)["actions"] = "([set_var('pos_x', 0), set_var('pos_y', pos_y + image_height + padding)])";
} else {
prev_blk_height_ -= img_size.y + padding_;
}
}
if (curr_item == nullptr || new_text_block) {
if (!in_table) {
// table will calculate this by itself, no need to calculate here
prev_blk_height_ += txt_height_ + padding_;
txt_height_ = 0;
}
curr_item = &(text_dom_.add_child("text"));
default_text_config(curr_item);
new_text_block = false;
}
(*curr_item)["font_size"] = font::SIZE_NORMAL;
int tmp_h = get_text_size(*curr_item, w_ - (x_ == 0 ? float_size.x : x_)).y;
(*curr_item)["text"] = (*curr_item)["text"].str() + line;
point text_size = get_text_size(*curr_item, w_ - (x_ == 0 ? float_size.x : x_));
text_size.x -= x_;
if (wrap_mode && (float_size.y > 0) && (text_size.y > float_size.y)) {
DBG_GUI_RL << "wrap start";
size_t len = get_split_location((*curr_item)["text"].str(), point(w_ - float_size.x, float_size.y));
DBG_GUI_RL << "wrap around area: " << float_size;
// first part of the text
t_string* removed_part = new t_string((*curr_item)["text"].str().substr(len+1));
(*curr_item)["text"] = (*curr_item)["text"].str().substr(0, len);
(*curr_item)["maximum_width"] = w_ - float_size.x;
(*curr_item)["actions"] = "([set_var('pos_x', 0), set_var('ww', 0), set_var('pos_y', pos_y + text_height + " + std::to_string(0.3*font::get_max_height(font::SIZE_NORMAL)) + ")])";
// Height update
int ah = get_text_size(*curr_item, w_ - float_size.x).y; // no x_?
if (tmp_h > ah) {
tmp_h = 0;
}
txt_height_ += ah - tmp_h;
prev_blk_height_ += txt_height_ + 0.3*font::get_max_height(font::SIZE_NORMAL);
DBG_GUI_RL << "wrap: " << prev_blk_height_ << "," << txt_height_;
txt_height_ = 0;
// New text block
x_ = 0;
wrap_mode = false;
// rest of the text
curr_item = &(text_dom_.add_child("text"));
default_text_config(curr_item);
tmp_h = get_text_size(*curr_item, w_).y;
add_text_with_attribute(*curr_item, *removed_part);
} else if ((float_size.y > 0) && (text_size.y < float_size.y)) {
// text height less than floating image's height, don't split
DBG_GUI_RL << "no wrap";
(*curr_item)["actions"] = "([set_var('pos_y', pos_y + text_height)])";
}
if (!wrap_mode) {
float_size = point(0,0);
}
// Incremental height update for text
int ah = get_text_size(*curr_item, w_ - (x_ == 0 ? float_size.x : x_)).y;
if (tmp_h > ah) {
tmp_h = 0;
}
txt_height_ += ah - tmp_h;
is_image = false;
}
if (!is_image && !wrap_mode && img_size.y > 0) {
if (!in_table) {
img_size = point(0,0);
@ -673,15 +685,14 @@ void rich_label::set_parsed_text(const config& parsed_text)
// reset all canvas variables to zero, otherwise they grow infinitely
config& break_cfg = text_dom_.add_child("text");
default_text_config(&break_cfg);
break_cfg["text"] = " ";
default_text_config(&break_cfg, " ");
break_cfg["actions"] = "([set_var('pos_x', 0), set_var('pos_y', 0), set_var('img_x', 0), set_var('img_y', 0), set_var('ww', 0), set_var('tw', 0)])";
DBG_GUI_RL << text_dom_.debug();
DBG_GUI_RL << "Height: " << h_;
// DEBUG: draw boxes around links
#if false
#if DEBUG_LINES
for (const auto& entry : links_) {
config& link_rect_cfg = text_dom_.add_child("rectangle");
link_rect_cfg["x"] = entry.first.x;

View file

@ -615,10 +615,10 @@ std::string unit_topic_generator::operator()() const {
}
// Print the detailed description about the unit.
ss << "\n\n" << detailed_description;
ss << "\n" << detailed_description;
if(const auto notes = type_.special_notes(); !notes.empty()) {
ss << "\n\n" << _("<header>Special Notes</header>") << "\n";
ss << "\n" << _("<header>Special Notes</header>");
for(const auto& note : notes) {
ss << font::unicode_bullet << " <i>" << note << "</i>" << '\n';
}
@ -648,7 +648,7 @@ std::string unit_topic_generator::operator()() const {
std::string lang_type = string_table["type_" + attack.type()];
// Attack icon
attack_ss << "<img>src='" << attack.icon() << "'</img> <jump/>";
attack_ss << "<img>src='" << attack.icon() << "'</img><jump/>";
// attack name
attack_ss << lang_weapon << "<jump/>";
@ -794,7 +794,7 @@ std::string unit_topic_generator::operator()() const {
// row.emplace_back("<img>src='" + final_image + "'</img> " +
// make_link(m.name, "..terrain_" + m.id),
// font::pango_line_width(m.name, normal_font_size) + (high_res ? 32 : 16) );
ss << "<img>src='" + final_image + "'</img> " + make_link(m.name, "..terrain_" + m.id) << "<jump/>";
ss << "<img>src='" + final_image + "'</img>" + make_link(m.name, "..terrain_" + m.id) << "<jump/>";
//defense - range: +10 % .. +70 %
// passing false to select the more saturated red-to-green scale