Help Viewer: Parse help markup to Pango markup... mostly.
The help parser now outputs a config rather than a vector of strings of which some should be taken literally and others parsed as WML.
This commit is contained in:
parent
19eee7e806
commit
11dbcb1464
5 changed files with 105 additions and 44 deletions
|
@ -37,6 +37,8 @@
|
|||
|
||||
#include "help/help.hpp"
|
||||
#include "help/help_impl.hpp"
|
||||
#include "font/pango/escape.hpp"
|
||||
#include "font/pango/hyperlink.hpp"
|
||||
|
||||
namespace gui2
|
||||
{
|
||||
|
@ -85,6 +87,69 @@ void help_browser::add_topic(window& window, const std::string& topic_id, const
|
|||
topic_tree.add_node("topic", data).set_id(std::string(expands ? "+" : "-") + topic_id);
|
||||
}
|
||||
|
||||
static std::string format_help_text(const config& cfg) {
|
||||
std::stringstream ss;
|
||||
for(auto& item : cfg.all_children_range()) {
|
||||
if(item.key == "text") {
|
||||
ss << font::escape_text(item.cfg["text"]);
|
||||
} else if(item.key == "ref") {
|
||||
if(item.cfg["dst"].empty()) {
|
||||
std::stringstream msg;
|
||||
msg << "Ref markup must have dst attribute. Please submit a bug"
|
||||
" report if you have not modified the game files yourself. Erroneous config: " << cfg;
|
||||
throw help::parse_error(msg.str());
|
||||
};
|
||||
// TODO: Get the proper link shade from somewhere
|
||||
ss << font::format_as_link(font::escape_text(item.cfg["text"]), "#aaaa00");
|
||||
} else if(item.key == "img") {
|
||||
if(item.cfg["src"].empty()) {
|
||||
throw help::parse_error("Img markup must have src attribute.");
|
||||
}
|
||||
// For now, just output a placeholder so we know an image is supposed to be there.
|
||||
ss << "[img]" << font::escape_text(item.cfg["src"]) << "[/img]";
|
||||
} else if(item.key == "bold") {
|
||||
if(item.cfg["text"].empty()) {
|
||||
throw help::parse_error("Bold markup must have text attribute.");
|
||||
}
|
||||
ss << "<b>" << font::escape_text(item.cfg["text"]) << "</b>";
|
||||
} else if(item.key == "italic") {
|
||||
if(item.cfg["text"].empty()) {
|
||||
throw help::parse_error("Italic markup must have text attribute.");
|
||||
}
|
||||
ss << "<i>" << font::escape_text(item.cfg["text"]) << "</i>";
|
||||
} else if(item.key == "header") {
|
||||
if(item.cfg["text"].empty()) {
|
||||
throw help::parse_error("Header markup must have text attribute.");
|
||||
}
|
||||
ss << "<big>" << font::escape_text(item.cfg["text"]) << "</big>";
|
||||
} else if(item.key == "jump") {
|
||||
// Seems like this creates something invisible?
|
||||
if(item.cfg["amount"].empty() && item.cfg["to"].empty()) {
|
||||
throw help::parse_error("Jump markup must have either a to or an amount attribute.");
|
||||
}
|
||||
} else if(item.key == "format") {
|
||||
if(item.cfg["text"].empty()) {
|
||||
throw help::parse_error("Header markup must have text attribute.");
|
||||
}
|
||||
ss << "<span";
|
||||
if(item.cfg.has_attribute("font_size")) {
|
||||
ss << " size='" << item.cfg["font_size"].to_int(font::SIZE_NORMAL) << "'";
|
||||
}
|
||||
if(item.cfg.has_attribute("color")) {
|
||||
ss << " color='" << help::string_to_color(item.cfg["color"]).to_hex_string() << "'";
|
||||
}
|
||||
if(item.cfg["bold"]) {
|
||||
ss << " font_weight='bold'";
|
||||
}
|
||||
if(item.cfg["italic"]) {
|
||||
ss << " font_style='italic'";
|
||||
}
|
||||
ss << '>' << font::escape_text(item.cfg["text"]) << "</span>";
|
||||
}
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void help_browser::on_topic_select(window& window)
|
||||
{
|
||||
multi_page& topic_pages = find_widget<multi_page>(&window, "topic_text_pages", false);
|
||||
|
@ -118,7 +183,7 @@ void help_browser::on_topic_select(window& window)
|
|||
std::map<std::string, string_map> data;
|
||||
string_map item;
|
||||
|
||||
item["label"] = utils::join(topic->text.parsed_text(), "");
|
||||
item["label"] = format_help_text(topic->text.parsed_text());
|
||||
data.emplace("topic_text", item);
|
||||
|
||||
parsed_pages_.emplace(topic_id, topic_pages.get_page_count());
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "units/race.hpp" // for unit_race, etc
|
||||
#include "resources.hpp" // for tod_manager, config_manager
|
||||
#include "sdl/surface.hpp" // for surface
|
||||
#include "serialization/parser.hpp"
|
||||
#include "serialization/string_utils.hpp" // for split, quoted_split, etc
|
||||
#include "serialization/unicode_cast.hpp" // for unicode_cast
|
||||
#include "serialization/unicode_types.hpp" // for char_t, etc
|
||||
|
@ -44,6 +45,7 @@
|
|||
#include "units/types.hpp" // for unit_type, unit_type_data, etc
|
||||
#include "serialization/unicode.hpp" // for iterator
|
||||
#include "color.hpp"
|
||||
#include "config_assign.hpp"
|
||||
|
||||
#include <cassert> // for assert
|
||||
#include <algorithm> // for sort, find, transform, etc
|
||||
|
@ -344,7 +346,7 @@ topic_text &topic_text::operator=(topic_generator *g)
|
|||
return *this;
|
||||
}
|
||||
|
||||
const std::vector<std::string>& topic_text::parsed_text() const
|
||||
const config& topic_text::parsed_text() const
|
||||
{
|
||||
if (generator_) {
|
||||
parsed_text_ = parse_text((*generator_)());
|
||||
|
@ -1165,9 +1167,9 @@ const section *find_section(const section &sec, const std::string &id)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<std::string> parse_text(const std::string &text)
|
||||
config parse_text(const std::string &text)
|
||||
{
|
||||
std::vector<std::string> res;
|
||||
config res;
|
||||
bool last_char_escape = false;
|
||||
const char escape_char = '\\';
|
||||
std::stringstream ss;
|
||||
|
@ -1185,7 +1187,7 @@ std::vector<std::string> parse_text(const std::string &text)
|
|||
ss << c;
|
||||
}
|
||||
else {
|
||||
res.push_back(ss.str());
|
||||
res.add_child("text", config_of("text", ss.str()));
|
||||
ss.str("");
|
||||
state = ELEMENT_NAME;
|
||||
}
|
||||
|
@ -1212,10 +1214,18 @@ std::vector<std::string> parse_text(const std::string &text)
|
|||
msg << "Unterminated element: " << element_name;
|
||||
throw parse_error(msg.str());
|
||||
}
|
||||
s.str("");
|
||||
const std::string contents = text.substr(pos + 1, end_pos - pos - 1);
|
||||
const std::string element = convert_to_wml(element_name, contents);
|
||||
res.push_back(element);
|
||||
s.str(convert_to_wml(element_name, contents));
|
||||
s.seekg(0);
|
||||
try {
|
||||
config cfg;
|
||||
read(cfg, s);
|
||||
res.append_children(cfg);
|
||||
} catch(config::error& e) {
|
||||
std::stringstream msg;
|
||||
msg << "Error when parsing help markup as WML: '" << e.message << "'";
|
||||
throw parse_error(msg.str());
|
||||
}
|
||||
pos = end_pos + end_element_name.size() - 1;
|
||||
state = OTHER;
|
||||
}
|
||||
|
@ -1233,7 +1243,7 @@ std::vector<std::string> parse_text(const std::string &text)
|
|||
}
|
||||
if (!ss.str().empty()) {
|
||||
// Add the last string.
|
||||
res.push_back(ss.str());
|
||||
res.add_child("text", config_of("text", ss.str()));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ public:
|
|||
/// contained in generator_.
|
||||
class topic_text
|
||||
{
|
||||
mutable std::vector< std::string > parsed_text_;
|
||||
mutable config parsed_text_;
|
||||
mutable topic_generator *generator_;
|
||||
public:
|
||||
~topic_text();
|
||||
|
@ -104,7 +104,7 @@ public:
|
|||
topic_text &operator=(topic_generator *g);
|
||||
topic_text(const topic_text& t);
|
||||
|
||||
const std::vector<std::string>& parsed_text() const;
|
||||
const config& parsed_text() const;
|
||||
};
|
||||
|
||||
/// A topic contains a title, an id and some text.
|
||||
|
@ -287,7 +287,7 @@ const section *find_section(const section &sec, const std::string &id);
|
|||
/// Parse a text string. Return a vector with the different parts of the
|
||||
/// text. Each markup item is a separate part while the text between
|
||||
/// markups are separate parts.
|
||||
std::vector<std::string> parse_text(const std::string &text);
|
||||
config parse_text(const std::string &text);
|
||||
|
||||
/// Convert the contents to wml attributes, surrounded within
|
||||
/// [element_name]...[/element_name]. Return the resulting WML.
|
||||
|
|
|
@ -113,41 +113,22 @@ void help_text_area::set_items()
|
|||
down_one_line();
|
||||
}
|
||||
// Parse and add the text.
|
||||
const std::vector<std::string>& parsed_items = shown_topic_->text.parsed_text();
|
||||
std::vector<std::string>::const_iterator it;
|
||||
for (it = parsed_items.begin(); it != parsed_items.end(); ++it) {
|
||||
if (!(*it).empty() && (*it)[0] == '[') {
|
||||
// Should be parsed as WML.
|
||||
try {
|
||||
config cfg;
|
||||
std::istringstream stream(*it);
|
||||
read(cfg, stream);
|
||||
|
||||
const config& parsed_items = shown_topic_->text.parsed_text();
|
||||
for(auto& item : parsed_items.all_children_range()) {
|
||||
#define TRY(name) do { \
|
||||
if (config &child = cfg.child(#name)) \
|
||||
handle_##name##_cfg(child); \
|
||||
} while (0)
|
||||
|
||||
TRY(ref);
|
||||
TRY(img);
|
||||
TRY(bold);
|
||||
TRY(italic);
|
||||
TRY(header);
|
||||
TRY(jump);
|
||||
TRY(format);
|
||||
if (config &child = item.cfg.child(#name)) \
|
||||
handle_##name##_cfg(child); \
|
||||
} while (0)
|
||||
|
||||
TRY(text);
|
||||
TRY(ref);
|
||||
TRY(img);
|
||||
TRY(bold);
|
||||
TRY(italic);
|
||||
TRY(header);
|
||||
TRY(jump);
|
||||
TRY(format);
|
||||
#undef TRY
|
||||
|
||||
}
|
||||
catch (config::error& e) {
|
||||
std::stringstream msg;
|
||||
msg << "Error when parsing help markup as WML: '" << e.message << "'";
|
||||
throw parse_error(msg.str());
|
||||
}
|
||||
}
|
||||
else {
|
||||
add_text_item(*it);
|
||||
}
|
||||
}
|
||||
down_one_line(); // End the last line.
|
||||
int h = height();
|
||||
|
@ -290,6 +271,10 @@ void help_text_area::handle_format_cfg(const config &cfg)
|
|||
add_text_item(text, "", false, font_size, bold, italic, color);
|
||||
}
|
||||
|
||||
void help_text_area::handle_text_cfg(const config& cfg) {
|
||||
add_text_item(cfg["text"]);
|
||||
}
|
||||
|
||||
void help_text_area::add_text_item(const std::string& text, const std::string& ref_dst,
|
||||
bool broken_link, int _font_size, bool bold, bool italic,
|
||||
color_t text_color
|
||||
|
|
|
@ -104,6 +104,7 @@ private:
|
|||
void handle_header_cfg(const config &cfg);
|
||||
void handle_jump_cfg(const config &cfg);
|
||||
void handle_format_cfg(const config &cfg);
|
||||
void handle_text_cfg(const config &cfg);
|
||||
|
||||
void draw_contents();
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue