/*
- * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <dali/integration-api/debug.h>
#include <dali/devel-api/adaptor-framework/clipboard-event-notifier.h>
#include <dali/devel-api/text-abstraction/font-client.h>
+#include <dali/devel-api/adaptor-framework/key-devel.h>
// INTERNAL INCLUDES
+#include <dali-toolkit/public-api/controls/text-controls/placeholder-properties.h>
#include <dali-toolkit/internal/text/bidirectional-support.h>
#include <dali-toolkit/internal/text/character-set-conversion.h>
#include <dali-toolkit/internal/text/layouts/layout-parameters.h>
const std::string EMPTY_STRING("");
-const char * const PLACEHOLDER_TEXT = "placeholderText";
-const char * const PLACEHOLDER_TEXT_FOCUSED = "placeholderTextFocused";
-const char * const PLACEHOLDER_COLOR = "placeholderColor";
-const char * const PLACEHOLDER_FONT_FAMILY = "placeholderFontFamily";
-const char * const PLACEHOLDER_FONT_STYLE = "placeholderFontStyle";
-const char * const PLACEHOLDER_POINT_SIZE = "placeholderPointSize";
-const char * const PLACEHOLDER_PIXEL_SIZE = "placeholderPixelSize";
+const std::string KEY_C_NAME = "c";
+const std::string KEY_V_NAME = "v";
+const std::string KEY_X_NAME = "x";
+
+const char * const PLACEHOLDER_TEXT = "text";
+const char * const PLACEHOLDER_TEXT_FOCUSED = "textFocused";
+const char * const PLACEHOLDER_COLOR = "color";
+const char * const PLACEHOLDER_FONT_FAMILY = "fontFamily";
+const char * const PLACEHOLDER_FONT_STYLE = "fontStyle";
+const char * const PLACEHOLDER_POINT_SIZE = "pointSize";
+const char * const PLACEHOLDER_PIXEL_SIZE = "pixelSize";
+const char * const PLACEHOLDER_ELLIPSIS = "ellipsis";
float ConvertToEven( float value )
{
// public : Configure the text controller.
-void Controller::EnableTextInput( DecoratorPtr decorator )
+void Controller::EnableTextInput( DecoratorPtr decorator, InputMethodContext& inputMethodContext )
{
if( !decorator )
{
if( NULL == mImpl->mEventData )
{
- mImpl->mEventData = new EventData( decorator );
+ mImpl->mEventData = new EventData( decorator, inputMethodContext );
}
}
void Controller::SetMarkupProcessorEnabled( bool enable )
{
- mImpl->mMarkupProcessorEnabled = enable;
+ if( enable != mImpl->mMarkupProcessorEnabled )
+ {
+ //If Text was already set, call the SetText again for enabling or disabling markup
+ mImpl->mMarkupProcessorEnabled = enable;
+ std::string text;
+ GetText( text );
+ SetText( text );
+ }
}
bool Controller::IsMarkupProcessorEnabled() const
CharacterDirection Controller::GetAutoScrollDirection() const
{
- return mImpl->mAutoScrollDirectionRTL;
+ return mImpl->mIsTextDirectionRTL;
}
float Controller::GetAutoScrollLineAlignment() const
ALIGN |
REORDER );
+ mImpl->mTextUpdateInfo.mFullRelayoutNeeded = true;
mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | layoutOperations );
mImpl->RequestRelayout();
return Layout::Engine::MULTI_LINE_BOX == mImpl->mLayoutEngine.GetLayout();
}
-void Controller::SetHorizontalAlignment( Layout::HorizontalAlignment alignment )
+void Controller::SetHorizontalAlignment( Text::HorizontalAlignment::Type alignment )
{
if( alignment != mImpl->mModel->mHorizontalAlignment )
{
}
}
-Layout::HorizontalAlignment Controller::GetHorizontalAlignment() const
+Text::HorizontalAlignment::Type Controller::GetHorizontalAlignment() const
{
return mImpl->mModel->mHorizontalAlignment;
}
-void Controller::SetVerticalAlignment( Layout::VerticalAlignment alignment )
+void Controller::SetVerticalAlignment( VerticalAlignment::Type alignment )
{
if( alignment != mImpl->mModel->mVerticalAlignment )
{
}
}
-Layout::VerticalAlignment Controller::GetVerticalAlignment() const
+VerticalAlignment::Type Controller::GetVerticalAlignment() const
{
return mImpl->mModel->mVerticalAlignment;
}
-void Controller::SetLineWrapMode( Layout::LineWrap::Mode lineWrapMode )
+bool Controller::IsIgnoreSpacesAfterText() const
+{
+ return mImpl->mModel->mIgnoreSpacesAfterText;
+}
+
+void Controller::SetIgnoreSpacesAfterText( bool ignore )
+{
+ mImpl->mModel->mIgnoreSpacesAfterText = ignore;
+}
+
+bool Controller::IsMatchSystemLanguageDirection() const
+{
+ return mImpl->mModel->mMatchSystemLanguageDirection;
+}
+
+void Controller::SetMatchSystemLanguageDirection( bool match )
+{
+ mImpl->mModel->mMatchSystemLanguageDirection = match;
+}
+
+
+void Controller::SetLineWrapMode( Text::LineWrap::Mode lineWrapMode )
{
if( lineWrapMode != mImpl->mModel->mLineWrapMode )
{
}
}
-Layout::LineWrap::Mode Controller::GetLineWrapMode() const
+Text::LineWrap::Mode Controller::GetLineWrapMode() const
{
return mImpl->mModel->mLineWrapMode;
}
return mImpl->mModel->mElideEnabled;
}
+void Controller::SetPlaceholderTextElideEnabled( bool enabled )
+{
+ mImpl->mEventData->mIsPlaceholderElideEnabled = enabled;
+ mImpl->mEventData->mPlaceholderEllipsisFlag = true;
+
+ // Update placeholder if there is no text
+ if( mImpl->IsShowingPlaceholderText() ||
+ ( 0u == mImpl->mModel->mLogicalModel->mText.Count() ) )
+ {
+ ShowPlaceholderText();
+ }
+}
+
+bool Controller::IsPlaceholderTextElideEnabled() const
+{
+ return mImpl->mEventData->mIsPlaceholderElideEnabled;
+}
+
void Controller::SetSelectionEnabled( bool enabled )
{
mImpl->mEventData->mSelectionEnabled = enabled;
return mImpl->mEventData->mSelectionEnabled;
}
+void Controller::SetShiftSelectionEnabled( bool enabled )
+{
+ mImpl->mEventData->mShiftSelectionFlag = enabled;
+}
+
+bool Controller::IsShiftSelectionEnabled() const
+{
+ return mImpl->mEventData->mShiftSelectionFlag;
+}
+
+void Controller::SetGrabHandleEnabled( bool enabled )
+{
+ mImpl->mEventData->mGrabHandleEnabled = enabled;
+}
+
+bool Controller::IsGrabHandleEnabled() const
+{
+ return mImpl->mEventData->mGrabHandleEnabled;
+}
+
// public : Update
void Controller::SetText( const std::string& text )
DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SetText\n" );
// Reset keyboard as text changed
- mImpl->ResetImfManager();
+ mImpl->ResetInputMethodContext();
// Remove the previously set text and style.
ResetText();
// The natural size needs to be re-calculated.
mImpl->mRecalculateNaturalSize = true;
+ // The text direction needs to be updated.
+ mImpl->mUpdateTextDirection = true;
+
// Apply modifications to the model
mImpl->mOperationsPending = ALL_OPERATIONS;
}
bool Controller::IsDefaultFontWeightDefined() const
{
- return mImpl->mFontDefaults->weightDefined;
+ if( NULL != mImpl->mFontDefaults )
+ {
+ return mImpl->mFontDefaults->weightDefined;
+ }
+
+ return false;
}
FontWeight Controller::GetDefaultFontWeight() const
bool Controller::IsDefaultFontWidthDefined() const
{
- return mImpl->mFontDefaults->widthDefined;
+ if( NULL != mImpl->mFontDefaults )
+ {
+ return mImpl->mFontDefaults->widthDefined;
+ }
+
+ return false;
}
FontWidth Controller::GetDefaultFontWidth() const
bool Controller::IsDefaultFontSlantDefined() const
{
- return mImpl->mFontDefaults->slantDefined;
+ if( NULL != mImpl->mFontDefaults )
+ {
+ return mImpl->mFontDefaults->slantDefined;
+ }
+ return false;
}
FontSlant Controller::GetDefaultFontSlant() const
return mImpl->mModel->mVisualModel->GetShadowColor();
}
+void Controller::SetShadowBlurRadius( const float& shadowBlurRadius )
+{
+ if ( fabsf( GetShadowBlurRadius() - shadowBlurRadius ) > Math::MACHINE_EPSILON_1 )
+ {
+ mImpl->mModel->mVisualModel->SetShadowBlurRadius( shadowBlurRadius );
+
+ mImpl->RequestRelayout();
+ }
+}
+
+const float& Controller::GetShadowBlurRadius() const
+{
+ return mImpl->mModel->mVisualModel->GetShadowBlurRadius();
+}
+
void Controller::SetUnderlineColor( const Vector4& color )
{
mImpl->mModel->mVisualModel->SetUnderlineColor( color );
return mImpl->mModel->mVisualModel->GetUnderlineHeight();
}
+void Controller::SetOutlineColor( const Vector4& color )
+{
+ mImpl->mModel->mVisualModel->SetOutlineColor( color );
+
+ mImpl->RequestRelayout();
+}
+
+const Vector4& Controller::GetOutlineColor() const
+{
+ return mImpl->mModel->mVisualModel->GetOutlineColor();
+}
+
+void Controller::SetOutlineWidth( unsigned int width )
+{
+ mImpl->mModel->mVisualModel->SetOutlineWidth( width );
+
+ mImpl->RequestRelayout();
+}
+
+unsigned int Controller::GetOutlineWidth() const
+{
+ return mImpl->mModel->mVisualModel->GetOutlineWidth();
+}
+
+void Controller::SetBackgroundColor( const Vector4& color )
+{
+ mImpl->mModel->mVisualModel->SetBackgroundColor( color );
+
+ mImpl->RequestRelayout();
+}
+
+const Vector4& Controller::GetBackgroundColor() const
+{
+ return mImpl->mModel->mVisualModel->GetBackgroundColor();
+}
+
+void Controller::SetBackgroundEnabled( bool enabled )
+{
+ mImpl->mModel->mVisualModel->SetBackgroundEnabled( enabled );
+
+ mImpl->RequestRelayout();
+}
+
+bool Controller::IsBackgroundEnabled() const
+{
+ return mImpl->mModel->mVisualModel->IsBackgroundEnabled();
+}
+
void Controller::SetDefaultEmbossProperties( const std::string& embossProperties )
{
if( NULL == mImpl->mEmbossDefaults )
return EMPTY_STRING;
}
-void Controller::SetDefaultLineSpacing( float lineSpacing )
+bool Controller::SetDefaultLineSpacing( float lineSpacing )
{
- //TODO finish implementation
- mImpl->mLayoutEngine.SetDefaultLineSpacing( lineSpacing );
+ if( std::abs(lineSpacing - mImpl->mLayoutEngine.GetDefaultLineSpacing()) > Math::MACHINE_EPSILON_1000 )
+ {
+ mImpl->mLayoutEngine.SetDefaultLineSpacing(lineSpacing);
+ mImpl->mRecalculateNaturalSize = true;
+ return true;
+ }
+ return false;
}
float Controller::GetDefaultLineSpacing() const
mImpl->mShadowSetByString = setByString;
}
+bool Controller::IsOutlineSetByString()
+{
+ return mImpl->mOutlineSetByString;
+}
+
+void Controller::OutlineSetByString( bool setByString )
+{
+ mImpl->mOutlineSetByString = setByString;
+}
+
bool Controller::IsFontStyleSetByString()
{
return mImpl->mFontStyleSetByString;
mImpl->UpdateModel( onlyOnceOperations );
// Layout the text for the new width.
- mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | LAYOUT );
+ mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | LAYOUT | REORDER );
// Store the actual control's size to restore later.
const Size actualControlSize = mImpl->mModel->mVisualModel->mControlSize;
DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
static_cast<OperationsMask>( onlyOnceOperations |
- LAYOUT ),
+ LAYOUT | REORDER ),
naturalSize.GetVectorXY() );
// Do not do again the only once operations.
// Clear the update info. This info will be set the next time the text is updated.
mImpl->mTextUpdateInfo.Clear();
+ mImpl->mTextUpdateInfo.mClearAll = true;
// Restore the actual control's size.
mImpl->mModel->mVisualModel->mControlSize = actualControlSize;
Size layoutSize;
if( fabsf( width - mImpl->mModel->mVisualModel->mControlSize.width ) > Math::MACHINE_EPSILON_1000 ||
- mImpl->mTextUpdateInfo.mFullRelayoutNeeded )
+ mImpl->mTextUpdateInfo.mFullRelayoutNeeded ||
+ mImpl->mTextUpdateInfo.mClearAll )
{
// Operations that can be done only once until the text changes.
const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
// Clear the update info. This info will be set the next time the text is updated.
mImpl->mTextUpdateInfo.Clear();
+ mImpl->mTextUpdateInfo.mClearAll = true;
// Restore the actual control's width.
mImpl->mModel->mVisualModel->mControlSize.width = actualControlWidth;
Property::Key& key = keyValue.first;
Property::Value& value = keyValue.second;
- if( key == PLACEHOLDER_TEXT )
+ if( key == Toolkit::Text::PlaceHolder::Property::TEXT || key == PLACEHOLDER_TEXT )
{
std::string text = "";
value.Get( text );
SetPlaceholderText( Controller::PLACEHOLDER_TYPE_INACTIVE, text );
}
- else if( key == PLACEHOLDER_TEXT_FOCUSED )
+ else if( key == Toolkit::Text::PlaceHolder::Property::TEXT_FOCUSED || key == PLACEHOLDER_TEXT_FOCUSED )
{
std::string text = "";
value.Get( text );
SetPlaceholderText( Controller::PLACEHOLDER_TYPE_ACTIVE, text );
}
- else if( key == PLACEHOLDER_COLOR )
+ else if( key == Toolkit::Text::PlaceHolder::Property::COLOR || key == PLACEHOLDER_COLOR )
{
Vector4 textColor;
value.Get( textColor );
SetPlaceholderTextColor( textColor );
}
}
- else if( key == PLACEHOLDER_FONT_FAMILY )
+ else if( key == Toolkit::Text::PlaceHolder::Property::FONT_FAMILY || key == PLACEHOLDER_FONT_FAMILY )
{
std::string fontFamily = "";
value.Get( fontFamily );
SetPlaceholderFontFamily( fontFamily );
}
- else if( key == PLACEHOLDER_FONT_STYLE )
+ else if( key == Toolkit::Text::PlaceHolder::Property::FONT_STYLE || key == PLACEHOLDER_FONT_STYLE )
{
SetFontStyleProperty( this, value, Text::FontStyle::PLACEHOLDER );
}
- else if( key == PLACEHOLDER_POINT_SIZE )
+ else if( key == Toolkit::Text::PlaceHolder::Property::POINT_SIZE || key == PLACEHOLDER_POINT_SIZE )
{
float pointSize;
value.Get( pointSize );
SetPlaceholderTextFontSize( pointSize, Text::Controller::POINT_SIZE );
}
}
- else if( key == PLACEHOLDER_PIXEL_SIZE )
+ else if( key == Toolkit::Text::PlaceHolder::Property::PIXEL_SIZE || key == PLACEHOLDER_PIXEL_SIZE )
{
float pixelSize;
value.Get( pixelSize );
SetPlaceholderTextFontSize( pixelSize, Text::Controller::PIXEL_SIZE );
}
}
+ else if( key == Toolkit::Text::PlaceHolder::Property::ELLIPSIS || key == PLACEHOLDER_ELLIPSIS )
+ {
+ bool ellipsis;
+ value.Get( ellipsis );
+ SetPlaceholderTextElideEnabled( ellipsis );
+ }
}
}
{
if( !mImpl->mEventData->mPlaceholderTextActive.empty() )
{
- map[ PLACEHOLDER_TEXT_FOCUSED ] = mImpl->mEventData->mPlaceholderTextActive;
+ map[ Text::PlaceHolder::Property::TEXT_FOCUSED ] = mImpl->mEventData->mPlaceholderTextActive;
}
if( !mImpl->mEventData->mPlaceholderTextInactive.empty() )
{
- map[ PLACEHOLDER_TEXT ] = mImpl->mEventData->mPlaceholderTextInactive;
+ map[ Text::PlaceHolder::Property::TEXT ] = mImpl->mEventData->mPlaceholderTextInactive;
}
- map[ PLACEHOLDER_COLOR ] = mImpl->mEventData->mPlaceholderTextColor;
- map[ PLACEHOLDER_FONT_FAMILY ] = GetPlaceholderFontFamily();
+ map[ Text::PlaceHolder::Property::COLOR ] = mImpl->mEventData->mPlaceholderTextColor;
+ map[ Text::PlaceHolder::Property::FONT_FAMILY ] = GetPlaceholderFontFamily();
Property::Value fontStyleMapGet;
GetFontStyleProperty( this, fontStyleMapGet, Text::FontStyle::PLACEHOLDER );
- map[ PLACEHOLDER_FONT_STYLE ] = fontStyleMapGet;
+ map[ Text::PlaceHolder::Property::FONT_STYLE ] = fontStyleMapGet;
// Choose font size : POINT_SIZE or PIXEL_SIZE
if( !mImpl->mEventData->mIsPlaceholderPixelSize )
{
- map[ PLACEHOLDER_POINT_SIZE ] = GetPlaceholderTextFontSize( Text::Controller::POINT_SIZE );
+ map[ Text::PlaceHolder::Property::POINT_SIZE ] = GetPlaceholderTextFontSize( Text::Controller::POINT_SIZE );
}
else
{
- map[ PLACEHOLDER_PIXEL_SIZE ] = GetPlaceholderTextFontSize( Text::Controller::PIXEL_SIZE );
+ map[ Text::PlaceHolder::Property::PIXEL_SIZE ] = GetPlaceholderTextFontSize( Text::Controller::PIXEL_SIZE );
+ }
+
+ if( mImpl->mEventData->mPlaceholderEllipsisFlag )
+ {
+ map[ Text::PlaceHolder::Property::ELLIPSIS ] = IsPlaceholderTextElideEnabled();
}
}
}
+Toolkit::DevelText::TextDirection::Type Controller::GetTextDirection()
+{
+ // Make sure the model is up-to-date before layouting
+ ProcessModifyEvents();
+
+ if ( mImpl->mUpdateTextDirection )
+ {
+ // 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 |
+ GET_WORD_BREAKS |
+ BIDI_INFO |
+ SHAPE_TEXT |
+ GET_GLYPH_METRICS );
+
+ // Set the update info to relayout the whole text.
+ 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 );
+
+ Vector3 naturalSize;
+ DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
+ static_cast<OperationsMask>( onlyOnceOperations |
+ LAYOUT | REORDER | UPDATE_DIRECTION ),
+ naturalSize.GetVectorXY() );
+
+ // Do not do again the only once operations.
+ mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
+
+ // Clear the update info. This info will be set the next time the text is updated.
+ mImpl->mTextUpdateInfo.Clear();
+
+ mImpl->mUpdateTextDirection = false;
+ }
+
+ return mImpl->mIsTextDirectionRTL ? Toolkit::DevelText::TextDirection::RIGHT_TO_LEFT : Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT;
+}
+
+Toolkit::DevelText::VerticalLineAlignment::Type Controller::GetVerticalLineAlignment() const
+{
+ return mImpl->mModel->GetVerticalLineAlignment();
+}
+
+void Controller::SetVerticalLineAlignment( Toolkit::DevelText::VerticalLineAlignment::Type alignment )
+{
+ mImpl->mModel->mVerticalLineAlignment = alignment;
+}
+
// public : Relayout.
-Controller::UpdateTextType Controller::Relayout( const Size& size )
+Controller::UpdateTextType Controller::Relayout( const Size& size, Dali::LayoutDirection::Type layoutDirection )
{
DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::Relayout %p size %f,%f, autoScroll[%s]\n", this, size.width, size.height, mImpl->mIsAutoScrollEnabled ?"true":"false" );
UpdateTextType updateTextType = NONE_UPDATED;
+ mImpl->mLayoutDirection = layoutDirection;
if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
{
if( 0u != mImpl->mModel->mVisualModel->mGlyphPositions.Count() )
{
DALI_LOG_INFO( gLogFilter, Debug::Verbose, "new size (previous size %f,%f)\n", mImpl->mModel->mVisualModel->mControlSize.width, mImpl->mModel->mVisualModel->mControlSize.height );
+ if( ( 0 == mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd ) &&
+ ( 0 == mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters ) &&
+ ( ( mImpl->mModel->mVisualModel->mControlSize.width < Math::MACHINE_EPSILON_1000 ) || ( mImpl->mModel->mVisualModel->mControlSize.height < Math::MACHINE_EPSILON_1000 ) ) )
+ {
+ mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mModel->mLogicalModel->mText.Count();
+ }
+
// Layout operations that need to be done if the size changes.
mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
LAYOUT |
COLOR );
}
+ // Set the update info to elide the text.
+ if( mImpl->mModel->mElideEnabled ||
+ ( ( NULL != mImpl->mEventData ) && mImpl->mEventData->mIsPlaceholderElideEnabled ) )
+ {
+ // Update Text layout for applying elided
+ mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
+ ALIGN |
+ LAYOUT |
+ UPDATE_LAYOUT_SIZE |
+ REORDER );
+ mImpl->mTextUpdateInfo.mFullRelayoutNeeded = true;
+ mImpl->mTextUpdateInfo.mCharacterIndex = 0u;
+ }
+
// Make sure the model is up-to-date before layouting.
ProcessModifyEvents();
bool updated = mImpl->UpdateModel( mImpl->mOperationsPending );
mImpl->mEventData->mUpdateCursorPosition = true; //If editing started without tap event, cursor update must be triggered.
mImpl->mEventData->mUpdateInputStyle = true;
}
- mImpl->NotifyImfMultiLineStatus();
+ mImpl->NotifyInputMethodContextMultiLineStatus();
if( mImpl->IsShowingPlaceholderText() )
{
// Show alternative placeholder-text when editing
{
int keyCode = keyEvent.keyCode;
const std::string& keyString = keyEvent.keyPressed;
+ const std::string keyName = keyEvent.keyPressedName;
const bool isNullKey = ( 0 == keyCode ) && ( keyString.empty() );
{
// In some platforms arrive key events with no key code.
// Do nothing.
+ return false;
}
- else if( Dali::DALI_KEY_ESCAPE == keyCode )
+ else if( Dali::DALI_KEY_ESCAPE == keyCode || Dali::DALI_KEY_BACK == keyCode || Dali::DALI_KEY_SEARCH == keyCode )
{
- // Escape key is a special case which causes focus loss
- KeyboardFocusLostEvent();
-
- // Will request for relayout.
- relayoutNeeded = true;
+ // Do nothing
+ return false;
}
else if( ( Dali::DALI_KEY_CURSOR_LEFT == keyCode ) ||
( Dali::DALI_KEY_CURSOR_RIGHT == keyCode ) ||
uint32_t numberOfLines = mImpl->mModel->GetNumberOfLines();
// Logic to determine whether this text control will lose focus or not.
- if( ( Dali::DALI_KEY_CURSOR_LEFT == keyCode && 0 == cursorPosition ) ||
- ( Dali::DALI_KEY_CURSOR_RIGHT == keyCode && numberOfCharacters == cursorPosition) ||
+ if( ( Dali::DALI_KEY_CURSOR_LEFT == keyCode && 0 == cursorPosition && !keyEvent.IsShiftModifier() ) ||
+ ( Dali::DALI_KEY_CURSOR_RIGHT == keyCode && numberOfCharacters == cursorPosition && !keyEvent.IsShiftModifier() ) ||
( Dali::DALI_KEY_CURSOR_DOWN == keyCode && cursorLine == numberOfLines -1 ) ||
( Dali::DALI_KEY_CURSOR_DOWN == keyCode && numberOfCharacters == cursorPosition && cursorLine -1 == numberOfLines -1 ) ||
( Dali::DALI_KEY_CURSOR_UP == keyCode && cursorLine == 0 ) ||
( Dali::DALI_KEY_CURSOR_UP == keyCode && numberOfCharacters == cursorPosition && cursorLine == 1 ) )
{
+ // Release the active highlight.
+ if( mImpl->mEventData->mState == EventData::SELECTING )
+ {
+ mImpl->ChangeState( EventData::EDITING );
+
+ // Update selection position.
+ mImpl->mEventData->mLeftSelectionPosition = mImpl->mEventData->mPrimaryCursorPosition;
+ mImpl->mEventData->mRightSelectionPosition = mImpl->mEventData->mPrimaryCursorPosition;
+ mImpl->mEventData->mUpdateCursorPosition = true;
+ mImpl->RequestRelayout();
+ }
return false;
}
mImpl->mEventData->mCheckScrollAmount = true;
Event event( Event::CURSOR_KEY_EVENT );
event.p1.mInt = keyCode;
+ event.p2.mBool = keyEvent.IsShiftModifier();
mImpl->mEventData->mEventQueue.push_back( event );
// Will request for relayout.
relayoutNeeded = true;
}
- else if( Dali::DALI_KEY_BACKSPACE == keyCode )
+ else if ( Dali::DevelKey::DALI_KEY_CONTROL_LEFT == keyCode || Dali::DevelKey::DALI_KEY_CONTROL_RIGHT == keyCode )
+ {
+ // Left or Right Control key event is received before Ctrl-C/V/X key event is received
+ // If not handle it here, any selected text will be deleted
+
+ // Do nothing
+ return false;
+ }
+ else if ( keyEvent.IsCtrlModifier() )
+ {
+ bool consumed = false;
+ if (keyName == KEY_C_NAME)
+ {
+ // Ctrl-C to copy the selected text
+ TextPopupButtonTouched( Toolkit::TextSelectionPopup::COPY );
+ consumed = true;
+ }
+ else if (keyName == KEY_V_NAME)
+ {
+ // Ctrl-V to paste the copied text
+ TextPopupButtonTouched( Toolkit::TextSelectionPopup::PASTE );
+ consumed = true;
+ }
+ else if (keyName == KEY_X_NAME)
+ {
+ // Ctrl-X to cut the selected text
+ TextPopupButtonTouched( Toolkit::TextSelectionPopup::CUT );
+ consumed = true;
+ }
+ return consumed;
+ }
+ else if( ( Dali::DALI_KEY_BACKSPACE == keyCode ) ||
+ ( Dali::DevelKey::DALI_KEY_DELETE == keyCode ) )
{
- textChanged = BackspaceKeyEvent();
+ textChanged = DeleteEvent( keyCode );
// Will request for relayout.
relayoutNeeded = true;
}
else if( Dali::DALI_KEY_SHIFT_LEFT == keyCode )
{
- // DALI_KEY_SHIFT_LEFT is the key code for the Left Shift. It's sent (by the imf?) when the predictive text is enabled
+ // DALI_KEY_SHIFT_LEFT is the key code for the Left 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.
+ return false;
}
else if( ( Dali::DALI_KEY_VOLUME_UP == keyCode ) || ( Dali::DALI_KEY_VOLUME_DOWN == keyCode ) )
{
// This branch avoids calling the InsertText() method of the 'else' branch which can delete selected text.
// Do nothing.
+ return false;
}
else
{
DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p keyString %s\n", this, keyString.c_str() );
- // IMF manager is no longer handling key-events
+ // InputMethodContext is no longer handling key-events
mImpl->ClearPreEditFlag();
InsertText( keyString, COMMIT );
( Dali::DALI_KEY_VOLUME_UP != keyCode ) &&
( Dali::DALI_KEY_VOLUME_DOWN != keyCode ) )
{
- // Should not change the state if the key is the shift send by the imf manager.
+ // Should not change the state if the key is the shift send by the InputMethodContext.
// Otherwise, when the state is SELECTING the text controller can't send the right
- // surrounding info to the imf.
+ // surrounding info to the InputMethodContext.
mImpl->ChangeState( EventData::EDITING );
// Will request for relayout.
}
// Reset keyboard as tap event has occurred.
- mImpl->ResetImfManager();
+ mImpl->ResetInputMethodContext();
}
void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
}
else if( !mImpl->IsClipboardVisible() )
{
- // Reset the imf manager to commit the pre-edit before selecting the text.
- mImpl->ResetImfManager();
+ // Reset the InputMethodContext to commit the pre-edit before selecting the text.
+ mImpl->ResetInputMethodContext();
Event event( Event::LONG_PRESS_EVENT );
event.p1.mInt = state;
}
}
-ImfManager::ImfCallbackData Controller::OnImfEvent( ImfManager& imfManager, const ImfManager::ImfEventData& imfEvent )
+InputMethodContext::CallbackData Controller::OnInputMethodContextEvent( InputMethodContext& inputMethodContext, const InputMethodContext::EventData& inputMethodContextEvent )
{
// Whether the text needs to be relaid-out.
bool requestRelayout = false;
- // Whether to retrieve the text and cursor position to be sent to the IMF manager.
+ // Whether to retrieve the text and cursor position to be sent to the InputMethodContext.
bool retrieveText = false;
bool retrieveCursor = false;
- switch( imfEvent.eventName )
+ switch( inputMethodContextEvent.eventName )
{
- case ImfManager::COMMIT:
+ case InputMethodContext::COMMIT:
{
- InsertText( imfEvent.predictiveString, Text::Controller::COMMIT );
+ InsertText( inputMethodContextEvent.predictiveString, Text::Controller::COMMIT );
requestRelayout = true;
retrieveCursor = true;
break;
}
- case ImfManager::PREEDIT:
+ case InputMethodContext::PRE_EDIT:
{
- InsertText( imfEvent.predictiveString, Text::Controller::PRE_EDIT );
+ InsertText( inputMethodContextEvent.predictiveString, Text::Controller::PRE_EDIT );
requestRelayout = true;
retrieveCursor = true;
break;
}
- case ImfManager::DELETESURROUNDING:
+ case InputMethodContext::DELETE_SURROUNDING:
{
- const bool textDeleted = RemoveText( imfEvent.cursorOffset,
- imfEvent.numberOfChars,
+ const bool textDeleted = RemoveText( inputMethodContextEvent.cursorOffset,
+ inputMethodContextEvent.numberOfChars,
DONT_UPDATE_INPUT_STYLE );
if( textDeleted )
}
break;
}
- case ImfManager::GETSURROUNDING:
+ case InputMethodContext::GET_SURROUNDING:
{
retrieveText = true;
retrieveCursor = true;
break;
}
- case ImfManager::PRIVATECOMMAND:
+ case InputMethodContext::PRIVATE_COMMAND:
{
// PRIVATECOMMAND event is just for getting the private command message
retrieveText = true;
retrieveCursor = true;
break;
}
- case ImfManager::VOID:
+ case InputMethodContext::VOID:
{
// do nothing
break;
if( retrieveText )
{
- mImpl->GetText( numberOfWhiteSpaces, text );
+ if( !mImpl->IsShowingPlaceholderText() )
+ {
+ // Retrieves the normal text string.
+ mImpl->GetText( numberOfWhiteSpaces, text );
+ }
+ else
+ {
+ // When the current text is Placeholder Text, the surrounding text should be empty string.
+ // It means DALi should send empty string ("") to IME.
+ text = "";
+ }
}
- ImfManager::ImfCallbackData callbackData( ( retrieveText || retrieveCursor ), cursorPosition, text, false );
+ InputMethodContext::CallbackData callbackData( ( retrieveText || retrieveCursor ), cursorPosition, text, false );
if( requestRelayout &&
( NULL != mImpl->mEditableControlInterface ) )
std::string stringToPaste( notifier.GetContent() );
// Commit the current pre-edit text; the contents of the clipboard should be appended
- mImpl->ResetImfManager();
+ mImpl->ResetInputMethodContext();
// Temporary disable hiding clipboard
mImpl->SetClipboardHideEnable( false );
// TODO: At the moment the underline runs are only for pre-edit.
mImpl->mModel->mVisualModel->mUnderlineRuns.Clear();
- // Remove the previous IMF pre-edit.
+ // Remove the previous InputMethodContext pre-edit.
if( mImpl->mEventData->mPreEditFlag && ( 0u != mImpl->mEventData->mPreEditLength ) )
{
removedPrevious = RemoveText( -static_cast<int>( mImpl->mEventData->mPrimaryCursorPosition - mImpl->mEventData->mPreEditStartPosition ),
mImpl->ChangeState( EventData::EDITING );
- // Handle the IMF (predicitive text) state changes
+ // Handle the InputMethodContext (predicitive text) state changes
if( COMMIT == type )
{
- // IMF manager is no longer handling key-events
+ // InputMethodContext is no longer handling key-events
mImpl->ClearPreEditFlag();
}
else // PRE_EDIT
{
DALI_LOG_INFO( gLogFilter, Debug::Verbose, "MaxLengthReached (%d)\n", mImpl->mModel->mLogicalModel->mText.Count() );
- mImpl->ResetImfManager();
+ mImpl->ResetInputMethodContext();
if( NULL != mImpl->mEditableControlInterface )
{
const CharacterIndex lastIndex = startIndex + ( ( requestedNumberOfCharacters > 0u ) ? requestedNumberOfCharacters - 1u : 0u );
const GlyphIndex startGlyphIndex = mImpl->mTextUpdateInfo.mStartGlyphIndex;
+
+ // Make sure the index is not out of bound
+ if ( charactersToGlyph.Count() != glyphsPerCharacter.Count() ||
+ requestedNumberOfCharacters > charactersToGlyph.Count() ||
+ ( lastIndex >= charactersToGlyph.Count() && charactersToGlyph.Count() > 0u ) )
+ {
+ std::string currentText;
+ GetText( currentText );
+
+ DALI_LOG_ERROR( "Controller::DoRelayout: Attempting to access invalid buffer\n" );
+ DALI_LOG_ERROR( "Current text is: %s\n", currentText.c_str() );
+ DALI_LOG_ERROR( "startIndex: %u, lastIndex: %u, requestedNumberOfCharacters: %u, charactersToGlyph.Count = %lu, glyphsPerCharacter.Count = %lu\n", startIndex, lastIndex, requestedNumberOfCharacters, charactersToGlyph.Count(), glyphsPerCharacter.Count());
+
+ return false;
+ }
+
const Length numberOfGlyphs = ( requestedNumberOfCharacters > 0u ) ? *( charactersToGlyphBuffer + lastIndex ) + *( glyphsPerCharacterBuffer + lastIndex ) - startGlyphIndex : 0u;
const Length totalNumberOfGlyphs = mImpl->mModel->mVisualModel->mGlyphs.Count();
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,
glyphsPerCharacterBuffer,
totalNumberOfGlyphs,
mImpl->mModel->mHorizontalAlignment,
- mImpl->mModel->mLineWrapMode );
+ mImpl->mModel->mLineWrapMode,
+ outlineWidth,
+ mImpl->mModel->mIgnoreSpacesAfterText,
+ mImpl->mModel->mMatchSystemLanguageDirection );
// Resize the vector of positions to have the same size than the vector of glyphs.
Vector<Vector2>& glyphPositions = mImpl->mModel->mVisualModel->mGlyphPositions;
layoutParameters.startLineIndex = mImpl->mTextUpdateInfo.mStartLineIndex;
layoutParameters.estimatedNumberOfLines = mImpl->mTextUpdateInfo.mEstimatedNumberOfLines;
+ // Update the ellipsis
+ bool elideTextEnabled = mImpl->mModel->mElideEnabled;
+
+ if( NULL != mImpl->mEventData )
+ {
+ if( mImpl->mEventData->mPlaceholderEllipsisFlag && mImpl->IsShowingPlaceholderText() )
+ {
+ elideTextEnabled = mImpl->mEventData->mIsPlaceholderElideEnabled;
+ }
+ else if( EventData::INACTIVE != mImpl->mEventData->mState )
+ {
+ // Disable ellipsis when editing
+ elideTextEnabled = false;
+ }
+
+ // Reset the scroll position in inactive state
+ if( elideTextEnabled && ( mImpl->mEventData->mState == EventData::INACTIVE ) )
+ {
+ ResetScrollPosition();
+ }
+ }
+
// Update the visual model.
Size newLayoutSize;
viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
glyphPositions,
mImpl->mModel->mVisualModel->mLines,
newLayoutSize,
- mImpl->mModel->mElideEnabled );
+ elideTextEnabled );
viewUpdated = viewUpdated || ( newLayoutSize != layoutSize );
if( NO_OPERATION != ( UPDATE_DIRECTION & operations ) )
{
- mImpl->mAutoScrollDirectionRTL = false;
+ mImpl->mIsTextDirectionRTL = false;
}
// Reorder the lines
const LineRun* const firstline = mImpl->mModel->mVisualModel->mLines.Begin();
if ( firstline )
{
- mImpl->mAutoScrollDirectionRTL = firstline->direction;
+ mImpl->mIsTextDirectionRTL = firstline->direction;
}
}
}
requestedNumberOfCharacters,
mImpl->mModel->mHorizontalAlignment,
lines,
- mImpl->mModel->mAlignmentOffset );
+ mImpl->mModel->mAlignmentOffset,
+ mImpl->mLayoutDirection,
+ mImpl->mModel->mMatchSystemLanguageDirection );
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() );
+ DALI_LOG_INFO( gLogFilter, Debug::Concise, "Controller::DoRelayout [%p] mImpl->mIsTextDirectionRTL[%s] [%s]\n", this, (mImpl->mIsTextDirectionRTL)?"true":"false", currentText.c_str() );
#endif
DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::DoRelayout, view updated %s\n", ( viewUpdated ? "true" : "false" ) );
return viewUpdated;
switch( mImpl->mModel->mVerticalAlignment )
{
- case Layout::VERTICAL_ALIGN_TOP:
+ case VerticalAlignment::TOP:
{
mImpl->mModel->mScrollPosition.y = 0.f;
break;
}
- case Layout::VERTICAL_ALIGN_CENTER:
+ case VerticalAlignment::CENTER:
{
mImpl->mModel->mScrollPosition.y = floorf( 0.5f * ( controlSize.height - layoutSize.height ) ); // try to avoid pixel alignment.
break;
}
- case Layout::VERTICAL_ALIGN_BOTTOM:
+ case VerticalAlignment::BOTTOM:
{
mImpl->mModel->mScrollPosition.y = controlSize.height - layoutSize.height;
break;
{
// When the text is being modified, delay cursor blinking
mImpl->mEventData->mDecorator->DelayCursorBlink();
+
+ // Update selection position after modifying the text
+ mImpl->mEventData->mLeftSelectionPosition = mImpl->mEventData->mPrimaryCursorPosition;
+ mImpl->mEventData->mRightSelectionPosition = mImpl->mEventData->mPrimaryCursorPosition;
}
// Discard temporary text
// The natural size needs to be re-calculated.
mImpl->mRecalculateNaturalSize = true;
+ // The text direction needs to be updated.
+ mImpl->mUpdateTextDirection = true;
+
// Apply modifications to the model
mImpl->mOperationsPending = ALL_OPERATIONS;
}
// The natural size needs to be re-calculated.
mImpl->mRecalculateNaturalSize = true;
+ // The text direction needs to be updated.
+ mImpl->mUpdateTextDirection = true;
+
// Apply modifications to the model; TODO - Optimize this
mImpl->mOperationsPending = ALL_OPERATIONS;
}
// The natural size needs to be re-calculated.
mImpl->mRecalculateNaturalSize = true;
+ // The text direction needs to be updated.
+ mImpl->mUpdateTextDirection = true;
+
// Apply modifications to the model; TODO - Optimize this
mImpl->mOperationsPending = ALL_OPERATIONS;
}
}
}
-bool Controller::BackspaceKeyEvent()
+bool Controller::DeleteEvent( int keyCode )
{
- DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p DALI_KEY_BACKSPACE\n", this );
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p KeyCode : %d \n", this, keyCode );
bool removed = false;
return removed;
}
- // IMF manager is no longer handling key-events
+ // InputMethodContext is no longer handling key-events
mImpl->ClearPreEditFlag();
if( EventData::SELECTING == mImpl->mEventData->mState )
{
removed = RemoveSelectedText();
}
- else if( mImpl->mEventData->mPrimaryCursorPosition > 0 )
+ else if( ( mImpl->mEventData->mPrimaryCursorPosition > 0 ) && ( keyCode == Dali::DALI_KEY_BACKSPACE) )
{
// Remove the character before the current cursor position
removed = RemoveText( -1,
1,
UPDATE_INPUT_STYLE );
}
+ else if( keyCode == Dali::DevelKey::DALI_KEY_DELETE )
+ {
+ // Remove the character after the current cursor position
+ removed = RemoveText( 0,
+ 1,
+ UPDATE_INPUT_STYLE );
+ }
if( removed )
{
// The natural size needs to be re-calculated.
mImpl->mRecalculateNaturalSize = true;
+ // The text direction needs to be updated.
+ mImpl->mUpdateTextDirection = true;
+
// Apply modifications to the model
mImpl->mOperationsPending = ALL_OPERATIONS;
}
// The natural size needs to be re-calculated.
mImpl->mRecalculateNaturalSize = true;
+ // The text direction needs to be updated.
+ mImpl->mUpdateTextDirection = true;
+
// Apply modifications to the model
mImpl->mOperationsPending = ALL_OPERATIONS;
mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
VALIDATE_FONTS |
SHAPE_TEXT |
+ BIDI_INFO |
GET_GLYPH_METRICS |
LAYOUT |
UPDATE_LAYOUT_SIZE |
}
}
+void Controller::SetControlInterface( ControlInterface* controlInterface )
+{
+ mImpl->mControlInterface = controlInterface;
+}
+
+bool Controller::ShouldClearFocusOnEscape() const
+{
+ return mImpl->mShouldClearFocusOnEscape;
+}
+
// private : Private contructors & copy operator.
Controller::Controller()