// EXTERNAL INCLUDES
#include <limits>
+#include <cmath>
#include <memory.h>
#include <dali/public-api/adaptor-framework/key.h>
#include <dali/integration-api/debug.h>
const char * const PLACEHOLDER_POINT_SIZE = "pointSize";
const char * const PLACEHOLDER_PIXEL_SIZE = "pixelSize";
const char * const PLACEHOLDER_ELLIPSIS = "ellipsis";
+const unsigned int MAX_TEXT_LENGTH = 1024u * 32u;
float ConvertToEven( float value )
{
return static_cast<float>( intValue + ( intValue & 1 ) );
}
+int ConvertPixelToPint( float pixel )
+{
+ unsigned int horizontalDpi = 0u;
+ unsigned int verticalDpi = 0u;
+ Dali::TextAbstraction::FontClient fontClient = Dali::TextAbstraction::FontClient::Get();
+ fontClient.GetDpi( horizontalDpi, verticalDpi );
+
+ return ( pixel * 72.f ) / static_cast< float >( horizontalDpi );
+}
+
+
} // namespace
namespace Dali
void Controller::SetMaximumNumberOfCharacters( Length maxCharacters )
{
- mImpl->mMaximumNumberOfCharacters = maxCharacters;
+ mImpl->mMaximumNumberOfCharacters = std::min( maxCharacters, MAX_TEXT_LENGTH );
}
int Controller::GetMaximumNumberOfCharacters()
// Set the flag to redo the alignment operation.
mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | ALIGN );
+ if( mImpl->mEventData )
+ {
+ mImpl->mEventData->mUpdateAlignment = true;
+
+ // Update the cursor if it's in editing mode
+ if( EventData::IsEditingState( mImpl->mEventData->mState ) )
+ {
+ mImpl->ChangeState( EventData::EDITING );
+ mImpl->mEventData->mUpdateCursorPosition = true;
+ }
+ }
+
mImpl->RequestRelayout();
}
}
mImpl->mLayoutDirection = layoutDirection;
}
+bool Controller::IsShowingRealText() const
+{
+ return mImpl->IsShowingRealText();
+}
+
void Controller::SetLineWrapMode( Text::LineWrap::Mode lineWrapMode )
{
return mImpl->mModel->mElideEnabled;
}
+void Controller::SetTextFitEnabled(bool enabled)
+{
+ mImpl->mTextFitEnabled = enabled;
+}
+
+bool Controller::IsTextFitEnabled() const
+{
+ return mImpl->mTextFitEnabled;
+}
+
+void Controller::SetTextFitMinSize( float minSize, FontSizeType type )
+{
+ switch( type )
+ {
+ case POINT_SIZE:
+ {
+ mImpl->mTextFitMinSize = minSize;
+ break;
+ }
+ case PIXEL_SIZE:
+ {
+ mImpl->mTextFitMinSize = ConvertPixelToPint( minSize );
+ break;
+ }
+ }
+}
+
+float Controller::GetTextFitMinSize() const
+{
+ return mImpl->mTextFitMinSize;
+}
+
+void Controller::SetTextFitMaxSize( float maxSize, FontSizeType type )
+{
+ switch( type )
+ {
+ case POINT_SIZE:
+ {
+ mImpl->mTextFitMaxSize = maxSize;
+ break;
+ }
+ case PIXEL_SIZE:
+ {
+ mImpl->mTextFitMaxSize = ConvertPixelToPint( maxSize );
+ break;
+ }
+ }
+}
+
+float Controller::GetTextFitMaxSize() const
+{
+ return mImpl->mTextFitMaxSize;
+}
+
+void Controller::SetTextFitStepSize( float step, FontSizeType type )
+{
+ switch( type )
+ {
+ case POINT_SIZE:
+ {
+ mImpl->mTextFitStepSize = step;
+ break;
+ }
+ case PIXEL_SIZE:
+ {
+ mImpl->mTextFitStepSize = ConvertPixelToPint( step );
+ break;
+ }
+ }
+}
+
+float Controller::GetTextFitStepSize() const
+{
+ return mImpl->mTextFitStepSize;
+}
+
+void Controller::SetTextFitContentSize(Vector2 size)
+{
+ mImpl->mTextFitContentSize = size;
+}
+
+Vector2 Controller::GetTextFitContentSize() const
+{
+ return mImpl->mTextFitContentSize;
+}
+
void Controller::SetPlaceholderTextElideEnabled( bool enabled )
{
mImpl->mEventData->mIsPlaceholderElideEnabled = enabled;
return mImpl->mEventData->mGrabHandleEnabled;
}
+void Controller::SetGrabHandlePopupEnabled(bool enabled)
+{
+ mImpl->mEventData->mGrabHandlePopupEnabled = enabled;
+}
+
+bool Controller::IsGrabHandlePopupEnabled() const
+{
+ return mImpl->mEventData->mGrabHandlePopupEnabled;
+}
+
// public : Update
void Controller::SetText( const std::string& text )
utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
}
+ // Limit the text size. If the text size is too large, crash or deadlock will occur.
+ if( textSize > MAX_TEXT_LENGTH )
+ {
+ DALI_LOG_WARNING( "The text size is too large(%d), limit the length to 32,768u\n", textSize );
+ textSize = MAX_TEXT_LENGTH;
+ }
+
// Convert text into UTF-32
Vector<Character>& utf32Characters = mImpl->mModel->mLogicalModel->mText;
utf32Characters.Resize( textSize );
{
mImpl->mModel->mVisualModel->SetTextColor( color );
+ mImpl->mModel->mLogicalModel->mColorRuns.Clear();
+
+ mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | COLOR );
+
mImpl->RequestRelayout();
}
}
bool Controller::SetDefaultLineSpacing( float lineSpacing )
{
- if( std::abs(lineSpacing - mImpl->mLayoutEngine.GetDefaultLineSpacing()) > Math::MACHINE_EPSILON_1000 )
+ if( std::fabs( lineSpacing - mImpl->mLayoutEngine.GetDefaultLineSpacing() ) > Math::MACHINE_EPSILON_1000 )
{
mImpl->mLayoutEngine.SetDefaultLineSpacing(lineSpacing);
mImpl->mRecalculateNaturalSize = true;
mImpl->mEventData->mInputStyle.textColor = color;
mImpl->mEventData->mInputStyle.isDefaultColor = false;
- if( EventData::SELECTING == mImpl->mEventData->mState )
+ if( EventData::SELECTING == mImpl->mEventData->mState || EventData::EDITING == mImpl->mEventData->mState || EventData::INACTIVE == mImpl->mEventData->mState )
{
const bool handlesCrossed = mImpl->mEventData->mLeftSelectionPosition > mImpl->mEventData->mRightSelectionPosition;
mImpl->mEventData->mInputStyle.familyName = fontFamily;
mImpl->mEventData->mInputStyle.isFamilyDefined = true;
- if( EventData::SELECTING == mImpl->mEventData->mState )
+ if( EventData::SELECTING == mImpl->mEventData->mState || EventData::EDITING == mImpl->mEventData->mState || EventData::INACTIVE == mImpl->mEventData->mState )
{
CharacterIndex startOfSelectedText = 0u;
Length lengthOfSelectedText = 0u;
mImpl->mEventData->mInputStyle.weight = weight;
mImpl->mEventData->mInputStyle.isWeightDefined = true;
- if( EventData::SELECTING == mImpl->mEventData->mState )
+ if( EventData::SELECTING == mImpl->mEventData->mState || EventData::EDITING == mImpl->mEventData->mState || EventData::INACTIVE == mImpl->mEventData->mState )
{
CharacterIndex startOfSelectedText = 0u;
Length lengthOfSelectedText = 0u;
mImpl->mEventData->mInputStyle.width = width;
mImpl->mEventData->mInputStyle.isWidthDefined = true;
- if( EventData::SELECTING == mImpl->mEventData->mState )
+ if( EventData::SELECTING == mImpl->mEventData->mState || EventData::EDITING == mImpl->mEventData->mState || EventData::INACTIVE == mImpl->mEventData->mState )
{
CharacterIndex startOfSelectedText = 0u;
Length lengthOfSelectedText = 0u;
mImpl->mEventData->mInputStyle.slant = slant;
mImpl->mEventData->mInputStyle.isSlantDefined = true;
- if( EventData::SELECTING == mImpl->mEventData->mState )
+ if( EventData::SELECTING == mImpl->mEventData->mState || EventData::EDITING == mImpl->mEventData->mState || EventData::INACTIVE == mImpl->mEventData->mState )
{
CharacterIndex startOfSelectedText = 0u;
Length lengthOfSelectedText = 0u;
mImpl->mEventData->mInputStyle.size = size;
mImpl->mEventData->mInputStyle.isSizeDefined = true;
- if( EventData::SELECTING == mImpl->mEventData->mState )
+ if( EventData::SELECTING == mImpl->mEventData->mState || EventData::EDITING == mImpl->mEventData->mState || EventData::INACTIVE == mImpl->mEventData->mState )
{
CharacterIndex startOfSelectedText = 0u;
Length lengthOfSelectedText = 0u;
GET_SCRIPTS |
VALIDATE_FONTS |
GET_LINE_BREAKS |
- GET_WORD_BREAKS |
BIDI_INFO |
SHAPE_TEXT |
GET_GLYPH_METRICS );
return naturalSize;
}
+bool Controller::CheckForTextFit( float pointSize, Size& layoutSize )
+{
+ Size textSize;
+ mImpl->mFontDefaults->mFitPointSize = pointSize;
+ mImpl->mFontDefaults->sizeDefined = true;
+ ClearFontData();
+
+ // Operations that can be done only once until the text changes.
+ const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
+ GET_SCRIPTS |
+ VALIDATE_FONTS |
+ GET_LINE_BREAKS |
+ BIDI_INFO |
+ SHAPE_TEXT|
+ GET_GLYPH_METRICS );
+
+ mImpl->mTextUpdateInfo.mParagraphCharacterIndex = 0u;
+ mImpl->mTextUpdateInfo.mRequestedNumberOfCharacters = mImpl->mModel->mLogicalModel->mText.Count();
+
+ // Make sure the model is up-to-date before layouting
+ mImpl->UpdateModel( onlyOnceOperations );
+
+ DoRelayout( Size( layoutSize.width, MAX_FLOAT ),
+ static_cast<OperationsMask>( onlyOnceOperations | LAYOUT),
+ textSize);
+
+ // Clear the update info. This info will be set the next time the text is updated.
+ mImpl->mTextUpdateInfo.Clear();
+ mImpl->mTextUpdateInfo.mClearAll = true;
+
+ if( textSize.width > layoutSize.width || textSize.height > layoutSize.height )
+ {
+ return false;
+ }
+ return true;
+}
+
+void Controller::FitPointSizeforLayout( Size layoutSize )
+{
+ const OperationsMask operations = mImpl->mOperationsPending;
+ if( NO_OPERATION != ( UPDATE_LAYOUT_SIZE & operations ) )
+ {
+ bool actualellipsis = mImpl->mModel->mElideEnabled;
+ float minPointSize = mImpl->mTextFitMinSize;
+ float maxPointSize = mImpl->mTextFitMaxSize;
+ float pointInterval = mImpl->mTextFitStepSize;
+
+ mImpl->mModel->mElideEnabled = false;
+ Vector<float> pointSizeArray;
+
+ // check zero value
+ if( pointInterval < 1.f )
+ {
+ mImpl->mTextFitStepSize = pointInterval = 1.0f;
+ }
+
+ pointSizeArray.Reserve( static_cast< unsigned int >( ceil( ( maxPointSize - minPointSize ) / pointInterval ) ) );
+
+ for( float i = minPointSize; i < maxPointSize; i += pointInterval )
+ {
+ pointSizeArray.PushBack( i );
+ }
+
+ pointSizeArray.PushBack( maxPointSize );
+
+ int bestSizeIndex = 0;
+ int min = bestSizeIndex + 1;
+ int max = pointSizeArray.Size() - 1;
+ while( min <= max )
+ {
+ int destI = ( min + max ) / 2;
+
+ if( CheckForTextFit( pointSizeArray[destI], layoutSize ) )
+ {
+ bestSizeIndex = min;
+ min = destI + 1;
+ }
+ else
+ {
+ max = destI - 1;
+ bestSizeIndex = max;
+ }
+ }
+
+ mImpl->mModel->mElideEnabled = actualellipsis;
+ mImpl->mFontDefaults->mFitPointSize = pointSizeArray[bestSizeIndex];
+ mImpl->mFontDefaults->sizeDefined = true;
+ ClearFontData();
+ }
+}
+
float Controller::GetHeightForWidth( float width )
{
DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::GetHeightForWidth %p width %f\n", this, width );
GET_SCRIPTS |
VALIDATE_FONTS |
GET_LINE_BREAKS |
- GET_WORD_BREAKS |
BIDI_INFO |
SHAPE_TEXT |
GET_GLYPH_METRICS );
GET_SCRIPTS |
VALIDATE_FONTS |
GET_LINE_BREAKS |
- GET_WORD_BREAKS |
BIDI_INFO |
SHAPE_TEXT |
GET_GLYPH_METRICS );
// This branch avoids calling the InsertText() method of the 'else' branch which can delete selected text.
}
- else if( Dali::DALI_KEY_SHIFT_LEFT == keyCode )
+ else if( ( Dali::DALI_KEY_SHIFT_LEFT == keyCode ) || ( Dali::DALI_KEY_SHIFT_RIGHT == keyCode ) )
{
- // DALI_KEY_SHIFT_LEFT is the key code for the Left Shift. It's sent (by the InputMethodContext?) when the predictive text is enabled
+ // DALI_KEY_SHIFT_LEFT or DALI_KEY_SHIFT_RIGHT is the key code for Shift. It's sent (by the InputMethodContext?) when the predictive text is enabled
// and a character is typed after the type of a upper case latin character.
// Do nothing.
{
DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p keyString %s\n", this, keyString.c_str() );
- // InputMethodContext is no longer handling key-events
- mImpl->ClearPreEditFlag();
+ if( !keyString.empty() )
+ {
+ // InputMethodContext is no longer handling key-events
+ mImpl->ClearPreEditFlag();
- InsertText( keyString, COMMIT );
- textChanged = true;
+ InsertText( keyString, COMMIT );
+
+ textChanged = true;
+
+ // Will request for relayout.
+ relayoutNeeded = true;
+ }
- // Will request for relayout.
- relayoutNeeded = true;
}
if ( ( mImpl->mEventData->mState != EventData::INTERRUPTED ) &&
( mImpl->mEventData->mState != EventData::INACTIVE ) &&
( !isNullKey ) &&
( Dali::DALI_KEY_SHIFT_LEFT != keyCode ) &&
+ ( Dali::DALI_KEY_SHIFT_RIGHT != keyCode ) &&
( Dali::DALI_KEY_VOLUME_UP != keyCode ) &&
( Dali::DALI_KEY_VOLUME_DOWN != keyCode ) )
{
}
}
+void Controller::SelectEvent( float x, float y, bool selectAll )
+{
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SelectEvent\n" );
+
+ if( NULL != mImpl->mEventData )
+ {
+ if( selectAll )
+ {
+ Event event( Event::SELECT_ALL );
+ mImpl->mEventData->mEventQueue.push_back( event );
+ }
+ else
+ {
+ Event event( Event::SELECT );
+ event.p2.mFloat = x;
+ event.p3.mFloat = y;
+ mImpl->mEventData->mEventQueue.push_back( event );
+ }
+
+ mImpl->mEventData->mCheckScrollAmount = true;
+ mImpl->mEventData->mIsLeftHandleSelected = true;
+ mImpl->mEventData->mIsRightHandleSelected = true;
+ mImpl->RequestRelayout();
+ }
+}
+
InputMethodContext::CallbackData Controller::OnInputMethodContextEvent( InputMethodContext& inputMethodContext, const InputMethodContext::EventData& inputMethodContextEvent )
{
// Whether the text needs to be relaid-out.
mImpl->mModel->mLogicalModel->RetrieveStyle( styleIndex, style );
// Whether to add a new text color run.
- const bool addColorRun = ( style.textColor != mImpl->mEventData->mInputStyle.textColor );
+ const bool addColorRun = ( style.textColor != mImpl->mEventData->mInputStyle.textColor ) && !mImpl->mEventData->mInputStyle.isDefaultColor;
// Whether to add a new font run.
- const bool addFontNameRun = style.familyName != mImpl->mEventData->mInputStyle.familyName;
- const bool addFontWeightRun = style.weight != mImpl->mEventData->mInputStyle.weight;
- const bool addFontWidthRun = style.width != mImpl->mEventData->mInputStyle.width;
- const bool addFontSlantRun = style.slant != mImpl->mEventData->mInputStyle.slant;
- const bool addFontSizeRun = style.size != mImpl->mEventData->mInputStyle.size;
+ const bool addFontNameRun = ( style.familyName != mImpl->mEventData->mInputStyle.familyName ) && mImpl->mEventData->mInputStyle.isFamilyDefined;
+ const bool addFontWeightRun = ( style.weight != mImpl->mEventData->mInputStyle.weight ) && mImpl->mEventData->mInputStyle.isWeightDefined;
+ const bool addFontWidthRun = ( style.width != mImpl->mEventData->mInputStyle.width ) && mImpl->mEventData->mInputStyle.isWidthDefined;
+ const bool addFontSlantRun = ( style.slant != mImpl->mEventData->mInputStyle.slant ) && mImpl->mEventData->mInputStyle.isSlantDefined;
+ const bool addFontSizeRun = ( style.size != mImpl->mEventData->mInputStyle.size ) && mImpl->mEventData->mInputStyle.isSizeDefined ;
// Add style runs.
if( addColorRun )
}
}
+ // If the number of current text and the number of characters to be deleted are same,
+ // it means all texts should be removed and all Preedit variables should be initialized.
+ if( ( currentText.Count() - numberOfCharacters == 0 ) && ( cursorIndex == 0 ) )
+ {
+ if( mImpl->mEventData )
+ {
+ mImpl->mEventData->mPreEditStartPosition = 0;
+ mImpl->mEventData->mPreEditLength = 0;
+ }
+ }
+
// Updates the text style runs by removing characters. Runs with no characters are removed.
mImpl->mModel->mLogicalModel->UpdateTextStyleRuns( cursorIndex, -numberOfCharacters );
// Make sure the index is not out of bound
if ( charactersToGlyph.Count() != glyphsPerCharacter.Count() ||
requestedNumberOfCharacters > charactersToGlyph.Count() ||
- ( lastIndex >= charactersToGlyph.Count() && charactersToGlyph.Count() > 0u ) )
+ ( lastIndex > charactersToGlyph.Count() && charactersToGlyph.Count() > 0u ) )
{
std::string currentText;
GetText( currentText );
return true;
}
- const Vector<LineBreakInfo>& lineBreakInfo = mImpl->mModel->mLogicalModel->mLineBreakInfo;
- const Vector<WordBreakInfo>& wordBreakInfo = mImpl->mModel->mLogicalModel->mWordBreakInfo;
- const Vector<CharacterDirection>& characterDirection = mImpl->mModel->mLogicalModel->mCharacterDirections;
- const Vector<GlyphInfo>& glyphs = mImpl->mModel->mVisualModel->mGlyphs;
- const Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mModel->mVisualModel->mGlyphsToCharacters;
- const Vector<Length>& charactersPerGlyph = mImpl->mModel->mVisualModel->mCharactersPerGlyph;
const Character* const textBuffer = mImpl->mModel->mLogicalModel->mText.Begin();
- const float outlineWidth = static_cast<float>( mImpl->mModel->GetOutlineWidth() );
// Set the layout parameters.
Layout::Parameters layoutParameters( size,
- textBuffer,
- lineBreakInfo.Begin(),
- wordBreakInfo.Begin(),
- ( 0u != characterDirection.Count() ) ? characterDirection.Begin() : NULL,
- glyphs.Begin(),
- glyphsToCharactersMap.Begin(),
- charactersPerGlyph.Begin(),
- charactersToGlyphBuffer,
- glyphsPerCharacterBuffer,
- totalNumberOfGlyphs,
- mImpl->mModel->mHorizontalAlignment,
- mImpl->mModel->mLineWrapMode,
- outlineWidth,
- mImpl->mModel->mIgnoreSpacesAfterText,
- mImpl->mModel->mMatchSystemLanguageDirection );
+ mImpl->mModel );
// Resize the vector of positions to have the same size than the vector of glyphs.
Vector<Vector2>& glyphPositions = mImpl->mModel->mVisualModel->mGlyphPositions;
// The laid-out lines.
Vector<LineRun>& lines = mImpl->mModel->mVisualModel->mLines;
+ CharacterIndex alignStartIndex = startIndex;
+ Length alignRequestedNumberOfCharacters = requestedNumberOfCharacters;
+
+ // the whole text needs to be full aligned.
+ // If you do not do a full aligned, only the last line of the multiline input is aligned.
+ if( mImpl->mEventData && mImpl->mEventData->mUpdateAlignment )
+ {
+ alignStartIndex = 0u;
+ alignRequestedNumberOfCharacters = mImpl->mModel->mLogicalModel->mText.Count();
+ mImpl->mEventData->mUpdateAlignment = false;
+ }
+
// Need to align with the control's size as the text may contain lines
// starting either with left to right text or right to left.
mImpl->mLayoutEngine.Align( size,
- startIndex,
- requestedNumberOfCharacters,
+ alignStartIndex,
+ alignRequestedNumberOfCharacters,
mImpl->mModel->mHorizontalAlignment,
lines,
mImpl->mModel->mAlignmentOffset,
mImpl->mOperationsPending = ALL_OPERATIONS;
}
-void Controller::SelectEvent( float x, float y, bool selectAll )
-{
- DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SelectEvent\n" );
-
- if( NULL != mImpl->mEventData )
- {
- if( selectAll )
- {
- Event event( Event::SELECT_ALL );
- mImpl->mEventData->mEventQueue.push_back( event );
- }
- else
- {
- Event event( Event::SELECT );
- event.p2.mFloat = x;
- event.p3.mFloat = y;
- mImpl->mEventData->mEventQueue.push_back( event );
- }
-
- mImpl->mEventData->mCheckScrollAmount = true;
- mImpl->mEventData->mIsLeftHandleSelected = true;
- mImpl->mEventData->mIsRightHandleSelected = true;
- mImpl->RequestRelayout();
- }
-}
-
bool Controller::DeleteEvent( int keyCode )
{
DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p KeyCode : %d \n", this, keyCode );