InfoBlock objects' height adapts to text height.
SVN-Revision: 4981
#include "sector.hpp"
#include "log.hpp"
#include "object/player.hpp"
+#include "main.hpp"
namespace {
const float SCROLL_DELAY = 0.5;
const float SCROLL_DISTANCE = 16;
+ const float WIDTH = 400;
+ const float HEIGHT = 200;
}
InfoBlock::InfoBlock(const lisp::Lisp& lisp)
- : Block(sprite_manager->create("images/objects/bonus_block/infoblock.sprite")), shown_pct(0), dest_pct(0), slowdown_scroll(0)
+ : Block(sprite_manager->create("images/objects/bonus_block/infoblock.sprite")), shown_pct(0), dest_pct(0)
{
Vector pos;
lisp.get("x", pos.x);
//stopped = false;
//ringing = new AmbientSound(get_pos(), 0.5, 300, 1, "sounds/phone.wav");
//Sector::current()->add_object(ringing);
- infoBox.reset(new InfoBox(message));
+
+ // Split text string lines into a vector
+ lines = InfoBoxLine::split(message, 400);
+ lines_height = 0;
+ for(size_t i = 0; i < lines.size(); ++i) lines_height+=lines[i]->get_height();
}
InfoBlock::~InfoBlock()
{
+ for(std::vector<InfoBoxLine*>::iterator i = lines.begin(); i != lines.end(); i++) {
+ delete *i;
+ }
}
void
void
InfoBlock::update(float delta)
{
+ Block::update(delta);
+
if (delta == 0) return;
// hide message if player is too far away or above infoblock
Vector dist = (p2 - p1);
float d = dist.norm();
if (d > 128 || dist.y < 0) dest_pct = 0;
- slowdown_scroll += delta;
- if ( slowdown_scroll > SCROLL_DELAY ) {
- slowdown_scroll = 0;
- if (dist.x > SCROLL_DISTANCE) {
- infoBox->scrolldown();
- }
- if( dist.x < -SCROLL_DISTANCE) {
- infoBox->scrollup();
- }
- }
}
}
{
Block::draw(context);
- if (shown_pct > 0) {
- context.push_transform();
- context.set_translation(Vector(0, 0));
- context.set_alpha(shown_pct);
- infoBox->draw(context);
- context.pop_transform();
+ if (shown_pct <= 0) return;
+
+ context.push_transform();
+ //context.set_translation(Vector(0, 0));
+ context.set_alpha(shown_pct);
+
+ //float x1 = SCREEN_WIDTH/2-200;
+ //float y1 = SCREEN_HEIGHT/2-200;
+ float border = 8;
+ float width = 400; // this is the text width only
+ float height = lines_height; // this is the text height only
+ float x1 = (get_bbox().p1.x + get_bbox().p2.x)/2 - width/2;
+ float x2 = (get_bbox().p1.x + get_bbox().p2.x)/2 + width/2;
+ float y1 = original_y - height;
+
+ // lines_height includes one ITEMS_SPACE too much, so the bottom border is reduced by 4px
+ context.draw_filled_rect(Vector(x1-border, y1-border), Vector(width+2*border, height+2*border-4), Color(0.6f, 0.7f, 0.8f, 0.5f), LAYER_GUI-50);
+
+ float y = y1;
+ for(size_t i = 0; i < lines.size(); ++i) {
+ if(y >= y1 + height) {
+ //log_warning << "Too many lines of text in InfoBlock" << std::endl;
+ //dest_pct = 0;
+ //shown_pct = 0;
+ break;
+ }
+
+ lines[i]->draw(context, Rect(x1, y, x2, y), LAYER_GUI-50+1);
+ y += lines[i]->get_height();
}
+
+ context.pop_transform();
}
void
//bool stopped;
float shown_pct; /**< Value in the range of 0..1, depending on how much of the infobox is currently shown */
float dest_pct; /**< With each call to update(), shown_pct will slowly transition to this value */
- std::auto_ptr<InfoBox> infoBox;
Player* get_nearest_player();
-private:
- float slowdown_scroll;
+ std::vector<InfoBoxLine*> lines; /**< lines of text (or images) to display */
+ float lines_height;
};
#endif
}
// Split text string lines into a vector
- lines = InfoBoxLine::split(text, 40);
+ lines = InfoBoxLine::split(text, SCREEN_WIDTH - 2*LEFT_BORDER);
// load background image
background.reset(new Surface("images/background/" + background_file));
float y = SCREEN_HEIGHT - scroll;
for(size_t i = 0; i < lines.size(); i++) {
- lines[i]->draw(context, Vector(LEFT_BORDER, y), LAYER_GUI);
+ lines[i]->draw(context, Rect(LEFT_BORDER, y, SCREEN_WIDTH - 2*LEFT_BORDER, y), LAYER_GUI);
y += lines[i]->get_height();
}
: firstline(0)
{
// Split text string lines into a vector
- lines = InfoBoxLine::split(text, 23);
+ lines = InfoBoxLine::split(text, 400);
try
{
break;
}
- lines[i]->draw(context, Vector(x1, y), LAYER_GUI);
+ lines[i]->draw(context, Rect(x1, y, x1+width, y), LAYER_GUI);
y += lines[i]->get_height();
}
{
}
-InfoBoxLine::InfoBoxLine(char format_char, const std::string& text) : lineType(NORMAL), font(white_text), text(text), image(0)
-{
+namespace {
+Font* get_font_by_format_char(char format_char) {
switch(format_char)
{
case ' ':
- lineType = SMALL;
- font = white_small_text;
+ return white_small_text;
break;
case '\t':
- lineType = NORMAL;
- font = white_text;
+ return white_text;
break;
case '-':
- lineType = HEADING;
- font = white_big_text;
+ return white_big_text;
break;
case '*':
- lineType = REFERENCE;
- font = blue_text;
+ return blue_text;
break;
case '#':
- lineType = NORMAL_LEFT;
- font = white_text;
+ return white_text;
break;
case '!':
- lineType = IMAGE;
- image = new Surface(text);
+ return 0;
break;
default:
+ return 0;
log_warning << "Unknown format_char: '" << format_char << "'" << std::endl;
break;
}
}
+InfoBoxLine::LineType get_linetype_by_format_char(char format_char) {
+ switch(format_char)
+ {
+ case ' ':
+ return InfoBoxLine::SMALL;
+ break;
+ case '\t':
+ return InfoBoxLine::NORMAL;
+ break;
+ case '-':
+ return InfoBoxLine::HEADING;
+ break;
+ case '*':
+ return InfoBoxLine::REFERENCE;
+ break;
+ case '#':
+ return InfoBoxLine::NORMAL_LEFT;
+ break;
+ case '!':
+ return InfoBoxLine::IMAGE;
+ break;
+ default:
+ return InfoBoxLine::SMALL;
+ log_warning << "Unknown format_char: '" << format_char << "'" << std::endl;
+ break;
+ }
+}
+}
+
+InfoBoxLine::InfoBoxLine(char format_char, const std::string& text) : lineType(NORMAL), font(white_text), text(text), image(0)
+{
+ font = get_font_by_format_char(format_char);
+ lineType = get_linetype_by_format_char(format_char);
+ if (lineType == IMAGE) image = new Surface(text);
+}
+
InfoBoxLine::~InfoBoxLine()
{
delete image;
}
const std::vector<InfoBoxLine*>
-InfoBoxLine::split(const std::string& text, int line_length)
+InfoBoxLine::split(const std::string& text, float width)
{
std::vector<InfoBoxLine*> lines;
// append wrapped parts of line into list
std::string overflow;
do {
- lines.push_back(new InfoBoxLine(format_char, Font::wrap_to_chars(s, line_length, &overflow)));
+ Font* font = get_font_by_format_char(format_char);
+ std::string s2 = s;
+ if (font) s2 = font->wrap_to_width(s2, width, &overflow);
+ lines.push_back(new InfoBoxLine(format_char, s2));
s = overflow;
} while (s.length() > 0);
}
void
-InfoBoxLine::draw(DrawingContext& context, const Vector& position, int layer)
+InfoBoxLine::draw(DrawingContext& context, const Rect& bbox, int layer)
{
+ Vector position = bbox.p1;
switch (lineType) {
case IMAGE:
- context.draw_surface(image, Vector( (SCREEN_WIDTH - image->get_width()) / 2, position.y), layer);
+ context.draw_surface(image, Vector( (bbox.p1.x + bbox.p2.x - image->get_width()) / 2, position.y), layer);
break;
case NORMAL_LEFT:
context.draw_text(font, text, Vector(position.x, position.y), ALIGN_LEFT, layer);
break;
default:
- context.draw_text(font, text, Vector(SCREEN_WIDTH/2, position.y), ALIGN_CENTER, layer);
+ context.draw_text(font, text, Vector((bbox.p1.x + bbox.p2.x) / 2, position.y), ALIGN_CENTER, layer);
break;
}
}
#include "screen.hpp"
#include "math/vector.hpp"
+#include "math/rect.hpp"
class DrawingContext;
class Surface;
*/
class InfoBoxLine
{
-private:
+public:
enum LineType { NORMAL, NORMAL_LEFT, SMALL, HEADING, REFERENCE, IMAGE};
- LineType lineType;
- Font* font;
- std::string text;
- Surface* image;
-public:
InfoBoxLine(char format_char, const std::string& text);
~InfoBoxLine();
- void draw(DrawingContext& context, const Vector& position, int layer);
+ void draw(DrawingContext& context, const Rect& bbox, int layer);
float get_height();
- static const std::vector<InfoBoxLine*> split(const std::string& text, int line_length);
+ static const std::vector<InfoBoxLine*> split(const std::string& text, float width);
+
+private:
+ InfoBoxLine::LineType lineType;
+ Font* font;
+ std::string text;
+ Surface* image;
};
/** This class is displaying a box with information text inside the game
return s;
}
+std::string
+Font::wrap_to_width(const std::string& s, float width, std::string* overflow)
+{
+ // if text is already smaller, return full text
+ if (get_text_width(s) <= width) {
+ if (overflow) *overflow = "";
+ return s;
+ }
+
+ // if we can find a whitespace character to break at, return text up to this character
+ for (int i = s.length()-1; i >= 0; i--) {
+ std::string s2 = s.substr(0,i);
+ if (s[i] != ' ') continue;
+ if (get_text_width(s2) <= width) {
+ if (overflow) *overflow = s.substr(i+1);
+ return s.substr(0, i);
+ }
+ }
+
+ // FIXME: hard-wrap at width, taking care of multibyte characters
+ if (overflow) *overflow = "";
+ return s;
+}
+
void
Font::draw(const std::string& text, const Vector& pos_, FontAlignment alignment,
DrawingEffect drawing_effect, float alpha) const
*/
static std::string wrap_to_chars(const std::string& text, int max_chars, std::string* overflow);
+ /**
+ * returns the given string, truncated (preferrably at whitespace) to be at most "width" pixels wide
+ */
+ std::string wrap_to_width(const std::string& text, float width, std::string* overflow);
+
/** Draws the given text to the screen. Also needs the position.
* Type of alignment, drawing effect and alpha are optional. */
void draw(const std::string& text, const Vector& pos,