return mImpl->mMarkupProcessorEnabled;
}
+void Controller::SetAutoScrollEnabled( bool enable )
+{
+ DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::SetAutoScrollEnabled[%s] SingleBox[%s]-> [%p]\n", (enable)?"true":"false", ( mImpl->mLayoutEngine.GetLayout() == LayoutEngine::SINGLE_LINE_BOX)?"true":"false", this );
+
+ if ( mImpl->mLayoutEngine.GetLayout() == LayoutEngine::SINGLE_LINE_BOX )
+ {
+ if ( enable )
+ {
+ DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::SetAutoScrollEnabled for SINGLE_LINE_BOX\n" );
+ mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
+ LAYOUT |
+ ALIGN |
+ UPDATE_ACTUAL_SIZE |
+ UPDATE_DIRECTION |
+ REORDER );
+
+ }
+ else
+ {
+ DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::SetAutoScrollEnabled Disabling autoscroll\n");
+ mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
+ LAYOUT |
+ ALIGN |
+ UPDATE_ACTUAL_SIZE |
+ REORDER );
+ }
+
+ mImpl->mAutoScrollEnabled = enable;
+ mImpl->RequestRelayout();
+ }
+ else
+ {
+ DALI_LOG_WARNING( "Attempted AutoScrolling on a non SINGLE_LINE_BOX, request ignored" );
+ mImpl->mAutoScrollEnabled = false;
+ }
+}
+
+bool Controller::IsAutoScrollEnabled() const
+{
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::IsAutoScrollEnabled[%s]\n", (mImpl->mAutoScrollEnabled)?"true":"false" );
+
+ return mImpl->mAutoScrollEnabled;
+}
+
+CharacterDirection Controller::GetAutoScrollDirection() const
+{
+ return mImpl->mAutoScrollDirectionRTL;
+}
+
+float Controller::GetAutoScrollLineAlignment() const
+{
+ float offset = 0.f;
+
+ if( mImpl->mVisualModel &&
+ ( 0u != mImpl->mVisualModel->mLines.Count() ) )
+ {
+ offset = ( *mImpl->mVisualModel->mLines.Begin() ).alignmentOffset;
+ }
+
+ return offset;
+}
+
void Controller::SetText( const std::string& text )
{
DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SetText\n" );
void Controller::UpdateAfterFontChange( const std::string& newDefaultFont )
{
- DALI_LOG_INFO( gLogFilter, Debug::Concise, "Controller::UpdateAfterFontChange");
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::UpdateAfterFontChange");
if( !mImpl->mFontDefaults->familyDefined ) // If user defined font then should not update when system font changes
{
// Cursor position retreat
oldCursorIndex = cursorIndex;
+ mImpl->mEventData->mScrollAfterDelete = true;
+
DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p removed %d\n", this, numberOfCharacters );
removed = true;
}
const Vector2& Controller::GetScrollPosition() const
{
- if( NULL != mImpl->mEventData )
- {
- return mImpl->mEventData->mScrollPosition;
- }
-
- return Vector2::ZERO;
-}
-
-const Vector2& Controller::GetAlignmentOffset() const
-{
- return mImpl->mAlignmentOffset;
+ return mImpl->mScrollPosition;
}
Vector3 Controller::GetNaturalSize()
mImpl->mTextUpdateInfo.mParagraphCharacterIndex = 0u;
mImpl->mTextUpdateInfo.mRequestedNumberOfCharacters = mImpl->mLogicalModel->mText.Count();
- // Store the actual control's width.
- const float actualControlWidth = mImpl->mVisualModel->mControlSize.width;
+ // Store the actual control's size to restore later.
+ const Size actualControlSize = mImpl->mVisualModel->mControlSize;
DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
static_cast<OperationsMask>( onlyOnceOperations |
// Clear the update info. This info will be set the next time the text is updated.
mImpl->mTextUpdateInfo.Clear();
- // Restore the actual control's width.
- mImpl->mVisualModel->mControlSize.width = actualControlWidth;
+ // Restore the actual control's size.
+ mImpl->mVisualModel->mControlSize = actualControlSize;
DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::GetNaturalSize calculated %f,%f,%f\n", naturalSize.x, naturalSize.y, naturalSize.z );
}
bool Controller::Relayout( const Size& size )
{
- DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::Relayout %p size %f,%f\n", this, size.width, size.height );
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::Relayout %p size %f,%f, autoScroll[%s]\n", this, size.width, size.height, (mImpl->mAutoScrollEnabled)?"true":"false" );
if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
{
// Whether the text control is editable
const bool isEditable = NULL != mImpl->mEventData;
- // Keep the current offset and alignment as it will be used to update the decorator's positions (if the size changes).
+ // Keep the current offset as it will be used to update the decorator's positions (if the size changes).
Vector2 offset;
if( newSize && isEditable )
{
- offset = mImpl->mAlignmentOffset + mImpl->mEventData->mScrollPosition;
+ offset = mImpl->mScrollPosition;
}
- // After doing the text layout, the alignment offset to place the actor in the desired position can be calculated.
- CalculateTextAlignment( size );
+ if( !isEditable || !IsMultiLineEnabled() )
+ {
+ // After doing the text layout, the vertical offset to place the actor in the desired position can be calculated.
+ CalculateVerticalOffset( size );
+ }
if( isEditable )
{
mImpl->ClampHorizontalScroll( layoutSize );
// Update the decorator's positions is needed if there is a new size.
- mImpl->mEventData->mDecorator->UpdatePositions( mImpl->mAlignmentOffset + mImpl->mEventData->mScrollPosition - offset );
+ mImpl->mEventData->mDecorator->UpdatePositions( mImpl->mScrollPosition - offset );
}
// Move the cursor, grab handle etc.
if( NULL != mImpl->mEventData )
{
// Reset the scroll position.
- mImpl->mEventData->mScrollPosition = Vector2::ZERO;
+ mImpl->mScrollPosition = Vector2::ZERO;
mImpl->mEventData->mScrollAfterUpdatePosition = true;
}
}
// Apply modifications to the model; TODO - Optimize this
mImpl->mOperationsPending = ALL_OPERATIONS;
-
- // Queue a cursor reposition event; this must wait until after DoRelayout()
- if( EventData::IsEditingState( mImpl->mEventData->mState ) )
- {
- mImpl->mEventData->mUpdateCursorPosition = true;
- mImpl->mEventData->mScrollAfterUpdatePosition = true;
- }
}
void Controller::TextDeletedEvent()
// Apply modifications to the model; TODO - Optimize this
mImpl->mOperationsPending = ALL_OPERATIONS;
-
- // Queue a cursor reposition event; this must wait until after DoRelayout()
- mImpl->mEventData->mUpdateCursorPosition = true;
- if( 0u != mImpl->mLogicalModel->mText.Count() )
- {
- mImpl->mEventData->mScrollAfterDelete = true;
- }
}
bool Controller::DoRelayout( const Size& size,
const CharacterIndex startIndex = mImpl->mTextUpdateInfo.mParagraphCharacterIndex;
const Length requestedNumberOfCharacters = mImpl->mTextUpdateInfo.mRequestedNumberOfCharacters;
- if( LAYOUT & operations )
+ if( NO_OPERATION != ( LAYOUT & operations ) )
{
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::DoRelayout LAYOUT & operations\n");
+
// Some vectors with data needed to layout and reorder may be void
// after the first time the text has been laid out.
// Fill the vectors again.
if( 0u == totalNumberOfGlyphs )
{
- if( UPDATE_ACTUAL_SIZE & operations )
+ if( NO_OPERATION != ( UPDATE_ACTUAL_SIZE & operations ) )
{
mImpl->mVisualModel->SetLayoutSize( Size::ZERO );
}
mImpl->mVisualModel->mLines,
layoutSize );
+
if( viewUpdated )
{
+ if ( NO_OPERATION != ( UPDATE_DIRECTION & operations ) )
+ {
+ mImpl->mAutoScrollDirectionRTL = false;
+ }
+
// Reorder the lines
- if( REORDER & operations )
+ if( NO_OPERATION != ( REORDER & operations ) )
{
Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
Vector<BidirectionalLineInfoRun>& bidirectionalLineInfo = mImpl->mLogicalModel->mBidirectionalLineInfo;
layoutParameters.lineBidirectionalInfoRunsBuffer = bidirectionalLineInfo.Begin();
layoutParameters.numberOfBidirectionalInfoRuns = bidirectionalLineInfo.Count();
- // TODO: update the conversion map instead creating it from scratch.
- // Note this tables store indices to characters, so update the table means modify all the indices of the text after the last updated character.
- // It's better to refactor this. Store this table per line and don't update the indices.
- // For the cursor position probably is better to use the function instead creating a table.
- // Set the bidirectional info into the model.
- mImpl->mLogicalModel->SetVisualToLogicalMap( layoutParameters.lineBidirectionalInfoRunsBuffer,
- layoutParameters.numberOfBidirectionalInfoRuns,
- 0u,
- mImpl->mLogicalModel->mText.Count() );
-
// Re-layout the text. Reorder those lines with right to left characters.
mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
startIndex,
requestedNumberOfCharacters,
glyphPositions );
+ if ( ( NO_OPERATION != ( UPDATE_DIRECTION & operations ) ) && ( numberOfLines > 0 ) )
+ {
+ const LineRun* const firstline = mImpl->mVisualModel->mLines.Begin();
+ if ( firstline )
+ {
+ mImpl->mAutoScrollDirectionRTL = firstline->direction;
+ }
+ }
}
} // REORDER
// Sets the actual size.
- if( UPDATE_ACTUAL_SIZE & operations )
+ if( NO_OPERATION != ( UPDATE_ACTUAL_SIZE & operations ) )
{
mImpl->mVisualModel->SetLayoutSize( layoutSize );
}
layoutSize = mImpl->mVisualModel->GetLayoutSize();
}
- if( ALIGN & operations )
+ if( NO_OPERATION != ( ALIGN & operations ) )
{
// The laid-out lines.
Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
viewUpdated = true;
}
-
+#if defined(DEBUG_ENABLED)
+ std::string currentText;
+ GetText( currentText );
+ DALI_LOG_INFO( gLogFilter, Debug::Concise, "Controller::DoRelayout [%p] mImpl->mAutoScrollDirectionRTL[%s] [%s]\n", this, (mImpl->mAutoScrollDirectionRTL)?"true":"false", currentText.c_str() );
+#endif
DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout, view updated %s\n", ( viewUpdated ? "true" : "false" ) );
return viewUpdated;
}
return mImpl->mLayoutEngine.GetVerticalAlignment();
}
-void Controller::CalculateTextAlignment( const Size& controlSize )
+void Controller::CalculateVerticalOffset( const Size& controlSize )
{
Size layoutSize = mImpl->mVisualModel->GetLayoutSize();
layoutSize.height = mImpl->GetDefaultFontLineHeight();
}
- if( LayoutEngine::SINGLE_LINE_BOX == mImpl->mLayoutEngine.GetLayout() )
- {
- // Get the direction of the first character.
- const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
-
- // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
- LayoutEngine::HorizontalAlignment horizontalAlignment = mImpl->mLayoutEngine.GetHorizontalAlignment();
- if( firstParagraphDirection )
- {
- switch( horizontalAlignment )
- {
- case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
- {
- horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_END;
- break;
- }
- case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
- {
- // Nothing to do.
- break;
- }
- case LayoutEngine::HORIZONTAL_ALIGN_END:
- {
- horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_BEGIN;
- break;
- }
- }
- }
-
- switch( horizontalAlignment )
- {
- case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
- {
- mImpl->mAlignmentOffset.x = 0.f;
- break;
- }
- case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
- {
- mImpl->mAlignmentOffset.x = floorf( 0.5f * ( controlSize.width - layoutSize.width ) ); // try to avoid pixel alignment.
- break;
- }
- case LayoutEngine::HORIZONTAL_ALIGN_END:
- {
- mImpl->mAlignmentOffset.x = controlSize.width - layoutSize.width;
- break;
- }
- }
- }
-
- const LayoutEngine::VerticalAlignment verticalAlignment = mImpl->mLayoutEngine.GetVerticalAlignment();
- switch( verticalAlignment )
+ switch( mImpl->mLayoutEngine.GetVerticalAlignment() )
{
case LayoutEngine::VERTICAL_ALIGN_TOP:
{
- mImpl->mAlignmentOffset.y = 0.f;
+ mImpl->mScrollPosition.y = 0.f;
break;
}
case LayoutEngine::VERTICAL_ALIGN_CENTER:
{
- mImpl->mAlignmentOffset.y = floorf( 0.5f * ( controlSize.height - layoutSize.height ) ); // try to avoid pixel alignment.
+ mImpl->mScrollPosition.y = floorf( 0.5f * ( controlSize.height - layoutSize.height ) ); // try to avoid pixel alignment.
break;
}
case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
{
- mImpl->mAlignmentOffset.y = controlSize.height - layoutSize.height;
+ mImpl->mScrollPosition.y = controlSize.height - layoutSize.height;
break;
}
}
// TODO: At the moment the underline runs are only for pre-edit.
mImpl->mVisualModel->mUnderlineRuns.Clear();
- Vector<Character> utf32Characters;
- Length characterCount( 0u );
+ // Keep the current number of characters.
+ const Length currentNumberOfCharacters = mImpl->IsShowingRealText() ? mImpl->mLogicalModel->mText.Count() : 0u;
- // Remove the previous IMF pre-edit (predicitive text)
- if( mImpl->mEventData->mPreEditFlag &&
- ( 0u != mImpl->mEventData->mPreEditLength ) )
+ // Remove the previous IMF pre-edit.
+ if( mImpl->mEventData->mPreEditFlag && ( 0u != mImpl->mEventData->mPreEditLength ) )
{
- const CharacterIndex offset = mImpl->mEventData->mPrimaryCursorPosition - mImpl->mEventData->mPreEditStartPosition;
-
- removedPrevious = RemoveText( -static_cast<int>( offset ),
+ removedPrevious = RemoveText( -static_cast<int>( mImpl->mEventData->mPrimaryCursorPosition - mImpl->mEventData->mPreEditStartPosition ),
mImpl->mEventData->mPreEditLength,
DONT_UPDATE_INPUT_STYLE );
}
else
{
- // Remove the previous Selection
+ // Remove the previous Selection.
removedPrevious = RemoveSelectedText();
}
+ Vector<Character> utf32Characters;
+ Length characterCount = 0u;
+
if( !text.empty() )
{
// Convert text into UTF-32
DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Inserted %d characters, new size %d new cursor %d\n", maxSizeOfNewText, mImpl->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition );
}
+ const Length numberOfCharacters = mImpl->IsShowingRealText() ? mImpl->mLogicalModel->mText.Count() : 0u;
+
if( ( 0u == mImpl->mLogicalModel->mText.Count() ) &&
mImpl->IsPlaceholderAvailable() )
{
{
// Queue an inserted event
mImpl->QueueModifyEvent( ModifyEvent::TEXT_INSERTED );
+
+ mImpl->mEventData->mUpdateCursorPosition = true;
+ if( numberOfCharacters < currentNumberOfCharacters )
+ {
+ mImpl->mEventData->mScrollAfterDelete = true;
+ }
+ else
+ {
+ mImpl->mEventData->mScrollAfterUpdatePosition = true;
+ }
}
if( maxLengthReached )
// This is to avoid unnecessary relayouts when tapping an empty text-field
bool relayoutNeeded( false );
- if( ( EventData::EDITING_WITH_PASTE_POPUP == mImpl->mEventData->mState ) ||
+ if( ( EventData::EDITING_WITH_POPUP == mImpl->mEventData->mState ) ||
( EventData::EDITING_WITH_PASTE_POPUP == mImpl->mEventData->mState ) )
{
- mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE); // If Popup shown hide it here so can be shown again if required.
+ mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE ); // If Popup shown hide it here so can be shown again if required.
}
if( mImpl->IsShowingRealText() && ( EventData::INACTIVE != mImpl->mEventData->mState ) )
// Commit the current pre-edit text; the contents of the clipboard should be appended
mImpl->ResetImfManager();
+ // Temporary disable hiding clipboard
+ mImpl->SetClipboardHideEnable( false );
+
// Paste
PasteText( stringToPaste );
+
+ mImpl->SetClipboardHideEnable( true );
}
void Controller::TextPopupButtonTouched( Dali::Toolkit::TextSelectionPopup::Buttons button )
else
{
ShowPlaceholderText();
- mImpl->mEventData->mUpdateCursorPosition = true;
}
+
+ mImpl->mEventData->mUpdateCursorPosition = true;
+ mImpl->mEventData->mScrollAfterDelete = true;
+
mImpl->RequestRelayout();
mImpl->mControlInterface.TextChanged();
break;
else
{
ShowPlaceholderText();
- mImpl->mEventData->mUpdateCursorPosition = true;
}
+ mImpl->mEventData->mUpdateCursorPosition = true;
+ mImpl->mEventData->mScrollAfterDelete = true;
}
requestRelayout = true;
break;
else
{
ShowPlaceholderText();
- mImpl->mEventData->mUpdateCursorPosition = true;
}
+ mImpl->mEventData->mUpdateCursorPosition = true;
+ mImpl->mEventData->mScrollAfterDelete = true;
}
return removed;