2 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/text-controller.h>
24 #include <dali/public-api/adaptor-framework/key.h>
25 #include <dali/public-api/text-abstraction/font-client.h>
28 #include <dali-toolkit/internal/text/bidirectional-support.h>
29 #include <dali-toolkit/internal/text/character-set-conversion.h>
30 #include <dali-toolkit/internal/text/layouts/layout-engine.h>
31 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
32 #include <dali-toolkit/internal/text/logical-model-impl.h>
33 #include <dali-toolkit/internal/text/multi-language-support.h>
34 #include <dali-toolkit/internal/text/script-run.h>
35 #include <dali-toolkit/internal/text/segmentation.h>
36 #include <dali-toolkit/internal/text/shaper.h>
37 #include <dali-toolkit/internal/text/text-io.h>
38 #include <dali-toolkit/internal/text/text-view.h>
39 #include <dali-toolkit/internal/text/visual-model-impl.h>
46 const float MAX_FLOAT = std::numeric_limits<float>::max();
47 const std::string EMPTY_STRING;
51 REPLACE_TEXT, ///< Replace the entire text
52 INSERT_TEXT, ///< Insert characters at the current cursor position
53 DELETE_TEXT ///< Delete a character at the current cursor position
73 struct Controller::TextInput
75 // Used to queue input events until DoRelayout()
78 KEYBOARD_FOCUS_GAIN_EVENT,
79 KEYBOARD_FOCUS_LOST_EVENT,
95 Event( EventType eventType )
116 TextInput( LogicalModelPtr logicalModel,
117 VisualModelPtr visualModel,
118 DecoratorPtr decorator )
119 : mLogicalModel( logicalModel ),
120 mVisualModel( visualModel ),
121 mDecorator( decorator ),
123 mPrimaryCursorPosition( 0u ),
124 mSecondaryCursorPosition( 0u ),
125 mDecoratorUpdated( false ),
126 mCursorBlinkEnabled( true ),
127 mGrabHandleEnabled( true ),
128 mGrabHandlePopupEnabled( true ),
129 mSelectionEnabled( true ),
130 mHorizontalScrollingEnabled( true ),
131 mVerticalScrollingEnabled( false ),
132 mUpdateCursorPosition( false )
137 * @brief Helper to move the cursor, grab handle etc.
139 bool ProcessInputEvents( const Vector2& controlSize,
140 const Vector2& alignmentOffset )
142 mDecoratorUpdated = false;
146 for( vector<TextInput::Event>::iterator iter = mEventQueue.begin(); iter != mEventQueue.end(); ++iter )
150 case KEYBOARD_FOCUS_GAIN_EVENT:
152 OnKeyboardFocus( true );
155 case KEYBOARD_FOCUS_LOST_EVENT:
157 OnKeyboardFocus( false );
160 case CURSOR_KEY_EVENT:
162 OnCursorKeyEvent( *iter );
167 OnTapEvent( *iter, alignmentOffset );
172 OnPanEvent( *iter, controlSize, alignmentOffset );
175 case GRAB_HANDLE_EVENT:
177 OnGrabHandleEvent( *iter );
184 // The cursor must also be repositioned after inserts into the model
185 if( mUpdateCursorPosition )
187 UpdateCursorPosition();
188 mUpdateCursorPosition = false;
193 return mDecoratorUpdated;
196 void OnKeyboardFocus( bool hasFocus )
200 ChangeState( INACTIVE );
204 ChangeState( EDITING );
208 void OnCursorKeyEvent( const Event& event )
210 int keyCode = event.p1.mInt;
212 if( Dali::DALI_KEY_CURSOR_LEFT == keyCode )
216 else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode )
220 else if( Dali::DALI_KEY_CURSOR_UP == keyCode )
224 else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode )
230 void HandleCursorKey( int keyCode )
235 void OnTapEvent( const Event& event,
236 const Vector2& alignmentOffset )
238 unsigned int tapCount = event.p1.mUint;
242 ChangeState( EDITING );
244 float xPosition = event.p2.mFloat - alignmentOffset.x;
245 float yPosition = event.p3.mFloat - alignmentOffset.y;
247 GetClosestCursorPosition( mPrimaryCursorPosition, xPosition, yPosition, height );
248 mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height );
249 mUpdateCursorPosition = false;
251 mDecoratorUpdated = true;
253 else if( mSelectionEnabled &&
256 ChangeState( SELECTING );
258 RepositionSelectionHandles( event.p2.mFloat, event.p3.mFloat );
262 void OnPanEvent( const Event& event,
263 const Vector2& controlSize,
264 const Vector2& alignmentOffset )
266 int state = event.p1.mInt;
268 if( Gesture::Started == state ||
269 Gesture::Continuing == state )
271 const Vector2& actualSize = mVisualModel->GetActualSize();
273 if( mHorizontalScrollingEnabled )
275 const float displacementX = event.p2.mFloat;
276 mScrollPosition.x += displacementX;
278 // Clamp between -space & 0 (and the text alignment).
279 const float contentWidth = actualSize.width;
280 if( contentWidth > controlSize.width )
282 const float space = ( contentWidth - controlSize.width ) + alignmentOffset.x;
283 mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x;
284 mScrollPosition.x = ( mScrollPosition.x > -alignmentOffset.x ) ? -alignmentOffset.x : mScrollPosition.x;
286 mDecoratorUpdated = true;
290 mScrollPosition.x = 0.f;
294 if( mVerticalScrollingEnabled )
296 const float displacementY = event.p3.mFloat;
297 mScrollPosition.y += displacementY;
299 // Clamp between -space & 0 (and the text alignment).
300 if( actualSize.height > controlSize.height )
302 const float space = ( actualSize.height - controlSize.height ) + alignmentOffset.y;
303 mScrollPosition.y = ( mScrollPosition.y < -space ) ? -space : mScrollPosition.y;
304 mScrollPosition.y = ( mScrollPosition.y > -alignmentOffset.y ) ? -alignmentOffset.y : mScrollPosition.y;
306 mDecoratorUpdated = true;
310 mScrollPosition.y = 0.f;
316 void OnGrabHandleEvent( const Event& event )
318 unsigned int state = event.p1.mUint;
320 if( GRAB_HANDLE_PRESSED == state )
322 float xPosition = event.p2.mFloat;
323 float yPosition = event.p3.mFloat;
326 GetClosestCursorPosition( mPrimaryCursorPosition, xPosition, yPosition, height );
328 mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height );
329 //mDecorator->HidePopup();
330 ChangeState ( EDITING );
331 mDecoratorUpdated = true;
333 else if ( mGrabHandlePopupEnabled &&
334 GRAB_HANDLE_RELEASED == state )
336 //mDecorator->ShowPopup();
337 ChangeState ( EDITING_WITH_POPUP );
338 mDecoratorUpdated = true;
342 void RepositionSelectionHandles( float visualX, float visualY )
344 // TODO - Find which word was selected
346 const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
347 const Vector<Vector2>::SizeType glyphCount = glyphs.Count();
349 const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
350 const Vector<Vector2>::SizeType positionCount = positions.Count();
352 // Guard against glyphs which did not fit inside the layout
353 const Vector<Vector2>::SizeType count = (positionCount < glyphCount) ? positionCount : glyphCount;
357 float primaryX = positions[0].x;
358 float secondaryX = positions[count-1].x + glyphs[count-1].width;
360 // TODO - multi-line selection
361 const Vector<LineRun>& lines = mVisualModel->mLines;
362 float height = lines.Count() ? lines[0].lineSize.height : 0.0f;
364 mDecorator->SetPosition( PRIMARY_SELECTION_HANDLE, primaryX, 0.0f, height );
365 mDecorator->SetPosition( SECONDARY_SELECTION_HANDLE, secondaryX, 0.0f, height );
367 mDecorator->ClearHighlights();
368 mDecorator->AddHighlight( primaryX, 0.0f, secondaryX, height );
372 void ChangeState( State newState )
374 if( mState != newState )
378 if( INACTIVE == mState )
380 mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
381 mDecorator->StopCursorBlink();
382 mDecorator->SetGrabHandleActive( false );
383 mDecorator->SetSelectionActive( false );
384 mDecorator->SetPopupActive( false );
385 mDecoratorUpdated = true;
387 else if ( SELECTING == mState )
389 mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
390 mDecorator->StopCursorBlink();
391 mDecorator->SetGrabHandleActive( false );
392 mDecorator->SetSelectionActive( true );
393 mDecoratorUpdated = true;
395 else if( EDITING == mState )
397 mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
398 if( mCursorBlinkEnabled )
400 mDecorator->StartCursorBlink();
402 if( mGrabHandleEnabled )
404 mDecorator->SetGrabHandleActive( true );
406 if( mGrabHandlePopupEnabled )
408 mDecorator->SetPopupActive( false );
410 mDecorator->SetSelectionActive( false );
411 mDecoratorUpdated = true;
413 else if( EDITING_WITH_POPUP == mState )
415 mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
416 if( mCursorBlinkEnabled )
418 mDecorator->StartCursorBlink();
420 if( mGrabHandleEnabled )
422 mDecorator->SetGrabHandleActive( true );
424 if( mGrabHandlePopupEnabled )
426 mDecorator->SetPopupActive( true );
428 mDecorator->SetSelectionActive( false );
429 mDecoratorUpdated = true;
434 LineIndex GetClosestLine( float y )
436 LineIndex lineIndex( 0u );
438 const Vector<LineRun>& lines = mVisualModel->mLines;
439 for( float totalHeight = 0; lineIndex < lines.Count(); ++lineIndex )
441 totalHeight += lines[lineIndex].lineSize.height;
442 if( y < totalHeight )
451 void GetClosestCursorPosition( CharacterIndex& logical, float& visualX, float& visualY, float& height )
453 Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
454 Length numberOfLines = mVisualModel->mLines.Count();
455 if( 0 == numberOfGlyphs ||
461 // Transform to visual model coords
462 visualX -= mScrollPosition.x;
463 visualY -= mScrollPosition.y;
465 // Find which line is closest
466 LineIndex lineIndex( GetClosestLine( visualY ) );
468 const Vector<GlyphInfo>& glyphs = mVisualModel->mGlyphs;
469 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
471 const Vector<Vector2>& positions = mVisualModel->mGlyphPositions;
472 const Vector2* const positionsBuffer = positions.Begin();
474 unsigned int closestGlyph = 0;
475 bool leftOfGlyph( false ); // which side of the glyph?
476 float closestDistance = MAX_FLOAT;
478 const LineRun& line = mVisualModel->mLines[lineIndex];
479 GlyphIndex startGlyph = line.glyphIndex;
480 GlyphIndex endGlyph = line.glyphIndex + line.numberOfGlyphs;
481 DALI_ASSERT_DEBUG( endGlyph <= glyphs.Count() && "Invalid line info" );
483 for( GlyphIndex i = startGlyph; i < endGlyph; ++i )
485 const GlyphInfo& glyphInfo = *( glyphsBuffer + i );
486 const Vector2& position = *( positionsBuffer + i );
487 float glyphX = position.x + glyphInfo.width*0.5f;
488 float glyphY = position.y + glyphInfo.height*0.5f;
490 float distanceToGlyph = fabsf( glyphX - visualX ) + fabsf( glyphY - visualY );
492 if( distanceToGlyph < closestDistance )
494 closestDistance = distanceToGlyph;
496 leftOfGlyph = ( visualX < glyphX );
500 // Calculate the logical position
501 logical = mVisualModel->GetCharacterIndex( closestGlyph );
503 // Returns the visual position of the glyph
504 visualX = positions[closestGlyph].x;
507 visualX += glyphs[closestGlyph].width;
512 else// if ( RTL ) TODO
518 height = line.lineSize.height;
521 void UpdateCursorPosition()
523 if( 0 == mVisualModel->mGlyphs.Count() )
528 // FIXME GetGlyphIndex() is behaving strangely
530 GlyphIndex cursorGlyph = mVisualModel->GetGlyphIndex( mPrimaryCursorPosition );
532 GlyphIndex cursorGlyph( 0u );
533 for( cursorGlyph = 0; cursorGlyph < mVisualModel->mGlyphs.Count(); ++cursorGlyph )
535 if( mPrimaryCursorPosition == mVisualModel->GetCharacterIndex( cursorGlyph ) )
542 float visualX( 0.0f );
543 float visualY( 0.0f );
544 float height( 0.0f );
545 const Vector<LineRun>& lineRuns = mVisualModel->mLines;
547 if( cursorGlyph > 0 )
551 visualX = mVisualModel->mGlyphPositions[ cursorGlyph ].x;
553 visualX += mVisualModel->mGlyphs[ cursorGlyph ].width;
555 // Find the line height
556 GlyphIndex lastGlyph( 0 );
557 for( LineIndex lineIndex = 0u; lineIndex < lineRuns.Count(); ++lineIndex )
559 lastGlyph = (lineRuns[lineIndex].glyphIndex + lineRuns[lineIndex].numberOfGlyphs);
560 if( cursorGlyph < lastGlyph )
562 height = lineRuns[lineIndex].lineSize.height;
568 mDecorator->SetPosition( PRIMARY_CURSOR, visualX, visualY, height );
569 mDecoratorUpdated = true;
572 LogicalModelPtr mLogicalModel;
573 VisualModelPtr mVisualModel;
574 DecoratorPtr mDecorator;
576 std::string mPlaceholderText;
579 * This is used to delay handling events until after the model has been updated.
580 * The number of updates to the model is minimized to improve performance.
582 vector<Event> mEventQueue; ///< The queue of touch events etc.
584 State mState; ///< Selection mode, edit mode etc.
586 CharacterIndex mPrimaryCursorPosition; ///< Index into logical model for primary cursor
587 CharacterIndex mSecondaryCursorPosition; ///< Index into logical model for secondary cursor
590 * 0,0 means that the top-left corner of the layout matches the top-left corner of the UI control.
591 * Typically this will have a negative value with scrolling occurs.
593 Vector2 mScrollPosition; ///< The text is offset by this position when scrolling.
595 bool mDecoratorUpdated : 1; ///< True if the decorator was updated during event processing
596 bool mCursorBlinkEnabled : 1; ///< True if cursor should blink when active
597 bool mGrabHandleEnabled : 1; ///< True if grab handle is enabled
598 bool mGrabHandlePopupEnabled : 1; ///< True if the grab handle popu-up should be shown
599 bool mSelectionEnabled : 1; ///< True if selection handles, highlight etc. are enabled
600 bool mHorizontalScrollingEnabled : 1; ///< True if horizontal scrolling is enabled
601 bool mVerticalScrollingEnabled : 1; ///< True if vertical scrolling is enabled
602 bool mUpdateCursorPosition : 1; ///< True if the visual position of the cursor must be recalculated
605 struct Controller::FontDefaults
608 : mDefaultPointSize(0.0f),
613 FontId GetFontId( TextAbstraction::FontClient& fontClient )
617 Dali::TextAbstraction::PointSize26Dot6 pointSize = mDefaultPointSize*64;
618 mFontId = fontClient.GetFontId( mDefaultFontFamily, mDefaultFontStyle, pointSize );
624 std::string mDefaultFontFamily;
625 std::string mDefaultFontStyle;
626 float mDefaultPointSize;
630 struct Controller::Impl
632 Impl( ControlInterface& controlInterface )
633 : mControlInterface( controlInterface ),
636 mFontDefaults( NULL ),
644 mOperationsPending( NO_OPERATION ),
645 mRecalculateNaturalSize( true )
647 mLogicalModel = LogicalModel::New();
648 mVisualModel = VisualModel::New();
650 mFontClient = TextAbstraction::FontClient::Get();
652 mView.SetVisualModel( mVisualModel );
660 ControlInterface& mControlInterface; ///< Reference to the text controller.
661 LogicalModelPtr mLogicalModel; ///< Pointer to the logical model.
662 VisualModelPtr mVisualModel; ///< Pointer to the visual model.
663 FontDefaults* mFontDefaults; ///< Avoid allocating this when the user does not specify a font.
664 Controller::TextInput* mTextInput; ///< Avoid allocating everything for text input until EnableTextInput().
665 TextAbstraction::FontClient mFontClient; ///< Handle to the font client.
666 View mView; ///< The view interface to the rendering back-end.
667 LayoutEngine mLayoutEngine; ///< The layout engine.
668 std::vector<ModifyEvent> mModifyEvents; ///< Temporary stores the text set until the next relayout.
669 Size mControlSize; ///< The size of the control.
670 Vector2 mAlignmentOffset; ///< Vertical and horizontal offset of the whole text inside the control due to alignment.
671 OperationsMask mOperationsPending; ///< Operations pending to be done to layout the text.
672 bool mRecalculateNaturalSize:1; ///< Whether the natural size needs to be recalculated.
675 ControllerPtr Controller::New( ControlInterface& controlInterface )
677 return ControllerPtr( new Controller( controlInterface ) );
680 void Controller::SetText( const std::string& text )
682 // Cancel previously queued inserts etc.
683 mImpl->mModifyEvents.clear();
685 // Keep until size negotiation
687 event.type = REPLACE_TEXT;
689 mImpl->mModifyEvents.push_back( event );
691 if( mImpl->mTextInput )
693 // Cancel previously queued events
694 mImpl->mTextInput->mEventQueue.clear();
696 // TODO - Hide selection decorations
700 void Controller::GetText( std::string& text ) const
702 if( !mImpl->mModifyEvents.empty() &&
703 REPLACE_TEXT == mImpl->mModifyEvents[0].type )
705 text = mImpl->mModifyEvents[0].text;
709 // TODO - Convert from UTF-32
713 void Controller::SetPlaceholderText( const std::string& text )
715 if( !mImpl->mTextInput )
717 mImpl->mTextInput->mPlaceholderText = text;
721 void Controller::GetPlaceholderText( std::string& text ) const
723 if( !mImpl->mTextInput )
725 text = mImpl->mTextInput->mPlaceholderText;
729 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
731 if( !mImpl->mFontDefaults )
733 mImpl->mFontDefaults = new Controller::FontDefaults();
736 mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily;
737 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
738 mImpl->mOperationsPending = ALL_OPERATIONS;
739 mImpl->mRecalculateNaturalSize = true;
741 // Clear the font-specific data
742 mImpl->mLogicalModel->mFontRuns.Clear();
743 mImpl->mVisualModel->mGlyphs.Clear();
744 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
745 mImpl->mVisualModel->mCharactersToGlyph.Clear();
746 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
747 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
748 mImpl->mVisualModel->mGlyphPositions.Clear();
749 mImpl->mVisualModel->mLines.Clear();
754 const std::string& Controller::GetDefaultFontFamily() const
756 if( mImpl->mFontDefaults )
758 return mImpl->mFontDefaults->mDefaultFontFamily;
764 void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle )
766 if( !mImpl->mFontDefaults )
768 mImpl->mFontDefaults = new Controller::FontDefaults();
771 mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle;
772 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
773 mImpl->mOperationsPending = ALL_OPERATIONS;
774 mImpl->mRecalculateNaturalSize = true;
776 // Clear the font-specific data
777 mImpl->mLogicalModel->mFontRuns.Clear();
778 mImpl->mVisualModel->mGlyphs.Clear();
779 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
780 mImpl->mVisualModel->mCharactersToGlyph.Clear();
781 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
782 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
783 mImpl->mVisualModel->mGlyphPositions.Clear();
784 mImpl->mVisualModel->mLines.Clear();
789 const std::string& Controller::GetDefaultFontStyle() const
791 if( mImpl->mFontDefaults )
793 return mImpl->mFontDefaults->mDefaultFontStyle;
799 void Controller::SetDefaultPointSize( float pointSize )
801 if( !mImpl->mFontDefaults )
803 mImpl->mFontDefaults = new Controller::FontDefaults();
806 mImpl->mFontDefaults->mDefaultPointSize = pointSize;
807 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
808 mImpl->mOperationsPending = ALL_OPERATIONS;
809 mImpl->mRecalculateNaturalSize = true;
811 // Clear the font-specific data
812 mImpl->mLogicalModel->mFontRuns.Clear();
813 mImpl->mVisualModel->mGlyphs.Clear();
814 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
815 mImpl->mVisualModel->mCharactersToGlyph.Clear();
816 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
817 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
818 mImpl->mVisualModel->mGlyphPositions.Clear();
819 mImpl->mVisualModel->mLines.Clear();
824 float Controller::GetDefaultPointSize() const
826 if( mImpl->mFontDefaults )
828 return mImpl->mFontDefaults->mDefaultPointSize;
834 void Controller::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters )
836 if( mImpl->mFontDefaults )
839 fontRun.characterRun.characterIndex = 0;
840 fontRun.characterRun.numberOfCharacters = numberOfCharacters;
841 fontRun.fontId = mImpl->mFontDefaults->GetFontId( mImpl->mFontClient );
842 fontRun.isDefault = true;
844 fonts.PushBack( fontRun );
848 void Controller::EnableTextInput( DecoratorPtr decorator )
850 if( !mImpl->mTextInput )
852 mImpl->mTextInput = new TextInput( mImpl->mLogicalModel, mImpl->mVisualModel, decorator );
856 void Controller::SetEnableCursorBlink( bool enable )
858 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "TextInput disabled" );
860 if( mImpl->mTextInput )
862 mImpl->mTextInput->mCursorBlinkEnabled = enable;
865 mImpl->mTextInput->mDecorator )
867 mImpl->mTextInput->mDecorator->StopCursorBlink();
872 bool Controller::GetEnableCursorBlink() const
874 if( mImpl->mTextInput )
876 return mImpl->mTextInput->mCursorBlinkEnabled;
882 const Vector2& Controller::GetScrollPosition() const
884 if( mImpl->mTextInput )
886 return mImpl->mTextInput->mScrollPosition;
889 return Vector2::ZERO;
892 const Vector2& Controller::GetAlignmentOffset() const
894 return mImpl->mAlignmentOffset;
897 Vector3 Controller::GetNaturalSize()
901 // Make sure the model is up-to-date before layouting
902 ProcessModifyEvents();
904 if( mImpl->mRecalculateNaturalSize )
906 // Operations that can be done only once until the text changes.
907 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
915 // Make sure the model is up-to-date before layouting
916 UpdateModel( onlyOnceOperations );
918 // Operations that need to be done if the size changes.
919 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
923 DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
924 static_cast<OperationsMask>( onlyOnceOperations |
926 naturalSize.GetVectorXY() );
928 // Do not do again the only once operations.
929 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
931 // Do the size related operations again.
932 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
934 // Stores the natural size to avoid recalculate it again
935 // unless the text/style changes.
936 mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
938 mImpl->mRecalculateNaturalSize = false;
942 naturalSize = mImpl->mVisualModel->GetNaturalSize();
948 float Controller::GetHeightForWidth( float width )
950 // Make sure the model is up-to-date before layouting
951 ProcessModifyEvents();
954 if( width != mImpl->mControlSize.width )
956 // Operations that can be done only once until the text changes.
957 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
965 // Make sure the model is up-to-date before layouting
966 UpdateModel( onlyOnceOperations );
968 // Operations that need to be done if the size changes.
969 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
973 DoRelayout( Size( width, MAX_FLOAT ),
974 static_cast<OperationsMask>( onlyOnceOperations |
978 // Do not do again the only once operations.
979 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
981 // Do the size related operations again.
982 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
986 layoutSize = mImpl->mVisualModel->GetActualSize();
989 return layoutSize.height;
992 bool Controller::Relayout( const Size& size )
994 if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
996 bool glyphsRemoved( false );
997 if( 0u != mImpl->mVisualModel->GetNumberOfGlyphPositions() )
999 mImpl->mVisualModel->SetGlyphPositions( NULL, 0u );
1000 glyphsRemoved = true;
1003 // Not worth to relayout if width or height is equal to zero.
1004 return glyphsRemoved;
1007 if( size != mImpl->mControlSize )
1009 // Operations that need to be done if the size changes.
1010 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
1013 UPDATE_ACTUAL_SIZE |
1016 mImpl->mControlSize = size;
1019 // Make sure the model is up-to-date before layouting
1020 ProcessModifyEvents();
1021 UpdateModel( mImpl->mOperationsPending );
1024 bool updated = DoRelayout( mImpl->mControlSize,
1025 mImpl->mOperationsPending,
1028 // Do not re-do any operation until something changes.
1029 mImpl->mOperationsPending = NO_OPERATION;
1031 // After doing the text layout, the alignment offset to place the actor in the desired position can be calculated.
1032 CalculateTextAlignment( size );
1034 if( mImpl->mTextInput )
1036 // Move the cursor, grab handle etc.
1037 updated = mImpl->mTextInput->ProcessInputEvents( mImpl->mControlSize, mImpl->mAlignmentOffset ) || updated;
1043 void Controller::ProcessModifyEvents()
1045 std::vector<ModifyEvent>& events = mImpl->mModifyEvents;
1047 for( unsigned int i=0; i<events.size(); ++i )
1049 if( REPLACE_TEXT == events[0].type )
1051 // A (single) replace event should come first, otherwise we wasted time processing NOOP events
1052 DALI_ASSERT_DEBUG( 0 == i && "Unexpected REPLACE event" );
1054 ReplaceTextEvent( events[0].text );
1056 else if( INSERT_TEXT == events[0].type )
1058 InsertTextEvent( events[0].text );
1060 else if( DELETE_TEXT == events[0].type )
1066 // Discard temporary text
1070 void Controller::ReplaceTextEvent( const std::string& text )
1073 mImpl->mLogicalModel->mText.Clear();
1074 mImpl->mLogicalModel->mScriptRuns.Clear();
1075 mImpl->mLogicalModel->mFontRuns.Clear();
1076 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1077 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1078 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1079 mImpl->mLogicalModel->mCharacterDirections.Clear();
1080 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1081 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1082 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1083 mImpl->mVisualModel->mGlyphs.Clear();
1084 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1085 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1086 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1087 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1088 mImpl->mVisualModel->mGlyphPositions.Clear();
1089 mImpl->mVisualModel->mLines.Clear();
1091 // Convert text into UTF-32
1092 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1093 utf32Characters.Resize( text.size() );
1095 // This is a bit horrible but std::string returns a (signed) char*
1096 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1098 // Transform a text array encoded in utf8 into an array encoded in utf32.
1099 // It returns the actual number of characters.
1100 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1101 utf32Characters.Resize( characterCount );
1103 // Reset the cursor position
1104 if( mImpl->mTextInput )
1106 mImpl->mTextInput->mPrimaryCursorPosition = characterCount;
1107 // TODO - handle secondary cursor
1110 // The natural size needs to be re-calculated.
1111 mImpl->mRecalculateNaturalSize = true;
1113 // Apply modifications to the model
1114 mImpl->mOperationsPending = ALL_OPERATIONS;
1115 UpdateModel( ALL_OPERATIONS );
1116 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1118 UPDATE_ACTUAL_SIZE |
1122 void Controller::InsertTextEvent( const std::string& text )
1124 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1126 // TODO - Optimize this
1127 mImpl->mLogicalModel->mScriptRuns.Clear();
1128 mImpl->mLogicalModel->mFontRuns.Clear();
1129 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1130 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1131 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1132 mImpl->mLogicalModel->mCharacterDirections.Clear();
1133 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1134 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1135 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1136 mImpl->mVisualModel->mGlyphs.Clear();
1137 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1138 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1139 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1140 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1141 mImpl->mVisualModel->mGlyphPositions.Clear();
1142 mImpl->mVisualModel->mLines.Clear();
1144 // Convert text into UTF-32
1145 Vector<Character> utf32Characters;
1146 utf32Characters.Resize( text.size() );
1148 // This is a bit horrible but std::string returns a (signed) char*
1149 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1151 // Transform a text array encoded in utf8 into an array encoded in utf32.
1152 // It returns the actual number of characters.
1153 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1154 utf32Characters.Resize( characterCount );
1156 // Insert at current cursor position
1157 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1158 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1160 if( cursorIndex < modifyText.Count() )
1162 modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.End() );
1166 modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.End() );
1169 // Advance the cursor position
1172 // The natural size needs to be re-calculated.
1173 mImpl->mRecalculateNaturalSize = true;
1175 // Apply modifications to the model; TODO - Optimize this
1176 mImpl->mOperationsPending = ALL_OPERATIONS;
1177 UpdateModel( ALL_OPERATIONS );
1178 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1180 UPDATE_ACTUAL_SIZE |
1183 // Queue a cursor reposition event; this must wait until after DoRelayout()
1184 mImpl->mTextInput->mUpdateCursorPosition = true;
1187 void Controller::DeleteTextEvent()
1189 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1191 // TODO - Optimize this
1192 mImpl->mLogicalModel->mScriptRuns.Clear();
1193 mImpl->mLogicalModel->mFontRuns.Clear();
1194 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1195 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1196 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1197 mImpl->mLogicalModel->mCharacterDirections.Clear();
1198 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1199 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1200 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1201 mImpl->mVisualModel->mGlyphs.Clear();
1202 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1203 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1204 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1205 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1206 mImpl->mVisualModel->mGlyphPositions.Clear();
1207 mImpl->mVisualModel->mLines.Clear();
1209 // Delte at current cursor position
1210 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1211 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1213 if( cursorIndex > 0 &&
1214 cursorIndex-1 < modifyText.Count() )
1216 modifyText.Remove( modifyText.Begin() + cursorIndex - 1 );
1218 // Cursor position retreat
1222 // The natural size needs to be re-calculated.
1223 mImpl->mRecalculateNaturalSize = true;
1225 // Apply modifications to the model; TODO - Optimize this
1226 mImpl->mOperationsPending = ALL_OPERATIONS;
1227 UpdateModel( ALL_OPERATIONS );
1228 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1230 UPDATE_ACTUAL_SIZE |
1233 // Queue a cursor reposition event; this must wait until after DoRelayout()
1234 mImpl->mTextInput->mUpdateCursorPosition = true;
1237 void Controller::UpdateModel( OperationsMask operationsRequired )
1239 // Calculate the operations to be done.
1240 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1242 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1244 const Length numberOfCharacters = mImpl->mLogicalModel->GetNumberOfCharacters();
1246 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1247 if( GET_LINE_BREAKS & operations )
1249 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
1250 // calculate the bidirectional info for each 'paragraph'.
1251 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
1252 // is not shaped together).
1253 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
1255 SetLineBreakInfo( utf32Characters,
1259 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1260 if( GET_WORD_BREAKS & operations )
1262 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
1263 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
1265 SetWordBreakInfo( utf32Characters,
1269 const bool getScripts = GET_SCRIPTS & operations;
1270 const bool validateFonts = VALIDATE_FONTS & operations;
1272 Vector<ScriptRun>& scripts = mImpl->mLogicalModel->mScriptRuns;
1273 Vector<FontRun>& validFonts = mImpl->mLogicalModel->mFontRuns;
1275 if( getScripts || validateFonts )
1277 // Validates the fonts assigned by the application or assigns default ones.
1278 // It makes sure all the characters are going to be rendered by the correct font.
1279 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
1283 // Retrieves the scripts used in the text.
1284 multilanguageSupport.SetScripts( utf32Characters,
1291 if( 0u == validFonts.Count() )
1293 // Copy the requested font defaults received via the property system.
1294 // These may not be valid i.e. may not contain glyphs for the necessary scripts.
1295 GetDefaultFonts( validFonts, numberOfCharacters );
1298 // Validates the fonts. If there is a character with no assigned font it sets a default one.
1299 // After this call, fonts are validated.
1300 multilanguageSupport.ValidateFonts( utf32Characters,
1306 Vector<Character> mirroredUtf32Characters;
1307 bool textMirrored = false;
1308 if( BIDI_INFO & operations )
1310 // Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's
1311 // bidirectional info.
1313 Length numberOfParagraphs = 0u;
1315 const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
1316 for( Length index = 0u; index < numberOfCharacters; ++index )
1318 if( TextAbstraction::LINE_NO_BREAK == *( lineBreakInfoBuffer + index ) )
1320 ++numberOfParagraphs;
1324 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1325 bidirectionalInfo.Reserve( numberOfParagraphs );
1327 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
1328 SetBidirectionalInfo( utf32Characters,
1331 bidirectionalInfo );
1333 if( 0u != bidirectionalInfo.Count() )
1335 // This paragraph has right to left text. Some characters may need to be mirrored.
1336 // TODO: consider if the mirrored string can be stored as well.
1338 textMirrored = GetMirroredText( utf32Characters, mirroredUtf32Characters );
1340 // Only set the character directions if there is right to left characters.
1341 Vector<CharacterDirection>& directions = mImpl->mLogicalModel->mCharacterDirections;
1342 directions.Resize( numberOfCharacters );
1344 GetCharactersDirection( bidirectionalInfo,
1349 // There is no right to left characters. Clear the directions vector.
1350 mImpl->mLogicalModel->mCharacterDirections.Clear();
1355 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1356 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1357 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1358 if( SHAPE_TEXT & operations )
1360 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1362 ShapeText( textToShape,
1367 glyphsToCharactersMap,
1368 charactersPerGlyph );
1371 const Length numberOfGlyphs = glyphs.Count();
1373 if( GET_GLYPH_METRICS & operations )
1375 mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), numberOfGlyphs );
1378 if( 0u != numberOfGlyphs )
1380 // Create the glyph to character conversion table and the 'number of glyphs' per character.
1381 mImpl->mVisualModel->CreateCharacterToGlyphTable(numberOfCharacters );
1382 mImpl->mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
1386 bool Controller::DoRelayout( const Size& size,
1387 OperationsMask operationsRequired,
1390 bool viewUpdated( false );
1392 // Calculate the operations to be done.
1393 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1395 if( LAYOUT & operations )
1397 // Some vectors with data needed to layout and reorder may be void
1398 // after the first time the text has been laid out.
1399 // Fill the vectors again.
1401 Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
1403 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1404 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1405 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1406 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1407 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1409 // Set the layout parameters.
1410 LayoutParameters layoutParameters( size,
1411 mImpl->mLogicalModel->mText.Begin(),
1412 lineBreakInfo.Begin(),
1413 wordBreakInfo.Begin(),
1416 glyphsToCharactersMap.Begin(),
1417 charactersPerGlyph.Begin() );
1419 // The laid-out lines.
1420 // It's not possible to know in how many lines the text is going to be laid-out,
1421 // but it can be resized at least with the number of 'paragraphs' to avoid
1422 // some re-allocations.
1423 Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
1425 // Delete any previous laid out lines before setting the new ones.
1428 // The capacity of the bidirectional paragraph info is the number of paragraphs.
1429 lines.Reserve( mImpl->mLogicalModel->mBidirectionalParagraphInfo.Capacity() );
1431 // Resize the vector of positions to have the same size than the vector of glyphs.
1432 Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
1433 glyphPositions.Resize( numberOfGlyphs );
1435 // Update the visual model.
1436 viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
1443 // Reorder the lines
1444 if( REORDER & operations )
1446 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1448 // Check first if there are paragraphs with bidirectional info.
1449 if( 0u != bidirectionalInfo.Count() )
1452 const Length numberOfLines = mImpl->mVisualModel->GetNumberOfLines();
1454 // Reorder the lines.
1455 Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
1456 lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
1457 ReorderLines( bidirectionalInfo,
1459 lineBidirectionalInfoRuns );
1461 // Set the bidirectional info into the model.
1462 const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count();
1463 mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(),
1464 numberOfBidirectionalInfoRuns );
1466 // Set the bidirectional info per line into the layout parameters.
1467 layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin();
1468 layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns;
1470 // Get the character to glyph conversion table and set into the layout.
1471 layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
1473 // Get the glyphs per character table and set into the layout.
1474 layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
1476 // Re-layout the text. Reorder those lines with right to left characters.
1477 mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
1480 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
1481 for( Vector<BidirectionalLineInfoRun>::Iterator it = lineBidirectionalInfoRuns.Begin(),
1482 endIt = lineBidirectionalInfoRuns.End();
1486 BidirectionalLineInfoRun& bidiLineInfo = *it;
1488 free( bidiLineInfo.visualToLogicalMap );
1493 if( ALIGN & operations )
1495 mImpl->mLayoutEngine.Align( layoutParameters,
1501 // Sets the actual size.
1502 if( UPDATE_ACTUAL_SIZE & operations )
1504 mImpl->mVisualModel->SetActualSize( layoutSize );
1510 layoutSize = mImpl->mVisualModel->GetActualSize();
1516 void Controller::CalculateTextAlignment( const Size& size )
1518 // TODO : Calculate the vertical offset.
1520 // Get the direction of the first character.
1521 const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
1523 const Size& actualSize = mImpl->mVisualModel->GetActualSize();
1525 // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
1526 LayoutEngine::Alignment alignment = mImpl->mLayoutEngine.GetAlignment();
1527 if( firstParagraphDirection &&
1528 ( LayoutEngine::ALIGN_CENTER != alignment ) )
1530 if( LayoutEngine::ALIGN_BEGIN == alignment )
1532 alignment = LayoutEngine::ALIGN_END;
1536 alignment = LayoutEngine::ALIGN_BEGIN;
1542 case LayoutEngine::ALIGN_BEGIN:
1544 mImpl->mAlignmentOffset = Vector2::ZERO;
1547 case LayoutEngine::ALIGN_CENTER:
1549 mImpl->mAlignmentOffset.y = 0.f;
1550 const int intOffset = static_cast<int>( 0.5f * ( size.width - actualSize.width ) ); // try to avoid pixel alignment.
1551 mImpl->mAlignmentOffset.x = static_cast<float>( intOffset );
1554 case LayoutEngine::ALIGN_END:
1556 mImpl->mAlignmentOffset.y = 0.f;
1557 mImpl->mAlignmentOffset.x = size.width - actualSize.width;
1563 View& Controller::GetView()
1565 return mImpl->mView;
1568 LayoutEngine& Controller::GetLayoutEngine()
1570 return mImpl->mLayoutEngine;
1573 void Controller::RequestRelayout()
1575 mImpl->mControlInterface.RequestTextRelayout();
1578 void Controller::KeyboardFocusGainEvent()
1580 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusGainEvent" );
1582 if( mImpl->mTextInput )
1584 TextInput::Event event( TextInput::KEYBOARD_FOCUS_GAIN_EVENT );
1585 mImpl->mTextInput->mEventQueue.push_back( event );
1591 void Controller::KeyboardFocusLostEvent()
1593 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusLostEvent" );
1595 if( mImpl->mTextInput )
1597 TextInput::Event event( TextInput::KEYBOARD_FOCUS_LOST_EVENT );
1598 mImpl->mTextInput->mEventQueue.push_back( event );
1604 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
1606 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyEvent" );
1608 if( mImpl->mTextInput &&
1609 keyEvent.state == KeyEvent::Down )
1611 int keyCode = keyEvent.keyCode;
1612 const std::string& keyString = keyEvent.keyPressed;
1614 // Pre-process to separate modifying events from non-modifying input events.
1615 if( Dali::DALI_KEY_ESCAPE == keyCode )
1617 // Escape key is a special case which causes focus loss
1618 KeyboardFocusLostEvent();
1620 else if( Dali::DALI_KEY_CURSOR_LEFT == keyCode ||
1621 Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
1622 Dali::DALI_KEY_CURSOR_UP == keyCode ||
1623 Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1625 TextInput::Event event( TextInput::CURSOR_KEY_EVENT );
1626 event.p1.mInt = keyCode;
1627 mImpl->mTextInput->mEventQueue.push_back( event );
1629 else if( Dali::DALI_KEY_BACKSPACE == keyCode )
1631 // Queue a delete event
1633 event.type = DELETE_TEXT;
1634 mImpl->mModifyEvents.push_back( event );
1636 else if( !keyString.empty() )
1638 // Queue an insert event
1640 event.type = INSERT_TEXT;
1641 event.text = keyString;
1642 mImpl->mModifyEvents.push_back( event );
1645 mImpl->mTextInput->ChangeState( TextInput::EDITING ); // todo Confirm this is the best place to change the state of
1653 void Controller::TapEvent( unsigned int tapCount, float x, float y )
1655 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );
1657 if( mImpl->mTextInput )
1659 TextInput::Event event( TextInput::TAP_EVENT );
1660 event.p1.mUint = tapCount;
1661 event.p2.mFloat = x;
1662 event.p3.mFloat = y;
1663 mImpl->mTextInput->mEventQueue.push_back( event );
1669 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
1671 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected PanEvent" );
1673 if( mImpl->mTextInput )
1675 TextInput::Event event( TextInput::PAN_EVENT );
1676 event.p1.mInt = state;
1677 event.p2.mFloat = displacement.x;
1678 event.p3.mFloat = displacement.y;
1679 mImpl->mTextInput->mEventQueue.push_back( event );
1685 void Controller::GrabHandleEvent( GrabHandleState state, float x, float y )
1687 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" );
1689 if( mImpl->mTextInput )
1691 TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
1692 event.p1.mUint = state;
1693 event.p2.mFloat = x;
1694 event.p3.mFloat = y;
1695 mImpl->mTextInput->mEventQueue.push_back( event );
1701 Controller::~Controller()
1706 Controller::Controller( ControlInterface& controlInterface )
1709 mImpl = new Controller::Impl( controlInterface );
1714 } // namespace Toolkit