Refactored Font and Text classes.

--HG--
branch : dev
This commit is contained in:
Martí­n Lucas Golini
2017-03-24 04:49:14 -03:00
parent 97558261ae
commit bb0a6cacda
8 changed files with 263 additions and 326 deletions

View File

@@ -41,30 +41,6 @@ class EE_API Font {
/** @return The font id */
const Uint32& getId();
/** Shrink the String to a max width
* @param Str The string to shrink
* @param MaxWidth The Max Width posible
*/
void shrinkText( String& Str, const Uint32& characterSize, bool bold, Float outlineThickness, const Uint32& MaxWidth );
/** Shrink the string to a max width
* @param Str The string to shrink
* @param MaxWidth The Max Width posible
*/
void shrinkText( std::string& Str, const Uint32& characterSize, bool bold, Float outlineThickness, const Uint32& MaxWidth );
/** Cache the with of the current text */
void cacheWidth( const String& TextCache, const Uint32& characterSize, bool bold, Float outlineThickness, std::vector<Float>& LinesWidth, Float& CachedWidth, int& NumLines, int& LargestLineCharCount );
/** Finds the closest cursor position to the point position */
Int32 findClosestCursorPosFromPoint( const String& TextCache, const Uint32& characterSize, bool bold, Float outlineThickness, const Vector2i& pos );
/** Simulates a selection request and return the initial and end cursor position when the selection worked. Otherwise both parameters will be -1. */
void selectSubStringFromCursor(const String& TextCache, const Int32& CurPos, Int32& InitCur, Int32& EndCur );
/** @return The cursor position inside the string */
Vector2i getCursorPos( const String& TextCache, const Uint32& characterSize, bool bold, Float outlineThickness, const Uint32& Pos );
virtual const Info& getInfo() const = 0;
virtual const Glyph& getGlyph(Uint32 codePoint, unsigned int characterSize, bool bold, Float outlineThickness = 0) const = 0;

View File

@@ -102,6 +102,20 @@ class EE_API Text {
const int& getNumLines();
void setStyleConfig( const FontStyleConfig& styleConfig );
/** Finds the closest cursor position to the point position */
Int32 findCharacterFromPos( const Vector2i& pos );
/** Simulates a selection request and return the initial and end cursor position when the selection worked. Otherwise both parameters will be -1. */
void findWordFromCharacterIndex( const Int32& characterIndex, Int32& InitCur, Int32& EndCur );
/** Cache the with of the current text */
void getWidthInfo( std::vector<Float>& LinesWidth, Float& CachedWidth, int& NumLines, int& LargestLineCharCount );
/** Shrink the String to a max width
* @param MaxWidth The Max Width posible
*/
void shrinkText( const Uint32& MaxWidth );
protected:
struct VertexCoords {
Vector2f texCoords;

View File

@@ -93,13 +93,13 @@ class EE_API UITextView : public UIWidget {
Recti mRealPadding;
struct SelPosCache
{
SelPosCache( Vector2i ip, Vector2i ep ) :
SelPosCache( Vector2f ip, Vector2f ep ) :
initPos( ip ),
endPos( ep )
{}
Vector2i initPos;
Vector2i endPos;
Vector2f initPos;
Vector2f endPos;
};
std::vector<SelPosCache> mSelPosCache;
Int32 mLastSelCurInit;

View File

@@ -35,288 +35,4 @@ const Uint32& Font::getId() {
return mFontHash;
}
void Font::cacheWidth( const String& Text, const Uint32& characterSize, bool bold, Float outlineThickness, std::vector<Float>& LinesWidth, Float& CachedWidth, int& NumLines , int& LargestLineCharCount ) {
LinesWidth.clear();
Float Width = 0, MaxWidth = 0;
Int32 CharID;
Int32 Lines = 1;
Int32 CharCount = 0;
LargestLineCharCount = 0;
for (std::size_t i = 0; i < Text.size(); ++i) {
CharID = static_cast<Int32>( Text.at(i) );
Glyph glyph = getGlyph( CharID, characterSize, bold, outlineThickness );
if ( CharID != '\r' )
Width += glyph.advance;
CharCount++;
if ( CharID == '\t' )
Width += glyph.advance * 3;
if ( CharID == '\n' ) {
Lines++;
Float lWidth = ( CharID == '\t' ) ? glyph.advance * 4.f : glyph.advance;
LinesWidth.push_back( Width - lWidth );
Width = 0;
CharCount = 0;
} else {
if ( CharCount > LargestLineCharCount )
LargestLineCharCount = CharCount;
}
if ( Width > MaxWidth )
MaxWidth = Width;
}
if ( Text.size() && Text.at( Text.size() - 1 ) != '\n' ) {
LinesWidth.push_back( Width );
}
CachedWidth = MaxWidth;
NumLines = Lines;
}
Int32 Font::findClosestCursorPosFromPoint( const String& Text, const Uint32& characterSize, bool bold, Float outlineThickness, const Vector2i& pos ) {
Float Width = 0, lWidth = 0, Height = getLineSpacing(characterSize), lHeight = 0;
Int32 CharID;
std::size_t tSize = Text.size();
for (std::size_t i = 0; i < tSize; ++i) {
CharID = static_cast<Int32>( Text.at(i) );
Glyph glyph = getGlyph( CharID, characterSize, bold, outlineThickness );
lWidth = Width;
if ( CharID != '\r' )
Width += glyph.advance;
if ( CharID == '\t' ) {
Width += glyph.advance * 3;
}
if ( CharID == '\n' ) {
lWidth = 0;
Width = 0;
}
if ( pos.x <= Width && pos.x >= lWidth && pos.y <= Height && pos.y >= lHeight ) {
if ( i + 1 < tSize ) {
Int32 curDist = eeabs( pos.x - lWidth );
Int32 nextDist = eeabs( pos.x - ( lWidth + glyph.advance ) );
if ( nextDist < curDist ) {
return i + 1;
}
}
return i;
}
if ( CharID == '\n' ) {
lHeight = Height;
Height += getLineSpacing(characterSize);
if ( pos.x > Width && pos.y <= lHeight ) {
return i;
}
}
}
if ( pos.x >= Width ) {
return tSize;
}
return -1;
}
Vector2i Font::getCursorPos( const String& Text, const Uint32& characterSize, bool bold, Float outlineThickness, const Uint32& Pos ) {
Float Width = 0, Height = getLineSpacing(characterSize);
Int32 CharID;
std::size_t tSize = ( Pos < Text.size() ) ? Pos : Text.size();
for (std::size_t i = 0; i < tSize; ++i) {
CharID = static_cast<Int32>( Text.at(i) );
Glyph glyph = getGlyph( CharID, characterSize, bold, outlineThickness );
if ( CharID != '\r' )
Width += glyph.advance;
if ( CharID == '\t' ) {
Width += glyph.advance * 3;
}
if ( CharID == '\n' ) {
Width = 0;
Height += getLineSpacing(characterSize);
}
}
return Vector2i( Width, Height );
}
static bool isStopSelChar( Uint32 c ) {
return ( !String::isCharacter( c ) && !String::isNumber( c ) ) ||
' ' == c ||
'.' == c ||
',' == c ||
';' == c ||
':' == c ||
'\n' == c ||
'"' == c ||
'\'' == c;
}
void Font::selectSubStringFromCursor( const String& Text, const Int32& CurPos, Int32& InitCur, Int32& EndCur ) {
InitCur = 0;
EndCur = Text.size();
for ( std::size_t i = CurPos; i < Text.size(); i++ ) {
if ( isStopSelChar( Text[i] ) ) {
EndCur = i;
break;
}
}
if ( 0 == CurPos ) {
InitCur = 0;
}
for ( Int32 i = CurPos; i >= 0; i-- ) {
if ( isStopSelChar( Text[i] ) ) {
InitCur = i + 1;
break;
}
}
if ( InitCur == EndCur ) {
InitCur = EndCur = -1;
}
}
void Font::shrinkText( std::string& Str, const Uint32& characterSize, bool bold, Float outlineThickness, const Uint32& MaxWidth ) {
if ( !Str.size() )
return;
Float tCurWidth = 0.f;
Float tWordWidth = 0.f;
Float tMaxWidth = (Float) MaxWidth;
char * tChar = &Str[0];
char * tLastSpace = NULL;
while ( *tChar ) {
Glyph pChar = getGlyph( *tChar, characterSize, bold, outlineThickness );
Float fCharWidth = (Float)pChar.advance;
if ( ( *tChar ) == '\t' )
fCharWidth += pChar.advance * 3;
else if ( ( *tChar ) == '\r' )
fCharWidth = 0;
tWordWidth += fCharWidth;
if ( ' ' == *tChar || '\0' == *( tChar + 1 ) ) {
if ( tCurWidth + tWordWidth < tMaxWidth ) {
tCurWidth += tWordWidth;
tLastSpace = tChar;
tChar++;
} else {
if ( NULL != tLastSpace ) {
*tLastSpace = '\n';
tChar = tLastSpace + 1;
} else {
*tChar = '\n';
}
if ( '\0' == *( tChar + 1 ) )
tChar++;
tLastSpace = NULL;
tCurWidth = 0.f;
}
tWordWidth = 0.f;
} else if ( '\n' == *tChar ) {
tWordWidth = 0.f;
tCurWidth = 0.f;
tLastSpace = NULL;
tChar++;
} else {
tChar++;
}
}
}
void Font::shrinkText( String& Str, const Uint32& characterSize, bool bold, Float outlineThickness, const Uint32& MaxWidth ) {
if ( !Str.size() )
return;
Float tCurWidth = 0.f;
Float tWordWidth = 0.f;
Float tMaxWidth = (Float) MaxWidth;
String::StringBaseType * tChar = &Str[0];
String::StringBaseType * tLastSpace = NULL;
while ( *tChar ) {
Glyph pChar = getGlyph( *tChar, characterSize, bold, outlineThickness );
Float fCharWidth = (Float)pChar.advance;
if ( ( *tChar ) == '\t' )
fCharWidth += pChar.advance * 3;
else if ( ( *tChar ) == '\r' )
fCharWidth = 0;
// Add the new char width to the current word width
tWordWidth += fCharWidth;
if ( ' ' == *tChar || '\0' == *( tChar + 1 ) ) {
// If current width plus word width is minor to the max width, continue adding
if ( tCurWidth + tWordWidth < tMaxWidth ) {
tCurWidth += tWordWidth;
tLastSpace = tChar;
tChar++;
} else {
// If it was an space before, replace that space for an new line
// Start counting from the new line first character
if ( NULL != tLastSpace ) {
*tLastSpace = '\n';
tChar = tLastSpace + 1;
} else { // The word is larger than the current possible width
*tChar = '\n';
}
if ( '\0' == *( tChar + 1 ) )
tChar++;
// Set the last spaces as null, because is a new line
tLastSpace = NULL;
// New line, new current width
tCurWidth = 0.f;
}
// New word, so we reset the current word width
tWordWidth = 0.f;
} else if ( '\n' == *tChar ) {
tWordWidth = 0.f;
tCurWidth = 0.f;
tLastSpace = NULL;
tChar++;
} else {
tChar++;
}
}
}
}}

View File

@@ -240,6 +240,238 @@ Vector2f Text::findCharacterPos(std::size_t index) const {
return position;
}
Int32 Text::findCharacterFromPos( const Vector2i& pos ) {
if ( NULL == mFont )
return 0;
Float vspace = mFont->getLineSpacing(mRealCharacterSize);
Float Width = 0, lWidth = 0, Height = vspace, lHeight = 0;
Uint32 CharID;
Uint32 prevChar = 0;
std::size_t tSize = mString.size();
bool bold = (mStyle & Bold) != 0;
for (std::size_t i = 0; i < tSize; ++i) {
CharID = mString[i];
Glyph glyph = mFont->getGlyph( CharID, mRealCharacterSize, bold, mOutlineThickness );
lWidth = Width;
if ( CharID != '\r' ) {
Width += mFont->getKerning(prevChar, CharID, mRealCharacterSize);
prevChar = CharID;
Width += glyph.advance;
}
if ( CharID == '\t' ) {
Width += glyph.advance * 3;
}
if ( CharID == '\n' ) {
lWidth = 0;
Width = 0;
}
if ( pos.x <= Width && pos.x >= lWidth && pos.y <= Height && pos.y >= lHeight ) {
if ( i + 1 < tSize ) {
Int32 curDist = eeabs( pos.x - lWidth );
Int32 nextDist = eeabs( pos.x - ( lWidth + glyph.advance ) );
if ( nextDist < curDist ) {
return i + 1;
}
}
return i;
}
if ( CharID == '\n' ) {
lHeight = Height;
Height += vspace;
if ( pos.x > Width && pos.y <= lHeight ) {
return i;
}
}
}
if ( pos.x >= Width ) {
return tSize;
}
return -1;
}
static bool isStopSelChar( Uint32 c ) {
return ( !String::isCharacter( c ) && !String::isNumber( c ) ) ||
' ' == c ||
'.' == c ||
',' == c ||
';' == c ||
':' == c ||
'\n' == c ||
'"' == c ||
'\'' == c;
}
void Text::findWordFromCharacterIndex( const Int32& characterIndex, Int32& InitCur, Int32& EndCur ) {
InitCur = 0;
EndCur = mString.size();
for ( std::size_t i = characterIndex; i < mString.size(); i++ ) {
if ( isStopSelChar( mString[i] ) ) {
EndCur = i;
break;
}
}
if ( 0 == characterIndex ) {
InitCur = 0;
}
for ( Int32 i = characterIndex; i >= 0; i-- ) {
if ( isStopSelChar( mString[i] ) ) {
InitCur = i + 1;
break;
}
}
if ( InitCur == EndCur ) {
InitCur = EndCur = -1;
}
}
void Text::getWidthInfo( std::vector<Float>& LinesWidth, Float& CachedWidth, int& NumLines , int& LargestLineCharCount ) {
if ( NULL == mFont )
return;
LinesWidth.clear();
Float Width = 0, MaxWidth = 0;
Uint32 CharID;
Int32 Lines = 1;
Int32 CharCount = 0;
Uint32 prevChar = 0;
LargestLineCharCount = 0;
bool bold = (mStyle & Bold) != 0;
for (std::size_t i = 0; i < mString.size(); ++i) {
CharID = static_cast<Int32>( mString.at(i) );
Glyph glyph = mFont->getGlyph( CharID, mRealCharacterSize, bold, mOutlineThickness );
if ( CharID != '\r' ) {
Width += mFont->getKerning(prevChar, CharID, mRealCharacterSize);
prevChar = CharID;
Width += glyph.advance;
}
CharCount++;
if ( CharID == '\t' )
Width += glyph.advance * 3;
if ( CharID == '\n' ) {
Lines++;
Float lWidth = ( CharID == '\t' ) ? glyph.advance * 4.f : glyph.advance;
LinesWidth.push_back( Width - lWidth );
Width = 0;
CharCount = 0;
} else {
if ( CharCount > LargestLineCharCount )
LargestLineCharCount = CharCount;
}
if ( Width > MaxWidth )
MaxWidth = Width;
}
if ( mString.size() && mString.at( mString.size() - 1 ) != '\n' ) {
LinesWidth.push_back( Width );
}
CachedWidth = MaxWidth;
NumLines = Lines;
}
void Text::shrinkText( const Uint32& MaxWidth ) {
if ( !mString.size() || NULL == mFont )
return;
Float tCurWidth = 0.f;
Float tWordWidth = 0.f;
Float tMaxWidth = (Float) MaxWidth;
String::StringBaseType * tChar = &mString[0];
String::StringBaseType * tLastSpace = NULL;
Uint32 prevChar = 0;
bool bold = (mStyle & Bold) != 0;
while ( *tChar ) {
Glyph pChar = mFont->getGlyph( *tChar, mRealCharacterSize, bold, mOutlineThickness );
Float fCharWidth = (Float)pChar.advance;
if ( ( *tChar ) == '\t' )
fCharWidth += pChar.advance * 3;
else if ( ( *tChar ) == '\r' )
fCharWidth = 0;
// Add the new char width to the current word width
tWordWidth += fCharWidth;
if ( *tChar != '\r' ) {
tWordWidth += mFont->getKerning(prevChar, *tChar, mRealCharacterSize);
prevChar = *tChar;
}
if ( ' ' == *tChar || '\0' == *( tChar + 1 ) ) {
// If current width plus word width is minor to the max width, continue adding
if ( tCurWidth + tWordWidth < tMaxWidth ) {
tCurWidth += tWordWidth;
tLastSpace = tChar;
tChar++;
} else {
// If it was an space before, replace that space for an new line
// Start counting from the new line first character
if ( NULL != tLastSpace ) {
*tLastSpace = '\n';
tChar = tLastSpace + 1;
} else { // The word is larger than the current possible width
*tChar = '\n';
}
if ( '\0' == *( tChar + 1 ) )
tChar++;
// Set the last spaces as null, because is a new line
tLastSpace = NULL;
// New line, new current width
tCurWidth = 0.f;
}
// New word, so we reset the current word width
tWordWidth = 0.f;
} else if ( '\n' == *tChar ) {
tWordWidth = 0.f;
tCurWidth = 0.f;
tLastSpace = NULL;
tChar++;
} else {
tChar++;
}
}
mCachedWidthNeedUpdate = true;
mGeometryNeedUpdate = true;
mColorsNeedUpdate = true;
}
Rectf Text::getLocalBounds() {
ensureGeometryUpdate();
@@ -590,7 +822,7 @@ void Text::cacheWidth() {
return;
if ( NULL != mFont && mString.size() ) {
mFont->cacheWidth( mString, mRealCharacterSize, (mStyle & Bold), mOutlineThickness, mLinesWidth, mCachedWidth, mNumLines, mLargestLineCharCount );
getWidthInfo( mLinesWidth, mCachedWidth, mNumLines, mLargestLineCharCount );
mCachedWidthNeedUpdate = false;
} else {
mCachedWidth = 0;

View File

@@ -259,7 +259,7 @@ Uint32 UITextInput::onMouseClick( const Vector2i& Pos, const Uint32 Flags ) {
worldToControl( controlPos );
controlPos = PixelDensity::dpToPxI( controlPos ) - Vector2i( (Int32)mRealAlignOffset.x, (Int32)mRealAlignOffset.y );
Int32 curPos = mTextCache->getFont()->findClosestCursorPosFromPoint( mTextCache->getString(), mTextCache->getCharacterSizePx(), mTextCache->getStyle() & Text::Bold, mTextCache->getOutlineThickness(), controlPos );
Int32 curPos = mTextCache->findCharacterFromPos( controlPos );
if ( -1 != curPos ) {
mTextBuffer.setCursorPos( curPos );

View File

@@ -215,9 +215,7 @@ void UITextView::shrinkText( const Uint32& MaxWidth ) {
mTextCache->setString( mString );
}
String str = mTextCache->getString();
mTextCache->getFont()->shrinkText( str, mTextCache->getCharacterSizePx(), mTextCache->getStyle() & Text::Bold, mTextCache->getOutlineThickness(), MaxWidth );
mTextCache->setString( str );
mTextCache->shrinkText( MaxWidth );
}
void UITextView::onAutoSize() {
@@ -307,12 +305,12 @@ Uint32 UITextView::onMouseDoubleClick( const Vector2i& Pos, const Uint32 Flags )
worldToControl( controlPos );
controlPos = PixelDensity::dpToPxI( controlPos );
Int32 curPos = mTextCache->getFont()->findClosestCursorPosFromPoint( mTextCache->getString(), mTextCache->getCharacterSizePx(), mTextCache->getStyle() & Text::Bold, mTextCache->getOutlineThickness(), controlPos );
Int32 curPos = mTextCache->findCharacterFromPos( controlPos );
if ( -1 != curPos ) {
Int32 tSelCurInit, tSelCurEnd;
mTextCache->getFont()->selectSubStringFromCursor( mTextCache->getString(), curPos, tSelCurInit, tSelCurEnd );
mTextCache->findWordFromCharacterIndex( curPos, tSelCurInit, tSelCurEnd );
selCurInit( tSelCurInit );
selCurEnd( tSelCurEnd );
@@ -343,7 +341,7 @@ Uint32 UITextView::onMouseDown( const Vector2i& Pos, const Uint32 Flags ) {
worldToControl( controlPos );
controlPos = PixelDensity::dpToPxI( controlPos ) - Vector2i( (Int32)mRealAlignOffset.x, (Int32)mRealAlignOffset.y );
Int32 curPos = mTextCache->getFont()->findClosestCursorPosFromPoint( mTextCache->getString(), mTextCache->getCharacterSizePx(), mTextCache->getStyle() & Text::Bold, mTextCache->getOutlineThickness(), controlPos );
Int32 curPos = mTextCache->findCharacterFromPos( controlPos );
if ( -1 != curPos ) {
if ( -1 == selCurInit() || !( mControlFlags & UI_CTRL_FLAG_SELECTING ) ) {
@@ -370,7 +368,7 @@ void UITextView::drawSelection( Text * textCache ) {
}
Int32 lastEnd;
Vector2i initPos, endPos;
Vector2f initPos, endPos;
if ( mLastSelCurInit != selCurInit() || mLastSelCurEnd != selCurEnd() ) {
mSelPosCache.clear();
@@ -378,14 +376,14 @@ void UITextView::drawSelection( Text * textCache ) {
mLastSelCurEnd = selCurEnd();
do {
initPos = textCache->getFont()->getCursorPos( textCache->getString(), textCache->getCharacterSizePx(), textCache->getStyle() & Text::Bold, textCache->getOutlineThickness(), init );
initPos = textCache->findCharacterPos( init );
lastEnd = textCache->getString().find_first_of( '\n', init );
if ( lastEnd < end && -1 != lastEnd ) {
endPos = textCache->getFont()->getCursorPos( textCache->getString(), textCache->getCharacterSizePx(), textCache->getStyle() & Text::Bold, textCache->getOutlineThickness(), lastEnd );
endPos = textCache->findCharacterPos( lastEnd );
init = lastEnd + 1;
} else {
endPos = textCache->getFont()->getCursorPos( textCache->getString(), textCache->getCharacterSizePx(), textCache->getStyle() & Text::Bold, textCache->getOutlineThickness(), end );
endPos = textCache->findCharacterPos( end );
lastEnd = end;
}
@@ -396,15 +394,16 @@ void UITextView::drawSelection( Text * textCache ) {
if ( mSelPosCache.size() ) {
Primitives P;
P.setColor( mFontStyleConfig.FontSelectionBackColor );
Float vspace = textCache->getFont()->getLineSpacing( textCache->getCharacterSizePx() );
for ( size_t i = 0; i < mSelPosCache.size(); i++ ) {
initPos = mSelPosCache[i].initPos;
endPos = mSelPosCache[i].endPos;
P.drawRectangle( Rectf( mScreenPos.x + initPos.x + mRealAlignOffset.x + mRealPadding.Left,
mScreenPos.y + initPos.y - textCache->getFont()->getLineSpacing( textCache->getCharacterSizePx() ) + mRealAlignOffset.y + mRealPadding.Top,
mScreenPos.y + initPos.y + mRealAlignOffset.y + mRealPadding.Top,
mScreenPos.x + endPos.x + mRealAlignOffset.x + mRealPadding.Left,
mScreenPos.y + endPos.y + mRealAlignOffset.y + mRealPadding.Top )
mScreenPos.y + endPos.y + vspace + mRealAlignOffset.y + mRealPadding.Top )
);
}
}

View File

@@ -52,12 +52,12 @@ EE_MAIN_FUNC int main (int argc, char * argv []) {
fontTest = FontTrueType::New( "DejaVuSansMono" );
fontTest->loadFromFile( AppPath + "assets/fonts/DejaVuSansMono.ttf" );
fontTest->shrinkText( Txt, 24, false, 2, win->getWidth() - 96 );
text.setFont( fontTest );
text.setCharacterSize( 24 );
text.setAlign( TEXT_ALIGN_CENTER );
text.setString( Txt );
text.shrinkText( win->getWidth() - 96 );
// Set the font color to a substring of the text
// Create a gradient