diff --git a/src/floating_label.cpp b/src/floating_label.cpp index f8f04dc4b38b..dbee921ca208 100644 --- a/src/floating_label.cpp +++ b/src/floating_label.cpp @@ -14,9 +14,14 @@ #include "floating_label.hpp" -#include "display.hpp" +#include "font/standard_colors.hpp" #include "font/text.hpp" #include "log.hpp" +#include "sdl/render_utils.hpp" +#include "sdl/surface.hpp" +#include "utils/general.hpp" + +#include #include #include @@ -30,8 +35,8 @@ static lg::log_domain log_font("font"); namespace { -typedef std::map label_map; -label_map labels; +std::map labels; + int label_id = 1; std::stack> label_contexts; @@ -39,18 +44,13 @@ std::stack> label_contexts; namespace font { -floating_label::floating_label(const std::string& text, const surface& surf) -#if 0 - : img_(), -#else - : surf_(surf) - , buf_(nullptr) -#endif +floating_label::floating_label(const std::string& text) + : texture_(nullptr) , text_(text) , font_size_(SIZE_NORMAL) , color_(NORMAL_COLOR) , bgcolor_() - , bgalpha_(0) + , current_alpha_(255) , xpos_(0) , ypos_(0) , xmove_(0) @@ -58,13 +58,14 @@ floating_label::floating_label(const std::string& text, const surface& surf) , lifetime_(-1) , width_(-1) , height_(-1) - , clip_rect_(CVideo::get_singleton().screen_area()) + , clip_rect_(screen_area()) , alpha_change_(0) , visible_(true) , align_(CENTER_ALIGN) , border_(0) , scroll_(ANCHOR_LABEL_SCREEN) , use_markup_(true) + , fill_background_(false) { } @@ -76,7 +77,8 @@ void floating_label::move(double xmove, double ymove) int floating_label::xpos(size_t width) const { - int xpos = int(xpos_); + int xpos = static_cast(xpos_); + if(align_ == font::CENTER_ALIGN) { xpos -= width / 2; } else if(align_ == font::RIGHT_ALIGN) { @@ -86,89 +88,103 @@ int floating_label::xpos(size_t width) const return xpos; } -surface floating_label::create_surface() +texture floating_label::create_texture() { - if(surf_.null()) { - font::pango_text text; - text.set_foreground_color(color_); - text.set_font_size(font_size_); - text.set_maximum_width(width_ < 0 ? clip_rect_.w : width_); - text.set_maximum_height(height_ < 0 ? clip_rect_.h : height_, true); - - // ignore last '\n' - if(!text_.empty() && *(text_.rbegin()) == '\n') { - text.set_text(std::string(text_.begin(), text_.end() - 1), use_markup_); - } else { - text.set_text(text_, use_markup_); - } + if(texture_.null()) { + // + // Render text + // - surface foreground = text.render(); + // TODO: figure out why the global text renderer object gives too large a size. + font::pango_text renderer; - if(foreground == nullptr) { - ERR_FT << "could not create floating label's text" << std::endl; - return nullptr; - } + renderer.set_foreground_color(color_); + renderer.set_font_size(font_size_); + renderer.set_maximum_width(width_ < 0 ? clip_rect_.w : width_); + renderer.set_maximum_height(height_ < 0 ? clip_rect_.h : height_, true); - // combine foreground text with its background - if(bgalpha_ != 0) { - // background is a dark tooltip box - surface background = create_neutral_surface(foreground->w + border_ * 2, foreground->h + border_ * 2); + // Strip trailing newlines. + boost::trim_right(text_); - if(background == nullptr) { - ERR_FT << "could not create tooltip box" << std::endl; - return surf_ = foreground; - } + renderer.set_text(text_, use_markup_); - uint32_t color = SDL_MapRGBA(foreground->format, bgcolor_.r, bgcolor_.g, bgcolor_.b, bgalpha_); - sdl::fill_surface_rect(background, nullptr, color); + surface& foreground = renderer.render(); - // we make the text less transparent, because the blitting on the - // dark background will darken the anti-aliased part. - // This 1.13 value seems to restore the brightness of version 1.4 - // (where the text was blitted directly on screen) - adjust_surface_alpha(foreground, ftofxp(1.13)); - - SDL_Rect r{border_, border_, 0, 0}; - adjust_surface_alpha(foreground, SDL_ALPHA_OPAQUE); - sdl_blit(foreground, nullptr, background, &r); + if(foreground == nullptr) { + ERR_FT << "could not create floating label's text" << std::endl; + return texture(); + } - surf_ = background; - } else { - // background is blurred shadow of the text + // + // Add some text shadow if we're not drawing the background. + // + if(!fill_background_) { surface background = create_neutral_surface(foreground->w + 4, foreground->h + 4); sdl::fill_surface_rect(background, nullptr, 0); - SDL_Rect r{2, 2, 0, 0}; + + SDL_Rect r {2, 2, 0, 0}; sdl_blit(foreground, nullptr, background, &r); + background = shadow_image(background); if(background == nullptr) { ERR_FT << "could not create floating label's shadow" << std::endl; - return surf_ = foreground; + return texture_ = texture(foreground); } + sdl_blit(foreground, nullptr, background, &r); - surf_ = background; + + texture_ = texture(background); + } else { + texture_ = texture(foreground); } } - return surf_; + return texture_; } void floating_label::draw() { - create_surface(); - if(surf_ == nullptr) { + // No-op if texture is valid + create_texture(); + + if(texture_ == nullptr) { return; } - SDL_Rect rect = sdl::create_rect(xpos(surf_->w), ypos_, surf_->w, surf_->h); + const texture::info info = texture_.get_info(); + SDL_Rect rect = sdl::create_rect(xpos(info.w), ypos_, info.w, info.h); - // TODO: cache? - texture tex(surf_); + move(xmove_, ymove_); - CVideo::get_singleton().render_copy(tex, nullptr, &rect); -} + // Fade out moving floating labels + if(lifetime_ > 0) { + --lifetime_; + + if(alpha_change_ != 0 && (xmove_ != 0.0 || ymove_ != 0.0)) { + current_alpha_ = utils::clamp(current_alpha_ + alpha_change_, 0, 255); + set_texture_alpha(texture_, current_alpha_); + } + } -// NOTE: there used to be a "fade out moving floating labels" effect here. + // Draw a semi-transparent background background alpha provided. + // NOTE: doing this this way instead of embedding it as part of label texture itself does + // have the side effect of removing the background from the fadeout effect. However, in + // practical use only the non-background versions are used with the fadeout effect. I can do + // some alpha fadeout on the background later too if relevant. + if(fill_background_) { + SDL_Rect bg_rect { + rect.x - border_, + rect.y - border_, + rect.w + (border_ * 2), + rect.h + (border_ * 2) + }; + + sdl::fill_rectangle(bg_rect, bgcolor_); + } + + CVideo::get_singleton().render_copy(texture_, nullptr, &rect); +} int add_floating_label(const floating_label& flabel) { @@ -184,7 +200,7 @@ int add_floating_label(const floating_label& flabel) void move_floating_label(int handle, double xmove, double ymove) { - const label_map::iterator i = labels.find(handle); + const auto i = labels.find(handle); if(i != labels.end()) { i->second.move(xmove, ymove); } @@ -192,16 +208,16 @@ void move_floating_label(int handle, double xmove, double ymove) void scroll_floating_labels(double xmove, double ymove) { - for(label_map::iterator i = labels.begin(); i != labels.end(); ++i) { - if(i->second.scroll() == ANCHOR_LABEL_MAP) { - i->second.move(xmove, ymove); + for(auto& label : labels) { + if(label.second.scroll() == ANCHOR_LABEL_MAP) { + label.second.move(xmove, ymove); } } } void remove_floating_label(int handle) { - const label_map::iterator i = labels.find(handle); + const auto i = labels.find(handle); if(i != labels.end()) { labels.erase(i); } @@ -213,7 +229,7 @@ void remove_floating_label(int handle) void show_floating_label(int handle, bool value) { - const label_map::iterator i = labels.find(handle); + const auto i = labels.find(handle); if(i != labels.end()) { i->second.show(value); } @@ -221,25 +237,27 @@ void show_floating_label(int handle, bool value) SDL_Rect get_floating_label_rect(int handle) { - const label_map::iterator i = labels.find(handle); + const auto i = labels.find(handle); if(i != labels.end()) { - const surface surf = i->second.create_surface(); - if(surf != nullptr) { - return {0, 0, surf->w, surf->h}; + const texture& tex = i->second.create_texture(); + if(tex != nullptr) { + const texture::info info = tex.get_info(); + + return {0, 0, info.w, info.h}; } } + return sdl::empty_rect; } floating_label_context::floating_label_context() { - label_contexts.emplace(); + label_contexts.push(std::set()); } floating_label_context::~floating_label_context() { const std::set& context = label_contexts.top(); - while(!context.empty()) { // Remove_floating_label removes the passed label from the context. // This loop removes a different label in every iteration. @@ -255,15 +273,24 @@ void draw_floating_labels() return; } - const std::set& context = label_contexts.top(); + std::set& context = label_contexts.top(); - // draw the labels in the order they were added, so later added labels (likely to be tooltips) + // Remove expired labels. + for(auto itor = labels.begin(); itor != labels.end(); /* Handle increment in loop*/) { + if(context.count(itor->first) > 0 && itor->second.expired()) { + context.erase(itor->first); + labels.erase(itor++); + } else { + ++itor; + } + } + + // Draw the labels in the order they were added, so later added labels (likely to be tooltips) // are displayed over earlier added labels. - for(label_map::iterator i = labels.begin(); i != labels.end(); ++i) { - if(context.count(i->first) > 0) { - i->second.draw(); + for(auto& label : labels) { + if(context.count(label.first) > 0) { + label.second.draw(); } } } - } diff --git a/src/floating_label.hpp b/src/floating_label.hpp index 4e6ca0455ac8..f08d2cf261fc 100644 --- a/src/floating_label.hpp +++ b/src/floating_label.hpp @@ -15,7 +15,8 @@ #pragma once #include "color.hpp" -#include "sdl/surface.hpp" +#include "sdl/texture.hpp" + #include namespace font { @@ -35,7 +36,7 @@ enum LABEL_SCROLL_MODE { ANCHOR_LABEL_SCREEN, ANCHOR_LABEL_MAP }; class floating_label { public: - floating_label(const std::string& text, const surface& surface = nullptr); + explicit floating_label(const std::string& text); void set_font_size(int font_size) {font_size_ = font_size;} @@ -57,7 +58,7 @@ class floating_label void set_color(const color_t& color) {color_ = color;} void set_bg_color(const color_t& bg_color) { bgcolor_ = bg_color; - bgalpha_ = bg_color.a; + fill_background_ = bgcolor_.a != 255; } void set_border_size(int border) {border_ = border;} // set width for word wrapping (use -1 to disable it) @@ -71,7 +72,7 @@ class floating_label void move(double xmove, double ymove); void draw(); - surface create_surface(); + texture create_texture(); bool expired() const { return lifetime_ == 0; } @@ -82,11 +83,12 @@ class floating_label private: int xpos(size_t width) const; - surface surf_, buf_; + + texture texture_; std::string text_; int font_size_; color_t color_, bgcolor_; - int bgalpha_; + unsigned int current_alpha_; double xpos_, ypos_, xmove_, ymove_; int lifetime_; int width_, height_; @@ -97,6 +99,8 @@ class floating_label int border_; LABEL_SCROLL_MODE scroll_; bool use_markup_; + + bool fill_background_; }; diff --git a/src/tooltips.cpp b/src/tooltips.cpp index e97be9f2f277..8950ce057b02 100644 --- a/src/tooltips.cpp +++ b/src/tooltips.cpp @@ -72,7 +72,7 @@ static void show_tooltip(const tooltip& tip) unsigned int border = 10; - font::floating_label flabel(tip.message, tip.foreground); + font::floating_label flabel(tip.message/*, tip.foreground*/); flabel.use_markup(tip.markup); flabel.set_font_size(font_size); flabel.set_color(font::NORMAL_COLOR);