- // 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;
- }
-
- if( mImpl->mModel->mMatchSystemLanguageDirection && mImpl->mLayoutDirection != layoutDirection )
- {
- // Clear the update info. This info will be set the next time the text is updated.
- mImpl->mTextUpdateInfo.mClearAll = true;
- // Apply modifications to the model
- // Shape the text again is needed because characters like '()[]{}' have to be mirrored and the glyphs generated again.
- mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
- GET_GLYPH_METRICS |
- SHAPE_TEXT |
- UPDATE_DIRECTION |
- LAYOUT |
- BIDI_INFO |
- REORDER );
- mImpl->mLayoutDirection = layoutDirection;
- }
-
- // Make sure the model is up-to-date before layouting.
- ProcessModifyEvents();
- bool updated = mImpl->UpdateModel( mImpl->mOperationsPending );
-
- // Layout the text.
- Size layoutSize;
- updated = DoRelayout( size,
- mImpl->mOperationsPending,
- layoutSize ) || updated;
-
-
- if( updated )
- {
- updateTextType = MODEL_UPDATED;
- }
-
- // Do not re-do any operation until something changes.
- mImpl->mOperationsPending = NO_OPERATION;
- mImpl->mModel->mScrollPositionLast = mImpl->mModel->mScrollPosition;
-
- // Whether the text control is editable
- const bool isEditable = NULL != mImpl->mEventData;
-
- // 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->mModel->mScrollPosition;
- }
-
- 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 )
- {
- if( newSize )
- {
- // If there is a new size, the scroll position needs to be clamped.
- mImpl->ClampHorizontalScroll( layoutSize );
-
- // Update the decorator's positions is needed if there is a new size.
- mImpl->mEventData->mDecorator->UpdatePositions( mImpl->mModel->mScrollPosition - offset );
- }
-
- // Move the cursor, grab handle etc.
- if( mImpl->ProcessInputEvents() )
- {
- updateTextType = static_cast<UpdateTextType>( updateTextType | DECORATOR_UPDATED );
- }
- }
-
- // Clear the update info. This info will be set the next time the text is updated.
- mImpl->mTextUpdateInfo.Clear();
- DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::Relayout\n" );
-
- return updateTextType;
-}
-
-void Controller::RequestRelayout()
-{
- mImpl->RequestRelayout();
-}
-
-// public : Input style change signals.
-
-bool Controller::IsInputStyleChangedSignalsQueueEmpty()
-{
- return ( NULL == mImpl->mEventData ) || ( 0u == mImpl->mEventData->mInputStyleChangedQueue.Count() );
-}
-
-void Controller::ProcessInputStyleChangedSignals()
-{
- if( NULL == mImpl->mEventData )
- {
- // Nothing to do.
- return;
- }
-
- for( Vector<InputStyle::Mask>::ConstIterator it = mImpl->mEventData->mInputStyleChangedQueue.Begin(),
- endIt = mImpl->mEventData->mInputStyleChangedQueue.End();
- it != endIt;
- ++it )
- {
- const InputStyle::Mask mask = *it;
-
- if( NULL != mImpl->mEditableControlInterface )
- {
- // Emit the input style changed signal.
- mImpl->mEditableControlInterface->InputStyleChanged( mask );
- }
- }
-
- mImpl->mEventData->mInputStyleChangedQueue.Clear();
-}
-
-// public : Text-input Event Queuing.
-
-void Controller::KeyboardFocusGainEvent()
-{
- DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusGainEvent" );
-
- if( NULL != mImpl->mEventData )
- {
- if( ( EventData::INACTIVE == mImpl->mEventData->mState ) ||
- ( EventData::INTERRUPTED == mImpl->mEventData->mState ) )
- {
- mImpl->ChangeState( EventData::EDITING );
- mImpl->mEventData->mUpdateCursorPosition = true; //If editing started without tap event, cursor update must be triggered.
- mImpl->mEventData->mUpdateInputStyle = true;
- mImpl->mEventData->mScrollAfterUpdatePosition = true;
- }
- mImpl->NotifyInputMethodContextMultiLineStatus();
- if( mImpl->IsShowingPlaceholderText() )
- {
- // Show alternative placeholder-text when editing
- ShowPlaceholderText();
- }
-
- mImpl->RequestRelayout();
- }
-}
-
-void Controller::KeyboardFocusLostEvent()
-{
- DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyboardFocusLostEvent" );
-
- if( NULL != mImpl->mEventData )
- {
- if( EventData::INTERRUPTED != mImpl->mEventData->mState )
- {
- mImpl->ChangeState( EventData::INACTIVE );
-
- if( !mImpl->IsShowingRealText() )
- {
- // Revert to regular placeholder-text when not editing
- ShowPlaceholderText();
- }
- }
- }
- mImpl->RequestRelayout();
-}
-
-bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
-{
- DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected KeyEvent" );
-
- 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 std::string keyName = keyEvent.keyPressedName;
-
- const bool isNullKey = ( 0 == keyCode ) && ( keyString.empty() );
-
- // Pre-process to separate modifying events from non-modifying input events.
- if( isNullKey )
- {
- // In some platforms arrive key events with no key code.
- // Do nothing.
- return false;
- }
- else if( Dali::DALI_KEY_ESCAPE == keyCode || Dali::DALI_KEY_BACK == keyCode || Dali::DALI_KEY_SEARCH == keyCode )
- {
- // Do nothing
- return false;
- }
- 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 ) )
- {
- // 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 && !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::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 = DeleteEvent( keyCode );
-
- // Will request for relayout.
- relayoutNeeded = true;
- }
- else if( IsKey( keyEvent, Dali::DALI_KEY_POWER ) ||
- IsKey( keyEvent, Dali::DALI_KEY_MENU ) ||
- IsKey( keyEvent, Dali::DALI_KEY_HOME ) )
- {
- // 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 ) || ( Dali::DALI_KEY_SHIFT_RIGHT == keyCode ) )
- {
- // 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.
- 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() );
-
- if( !keyString.empty() )
- {
- // InputMethodContext is no longer handling key-events
- mImpl->ClearPreEditFlag();
-
- InsertText( keyString, COMMIT );
-
- textChanged = 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 ) )
- {
- // 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 InputMethodContext.
- mImpl->ChangeState( EventData::EDITING );
-
- // Will request for relayout.
- relayoutNeeded = true;
- }
-
- if( relayoutNeeded )
- {
- mImpl->RequestRelayout();
- }
- }
-
- if( textChanged &&
- ( NULL != mImpl->mEditableControlInterface ) )
- {
- // Do this last since it provides callbacks into application code
- mImpl->mEditableControlInterface->TextChanged();
- }
-
- return true;
-}
-
-void Controller::TapEvent( unsigned int tapCount, float x, float y )
-{
- DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected TapEvent" );
-
- if( NULL != mImpl->mEventData )
- {
- DALI_LOG_INFO( gLogFilter, Debug::Concise, "TapEvent state:%d \n", mImpl->mEventData->mState );
- EventData::State state( mImpl->mEventData->mState );
- bool relayoutNeeded( false ); // to avoid unnecessary relayouts when tapping an empty text-field
-
- if( mImpl->IsClipboardVisible() )
- {
- if( EventData::INACTIVE == state || EventData::EDITING == state)
- {
- mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE );
- }
- relayoutNeeded = true;
- }
- else if( 1u == tapCount )
- {
- if( EventData::EDITING_WITH_POPUP == state || EventData::EDITING_WITH_PASTE_POPUP == state )
- {
- 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 != state ) )
- {
- mImpl->ChangeState( EventData::EDITING_WITH_GRAB_HANDLE );
- relayoutNeeded = true;
- }
- else
- {
- if( mImpl->IsShowingPlaceholderText() && !mImpl->IsFocusedPlaceholderAvailable() )
- {
- // Hide placeholder text
- ResetText();
- }
-
- if( EventData::INACTIVE == state )
- {
- mImpl->ChangeState( EventData::EDITING );
- }
- else if( !mImpl->IsClipboardEmpty() )
- {
- mImpl->ChangeState( EventData::EDITING_WITH_POPUP );
- }
- relayoutNeeded = true;
- }
- }
- else if( 2u == tapCount )
- {
- if( mImpl->mEventData->mSelectionEnabled &&
- mImpl->IsShowingRealText() )
- {
- 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 )
- {
- Event event( Event::TAP_EVENT );
- event.p1.mUint = tapCount;
- event.p2.mFloat = x;
- event.p3.mFloat = y;
- mImpl->mEventData->mEventQueue.push_back( event );
-
- mImpl->RequestRelayout();
- }
- }
-
- // Reset keyboard as tap event has occurred.
- mImpl->ResetInputMethodContext();
-}
-
-void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
-{
- DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected PanEvent" );
-
- if( NULL != mImpl->mEventData )
- {
- Event event( Event::PAN_EVENT );
- event.p1.mInt = state;
- event.p2.mFloat = displacement.x;
- event.p3.mFloat = displacement.y;
- mImpl->mEventData->mEventQueue.push_back( event );
-
- mImpl->RequestRelayout();
- }
-}
-
-void Controller::LongPressEvent( Gesture::State state, float x, float y )
-{
- DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected LongPressEvent" );
-
- if( ( state == Gesture::Started ) &&
- ( NULL != mImpl->mEventData ) )
- {
- // 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 if( !mImpl->IsClipboardVisible() )
- {
- // Reset the InputMethodContext to commit the pre-edit before selecting the text.
- mImpl->ResetInputMethodContext();
-
- 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();
-
- mImpl->mEventData->mIsLeftHandleSelected = true;
- mImpl->mEventData->mIsRightHandleSelected = true;
- }
- }
-}
-
-void Controller::SelectEvent( float x, float y, SelectionType selectType )
-{
- DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::SelectEvent\n" );
-
- if( NULL != mImpl->mEventData )
- {
- if( selectType == SelectionType::ALL )
- {
- Event event( Event::SELECT_ALL );
- mImpl->mEventData->mEventQueue.push_back( event );
- }
- else if( selectType == SelectionType::NONE )
- {
- Event event( Event::SELECT_NONE );
- 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.
- bool requestRelayout = false;
-
- // Whether to retrieve the text and cursor position to be sent to the InputMethodContext.
- bool retrieveText = false;
- bool retrieveCursor = false;
-
- switch( inputMethodContextEvent.eventName )
- {
- case InputMethodContext::COMMIT:
- {
- InsertText( inputMethodContextEvent.predictiveString, Text::Controller::COMMIT );
- requestRelayout = true;
- retrieveCursor = true;
- break;
- }
- case InputMethodContext::PRE_EDIT:
- {
- InsertText( inputMethodContextEvent.predictiveString, Text::Controller::PRE_EDIT );
- requestRelayout = true;
- retrieveCursor = true;
- break;
- }
- case InputMethodContext::DELETE_SURROUNDING:
- {
- const bool textDeleted = RemoveText( inputMethodContextEvent.cursorOffset,
- inputMethodContextEvent.numberOfChars,
- DONT_UPDATE_INPUT_STYLE );
-
- if( textDeleted )
- {
- if( ( 0u != mImpl->mModel->mLogicalModel->mText.Count() ) ||
- !mImpl->IsPlaceholderAvailable() )
- {
- mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
- }
- else
- {
- ShowPlaceholderText();
- }
- mImpl->mEventData->mUpdateCursorPosition = true;
- mImpl->mEventData->mScrollAfterDelete = true;
-
- requestRelayout = true;
- }
- break;
- }
- case InputMethodContext::GET_SURROUNDING:
- {
- retrieveText = true;
- retrieveCursor = true;
- break;
- }
- case InputMethodContext::PRIVATE_COMMAND:
- {
- // PRIVATECOMMAND event is just for getting the private command message
- retrieveText = true;
- retrieveCursor = true;
- break;
- }
- case InputMethodContext::VOID:
- {
- // do nothing
- break;
- }
- } // end switch
-
- if( requestRelayout )
- {
- mImpl->mOperationsPending = ALL_OPERATIONS;
- mImpl->RequestRelayout();
- }
-
- std::string text;
- CharacterIndex cursorPosition = 0u;
- Length numberOfWhiteSpaces = 0u;
-
- if( retrieveCursor )
- {
- numberOfWhiteSpaces = mImpl->GetNumberOfWhiteSpaces( 0u );
-
- cursorPosition = mImpl->GetLogicalCursorPosition();
-
- if( cursorPosition < numberOfWhiteSpaces )
- {
- cursorPosition = 0u;
- }
- else
- {
- cursorPosition -= numberOfWhiteSpaces;
- }
- }
-
- if( retrieveText )
- {
- 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 = "";
- }
- }
-
- InputMethodContext::CallbackData callbackData( ( retrieveText || retrieveCursor ), cursorPosition, text, false );
-
- if( requestRelayout &&
- ( NULL != mImpl->mEditableControlInterface ) )
- {
- // Do this last since it provides callbacks into application code
- mImpl->mEditableControlInterface->TextChanged();
- }
-
- return callbackData;
-}
-
-void Controller::PasteClipboardItemEvent()
-{
- // Retrieve the clipboard contents first
- ClipboardEventNotifier notifier( ClipboardEventNotifier::Get() );
- std::string stringToPaste( notifier.GetContent() );
-
- // Commit the current pre-edit text; the contents of the clipboard should be appended
- mImpl->ResetInputMethodContext();
-
- // Temporary disable hiding clipboard
- mImpl->SetClipboardHideEnable( false );
-
- // Paste
- PasteText( stringToPaste );
-
- mImpl->SetClipboardHideEnable( true );
-}
-
-// protected : Inherit from Text::Decorator::ControllerInterface.
-
-void Controller::GetTargetSize( Vector2& targetSize )
-{
- targetSize = mImpl->mModel->mVisualModel->mControlSize;
-}
-
-void Controller::AddDecoration( Actor& actor, bool needsClipping )
-{
- if( NULL != mImpl->mEditableControlInterface )
- {
- mImpl->mEditableControlInterface->AddDecoration( actor, needsClipping );
- }
-}
-
-void Controller::DecorationEvent( HandleType handleType, HandleState state, float x, float y )
-{
- DALI_ASSERT_DEBUG( mImpl->mEventData && "Unexpected DecorationEvent" );
-
- if( NULL != mImpl->mEventData )
- {
- switch( handleType )
- {
- case GRAB_HANDLE:
- {
- Event event( Event::GRAB_HANDLE_EVENT );
- event.p1.mUint = state;
- event.p2.mFloat = x;
- event.p3.mFloat = y;
-
- mImpl->mEventData->mEventQueue.push_back( event );
- break;
- }
- case LEFT_SELECTION_HANDLE:
- {
- Event event( Event::LEFT_SELECTION_HANDLE_EVENT );
- event.p1.mUint = state;
- event.p2.mFloat = x;
- event.p3.mFloat = y;
-
- mImpl->mEventData->mEventQueue.push_back( event );
- break;
- }
- case RIGHT_SELECTION_HANDLE:
- {
- Event event( Event::RIGHT_SELECTION_HANDLE_EVENT );
- event.p1.mUint = state;
- event.p2.mFloat = x;
- event.p3.mFloat = y;
-
- mImpl->mEventData->mEventQueue.push_back( event );
- break;
- }
- case LEFT_SELECTION_HANDLE_MARKER:
- case RIGHT_SELECTION_HANDLE_MARKER:
- {
- // Markers do not move the handles.
- break;
- }
- case HANDLE_TYPE_COUNT:
- {
- DALI_ASSERT_DEBUG( !"Controller::HandleEvent. Unexpected handle type" );
- }
- }
-
- mImpl->RequestRelayout();
- }
-}
-
-// protected : Inherit from TextSelectionPopup::TextPopupButtonCallbackInterface.
-
-void Controller::TextPopupButtonTouched( Dali::Toolkit::TextSelectionPopup::Buttons button )
-{
- if( NULL == mImpl->mEventData )
- {
- return;
- }
-
- switch( button )
- {
- case Toolkit::TextSelectionPopup::CUT:
- {
- mImpl->SendSelectionToClipboard( true ); // Synchronous call to modify text
- mImpl->mOperationsPending = ALL_OPERATIONS;
-
- if( ( 0u != mImpl->mModel->mLogicalModel->mText.Count() ) ||
- !mImpl->IsPlaceholderAvailable() )
- {
- mImpl->QueueModifyEvent( ModifyEvent::TEXT_DELETED );
- }
- else
- {
- ShowPlaceholderText();
- }
-
- mImpl->mEventData->mUpdateCursorPosition = true;
- mImpl->mEventData->mScrollAfterDelete = true;
-
- mImpl->RequestRelayout();
-
- if( NULL != mImpl->mEditableControlInterface )
- {
- mImpl->mEditableControlInterface->TextChanged();
- }
- break;
- }
- case Toolkit::TextSelectionPopup::COPY:
- {
- mImpl->SendSelectionToClipboard( false ); // Text not modified
-
- mImpl->mEventData->mUpdateCursorPosition = true;
-
- mImpl->RequestRelayout(); // Cursor, Handles, Selection Highlight, Popup
- break;
- }
- case Toolkit::TextSelectionPopup::PASTE:
- {
- mImpl->RequestGetTextFromClipboard(); // Request clipboard service to retrieve an item
- break;
- }
- case Toolkit::TextSelectionPopup::SELECT:
- {
- const Vector2& currentCursorPosition = mImpl->mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
-
- if( mImpl->mEventData->mSelectionEnabled )
- {
- // Creates a SELECT event.
- SelectEvent( currentCursorPosition.x, currentCursorPosition.y, SelectionType::INTERACTIVE );
- }
- break;
- }
- case Toolkit::TextSelectionPopup::SELECT_ALL:
- {
- // Creates a SELECT_ALL event
- SelectEvent( 0.f, 0.f, SelectionType::ALL );
- break;
- }
- case Toolkit::TextSelectionPopup::CLIPBOARD:
- {
- mImpl->ShowClipboard();
- break;
- }
- case Toolkit::TextSelectionPopup::NONE:
- {
- // Nothing to do.
- break;
- }
- }
-}
-
-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 removedSelected = false;
- bool maxLengthReached = false;
-
- DALI_ASSERT_DEBUG( NULL != mImpl->mEventData && "Unexpected InsertText" )
-
- if( NULL == mImpl->mEventData )
- {
- return;
- }
-
- DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Controller::InsertText %p %s (%s) mPrimaryCursorPosition %d mPreEditFlag %d mPreEditStartPosition %d mPreEditLength %d\n",
- this, text.c_str(), (COMMIT == type ? "COMMIT" : "PRE_EDIT"),
- mImpl->mEventData->mPrimaryCursorPosition, mImpl->mEventData->mPreEditFlag, mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
-
- // TODO: At the moment the underline runs are only for pre-edit.
- mImpl->mModel->mVisualModel->mUnderlineRuns.Clear();
-
- // 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->mEventData->mPreEditLength,
- DONT_UPDATE_INPUT_STYLE );
-
- mImpl->mEventData->mPrimaryCursorPosition = mImpl->mEventData->mPreEditStartPosition;
- mImpl->mEventData->mPreEditLength = 0u;
- }
- else
- {
- // Remove the previous Selection.
- removedSelected = RemoveSelectedText();
-
- }
-
- Vector<Character> utf32Characters;
- Length characterCount = 0u;
-
- if( !text.empty() )
- {
- // Convert text into UTF-32
- utf32Characters.Resize( text.size() );
-
- // This is a bit horrible but std::string returns a (signed) char*
- const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
-
- // Transform a text array encoded in utf8 into an array encoded in utf32.
- // It returns the actual number of characters.
- characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
- utf32Characters.Resize( characterCount );
-
- DALI_ASSERT_DEBUG( text.size() >= utf32Characters.Count() && "Invalid UTF32 conversion length" );
- DALI_LOG_INFO( gLogFilter, Debug::Verbose, "UTF8 size %d, UTF32 size %d\n", text.size(), utf32Characters.Count() );
- }
-
- if( 0u != utf32Characters.Count() ) // Check if Utf8ToUtf32 conversion succeeded
- {
- // The placeholder text is no longer needed
- if( mImpl->IsShowingPlaceholderText() )
- {
- ResetText();
- }
-
- mImpl->ChangeState( EventData::EDITING );
-
- // Handle the InputMethodContext (predicitive text) state changes
- if( COMMIT == type )
- {
- // InputMethodContext is no longer handling key-events
- mImpl->ClearPreEditFlag();
- }
- else // PRE_EDIT
- {
- if( !mImpl->mEventData->mPreEditFlag )
- {
- DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Entered PreEdit state\n" );
-
- // Record the start of the pre-edit text
- mImpl->mEventData->mPreEditStartPosition = mImpl->mEventData->mPrimaryCursorPosition;
- }
-
- mImpl->mEventData->mPreEditLength = utf32Characters.Count();
- mImpl->mEventData->mPreEditFlag = true;
-
- DALI_LOG_INFO( gLogFilter, Debug::Verbose, "mPreEditStartPosition %d mPreEditLength %d\n", mImpl->mEventData->mPreEditStartPosition, mImpl->mEventData->mPreEditLength );
- }
-
- const Length numberOfCharactersInModel = mImpl->mModel->mLogicalModel->mText.Count();
-
- // Restrict new text to fit within Maximum characters setting.
- Length maxSizeOfNewText = std::min( ( mImpl->mMaximumNumberOfCharacters - numberOfCharactersInModel ), characterCount );
- maxLengthReached = ( characterCount > maxSizeOfNewText );
-
- // The cursor position.
- CharacterIndex& cursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
-
- // Update the text's style.
-
- // Updates the text style runs by adding characters.
- mImpl->mModel->mLogicalModel->UpdateTextStyleRuns( cursorIndex, maxSizeOfNewText );
-
- // Get the character index from the cursor index.
- const CharacterIndex styleIndex = ( cursorIndex > 0u ) ? cursorIndex - 1u : 0u;
-
- // Retrieve the text's style for the given index.
- InputStyle style;
- mImpl->RetrieveDefaultInputStyle( style );
- mImpl->mModel->mLogicalModel->RetrieveStyle( styleIndex, style );
-
- // Whether to add a new text color run.
- 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 ) && 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 )
- {
- const VectorBase::SizeType numberOfRuns = mImpl->mModel->mLogicalModel->mColorRuns.Count();
- mImpl->mModel->mLogicalModel->mColorRuns.Resize( numberOfRuns + 1u );
-
- ColorRun& colorRun = *( mImpl->mModel->mLogicalModel->mColorRuns.Begin() + numberOfRuns );
- colorRun.color = mImpl->mEventData->mInputStyle.textColor;
- colorRun.characterRun.characterIndex = cursorIndex;
- colorRun.characterRun.numberOfCharacters = maxSizeOfNewText;
- }
-
- if( addFontNameRun ||
- addFontWeightRun ||
- addFontWidthRun ||
- addFontSlantRun ||
- addFontSizeRun )
- {
- const VectorBase::SizeType numberOfRuns = mImpl->mModel->mLogicalModel->mFontDescriptionRuns.Count();
- mImpl->mModel->mLogicalModel->mFontDescriptionRuns.Resize( numberOfRuns + 1u );
-
- FontDescriptionRun& fontDescriptionRun = *( mImpl->mModel->mLogicalModel->mFontDescriptionRuns.Begin() + numberOfRuns );
-
- if( addFontNameRun )
- {
- fontDescriptionRun.familyLength = mImpl->mEventData->mInputStyle.familyName.size();
- fontDescriptionRun.familyName = new char[fontDescriptionRun.familyLength];
- memcpy( fontDescriptionRun.familyName, mImpl->mEventData->mInputStyle.familyName.c_str(), fontDescriptionRun.familyLength );
- fontDescriptionRun.familyDefined = true;
-
- // The memory allocated for the font family name is freed when the font description is removed from the logical model.
- }
-
- if( addFontWeightRun )
- {
- fontDescriptionRun.weight = mImpl->mEventData->mInputStyle.weight;
- fontDescriptionRun.weightDefined = true;
- }
-
- if( addFontWidthRun )
- {
- fontDescriptionRun.width = mImpl->mEventData->mInputStyle.width;
- fontDescriptionRun.widthDefined = true;
- }
-
- if( addFontSlantRun )
- {
- fontDescriptionRun.slant = mImpl->mEventData->mInputStyle.slant;
- fontDescriptionRun.slantDefined = true;
- }
-
- if( addFontSizeRun )
- {
- fontDescriptionRun.size = static_cast<PointSize26Dot6>( mImpl->mEventData->mInputStyle.size * 64.f );
- fontDescriptionRun.sizeDefined = true;
- }
-
- fontDescriptionRun.characterRun.characterIndex = cursorIndex;
- fontDescriptionRun.characterRun.numberOfCharacters = maxSizeOfNewText;
- }
-
- // Insert at current cursor position.
- Vector<Character>& modifyText = mImpl->mModel->mLogicalModel->mText;
-
- if( cursorIndex < numberOfCharactersInModel )
- {
- modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
- }
- else
- {
- modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.Begin() + maxSizeOfNewText );
- }
-
- // Mark the first paragraph to be updated.
- if( Layout::Engine::SINGLE_LINE_BOX == mImpl->mLayoutEngine.GetLayout() )
- {
- mImpl->mTextUpdateInfo.mCharacterIndex = 0;
- mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
- mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = numberOfCharactersInModel + maxSizeOfNewText;
- mImpl->mTextUpdateInfo.mClearAll = true;
- }
- else
- {
- mImpl->mTextUpdateInfo.mCharacterIndex = std::min( cursorIndex, mImpl->mTextUpdateInfo.mCharacterIndex );
- mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd += maxSizeOfNewText;
- }
-
- // Update the cursor index.
- cursorIndex += maxSizeOfNewText;
-
- 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 );
- }
-
- if( ( 0u == mImpl->mModel->mLogicalModel->mText.Count() ) &&
- mImpl->IsPlaceholderAvailable() )
- {
- // Show place-holder if empty after removing the pre-edit text
- ShowPlaceholderText();
- mImpl->mEventData->mUpdateCursorPosition = true;
- mImpl->ClearPreEditFlag();
- }
- else if( removedPrevious ||
- removedSelected ||
- ( 0 != utf32Characters.Count() ) )
- {
- // Queue an inserted event
- mImpl->QueueModifyEvent( ModifyEvent::TEXT_INSERTED );
-
- mImpl->mEventData->mUpdateCursorPosition = true;
- if( removedSelected )
- {
- mImpl->mEventData->mScrollAfterDelete = true;
- }
- else
- {
- mImpl->mEventData->mScrollAfterUpdatePosition = true;
- }
- }
-
- if( maxLengthReached )
- {
- DALI_LOG_INFO( gLogFilter, Debug::Verbose, "MaxLengthReached (%d)\n", mImpl->mModel->mLogicalModel->mText.Count() );
-
- mImpl->ResetInputMethodContext();
-
- if( NULL != mImpl->mEditableControlInterface )
- {
- // Do this last since it provides callbacks into application code
- mImpl->mEditableControlInterface->MaxLengthReached();
- }
- }
-}
-
-void Controller::PasteText( const std::string& stringToPaste )
-{
- InsertText( stringToPaste, Text::Controller::COMMIT );
- mImpl->ChangeState( EventData::EDITING );
- mImpl->RequestRelayout();
-
- if( NULL != mImpl->mEditableControlInterface )
- {
- // Do this last since it provides callbacks into application code
- mImpl->mEditableControlInterface->TextChanged();
- }
-}
-
-bool Controller::RemoveText( int cursorOffset,
- int numberOfCharacters,
- UpdateInputStyleType type )
-{
- bool removed = false;
-
- if( NULL == mImpl->mEventData )
- {
- return removed;
- }
-
- DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::RemoveText %p mText.Count() %d cursor %d cursorOffset %d numberOfCharacters %d\n",
- this, mImpl->mModel->mLogicalModel->mText.Count(), mImpl->mEventData->mPrimaryCursorPosition, cursorOffset, numberOfCharacters );
-
- if( !mImpl->IsShowingPlaceholderText() )
- {
- // Delete at current cursor position
- Vector<Character>& currentText = mImpl->mModel->mLogicalModel->mText;
- CharacterIndex& oldCursorIndex = mImpl->mEventData->mPrimaryCursorPosition;
-
- CharacterIndex cursorIndex = 0;
-
- // Validate the cursor position & number of characters
- if( ( static_cast< int >( mImpl->mEventData->mPrimaryCursorPosition ) + cursorOffset ) >= 0 )
- {
- cursorIndex = mImpl->mEventData->mPrimaryCursorPosition + cursorOffset;
- }
-
- if( ( cursorIndex + numberOfCharacters ) > currentText.Count() )
- {
- numberOfCharacters = currentText.Count() - cursorIndex;
- }
-
- if( mImpl->mEventData->mPreEditFlag || // If the preedit flag is enabled, it means two (or more) of them came together i.e. when two keys have been pressed at the same time.
- ( ( cursorIndex + numberOfCharacters ) <= mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters ) )
- {
- // Mark the paragraphs to be updated.
- if( Layout::Engine::SINGLE_LINE_BOX == mImpl->mLayoutEngine.GetLayout() )
- {
- mImpl->mTextUpdateInfo.mCharacterIndex = 0;
- mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters;
- mImpl->mTextUpdateInfo.mNumberOfCharactersToAdd = mImpl->mTextUpdateInfo.mPreviousNumberOfCharacters - numberOfCharacters;
- mImpl->mTextUpdateInfo.mClearAll = true;
- }
- else
- {
- mImpl->mTextUpdateInfo.mCharacterIndex = std::min( cursorIndex, mImpl->mTextUpdateInfo.mCharacterIndex );
- mImpl->mTextUpdateInfo.mNumberOfCharactersToRemove += numberOfCharacters;
- }
-
- // Update the input style and remove the text's style before removing the text.
-
- if( UPDATE_INPUT_STYLE == type )
- {
- // Keep a copy of the current input style.
- InputStyle currentInputStyle;
- currentInputStyle.Copy( mImpl->mEventData->mInputStyle );
-
- // Set first the default input style.
- mImpl->RetrieveDefaultInputStyle( mImpl->mEventData->mInputStyle );
-
- // Update the input style.
- mImpl->mModel->mLogicalModel->RetrieveStyle( cursorIndex, mImpl->mEventData->mInputStyle );
-
- // Compare if the input style has changed.
- const bool hasInputStyleChanged = !currentInputStyle.Equal( mImpl->mEventData->mInputStyle );