/* GWEN Copyright (c) 2010 Facepunch Studios See license in Gwen.h */ #include "../gwen.h" #include "gwen_text.h" #include "../gwen_skin.h" #include "../gwen_utility.h" using namespace Gwen; using namespace Gwen::ControlsInternal; GWEN_CONTROL_CONSTRUCTOR( Text ) { m_Font = NULL; m_ColorOverride = Color( 255, 255, 255, 0 ); m_Color = GetSkin()->Colors.Label.Default; SetMouseInputEnabled( false ); SetWrap( false ); } Text::~Text() { // NOTE: This font doesn't need to be released // Because it's a pointer to another font somewhere. } void Text::Layout( Skin::Base* skin ) { if ( m_bTextChanged ) { RefreshSize(); m_bTextChanged = false; } } Gwen::Font* Text::GetFont() { return m_Font; } void Text::SetFont( Gwen::Font* pFont ) { if ( m_Font == pFont ) return; m_Font = pFont; m_bTextChanged = true; // Change the font of multilines too! { TextLines::iterator it = m_Lines.begin(); TextLines::iterator itEnd = m_Lines.end(); while ( it != itEnd ) { (*it)->SetFont( m_Font ); ++it; } } Invalidate(); } void Text::SetString( const TextObject& str ) { if ( m_String == str ) return; m_String = str.Get(); m_bTextChanged = true; Invalidate(); } void Text::Render( Skin::Base* skin ) { if ( m_bWrap ) return; if ( Length() == 0 || !GetFont() ) return; if ( m_ColorOverride.a == 0 ) skin->GetRender()->SetDrawColor( m_Color ); else skin->GetRender()->SetDrawColor( m_ColorOverride ); skin->GetRender()->RenderText( GetFont(), Gwen::Point( GetPadding().left, GetPadding().top ), m_String.Get() ); } Gwen::Rect Text::GetCharacterPosition( int iChar ) { if ( !m_Lines.empty() ) { TextLines::iterator it = m_Lines.begin(); TextLines::iterator itEnd = m_Lines.end(); int iChars = 0; while ( it != itEnd ) { Text* pLine = *it; ++it; iChars += pLine->Length(); if ( iChars <= iChar ) continue; iChars -= pLine->Length(); Gwen::Rect rect = pLine->GetCharacterPosition( iChar - iChars ); rect.x += pLine->X(); rect.y += pLine->Y(); return rect; } } if ( Length() == 0 || iChar == 0 ) { Gwen::Point p = GetSkin()->GetRender()->MeasureText( GetFont(), " " ); return Gwen::Rect( 1, 0, 0, p.y ); } String sub = m_String.Get().substr( 0, iChar ); Gwen::Point p = GetSkin()->GetRender()->MeasureText( GetFont(), sub ); return Rect( p.x, 0, 0, p.y ); } int Text::GetClosestCharacter( Gwen::Point p ) { if ( !m_Lines.empty() ) { TextLines::iterator it = m_Lines.begin(); TextLines::iterator itEnd = m_Lines.end(); int iChars = 0; while ( it != itEnd ) { Text* pLine = *it; ++it; iChars += pLine->Length(); if ( p.y < pLine->Y() ) continue; if ( p.y > pLine->Bottom() ) continue; iChars -= pLine->Length(); int iLinePos = pLine->GetClosestCharacter( Gwen::Point( p.x - pLine->X(), p.y - pLine->Y() ) ); //if ( iLinePos > 0 && iLinePos == pLine->Length() ) iLinePos--; iLinePos--; return iChars + iLinePos; } } int iDistance = 4096; int iChar = 0; for ( size_t i=0; i iDistance ) continue; iDistance = iDist; iChar = i; } return iChar; } void Text::OnScaleChanged() { Invalidate(); } void Text::RefreshSize() { if ( m_bWrap ) { return RefreshSizeWrap(); } if ( !GetFont() ) { Debug::AssertCheck( 0, "Text::RefreshSize() - No Font!!\n" ); return; } Gwen::Point p( 1, GetFont()->size ); if ( Length() > 0 ) { p = GetSkin()->GetRender()->MeasureText( GetFont(), m_String.Get() ); } p.x += GetPadding().left + GetPadding().right; p.y += GetPadding().top + GetPadding().bottom; if ( p.x == Width() && p.y == Height() ) return; if ( p.y < GetFont()->size ) p.y = GetFont()->size; SetSize( p.x, p.y ); InvalidateParent(); Invalidate(); } void SplitWords(const Gwen::String &s, char delim, stl::vector &elems) { Gwen::String str; for ( unsigned int i=0; i words; SplitWords( GetText().Get(), ' ', words ); // Adding a bullshit word to the end simplifies the code below // which is anything but simple. words.push_back( "" ); if ( !GetFont() ) { Debug::AssertCheck( 0, "Text::RefreshSize() - No Font!!\n" ); return; } Point pFontSize = GetSkin()->GetRender()->MeasureText( GetFont(), " " ); int w = GetParent()->Width(); int x = 0, y = 0; Gwen::String strLine; stl::vector::iterator it = words.begin(); for (; it != words.end(); ++it ) { bool bFinishLine = false; //bool bWrapped = false; // If this word is a newline - make a newline (we still add it to the text) if ( (*it).c_str()[0] == '\n' ) bFinishLine = true; // Does adding this word drive us over the width? { strLine += (*it); Gwen::Point p = GetSkin()->GetRender()->MeasureText( GetFont(), strLine ); if ( p.x > Width() ) { bFinishLine = true; /*bWrapped = true;*/ } } // If this is the last word then finish the line // if ( --words.end() == it ) // NOTE: replaced above commented out 'if' statement with this to appease // the GCC compiler that comes with Marmalade SDK 6.0 stl::vector::iterator temp = words.end() - 1; if ( temp == it ) { bFinishLine = true; } if ( bFinishLine ) { Text* t = new Text( this ); t->SetFont( GetFont() ); t->SetString( strLine.substr( 0, strLine.length() - (*it).length() ) ); t->RefreshSize(); t->SetPos( x, y ); m_Lines.push_back( t ); // newline should start with the word that was too big strLine = *it; // Position the newline y += pFontSize.y; x = 0; //if ( strLine[0] == ' ' ) x -= pFontSize.x; } } // Size to children height and parent width { Point childsize = ChildrenSize(); SetSize( w, childsize.y ); } InvalidateParent(); Invalidate(); } int Text::NumLines() { return m_Lines.size(); } Text* Text::GetLine( int i ) { TextLines::iterator it = m_Lines.begin(); TextLines::iterator itEnd = m_Lines.end(); while ( it != itEnd ) { if ( i == 0 ) return *it; ++it; i--; } return NULL; } int Text::GetLineFromChar( int i ) { TextLines::iterator it = m_Lines.begin(); TextLines::iterator itEnd = m_Lines.end(); int iChars = 0; int iLine = 0; while ( it != itEnd ) { Text* pLine = *it; ++it; iChars += pLine->Length(); if ( iChars > i ) return iLine; iLine++; } return iLine; } int Text::GetStartCharFromLine( int i ) { TextLines::iterator it = m_Lines.begin(); TextLines::iterator itEnd = m_Lines.end(); int iChars = 0; while ( it != itEnd ) { Text* pLine = *it; ++it; if ( i == 0 ) return Gwen::Clamp( iChars, 0, Length() ); iChars += pLine->Length(); i--; } return Gwen::Clamp( iChars, 0, Length() ); } int Text::GetEndCharFromLine( int i ) { int iStart = GetStartCharFromLine( i ); Text* iLine = GetLine( i ); if ( iLine ) { iStart += iLine->Length(); } return Gwen::Clamp( iStart, 0, Length() ); } int Text::GetCharPosOnLine( int i ) { int iLine = GetLineFromChar( i ); Text* line = GetLine( iLine ); if ( !line ) return 0; int iStart = GetStartCharFromLine( iLine ); return i - iStart; }