Okay, here's some pseudocode to demonstrate what my function does:
class Textbox {
std::vector<sf::Sprite> drawable_characters;
sf::Font font;
public:
setString(const std::string& input);
draw(sf::RenderTarget& target); // you figure out this part
}
void Textbox::setString(const std::string& input) {
std::size_t n = 0;
unsigned int fontSize = 12;
bool bold = false;
sf::Vector2i position = /* some initial position */;
sf::Color color = sf::Color(255, 255, 255);
sf::Glyph glyph;
char last_literal = -1;
drawable_characters.clear();
while(n <= input.length()) {
// process escape sequences
while(input[n] == '\\') {
n++;
switch(input[n]) {
case 'n':
// insert a linebreak
position.y += font.getLineSpacing(fontSize);
position.x = /* x of initial position */;
last_literal = -1;
n++;
break;
case '{':
if( /* the following characters are "red}" */ ) {
color = sf::Color(255, 0, 0);
n += 4;
} else if( ... ) {
// more colors!
} else if( /* the next character is '}' */ ) {
color = sf::Color(255, 255, 255);
n++;
}
break;
default:
// throw an error?
break;
}
}
// done processing any escape sequences, so now we're on a literal character
char current_literal = input[n];
glyph = font.getGlyph(current_literal, fontSize, bold);
drawable_characters.push_back( sf::Sprite(font, glyph.getTextureRect()) );
drawable_characters.back().setColor(color);
// skip kerning if we're at the start of the current line
if(last_literal != -1)
position.x += font.getKerning(last_literal, current_literal, fontSize);
drawable_characters.back().setPosition(position.x, position.y + glyph.bounds.top);
position.x += glyph.advance;
last_literal = current_literal;
n++;
}
}
I should stress that this is something I scribbled down just now based on my real code, so while it should include all the important steps, there's almost certainly some bugs and missing pieces (beyond the obvious ones). And of course I left out word wrapping. But still, this should be enough for you to get your head around what's required and figure out the rest on your own.
In particular, keep an eye out for premature destruction errors. Remember things like the "white square problem" from the Texture tutorial? Similar things can happen when working with fonts, except for some reason they give you invisible squares instead of white squares so it's much less obvious that it's your fault.
P.S. If you're feeling particularly masochistic, here's what this function looks like in my real program (http://pastebin.com/hkcMLNNX).
In general, "stateful" methods are ones that depend on the current "state" of an object, typically represented by its member variables. This is as opposed to "stateless" methods that depend on nothing but their arguments, and will always give the same answer every time.
For example, sf::Window::getSize() is stateful, because the current window size is part of the class' state. A function like sin() or cos() is stateless, because their results depend on nothing but the arguments you give them.
What I meant by a "stateful loop" was something like this:
// here's the "state"
sf::Color current_color = // white;
for( ... ) {
if( /* the next chars are "{red}" */ ) {
current_color = // red;
} else if ( /* the next chars are "{}" */ ) {
current_color = // white;
}
// make sprite using the literal char
sprite.setColor(current_color);
}
For the purpose of pre-processing escape sequences in text, this design is better, because it is almost trivial to add more escape sequences later and allow escape sequences within other escape sequences, like this:
// here's the "state"
sf::Color current_color = // white;
unsigned int current_size = 12;
bool current_bold = false;
for( ... ) {
if( /* the next chars are "{red}" */ ) {
current_color = // red;
} else if( /* the next chars are "{bold}" */ ) {
current_bold = true;
} else if( /* the next chars are "{size:n}" */ ) {
current_size = n;
} else if ...
// make sprite using the literal char
... = font.getGlyph(font, current_size, current_bold);
// other stuff to make sprite
sprite.setColor(current_color);
}
Yeah that's more like it.
Though you should still use substr() for your substring matching; it's much easier than checking individual characters.
For that error, just use a debugger. You'll have to learn how to use one at some point, and subscript errors are exactly the sort of thing debuggers are perfect for pinning down.
int yPos = (int)textBox.top; /* RIGHT? */
1) Avoid C-style casts. Learn the C++-style ones (static_cast, dynamic_cast, reinterpret_cast, const_cast).
2) textBox is a very bad name for a member of a class called TextBox. Call it bounds or boundingBox or something more descriptive.
3) The actual logic of this statement seems fine. If the text appears where you want it to, then it is fine.