/*
- * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2017 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/public-api/adaptor-framework/key.h>
#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>
// INTERNAL INCLUDES
#include <dali-toolkit/internal/text/bidirectional-support.h>
}
}
+void Controller::SetPlaceholderText( const std::string& text )
+{
+ if( NULL != mImpl->mEventData )
+ {
+ mImpl->mEventData->mPlaceholderText = text;
+
+ // Update placeholder if there is no text
+ if( mImpl->IsShowingPlaceholderText() ||
+ ( 0u == mImpl->mModel->mLogicalModel->mText.Count() ) )
+ {
+ ShowPlaceholderText();
+ }
+ }
+}
+
+// This is overloading function for PLACEHOLDER_TEXT_FOCUSED in text-field
void Controller::SetPlaceholderText( PlaceholderType type, const std::string& text )
{
if( NULL != mImpl->mEventData )
}
}
+void Controller::GetPlaceholderText( std::string& text ) const
+{
+ if( NULL != mImpl->mEventData )
+ {
+ text = mImpl->mEventData->mPlaceholderText;
+ }
+}
+
+// This is overloading function for PLACEHOLDER_TEXT_FOCUSED in text-field
void Controller::GetPlaceholderText( PlaceholderType type, std::string& text ) const
{
if( NULL != mImpl->mEventData )
return TextAbstraction::FontSlant::NORMAL;
}
-void Controller::SetDefaultPointSize( float pointSize )
+void Controller::SetDefaultFontSize( float fontSize, FontSizeType type )
{
if( NULL == mImpl->mFontDefaults )
{
mImpl->mFontDefaults = new FontDefaults();
}
- mImpl->mFontDefaults->mDefaultPointSize = pointSize;
- mImpl->mFontDefaults->sizeDefined = true;
+ switch( type )
+ {
+ case POINT_SIZE:
+ {
+ mImpl->mFontDefaults->mDefaultPointSize = fontSize;
+ mImpl->mFontDefaults->sizeDefined = true;
+ break;
+ }
+ case PIXEL_SIZE:
+ {
+ // Point size = Pixel size * 72 / DPI
+ unsigned int horizontalDpi = 0u;
+ unsigned int verticalDpi = 0u;
+ TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+ fontClient.GetDpi( horizontalDpi, verticalDpi );
+
+ mImpl->mFontDefaults->mDefaultPointSize = ( fontSize * 72 ) / horizontalDpi;
+ mImpl->mFontDefaults->sizeDefined = true;
+ break;
+ }
+ default:
+ {
+ DALI_ASSERT_ALWAYS( false );
+ }
+ }
// Clear the font-specific data
ClearFontData();
mImpl->RequestRelayout();
}
-float Controller::GetDefaultPointSize() const
+float Controller::GetDefaultFontSize( FontSizeType type ) const
{
+ float value = 0.0f;
if( NULL != mImpl->mFontDefaults )
{
- return mImpl->mFontDefaults->mDefaultPointSize;
+ switch( type )
+ {
+ case POINT_SIZE:
+ {
+ value = mImpl->mFontDefaults->mDefaultPointSize;
+ break;
+ }
+ case PIXEL_SIZE:
+ {
+ // Pixel size = Point size * DPI / 72
+ unsigned int horizontalDpi = 0u;
+ unsigned int verticalDpi = 0u;
+ TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+ fontClient.GetDpi( horizontalDpi, verticalDpi );
+
+ value = mImpl->mFontDefaults->mDefaultPointSize * horizontalDpi / 72;
+ break;
+ }
+ default:
+ {
+ DALI_ASSERT_ALWAYS( false );
+ }
+ }
+ return value;
}
- return 0.0f;
+ return value;
}
void Controller::SetDefaultColor( const Vector4& color )
}
// Return the default font's point size if there is no EventData.
- return GetDefaultPointSize();
+ return GetDefaultFontSize( Text::Controller::POINT_SIZE );
}
void Controller::SetInputLineSpacing( float lineSpacing )
return false;
}
+void Controller::SetNoTextDoubleTapAction( NoTextTap::Action action )
+{
+ if( NULL != mImpl->mEventData )
+ {
+ mImpl->mEventData->mDoubleTapAction = action;
+ }
+}
+
+Controller::NoTextTap::Action Controller::GetNoTextDoubleTapAction() const
+{
+ NoTextTap::Action action = NoTextTap::NO_ACTION;
+
+ if( NULL != mImpl->mEventData )
+ {
+ action = mImpl->mEventData->mDoubleTapAction;
+ }
+
+ return action;
+}
+
+void Controller::SetNoTextLongPressAction( NoTextTap::Action action )
+{
+ if( NULL != mImpl->mEventData )
+ {
+ mImpl->mEventData->mLongPressAction = action;
+ }
+}
+
+Controller::NoTextTap::Action Controller::GetNoTextLongPressAction() const
+{
+ NoTextTap::Action action = NoTextTap::NO_ACTION;
+
+ if( NULL != mImpl->mEventData )
+ {
+ action = mImpl->mEventData->mLongPressAction;
+ }
+
+ return action;
+}
+
+bool Controller::IsUnderlineSetByString()
+{
+ return mImpl->mUnderlineSetByString;
+}
+
+void Controller::UnderlineSetByString( bool setByString )
+{
+ mImpl->mUnderlineSetByString = setByString;
+}
+
+bool Controller::IsShadowSetByString()
+{
+ return mImpl->mShadowSetByString;
+}
+
+void Controller::ShadowSetByString( bool setByString )
+{
+ mImpl->mShadowSetByString = setByString;
+}
+
+bool Controller::IsFontStyleSetByString()
+{
+ return mImpl->mFontStyleSetByString;
+}
+
+void Controller::FontStyleSetByString( bool setByString )
+{
+ mImpl->mFontStyleSetByString = setByString;
+}
+
// public : Queries & retrieves.
Layout::Engine& Controller::GetLayoutEngine()
ProcessModifyEvents();
Size layoutSize;
- if( fabsf( width - mImpl->mModel->mVisualModel->mControlSize.width ) > Math::MACHINE_EPSILON_1000 )
+ if( fabsf( width - mImpl->mModel->mVisualModel->mControlSize.width ) > Math::MACHINE_EPSILON_1000 ||
+ mImpl->mTextUpdateInfo.mFullRelayoutNeeded )
{
// Operations that can be done only once until the text changes.
const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
return layoutSize.height;
}
+int Controller::GetLineCount( float width )
+{
+ GetHeightForWidth( width );
+ int numberofLines = mImpl->mModel->GetNumberOfLines();
+ return numberofLines;
+}
+
const ModelInterface* const Controller::GetTextModel() const
{
return mImpl->mModel.Get();
return scrollAmount;
}
+bool Controller::GetTextScrollInfo( float& scrollPosition, float& controlHeight, float& layoutHeight )
+{
+ const Vector2& layout = mImpl->mModel->mVisualModel->GetLayoutSize();
+ bool isScrolled;
+
+ controlHeight = mImpl->mModel->mVisualModel->mControlSize.height;
+ layoutHeight = layout.height;
+ scrollPosition = mImpl->mModel->mScrollPosition.y;
+ isScrolled = !Equals( mImpl->mModel->mScrollPosition.y, mImpl->mModel->mScrollPositionLast.y, Math::MACHINE_EPSILON_1 );
+ return isScrolled;
+}
+
+void Controller::SetHiddenInputOption(const Property::Map& options )
+{
+ if( NULL == mImpl->mHiddenInput )
+ {
+ mImpl->mHiddenInput = new HiddenText( this );
+ }
+ mImpl->mHiddenInput->SetProperties(options);
+}
+
+void Controller::GetHiddenInputOption(Property::Map& options )
+{
+ if( NULL != mImpl->mHiddenInput )
+ {
+ mImpl->mHiddenInput->GetProperties(options);
+ }
+}
+
// public : Relayout.
Controller::UpdateTextType Controller::Relayout( const Size& size )
{
DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyEvent" );
- bool textChanged( false );
+ bool textChanged = false;
+ bool relayoutNeeded = false;
if( ( NULL != mImpl->mEventData ) &&
( keyEvent.state == KeyEvent::Down ) )
int keyCode = keyEvent.keyCode;
const std::string& keyString = keyEvent.keyPressed;
+ const bool isNullKey = ( 0 == keyCode ) && ( keyString.empty() );
+
// Pre-process to separate modifying events from non-modifying input events.
- if( Dali::DALI_KEY_ESCAPE == keyCode )
+ if( isNullKey )
+ {
+ // In some platforms arrive key events with no key code.
+ // Do nothing.
+ }
+ else if( Dali::DALI_KEY_ESCAPE == keyCode )
{
// Escape key is a special case which causes focus loss
KeyboardFocusLostEvent();
+
+ // Will request for relayout.
+ relayoutNeeded = true;
}
else if( ( Dali::DALI_KEY_CURSOR_LEFT == keyCode ) ||
( Dali::DALI_KEY_CURSOR_RIGHT == keyCode ) ||
( Dali::DALI_KEY_CURSOR_UP == keyCode ) ||
( Dali::DALI_KEY_CURSOR_DOWN == keyCode ) )
{
- mImpl->mEventData->mCheckScrollAmount = true;
+ // If don't have any text, do nothing.
+ if( !mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters )
+ {
+ return false;
+ }
+
+ uint32_t cursorPosition = mImpl->mEventData->mPrimaryCursorPosition;
+ uint32_t numberOfCharacters = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
+ uint32_t cursorLine = mImpl->mModel->mVisualModel->GetLineOfCharacter( cursorPosition );
+ 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) ||
+ ( 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 ) )
+ {
+ return false;
+ }
+ mImpl->mEventData->mCheckScrollAmount = true;
Event event( Event::CURSOR_KEY_EVENT );
event.p1.mInt = keyCode;
mImpl->mEventData->mEventQueue.push_back( event );
+
+ // Will request for relayout.
+ relayoutNeeded = true;
}
else if( Dali::DALI_KEY_BACKSPACE == keyCode )
{
textChanged = BackspaceKeyEvent();
+
+ // Will request for relayout.
+ relayoutNeeded = true;
}
else if( IsKey( keyEvent, Dali::DALI_KEY_POWER ) ||
IsKey( keyEvent, Dali::DALI_KEY_MENU ) ||
// Power key/Menu/Home key behaviour does not allow edit mode to resume.
mImpl->ChangeState( EventData::INACTIVE );
+ // Will request for relayout.
+ relayoutNeeded = true;
+
// This branch avoids calling the InsertText() method of the 'else' branch which can delete selected text.
}
else if( Dali::DALI_KEY_SHIFT_LEFT == keyCode )
// Do nothing.
}
+ 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.
+ }
else
{
DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::KeyEvent %p keyString %s\n", this, keyString.c_str() );
InsertText( keyString, COMMIT );
textChanged = true;
+
+ // Will request for relayout.
+ relayoutNeeded = true;
}
if ( ( mImpl->mEventData->mState != EventData::INTERRUPTED ) &&
( mImpl->mEventData->mState != EventData::INACTIVE ) &&
- ( Dali::DALI_KEY_SHIFT_LEFT != keyCode ) )
+ ( !isNullKey ) &&
+ ( Dali::DALI_KEY_SHIFT_LEFT != keyCode ) &&
+ ( 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.
// Otherwise, when the state is SELECTING the text controller can't send the right
// surrounding info to the imf.
mImpl->ChangeState( EventData::EDITING );
+
+ // Will request for relayout.
+ relayoutNeeded = true;
}
- mImpl->RequestRelayout();
+ if( relayoutNeeded )
+ {
+ mImpl->RequestRelayout();
+ }
}
if( textChanged &&
if( mImpl->mEventData->mSelectionEnabled &&
mImpl->IsShowingRealText() )
{
- SelectEvent( x, y, false );
+ relayoutNeeded = true;
+ mImpl->mEventData->mIsLeftHandleSelected = true;
+ mImpl->mEventData->mIsRightHandleSelected = true;
}
}
+
// Handles & cursors must be repositioned after Relayout() i.e. after the Model has been updated
if( relayoutNeeded )
{
if( ( state == Gesture::Started ) &&
( NULL != mImpl->mEventData ) )
{
- if( !mImpl->IsShowingRealText() )
+ // The 1st long-press on inactive text-field is treated as tap
+ if( EventData::INACTIVE == mImpl->mEventData->mState )
+ {
+ mImpl->ChangeState( EventData::EDITING );
+
+ Event event( Event::TAP_EVENT );
+ event.p1.mUint = 1;
+ event.p2.mFloat = x;
+ event.p3.mFloat = y;
+ mImpl->mEventData->mEventQueue.push_back( event );
+
+ mImpl->RequestRelayout();
+ }
+ else if( !mImpl->IsShowingRealText() )
{
Event event( Event::LONG_PRESS_EVENT );
event.p1.mInt = state;
+ event.p2.mFloat = x;
+ event.p3.mFloat = y;
mImpl->mEventData->mEventQueue.push_back( event );
mImpl->RequestRelayout();
}
- else
+ else if( !mImpl->IsClipboardVisible() )
{
- // The 1st long-press on inactive text-field is treated as tap
- if( EventData::INACTIVE == mImpl->mEventData->mState )
- {
- mImpl->ChangeState( EventData::EDITING );
+ // Reset the imf manager to commit the pre-edit before selecting the text.
+ mImpl->ResetImfManager();
- Event event( Event::TAP_EVENT );
- event.p1.mUint = 1;
- event.p2.mFloat = x;
- event.p3.mFloat = y;
- mImpl->mEventData->mEventQueue.push_back( event );
-
- mImpl->RequestRelayout();
- }
- else if( !mImpl->IsClipboardVisible() )
- {
- // Reset the imf manger to commit the pre-edit before selecting the text.
- mImpl->ResetImfManager();
+ Event event( Event::LONG_PRESS_EVENT );
+ event.p1.mInt = state;
+ event.p2.mFloat = x;
+ event.p3.mFloat = y;
+ mImpl->mEventData->mEventQueue.push_back( event );
+ mImpl->RequestRelayout();
- SelectEvent( x, y, false );
- }
+ mImpl->mEventData->mIsLeftHandleSelected = true;
+ mImpl->mEventData->mIsRightHandleSelected = true;
}
}
}
retrieveCursor = true;
break;
}
+ case ImfManager::PRIVATECOMMAND:
+ {
+ // PRIVATECOMMAND event is just for getting the private command message
+ retrieveText = true;
+ retrieveCursor = true;
+ break;
+ }
case ImfManager::VOID:
{
// do nothing
}
}
+void Controller::DisplayTimeExpired()
+{
+ mImpl->mEventData->mUpdateCursorPosition = true;
+ // Apply modifications to the model
+ mImpl->mOperationsPending = ALL_OPERATIONS;
+
+ mImpl->RequestRelayout();
+}
+
// private : Update.
void Controller::InsertText( const std::string& text, Controller::InsertType type )
{
- bool removedPrevious( false );
- bool maxLengthReached( false );
+ bool removedPrevious = false;
+ bool removedSelected = false;
+ bool maxLengthReached = false;
DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected InsertText" )
// TODO: At the moment the underline runs are only for pre-edit.
mImpl->mModel->mVisualModel->mUnderlineRuns.Clear();
- // Keep the current number of characters.
- const Length currentNumberOfCharacters = mImpl->IsShowingRealText() ? mImpl->mModel->mLogicalModel->mText.Count() : 0u;
-
// Remove the previous IMF pre-edit.
if( mImpl->mEventData->mPreEditFlag && ( 0u != mImpl->mEventData->mPreEditLength ) )
{
else
{
// Remove the previous Selection.
- removedPrevious = RemoveSelectedText();
+ removedSelected = RemoveSelectedText();
+
}
Vector<Character> utf32Characters;
DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Inserted %d characters, new size %d new cursor %d\n", maxSizeOfNewText, mImpl->mModel->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition );
}
- const Length numberOfCharacters = mImpl->IsShowingRealText() ? mImpl->mModel->mLogicalModel->mText.Count() : 0u;
-
if( ( 0u == mImpl->mModel->mLogicalModel->mText.Count() ) &&
mImpl->IsPlaceholderAvailable() )
{
mImpl->ClearPreEditFlag();
}
else if( removedPrevious ||
+ removedSelected ||
( 0 != utf32Characters.Count() ) )
{
// Queue an inserted event
mImpl->QueueModifyEvent( ModifyEvent::TEXT_INSERTED );
mImpl->mEventData->mUpdateCursorPosition = true;
- if( numberOfCharacters < currentNumberOfCharacters )
+ if( removedSelected )
{
mImpl->mEventData->mScrollAfterDelete = true;
}
Vector<Character>& currentText = mImpl->mModel->mLogicalModel->mText;
CharacterIndex& oldCursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
- CharacterIndex cursorIndex = oldCursorIndex;
+ CharacterIndex cursorIndex = 0;
// Validate the cursor position & number of characters
- if( static_cast< CharacterIndex >( std::abs( cursorOffset ) ) <= cursorIndex )
+ if( ( static_cast< int >( mImpl->mEventData->mPrimaryCursorPosition ) + cursorOffset ) >= 0 )
{
- cursorIndex = oldCursorIndex + cursorOffset;
+ cursorIndex = mImpl->mEventData->mPrimaryCursorPosition + cursorOffset;
}
if( ( cursorIndex + numberOfCharacters ) > currentText.Count() )
}
mImpl->mEventData->mCheckScrollAmount = true;
+ mImpl->mEventData->mIsLeftHandleSelected = true;
+ mImpl->mEventData->mIsRightHandleSelected = true;
mImpl->RequestRelayout();
}
}
const char* text( NULL );
size_t size( 0 );
- // TODO - Switch placeholder text styles when changing state
- if( ( EventData::INACTIVE != mImpl->mEventData->mState ) &&
- ( 0u != mImpl->mEventData->mPlaceholderTextActive.c_str() ) )
+ if( !mImpl->mEventData->mPlaceholderTextActive.empty() || !mImpl->mEventData->mPlaceholderTextInactive.empty() )
{
- text = mImpl->mEventData->mPlaceholderTextActive.c_str();
- size = mImpl->mEventData->mPlaceholderTextActive.size();
+ if( ( EventData::INACTIVE != mImpl->mEventData->mState ) &&
+ ( 0u != mImpl->mEventData->mPlaceholderTextActive.c_str() ) )
+ {
+ text = mImpl->mEventData->mPlaceholderTextActive.c_str();
+ size = mImpl->mEventData->mPlaceholderTextActive.size();
+ }
+ else
+ {
+ text = mImpl->mEventData->mPlaceholderTextInactive.c_str();
+ size = mImpl->mEventData->mPlaceholderTextInactive.size();
+ }
}
else
{
- text = mImpl->mEventData->mPlaceholderTextInactive.c_str();
- size = mImpl->mEventData->mPlaceholderTextInactive.size();
+ if( 0u != mImpl->mEventData->mPlaceholderText.c_str() )
+ {
+ text = mImpl->mEventData->mPlaceholderText.c_str();
+ size = mImpl->mEventData->mPlaceholderText.size();
+ }
}
mImpl->mTextUpdateInfo.mCharacterIndex = 0u;