This repository has been archived on 2023-07-11. You can view files and clone it, but cannot push or open issues or pull requests.
MyGameFramework/lib/gwen/controls/gwen_text.cpp
Gered c5cdddbeaa initial commit
current versions of all of my basic framework sources, build configurations/scripts, and supporting assets
2013-01-31 12:53:05 -05:00

402 lines
7.5 KiB
C++

/*
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<m_String.Get().length()+1; i++ )
{
Gwen::Rect cp = GetCharacterPosition( i );
int iDist = abs(cp.x - p.x) + abs(cp.y - p.y); // this isn't proper
if ( iDist > 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<Gwen::String> &elems)
{
Gwen::String str;
for ( unsigned int i=0; i<s.length(); i++ )
{
if ( s[i] == '\n' )
{
if ( !str.empty() ) elems.push_back( str );
elems.push_back( "\n" );
str.clear();
continue;
}
if ( s[i] == ' ' )
{
str += s[i];
elems.push_back( str );
str.clear();
continue;
}
str += s[i];
}
if ( !str.empty() ) elems.push_back( str );
}
void Text::RefreshSizeWrap()
{
RemoveAllChildren();
m_Lines.clear();
stl::vector<Gwen::String> 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<Gwen::String>::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<Gwen::String>::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;
}