diff --git a/src/gui/widgets/control.cpp b/src/gui/widgets/control.cpp index f7276d8860f..0d7c4947cc9 100644 --- a/src/gui/widgets/control.cpp +++ b/src/gui/widgets/control.cpp @@ -16,13 +16,18 @@ #include "control.hpp" +#include "font.hpp" #include "foreach.hpp" #include "gui/widgets/window.hpp" +#include "marked-up_text.hpp" + +#include namespace gui2 { tcontrol::tcontrol(const unsigned canvas_count) : label_() + , markup_mode_(NO_MARKUP) , use_tooltip_on_label_overflow_(true) , tooltip_() , help_message_() @@ -238,6 +243,17 @@ void tcontrol::set_label(const t_string& label) set_dirty(); } +void tcontrol::set_markup_mode(const tmarkup_mode markup_mode) +{ + if(markup_mode == markup_mode_) { + return; + } + + markup_mode_ = markup_mode; + update_canvas(); + set_dirty(); +} + void tcontrol::update_canvas() { const int max_width = get_text_maximum_width(); @@ -245,7 +261,22 @@ void tcontrol::update_canvas() // set label in canvases foreach(tcanvas& canvas, canvas_) { - canvas.set_variable("text", variant(label_)); + switch(markup_mode_) { + case NO_MARKUP : + canvas.set_variable("text", variant(label_)); + canvas.set_variable("text_markup", variant(false)); + break; + case PANGO_MARKUP : + canvas.set_variable("text", variant(label_)); + canvas.set_variable("text_markup", variant(true)); + break; + case WML_MARKUP : + canvas.set_variable("text", variant(get_pango_markup())); + canvas.set_variable("text_markup", variant(true)); + break; + default: + assert(false); + } canvas.set_variable("text_maximum_width", variant(max_width)); canvas.set_variable("text_maximum_height", variant(max_height)); canvas.set_variable("text_wrap_mode", variant(can_wrap() @@ -296,7 +327,20 @@ tpoint tcontrol::get_best_text_size(const tpoint& minimum_size, const tpoint& ma const tpoint border(config_->text_extra_width, config_->text_extra_height); tpoint size = minimum_size - border; - renderer_.set_text(label_, false); + switch(markup_mode_) { + case NO_MARKUP : + renderer_.set_text(label_, false); + break; + case PANGO_MARKUP : + renderer_.set_text(label_, true); + break; + case WML_MARKUP : + renderer_.set_text(get_pango_markup(), true); + break; + default: + assert(false); + } + renderer_.set_font_size(config_->text_font_size); renderer_.set_font_style(config_->text_font_style); @@ -342,5 +386,125 @@ tpoint tcontrol::get_best_text_size(const tpoint& minimum_size, const tpoint& ma return size; } +namespace { + +/** Converts a SDL_Color to a pango colour prefix. */ +std::string colour_prefix(const SDL_Color& colour) +{ + std::stringstream result; + + result << ""; + + return result.str(); +} + +} // namespace + +std::string tcontrol::get_pango_markup() const +{ + std::vector lines = utils::split(label_, '\n', 0); + + foreach(std::string& line, lines) { + if(line.empty()) { + continue; + } + + std::string pre = ""; + std::string post = ""; + bool proceed = true; + + while(!line.empty() && proceed) { + const char c = line[0]; + // The font 'constants' aren't seen as const so it's not possible + // to use a switch statement. + if(c == font::BAD_TEXT) { + pre += colour_prefix(font::BAD_COLOUR); + post = "" + post; + line.erase(0, 1); + } else if(c == font::GOOD_TEXT) { + pre += colour_prefix(font::GOOD_COLOUR); + post = "" + post; + line.erase(0, 1); + } else if(c == font::NORMAL_TEXT) { + pre += colour_prefix(font::NORMAL_COLOUR); + post = "" + post; + line.erase(0, 1); + } else if(c == font::BLACK_TEXT) { + pre += colour_prefix(font::BLACK_COLOUR); + post = "" + post; + line.erase(0, 1); + } else if(c == font::GRAY_TEXT) { + pre += colour_prefix(font::GRAY_COLOUR); + post = "" + post; + line.erase(0, 1); + } else if(c == font::LARGE_TEXT) { + pre += ""; + post = "" + post; + line.erase(0, 1); + } else if(c == font::SMALL_TEXT) { + pre += ""; + post = "" + post; + line.erase(0, 1); + } else if(c == font::BOLD_TEXT) { + pre += ""; + post = "" + post; + line.erase(0, 1); + + } else if(c == font::COLOR_TEXT) { + /* + * Crude parsing of <1,23,123>. + * + * The old engine didn't specify the error behaviour so we + * define our own. + * + * If no > found the entire line is discarded. + * If not three colours are found the colour part is ignored. + * Invalid colour channels are seen as 0. + */ + + // Find the closing >. + const size_t pos = line.find('>'); + if(pos == std::string::npos) { + line.clear(); + continue; + } + + std::vector rgb = utils::split( + line.substr(1, pos - 1)); + + // Remove the colour part of the string it's no longer needed + // and the next test might fail so we need to clean the + // string here. + line.erase(0, pos + 1); + + if(rgb.size() != 3) { + continue; + } + + SDL_Color colour; + colour.r = lexical_cast_default(rgb[0]); + colour.g = lexical_cast_default(rgb[1]); + colour.b = lexical_cast_default(rgb[2]); + + pre += colour_prefix(colour); + post = "" + post; + } else if(c == font::NULL_MARKUP) { + line.erase(0, 1); + proceed = false; + } else { + proceed = false; + } + } + + line = pre + line + post; + } + return utils::join(lines, '\n'); +} + } // namespace gui2 diff --git a/src/gui/widgets/control.hpp b/src/gui/widgets/control.hpp index 98e511aa988..88c7e2ac346 100644 --- a/src/gui/widgets/control.hpp +++ b/src/gui/widgets/control.hpp @@ -35,6 +35,26 @@ public: virtual ~tcontrol() {} + /** + * The markup mode of the label. + * + * The markup could have been a boolean but since we have the old + * wml markup as well we need to convert that markup to the markup used + * by pango. + * + * @todo once the wml markup is phased out this enum can be removed. + */ + enum tmarkup_mode { + NO_MARKUP, /**< The control doesn't use markup for its text. */ + PANGO_MARKUP, /**< The control uses the pango markup feature. */ + WML_MARKUP /**< + * The control uses the wml_markup feature. + * The engine can only handle pango markup and this + * markup will be converted to pango markup before + * sending to the layout engine. + */ + }; + /** * Sets the members of the control. * @@ -214,6 +234,9 @@ public: const t_string& label() const { return label_; } virtual void set_label(const t_string& label); + virtual void set_markup_mode(const tmarkup_mode markup_mode); + tmarkup_mode get_markup_mode() const { return markup_mode_; } + const t_string& tooltip() const { return tooltip_; } // Note setting the tooltip_ doesn't dirty an object. void set_tooltip(const t_string& tooltip) @@ -264,6 +287,9 @@ private: /** Contain the non-editable text associated with control. */ t_string label_; + /** The markup mode of the label_. */ + tmarkup_mode markup_mode_; + /** * If the text doesn't fit on the label should the text be used as tooltip? * @@ -365,6 +391,9 @@ private: /** Is the widget smaller as it's best size? */ bool shrunken_; + + /** Converts the label_ to a pango markup string. */ + std::string get_pango_markup() const; }; } // namespace gui2 diff --git a/src/gui/widgets/scroll_label.cpp b/src/gui/widgets/scroll_label.cpp index 5095fa49ed5..2a34f17af7b 100644 --- a/src/gui/widgets/scroll_label.cpp +++ b/src/gui/widgets/scroll_label.cpp @@ -39,6 +39,18 @@ void tscroll_label::set_label(const t_string& label) } } +void tscroll_label::set_markup_mode(const tmarkup_mode markup_mode) +{ + // Inherit. + tcontrol::set_markup_mode(markup_mode); + + if(content_grid()) { + tlabel* widget = content_grid()-> + find_widget("_label", false, true); + widget->set_markup_mode(markup_mode); + } +} + void tscroll_label::finalize_subclass() { assert(content_grid()); diff --git a/src/gui/widgets/scroll_label.hpp b/src/gui/widgets/scroll_label.hpp index 6d510980356..9c889bf5a27 100644 --- a/src/gui/widgets/scroll_label.hpp +++ b/src/gui/widgets/scroll_label.hpp @@ -39,6 +39,9 @@ public: /** Inherited from tcontrol. */ void set_label(const t_string& label); + /** Inherited from tcontrol. */ + void set_markup_mode(const tmarkup_mode markup_mode); + /** Inherited from tcontainer_. */ void set_self_active(const bool active) { state_ = active ? ENABLED : DISABLED; }