GUI2/Rich Label: minor cleanup (#9426)

This commit is contained in:
Charles Dang 2024-10-04 19:53:28 -04:00 committed by GitHub
parent d9bb46ce37
commit 57bd39d4a5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 97 additions and 116 deletions

View file

@ -46,6 +46,13 @@ static lg::log_domain log_rich_label("gui/widget/rich_label");
namespace gui2
{
namespace
{
using namespace std::string_literals;
/** Possible formatting tags, must be the same as those in gui2::text_shape::draw */
const std::array format_tags{ "bold"s, "b"s, "italic"s, "i"s, "underline"s, "u"s };
}
// ------------ WIDGET -----------{
@ -61,8 +68,7 @@ rich_label::rich_label(const implementation::builder_rich_label& builder)
, text_alpha_(ALPHA_OPAQUE)
, unparsed_text_()
, init_w_(builder.width(get_screen_size_variables()))
, w_(0)
, h_(0)
, size_(0, 0)
, padding_(5)
{
connect_signal<event::LEFT_BUTTON_CLICK>(
@ -73,55 +79,58 @@ rich_label::rich_label(const implementation::builder_rich_label& builder)
std::bind(&rich_label::signal_handler_mouse_leave, this, std::placeholders::_3));
}
wfl::map_formula_callable rich_label::setup_text_renderer(config text_cfg, unsigned width) {
wfl::map_formula_callable rich_label::setup_text_renderer(config text_cfg, unsigned width) const {
// Set up fake render to calculate text position
wfl::action_function_symbol_table functions;
static wfl::action_function_symbol_table functions;
wfl::map_formula_callable variables;
variables.add("text", wfl::variant(text_cfg["text"].str()));
variables.add("width", wfl::variant(width));
variables.add("text_wrap_mode", wfl::variant(PANGO_ELLIPSIZE_NONE));
variables.add("fake_draw", wfl::variant(true));
tshape_ = std::make_unique<gui2::text_shape>(text_cfg, functions);
tshape_->draw(variables);
gui2::text_shape{text_cfg, functions}.draw(variables);
return variables;
}
point rich_label::get_text_size(config& text_cfg, unsigned width) {
point rich_label::get_text_size(config& text_cfg, unsigned width) const {
wfl::map_formula_callable variables = setup_text_renderer(text_cfg, width);
return point(variables.query_value("text_width").as_int(), variables.query_value("text_height").as_int());
return {
variables.query_value("text_width").as_int(),
variables.query_value("text_height").as_int()
};
}
point rich_label::get_image_size(config& img_cfg) {
wfl::action_function_symbol_table functions;
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));
ishape_ = std::make_unique<gui2::image_shape>(img_cfg, functions);
ishape_->draw(variables);
return point(variables.query_value("image_width").as_int(), variables.query_value("image_height").as_int());
gui2::image_shape{img_cfg, functions}.draw(variables);
return {
variables.query_value("image_width").as_int(),
variables.query_value("image_height").as_int()
};
}
std::pair<size_t, size_t> rich_label::add_text(config& curr_item, std::string text) {
size_t start = curr_item["text"].str().size();
curr_item["text"] = curr_item["text"].str() + text;
size_t end = curr_item["text"].str().size();
return std::pair(start, end);
auto& attr = curr_item["text"];
size_t start = attr.str().size();
attr = attr.str() + std::move(text);
size_t end = attr.str().size();
return { start, end };
}
void rich_label::add_attribute(config& curr_item, std::string attr_name, size_t start, size_t end, std::string extra_data) {
config& attr_cfg = curr_item.add_child("attribute");
attr_cfg["name"] = attr_name;
attr_cfg["start"] = start;
attr_cfg["end"] = end == 0 ? curr_item["text"].str().size() : end;
if (!extra_data.empty()) {
attr_cfg["value"] = extra_data;
}
curr_item.add_child("attribute", config{
"name" , attr_name,
"start" , start,
"end" , end == 0 ? curr_item["text"].str().size() : end,
"value" , std::move(extra_data)
});
}
std::pair<size_t, size_t> rich_label::add_text_with_attribute(config& curr_item, std::string text, std::string attr_name, std::string extra_data) {
const auto& [start, end] = add_text(curr_item, text);
const auto [start, end] = add_text(curr_item, std::move(text));
add_attribute(curr_item, attr_name, start, end, extra_data);
return std::pair(start, end);
return { start, end };
}
void rich_label::add_image(config& curr_item, std::string name, std::string align, bool has_prev_image, bool floating) {
@ -191,45 +200,29 @@ void rich_label::add_link(config& curr_item, std::string name, std::string dest,
// Add link
if (t_end.x > t_start.x) {
rect link_rect = {
t_start.x,
t_start.y,
t_end.x - t_start.x,
font::get_max_height(font::SIZE_NORMAL),
};
links_.push_back(std::pair(link_rect, dest));
rect link_rect{ t_start, point{t_end.x - t_start.x, font::get_max_height(font::SIZE_NORMAL) }};
links_.emplace_back(link_rect, dest);
DBG_GUI_RL << "added link at rect: " << link_rect;
} else {
//link straddles two lines, break into two rects
point t_size(w_ - t_start.x - (origin.x == 0 ? img_width : 0), t_end.y - t_start.y);
point t_size(size_.x - t_start.x - (origin.x == 0 ? img_width : 0), t_end.y - t_start.y);
point link_start2(origin.x, t_start.y + 1.3*font::get_max_height(font::SIZE_NORMAL));
point t_size2(t_end.x, t_end.y - t_start.y);
rect link_rect = {
t_start.x,
t_start.y,
t_size.x,
font::get_max_height(font::SIZE_NORMAL),
};
rect link_rect{ t_start, point{ t_size.x, font::get_max_height(font::SIZE_NORMAL) } };
rect link_rect2{ link_start2, point{ t_size2.x, font::get_max_height(font::SIZE_NORMAL) } };
rect link_rect2 = {
link_start2.x,
link_start2.y,
t_size2.x,
font::get_max_height(font::SIZE_NORMAL),
};
links_.push_back(std::pair(link_rect, dest));
links_.push_back(std::pair(link_rect2, dest));
links_.emplace_back(link_rect, dest);
links_.emplace_back(link_rect2, dest);
DBG_GUI_RL << "added link at rect 1: " << link_rect;
DBG_GUI_RL << "added link at rect 2: " << link_rect2;
}
}
size_t rich_label::get_split_location(std::string 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;
@ -248,20 +241,14 @@ size_t rich_label::get_split_location(std::string text, const point& pos) {
void rich_label::set_topic(const help::topic* topic) {
styled_widget::set_label(topic->text.parsed_text().debug());
const auto& [text_dom, size] = get_parsed_text(topic->text.parsed_text(), point(0,0), init_w_, true);
text_dom_ = text_dom;
w_ = size.x;
h_ = size.y;
std::tie(text_dom_, size_) = get_parsed_text(topic->text.parsed_text(), point(0,0), init_w_, true);
}
void rich_label::set_label(const t_string& text) {
styled_widget::set_label(text);
unparsed_text_ = text;
help::topic_text marked_up_text(text);
const auto& [text_dom, size] = get_parsed_text(marked_up_text.parsed_text(), point(0,0), init_w_, true);
text_dom_ = text_dom;
w_ = size.x;
h_ = size.y;
std::tie(text_dom_, size_) = get_parsed_text(marked_up_text.parsed_text(), point(0,0), init_w_, true);
}
std::pair<config, point> rich_label::get_parsed_text(
@ -299,10 +286,8 @@ std::pair<config, point> rich_label::get_parsed_text(
DBG_GUI_RL << parsed_text.debug();
for(config::any_child tag : parsed_text.all_children_range()) {
config& child = tag.cfg;
if(tag.key == "img") {
for(const auto [key, child] : parsed_text.all_children_range()) {
if(key == "img") {
std::string name = child["src"];
std::string align = child["align"];
bool is_curr_float = child["float"].to_bool(false);
@ -339,7 +324,7 @@ std::pair<config, point> rich_label::get_parsed_text(
DBG_GUI_RL << "image: src=" << name << ", size=" << curr_img_size;
DBG_GUI_RL << "wrap mode: " << wrap_mode << ", floating: " << is_float;
} else if(tag.key == "table") {
} else if(key == "table") {
if (curr_item == nullptr) {
curr_item = &(text_dom.add_child("text"));
default_text_config(curr_item);
@ -355,10 +340,10 @@ std::pair<config, point> rich_label::get_parsed_text(
// init table vars
unsigned col_idx = 0, row_idx = 0;
unsigned rows = tag.cfg.child_count("row");
unsigned rows = child.child_count("row");
unsigned columns = 1;
if (rows > 0) {
columns = tag.cfg.mandatory_child("row").child_count("col");
columns = child.mandatory_child("row").child_count("col");
}
columns = (columns == 0) ? 1 : columns;
unsigned width = child["width"].to_int(init_width);
@ -377,15 +362,16 @@ std::pair<config, point> rich_label::get_parsed_text(
DBG_GUI_RL << __LINE__ << "start table : " << "row= " << rows << " col=" << columns << " width=" << width;
// optimal col width calculation
for (config& row : tag.cfg.child_range("row")) {
for(const config& row : child.child_range("row")) {
col_x = 0;
col_idx = 0;
for (config& col : row.child_range("col")) {
for(const config& col : row.child_range("col")) {
config col_cfg;
col_cfg.append_children(col);
config& col_txt_cfg = col_cfg.add_child("text");
col_txt_cfg.append_attributes(col);
col_cfg.append_children(col);
// attach data
const auto& [table_elem, size] = get_parsed_text(col_cfg, point(col_x, row_y), width/columns);
@ -403,26 +389,27 @@ std::pair<config, point> rich_label::get_parsed_text(
// table layouting
row_y = prev_blk_height;
row_idx = 0;
for (config& row : tag.cfg.child_range("row")) {
for(const config& row : child.child_range("row")) {
col_x = 0;
col_idx = 0;
max_row_height = 0;
for (config& col : row.child_range("col")) {
for(const config& col : row.child_range("col")) {
config col_cfg;
config& col_txt_cfg = col_cfg.add_child("text");
col_txt_cfg.append_attributes(col);
col_cfg.append_children(col);
config& col_txt_cfg = col_cfg.add_child("text");
col_txt_cfg.append_attributes(col);
// attach data
const auto& [table_elem, size] = get_parsed_text(col_cfg, point(col_x, row_y), col_widths[col_idx]);
text_dom.append(table_elem);
auto [table_elem, size] = get_parsed_text(col_cfg, point(col_x, row_y), col_widths[col_idx]);
text_dom.append(std::move(table_elem));
// column post-processing
max_row_height = std::max(max_row_height, static_cast<unsigned>(size.y));
col_x += col_widths[col_idx] + 2 * padding_;
config& end_cfg = std::prev(text_dom.ordered_end())->cfg;
config& end_cfg = text_dom.all_children_range().back().cfg;
end_cfg["actions"] = boost::str(boost::format("([set_var('pos_x', %d), set_var('pos_y', %d), set_var('tw', width - %d - %d)])") % col_x % row_y % col_x % (width/columns));
DBG_GUI_RL << "jump to next column";
@ -435,7 +422,7 @@ std::pair<config, point> rich_label::get_parsed_text(
}
row_y += max_row_height + padding_;
config& end_cfg = std::prev(text_dom.ordered_end())->cfg;
config& end_cfg = text_dom.all_children_range().back().cfg;
end_cfg["actions"] = boost::str(boost::format("([set_var('pos_x', 0), set_var('pos_y', %d), set_var('tw', width - %d - %d)])") % row_y % col_x % col_widths[columns-1]);
DBG_GUI_RL << "row height: " << max_row_height;
}
@ -443,7 +430,7 @@ std::pair<config, point> rich_label::get_parsed_text(
prev_blk_height = row_y;
text_height = 0;
config& end_cfg = std::prev(text_dom.ordered_end())->cfg;
config& end_cfg = text_dom.all_children_range().back().cfg;
end_cfg["actions"] = boost::str(boost::format("([set_var('pos_x', 0), set_var('pos_y', %d), set_var('tw', 0)])") % row_y);
is_image = false;
@ -454,7 +441,7 @@ std::pair<config, point> rich_label::get_parsed_text(
row_y = 0;
max_row_height = 0;
} else if(tag.key == "break" || tag.key == "br") {
} else if(key == "break" || key == "br") {
if (curr_item == nullptr) {
curr_item = &(text_dom.add_child("text"));
default_text_config(curr_item);
@ -535,43 +522,42 @@ std::pair<config, point> rich_label::get_parsed_text(
// }---------- TEXT TAGS -----------{
int tmp_h = get_text_size(*curr_item, init_width - (x == 0 ? float_size.x : x)).y;
if (is_text && tag.key == "text") {
if (is_text && key == "text") {
add_text_with_attribute(*curr_item, "\n\n");
}
is_text = false;
if(tag.key == "ref") {
if(key == "ref") {
add_link(*curr_item, line, child["dst"], point(x + origin.x, prev_blk_height), float_size.x);
is_image = false;
DBG_GUI_RL << "ref: dst=" << child["dst"];
} else if(std::find(format_tags_.begin(), format_tags_.end(), tag.key) != format_tags_.end()) {
} else if(std::find(format_tags.begin(), format_tags.end(), key) != format_tags.end()) {
add_text_with_attribute(*curr_item, line, tag.key);
add_text_with_attribute(*curr_item, line, key);
config parsed_children = get_parsed_text(child, point(x, prev_blk_height), init_width).first;
config parsed_children = get_parsed_text(tag.cfg, point(x, prev_blk_height), init_width).first;
for (config::any_child ch : parsed_children.all_children_range()) {
if (ch.key == "text") {
const auto& [start, end] = add_text(*curr_item, ch.cfg["text"]);
for (config attr : ch.cfg.child_range("attribute")) {
for (const auto [parsed_key, parsed_cfg] : parsed_children.all_children_range()) {
if (parsed_key == "text") {
const auto [start, end] = add_text(*curr_item, parsed_cfg["text"]);
for (const config& attr : parsed_cfg.child_range("attribute")) {
add_attribute(*curr_item, attr["name"], start + attr["start"].to_int(), start + attr["end"].to_int(), attr["value"]);
}
add_attribute(*curr_item, tag.key, start, end);
add_attribute(*curr_item, key, start, end);
} else {
text_dom.add_child(ch.key, ch.cfg);
text_dom.add_child(parsed_key, parsed_cfg);
}
}
is_image = false;
DBG_GUI_RL << tag.key << ": text=" << gui2::debug_truncate(line);
DBG_GUI_RL << key << ": text=" << gui2::debug_truncate(line);
} else if(tag.key == "header" || tag.key == "h") {
} else if(key == "header" || key == "h") {
const auto& [start, end] = add_text(*curr_item, line);
const auto [start, end] = add_text(*curr_item, line);
add_attribute(*curr_item, "face", start, end, "serif");
add_attribute(*curr_item, "color", start, end, font::string_to_color("white").to_hex_string());
add_attribute(*curr_item, "size", start, end, std::to_string(font::SIZE_TITLE - 2));
@ -580,10 +566,10 @@ std::pair<config, point> rich_label::get_parsed_text(
DBG_GUI_RL << "h: text=" << line;
} else if(tag.key == "character_entity") {
} else if(key == "character_entity") {
line = "&" + child["name"].str() + ";";
const auto& [start, end] = add_text(*curr_item, line);
const auto [start, end] = add_text(*curr_item, line);
add_attribute(*curr_item, "face", start, end, "monospace");
add_attribute(*curr_item, "color", start, end, font::string_to_color("red").to_hex_string());
@ -591,9 +577,9 @@ std::pair<config, point> rich_label::get_parsed_text(
DBG_GUI_RL << "entity: text=" << line;
} else if(tag.key == "span" || tag.key == "format") {
} else if(key == "span" || key == "format") {
const auto& [start, end] = add_text(*curr_item, line);
const auto [start, end] = add_text(*curr_item, line);
DBG_GUI_RL << "span/format: text=" << line;
DBG_GUI_RL << "attributes:";
@ -606,7 +592,7 @@ std::pair<config, point> rich_label::get_parsed_text(
is_image = false;
} else if (tag.key == "text") {
} else if (key == "text") {
DBG_GUI_RL << "text: text=" << gui2::debug_truncate(line) << "...";
@ -680,7 +666,7 @@ std::pair<config, point> rich_label::get_parsed_text(
(*curr_item)["actions"] = "([set_var('pos_x', 0), set_var('pos_y', pos_y + " + std::to_string(img_size.y) + ")])";
text_dom.append(*remaining_item);
remaining_item = nullptr;
curr_item = &std::prev(text_dom.ordered_end())->cfg;
curr_item = &text_dom.all_children_range().back().cfg;
}
}
@ -729,7 +715,7 @@ std::pair<config, point> rich_label::get_parsed_text(
h = std::max(static_cast<unsigned>(img_size.y), h);
DBG_GUI_RL << "Width: " << w << " Height: " << h << " Origin: " << origin;
return std::pair(text_dom, point(w, h - origin.y));
return { text_dom, point(w, h - origin.y) };
} // function ends
void rich_label::default_text_config(config* txt_ptr, t_string text) {

View file

@ -193,20 +193,13 @@ private:
/** The unparsed/raw text */
t_string unparsed_text_;
/** shapes used for size calculation */
std::unique_ptr<text_shape> tshape_;
std::unique_ptr<image_shape> ishape_;
/** Width and height of the canvas */
const unsigned init_w_;
unsigned w_, h_;
point size_;
/** Padding */
unsigned padding_;
/** Possible formatting tags, must be the same as those in gui2::text_shape::draw */
static const inline std::vector<std::string> format_tags_ = {"bold", "b", "italic", "i", "underline", "u"};
/** Create template for text config that can be shown in canvas */
void default_text_config(config* txt_ptr, t_string text = "");
@ -218,12 +211,12 @@ private:
void add_link(config& curr_item, std::string name, std::string dest, const point& origin, int img_width);
/** size calculation functions */
point get_text_size(config& text_cfg, unsigned width = 0);
point get_image_size(config& img_cfg);
point get_text_size(config& text_cfg, unsigned width = 0) const;
point get_image_size(config& img_cfg) const;
wfl::map_formula_callable setup_text_renderer(config text_cfg, unsigned width = 0);
wfl::map_formula_callable setup_text_renderer(config text_cfg, unsigned width = 0) const;
size_t get_split_location(std::string text, const point& pos);
size_t get_split_location(std::string_view text, const point& pos);
/** link variables and functions */
std::vector<std::pair<rect, std::string>> links_;
@ -242,9 +235,11 @@ private:
point calculate_best_size() const override
{
point size = styled_widget::calculate_best_size();
point new_size(w_ == 0 ? size.x : w_, h_ == 0 ? size.y : h_);
return new_size;
if(size_ == point{}) {
return styled_widget::calculate_best_size();
} else {
return size_;
}
}
public: