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 );
654 // Set the shadow properties to default
655 mVisualModel->SetShadowOffset( Vector2::ZERO );
656 mVisualModel->SetShadowColor( Vector4::ZERO );
664 ControlInterface& mControlInterface; ///< Reference to the text controller.
665 LogicalModelPtr mLogicalModel; ///< Pointer to the logical model.
666 VisualModelPtr mVisualModel; ///< Pointer to the visual model.
667 FontDefaults* mFontDefaults; ///< Avoid allocating this when the user does not specify a font.
668 Controller::TextInput* mTextInput; ///< Avoid allocating everything for text input until EnableTextInput().
669 TextAbstraction::FontClient mFontClient; ///< Handle to the font client.
670 View mView; ///< The view interface to the rendering back-end.
671 LayoutEngine mLayoutEngine; ///< The layout engine.
672 std::vector<ModifyEvent> mModifyEvents; ///< Temporary stores the text set until the next relayout.
673 Size mControlSize; ///< The size of the control.
674 Vector2 mAlignmentOffset; ///< Vertical and horizontal offset of the whole text inside the control due to alignment.
675 OperationsMask mOperationsPending; ///< Operations pending to be done to layout the text.
676 bool mRecalculateNaturalSize:1; ///< Whether the natural size needs to be recalculated.
679 ControllerPtr Controller::New( ControlInterface& controlInterface )
681 return ControllerPtr( new Controller( controlInterface ) );
684 void Controller::SetText( const std::string& text )
686 // Cancel previously queued inserts etc.
687 mImpl->mModifyEvents.clear();
689 // Keep until size negotiation
691 event.type = REPLACE_TEXT;
693 mImpl->mModifyEvents.push_back( event );
695 if( mImpl->mTextInput )
697 // Cancel previously queued events
698 mImpl->mTextInput->mEventQueue.clear();
700 // TODO - Hide selection decorations
704 void Controller::GetText( std::string& text ) const
706 if( !mImpl->mModifyEvents.empty() &&
707 REPLACE_TEXT == mImpl->mModifyEvents[0].type )
709 text = mImpl->mModifyEvents[0].text;
713 // TODO - Convert from UTF-32
717 void Controller::SetPlaceholderText( const std::string& text )
719 if( !mImpl->mTextInput )
721 mImpl->mTextInput->mPlaceholderText = text;
725 void Controller::GetPlaceholderText( std::string& text ) const
727 if( !mImpl->mTextInput )
729 text = mImpl->mTextInput->mPlaceholderText;
733 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
735 if( !mImpl->mFontDefaults )
737 mImpl->mFontDefaults = new Controller::FontDefaults();
740 mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily;
741 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
742 mImpl->mOperationsPending = ALL_OPERATIONS;
743 mImpl->mRecalculateNaturalSize = true;
745 // Clear the font-specific data
746 mImpl->mLogicalModel->mFontRuns.Clear();
747 mImpl->mVisualModel->mGlyphs.Clear();
748 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
749 mImpl->mVisualModel->mCharactersToGlyph.Clear();
750 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
751 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
752 mImpl->mVisualModel->mGlyphPositions.Clear();
753 mImpl->mVisualModel->mLines.Clear();
758 const std::string& Controller::GetDefaultFontFamily() const
760 if( mImpl->mFontDefaults )
762 return mImpl->mFontDefaults->mDefaultFontFamily;
768 void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle )
770 if( !mImpl->mFontDefaults )
772 mImpl->mFontDefaults = new Controller::FontDefaults();
775 mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle;
776 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
777 mImpl->mOperationsPending = ALL_OPERATIONS;
778 mImpl->mRecalculateNaturalSize = true;
780 // Clear the font-specific data
781 mImpl->mLogicalModel->mFontRuns.Clear();
782 mImpl->mVisualModel->mGlyphs.Clear();
783 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
784 mImpl->mVisualModel->mCharactersToGlyph.Clear();
785 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
786 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
787 mImpl->mVisualModel->mGlyphPositions.Clear();
788 mImpl->mVisualModel->mLines.Clear();
793 const std::string& Controller::GetDefaultFontStyle() const
795 if( mImpl->mFontDefaults )
797 return mImpl->mFontDefaults->mDefaultFontStyle;
803 void Controller::SetDefaultPointSize( float pointSize )
805 if( !mImpl->mFontDefaults )
807 mImpl->mFontDefaults = new Controller::FontDefaults();
810 mImpl->mFontDefaults->mDefaultPointSize = pointSize;
811 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
812 mImpl->mOperationsPending = ALL_OPERATIONS;
813 mImpl->mRecalculateNaturalSize = true;
815 // Clear the font-specific data
816 mImpl->mLogicalModel->mFontRuns.Clear();
817 mImpl->mVisualModel->mGlyphs.Clear();
818 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
819 mImpl->mVisualModel->mCharactersToGlyph.Clear();
820 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
821 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
822 mImpl->mVisualModel->mGlyphPositions.Clear();
823 mImpl->mVisualModel->mLines.Clear();
828 float Controller::GetDefaultPointSize() const
830 if( mImpl->mFontDefaults )
832 return mImpl->mFontDefaults->mDefaultPointSize;
838 void Controller::GetDefaultFonts( Vector<FontRun>& fonts, Length numberOfCharacters )
840 if( mImpl->mFontDefaults )
843 fontRun.characterRun.characterIndex = 0;
844 fontRun.characterRun.numberOfCharacters = numberOfCharacters;
845 fontRun.fontId = mImpl->mFontDefaults->GetFontId( mImpl->mFontClient );
846 fontRun.isDefault = true;
848 fonts.PushBack( fontRun );
852 const Vector2& Controller::GetShadowOffset() const
854 return mImpl->mVisualModel->GetShadowOffset();
857 const Vector4& Controller::GetShadowColor() const
859 return mImpl->mVisualModel->GetShadowColor();
862 void Controller::SetShadowOffset( const Vector2& shadowOffset )
864 mImpl->mVisualModel->SetShadowOffset( shadowOffset );
867 void Controller::SetShadowColor( const Vector4& shadowColor )
869 mImpl->mVisualModel->SetShadowColor( shadowColor );
872 void Controller::EnableTextInput( DecoratorPtr decorator )
874 if( !mImpl->mTextInput )
876 mImpl->mTextInput = new TextInput( mImpl->mLogicalModel, mImpl->mVisualModel, decorator );
880 void Controller::SetEnableCursorBlink( bool enable )
882 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "TextInput disabled" );
884 if( mImpl->mTextInput )
886 mImpl->mTextInput->mCursorBlinkEnabled = enable;
889 mImpl->mTextInput->mDecorator )
891 mImpl->mTextInput->mDecorator->StopCursorBlink();
896 bool Controller::GetEnableCursorBlink() const
898 if( mImpl->mTextInput )
900 return mImpl->mTextInput->mCursorBlinkEnabled;
906 const Vector2& Controller::GetScrollPosition() const
908 if( mImpl->mTextInput )
910 return mImpl->mTextInput->mScrollPosition;
913 return Vector2::ZERO;
916 const Vector2& Controller::GetAlignmentOffset() const
918 return mImpl->mAlignmentOffset;
921 Vector3 Controller::GetNaturalSize()
925 // Make sure the model is up-to-date before layouting
926 ProcessModifyEvents();
928 if( mImpl->mRecalculateNaturalSize )
930 // Operations that can be done only once until the text changes.
931 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
939 // Make sure the model is up-to-date before layouting
940 UpdateModel( onlyOnceOperations );
942 // Operations that need to be done if the size changes.
943 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
947 DoRelayout( Size( MAX_FLOAT, MAX_FLOAT ),
948 static_cast<OperationsMask>( onlyOnceOperations |
950 naturalSize.GetVectorXY() );
952 // Do not do again the only once operations.
953 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
955 // Do the size related operations again.
956 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
958 // Stores the natural size to avoid recalculate it again
959 // unless the text/style changes.
960 mImpl->mVisualModel->SetNaturalSize( naturalSize.GetVectorXY() );
962 mImpl->mRecalculateNaturalSize = false;
966 naturalSize = mImpl->mVisualModel->GetNaturalSize();
972 float Controller::GetHeightForWidth( float width )
974 // Make sure the model is up-to-date before layouting
975 ProcessModifyEvents();
978 if( width != mImpl->mControlSize.width )
980 // Operations that can be done only once until the text changes.
981 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
989 // Make sure the model is up-to-date before layouting
990 UpdateModel( onlyOnceOperations );
992 // Operations that need to be done if the size changes.
993 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
997 DoRelayout( Size( width, MAX_FLOAT ),
998 static_cast<OperationsMask>( onlyOnceOperations |
1002 // Do not do again the only once operations.
1003 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending & ~onlyOnceOperations );
1005 // Do the size related operations again.
1006 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending | sizeOperations );
1010 layoutSize = mImpl->mVisualModel->GetActualSize();
1013 return layoutSize.height;
1016 bool Controller::Relayout( const Size& size )
1018 if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
1020 bool glyphsRemoved( false );
1021 if( 0u != mImpl->mVisualModel->GetNumberOfGlyphPositions() )
1023 mImpl->mVisualModel->SetGlyphPositions( NULL, 0u );
1024 glyphsRemoved = true;
1027 // Not worth to relayout if width or height is equal to zero.
1028 return glyphsRemoved;
1031 if( size != mImpl->mControlSize )
1033 // Operations that need to be done if the size changes.
1034 mImpl->mOperationsPending = static_cast<OperationsMask>( mImpl->mOperationsPending |
1037 UPDATE_ACTUAL_SIZE |
1040 mImpl->mControlSize = size;
1043 // Make sure the model is up-to-date before layouting
1044 ProcessModifyEvents();
1045 UpdateModel( mImpl->mOperationsPending );
1048 bool updated = DoRelayout( mImpl->mControlSize,
1049 mImpl->mOperationsPending,
1052 // Do not re-do any operation until something changes.
1053 mImpl->mOperationsPending = NO_OPERATION;
1055 // After doing the text layout, the alignment offset to place the actor in the desired position can be calculated.
1056 CalculateTextAlignment( size );
1058 if( mImpl->mTextInput )
1060 // Move the cursor, grab handle etc.
1061 updated = mImpl->mTextInput->ProcessInputEvents( mImpl->mControlSize, mImpl->mAlignmentOffset ) || updated;
1067 void Controller::ProcessModifyEvents()
1069 std::vector<ModifyEvent>& events = mImpl->mModifyEvents;
1071 for( unsigned int i=0; i<events.size(); ++i )
1073 if( REPLACE_TEXT == events[0].type )
1075 // A (single) replace event should come first, otherwise we wasted time processing NOOP events
1076 DALI_ASSERT_DEBUG( 0 == i && "Unexpected REPLACE event" );
1078 ReplaceTextEvent( events[0].text );
1080 else if( INSERT_TEXT == events[0].type )
1082 InsertTextEvent( events[0].text );
1084 else if( DELETE_TEXT == events[0].type )
1090 // Discard temporary text
1094 void Controller::ReplaceTextEvent( const std::string& text )
1097 mImpl->mLogicalModel->mText.Clear();
1098 mImpl->mLogicalModel->mScriptRuns.Clear();
1099 mImpl->mLogicalModel->mFontRuns.Clear();
1100 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1101 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1102 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1103 mImpl->mLogicalModel->mCharacterDirections.Clear();
1104 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1105 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1106 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1107 mImpl->mVisualModel->mGlyphs.Clear();
1108 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1109 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1110 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1111 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1112 mImpl->mVisualModel->mGlyphPositions.Clear();
1113 mImpl->mVisualModel->mLines.Clear();
1115 // Convert text into UTF-32
1116 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1117 utf32Characters.Resize( text.size() );
1119 // This is a bit horrible but std::string returns a (signed) char*
1120 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1122 // Transform a text array encoded in utf8 into an array encoded in utf32.
1123 // It returns the actual number of characters.
1124 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1125 utf32Characters.Resize( characterCount );
1127 // Reset the cursor position
1128 if( mImpl->mTextInput )
1130 mImpl->mTextInput->mPrimaryCursorPosition = characterCount;
1131 // TODO - handle secondary cursor
1134 // The natural size needs to be re-calculated.
1135 mImpl->mRecalculateNaturalSize = true;
1137 // Apply modifications to the model
1138 mImpl->mOperationsPending = ALL_OPERATIONS;
1139 UpdateModel( ALL_OPERATIONS );
1140 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1142 UPDATE_ACTUAL_SIZE |
1146 void Controller::InsertTextEvent( const std::string& text )
1148 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1150 // TODO - Optimize this
1151 mImpl->mLogicalModel->mScriptRuns.Clear();
1152 mImpl->mLogicalModel->mFontRuns.Clear();
1153 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1154 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1155 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1156 mImpl->mLogicalModel->mCharacterDirections.Clear();
1157 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1158 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1159 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1160 mImpl->mVisualModel->mGlyphs.Clear();
1161 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1162 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1163 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1164 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1165 mImpl->mVisualModel->mGlyphPositions.Clear();
1166 mImpl->mVisualModel->mLines.Clear();
1168 // Convert text into UTF-32
1169 Vector<Character> utf32Characters;
1170 utf32Characters.Resize( text.size() );
1172 // This is a bit horrible but std::string returns a (signed) char*
1173 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
1175 // Transform a text array encoded in utf8 into an array encoded in utf32.
1176 // It returns the actual number of characters.
1177 Length characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
1178 utf32Characters.Resize( characterCount );
1180 // Insert at current cursor position
1181 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1182 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1184 if( cursorIndex < modifyText.Count() )
1186 modifyText.Insert( modifyText.Begin() + cursorIndex, utf32Characters.Begin(), utf32Characters.End() );
1190 modifyText.Insert( modifyText.End(), utf32Characters.Begin(), utf32Characters.End() );
1193 // Advance the cursor position
1196 // The natural size needs to be re-calculated.
1197 mImpl->mRecalculateNaturalSize = true;
1199 // Apply modifications to the model; TODO - Optimize this
1200 mImpl->mOperationsPending = ALL_OPERATIONS;
1201 UpdateModel( ALL_OPERATIONS );
1202 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1204 UPDATE_ACTUAL_SIZE |
1207 // Queue a cursor reposition event; this must wait until after DoRelayout()
1208 mImpl->mTextInput->mUpdateCursorPosition = true;
1211 void Controller::DeleteTextEvent()
1213 DALI_ASSERT_DEBUG( NULL != mImpl->mTextInput && "Unexpected InsertTextEvent" );
1215 // TODO - Optimize this
1216 mImpl->mLogicalModel->mScriptRuns.Clear();
1217 mImpl->mLogicalModel->mFontRuns.Clear();
1218 mImpl->mLogicalModel->mLineBreakInfo.Clear();
1219 mImpl->mLogicalModel->mWordBreakInfo.Clear();
1220 mImpl->mLogicalModel->mBidirectionalParagraphInfo.Clear();
1221 mImpl->mLogicalModel->mCharacterDirections.Clear();
1222 mImpl->mLogicalModel->mBidirectionalLineInfo.Clear();
1223 mImpl->mLogicalModel->mLogicalToVisualMap.Clear();
1224 mImpl->mLogicalModel->mVisualToLogicalMap.Clear();
1225 mImpl->mVisualModel->mGlyphs.Clear();
1226 mImpl->mVisualModel->mGlyphsToCharacters.Clear();
1227 mImpl->mVisualModel->mCharactersToGlyph.Clear();
1228 mImpl->mVisualModel->mCharactersPerGlyph.Clear();
1229 mImpl->mVisualModel->mGlyphsPerCharacter.Clear();
1230 mImpl->mVisualModel->mGlyphPositions.Clear();
1231 mImpl->mVisualModel->mLines.Clear();
1233 // Delte at current cursor position
1234 Vector<Character>& modifyText = mImpl->mLogicalModel->mText;
1235 CharacterIndex& cursorIndex = mImpl->mTextInput->mPrimaryCursorPosition;
1237 if( cursorIndex > 0 &&
1238 cursorIndex-1 < modifyText.Count() )
1240 modifyText.Remove( modifyText.Begin() + cursorIndex - 1 );
1242 // Cursor position retreat
1246 // The natural size needs to be re-calculated.
1247 mImpl->mRecalculateNaturalSize = true;
1249 // Apply modifications to the model; TODO - Optimize this
1250 mImpl->mOperationsPending = ALL_OPERATIONS;
1251 UpdateModel( ALL_OPERATIONS );
1252 mImpl->mOperationsPending = static_cast<OperationsMask>( LAYOUT |
1254 UPDATE_ACTUAL_SIZE |
1257 // Queue a cursor reposition event; this must wait until after DoRelayout()
1258 mImpl->mTextInput->mUpdateCursorPosition = true;
1261 void Controller::UpdateModel( OperationsMask operationsRequired )
1263 // Calculate the operations to be done.
1264 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1266 Vector<Character>& utf32Characters = mImpl->mLogicalModel->mText;
1268 const Length numberOfCharacters = mImpl->mLogicalModel->GetNumberOfCharacters();
1270 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1271 if( GET_LINE_BREAKS & operations )
1273 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
1274 // calculate the bidirectional info for each 'paragraph'.
1275 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
1276 // is not shaped together).
1277 lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK );
1279 SetLineBreakInfo( utf32Characters,
1283 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1284 if( GET_WORD_BREAKS & operations )
1286 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
1287 wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK );
1289 SetWordBreakInfo( utf32Characters,
1293 const bool getScripts = GET_SCRIPTS & operations;
1294 const bool validateFonts = VALIDATE_FONTS & operations;
1296 Vector<ScriptRun>& scripts = mImpl->mLogicalModel->mScriptRuns;
1297 Vector<FontRun>& validFonts = mImpl->mLogicalModel->mFontRuns;
1299 if( getScripts || validateFonts )
1301 // Validates the fonts assigned by the application or assigns default ones.
1302 // It makes sure all the characters are going to be rendered by the correct font.
1303 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
1307 // Retrieves the scripts used in the text.
1308 multilanguageSupport.SetScripts( utf32Characters,
1315 if( 0u == validFonts.Count() )
1317 // Copy the requested font defaults received via the property system.
1318 // These may not be valid i.e. may not contain glyphs for the necessary scripts.
1319 GetDefaultFonts( validFonts, numberOfCharacters );
1322 // Validates the fonts. If there is a character with no assigned font it sets a default one.
1323 // After this call, fonts are validated.
1324 multilanguageSupport.ValidateFonts( utf32Characters,
1330 Vector<Character> mirroredUtf32Characters;
1331 bool textMirrored = false;
1332 if( BIDI_INFO & operations )
1334 // Count the number of LINE_NO_BREAK to reserve some space for the vector of paragraph's
1335 // bidirectional info.
1337 Length numberOfParagraphs = 0u;
1339 const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
1340 for( Length index = 0u; index < numberOfCharacters; ++index )
1342 if( TextAbstraction::LINE_NO_BREAK == *( lineBreakInfoBuffer + index ) )
1344 ++numberOfParagraphs;
1348 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1349 bidirectionalInfo.Reserve( numberOfParagraphs );
1351 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
1352 SetBidirectionalInfo( utf32Characters,
1355 bidirectionalInfo );
1357 if( 0u != bidirectionalInfo.Count() )
1359 // This paragraph has right to left text. Some characters may need to be mirrored.
1360 // TODO: consider if the mirrored string can be stored as well.
1362 textMirrored = GetMirroredText( utf32Characters, mirroredUtf32Characters );
1364 // Only set the character directions if there is right to left characters.
1365 Vector<CharacterDirection>& directions = mImpl->mLogicalModel->mCharacterDirections;
1366 directions.Resize( numberOfCharacters );
1368 GetCharactersDirection( bidirectionalInfo,
1373 // There is no right to left characters. Clear the directions vector.
1374 mImpl->mLogicalModel->mCharacterDirections.Clear();
1379 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1380 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1381 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1382 if( SHAPE_TEXT & operations )
1384 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
1386 ShapeText( textToShape,
1391 glyphsToCharactersMap,
1392 charactersPerGlyph );
1394 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
1395 mImpl->mVisualModel->CreateGlyphsPerCharacterTable( numberOfCharacters );
1396 mImpl->mVisualModel->CreateCharacterToGlyphTable( numberOfCharacters );
1399 const Length numberOfGlyphs = glyphs.Count();
1401 if( GET_GLYPH_METRICS & operations )
1403 mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), numberOfGlyphs );
1407 bool Controller::DoRelayout( const Size& size,
1408 OperationsMask operationsRequired,
1411 bool viewUpdated( false );
1413 // Calculate the operations to be done.
1414 const OperationsMask operations = static_cast<OperationsMask>( mImpl->mOperationsPending & operationsRequired );
1416 if( LAYOUT & operations )
1418 // Some vectors with data needed to layout and reorder may be void
1419 // after the first time the text has been laid out.
1420 // Fill the vectors again.
1422 Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
1424 Vector<LineBreakInfo>& lineBreakInfo = mImpl->mLogicalModel->mLineBreakInfo;
1425 Vector<WordBreakInfo>& wordBreakInfo = mImpl->mLogicalModel->mWordBreakInfo;
1426 Vector<GlyphInfo>& glyphs = mImpl->mVisualModel->mGlyphs;
1427 Vector<CharacterIndex>& glyphsToCharactersMap = mImpl->mVisualModel->mGlyphsToCharacters;
1428 Vector<Length>& charactersPerGlyph = mImpl->mVisualModel->mCharactersPerGlyph;
1430 // Set the layout parameters.
1431 LayoutParameters layoutParameters( size,
1432 mImpl->mLogicalModel->mText.Begin(),
1433 lineBreakInfo.Begin(),
1434 wordBreakInfo.Begin(),
1437 glyphsToCharactersMap.Begin(),
1438 charactersPerGlyph.Begin() );
1440 // The laid-out lines.
1441 // It's not possible to know in how many lines the text is going to be laid-out,
1442 // but it can be resized at least with the number of 'paragraphs' to avoid
1443 // some re-allocations.
1444 Vector<LineRun>& lines = mImpl->mVisualModel->mLines;
1446 // Delete any previous laid out lines before setting the new ones.
1449 // The capacity of the bidirectional paragraph info is the number of paragraphs.
1450 lines.Reserve( mImpl->mLogicalModel->mBidirectionalParagraphInfo.Capacity() );
1452 // Resize the vector of positions to have the same size than the vector of glyphs.
1453 Vector<Vector2>& glyphPositions = mImpl->mVisualModel->mGlyphPositions;
1454 glyphPositions.Resize( numberOfGlyphs );
1456 // Update the visual model.
1457 viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
1464 // Reorder the lines
1465 if( REORDER & operations )
1467 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mImpl->mLogicalModel->mBidirectionalParagraphInfo;
1469 // Check first if there are paragraphs with bidirectional info.
1470 if( 0u != bidirectionalInfo.Count() )
1473 const Length numberOfLines = mImpl->mVisualModel->GetNumberOfLines();
1475 // Reorder the lines.
1476 Vector<BidirectionalLineInfoRun> lineBidirectionalInfoRuns;
1477 lineBidirectionalInfoRuns.Reserve( numberOfLines ); // Reserve because is not known yet how many lines have right to left characters.
1478 ReorderLines( bidirectionalInfo,
1480 lineBidirectionalInfoRuns );
1482 // Set the bidirectional info into the model.
1483 const Length numberOfBidirectionalInfoRuns = lineBidirectionalInfoRuns.Count();
1484 mImpl->mLogicalModel->SetVisualToLogicalMap( lineBidirectionalInfoRuns.Begin(),
1485 numberOfBidirectionalInfoRuns );
1487 // Set the bidirectional info per line into the layout parameters.
1488 layoutParameters.lineBidirectionalInfoRunsBuffer = lineBidirectionalInfoRuns.Begin();
1489 layoutParameters.numberOfBidirectionalInfoRuns = numberOfBidirectionalInfoRuns;
1491 // Get the character to glyph conversion table and set into the layout.
1492 layoutParameters.charactersToGlyphsBuffer = mImpl->mVisualModel->mCharactersToGlyph.Begin();
1494 // Get the glyphs per character table and set into the layout.
1495 layoutParameters.glyphsPerCharacterBuffer = mImpl->mVisualModel->mGlyphsPerCharacter.Begin();
1497 // Re-layout the text. Reorder those lines with right to left characters.
1498 mImpl->mLayoutEngine.ReLayoutRightToLeftLines( layoutParameters,
1501 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
1502 for( Vector<BidirectionalLineInfoRun>::Iterator it = lineBidirectionalInfoRuns.Begin(),
1503 endIt = lineBidirectionalInfoRuns.End();
1507 BidirectionalLineInfoRun& bidiLineInfo = *it;
1509 free( bidiLineInfo.visualToLogicalMap );
1514 if( ALIGN & operations )
1516 mImpl->mLayoutEngine.Align( layoutParameters,
1522 // Sets the actual size.
1523 if( UPDATE_ACTUAL_SIZE & operations )
1525 mImpl->mVisualModel->SetActualSize( layoutSize );
1531 layoutSize = mImpl->mVisualModel->GetActualSize();
1537 void Controller::CalculateTextAlignment( const Size& size )
1539 // Get the direction of the first character.
1540 const CharacterDirection firstParagraphDirection = mImpl->mLogicalModel->GetCharacterDirection( 0u );
1542 const Size& actualSize = mImpl->mVisualModel->GetActualSize();
1544 // If the first paragraph is right to left swap ALIGN_BEGIN and ALIGN_END;
1545 LayoutEngine::HorizontalAlignment horizontalAlignment = mImpl->mLayoutEngine.GetHorizontalAlignment();
1546 if( firstParagraphDirection &&
1547 ( LayoutEngine::HORIZONTAL_ALIGN_CENTER != horizontalAlignment ) )
1549 if( LayoutEngine::HORIZONTAL_ALIGN_BEGIN == horizontalAlignment )
1551 horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_END;
1555 horizontalAlignment = LayoutEngine::HORIZONTAL_ALIGN_BEGIN;
1559 switch( horizontalAlignment )
1561 case LayoutEngine::HORIZONTAL_ALIGN_BEGIN:
1563 mImpl->mAlignmentOffset.x = 0.f;
1566 case LayoutEngine::HORIZONTAL_ALIGN_CENTER:
1568 const int intOffset = static_cast<int>( 0.5f * ( size.width - actualSize.width ) ); // try to avoid pixel alignment.
1569 mImpl->mAlignmentOffset.x = static_cast<float>( intOffset );
1572 case LayoutEngine::HORIZONTAL_ALIGN_END:
1574 mImpl->mAlignmentOffset.x = size.width - actualSize.width;
1579 const LayoutEngine::VerticalAlignment verticalAlignment = mImpl->mLayoutEngine.GetVerticalAlignment();
1580 switch( verticalAlignment )
1582 case LayoutEngine::VERTICAL_ALIGN_TOP:
1584 mImpl->mAlignmentOffset.y = 0.f;
1587 case LayoutEngine::VERTICAL_ALIGN_CENTER:
1589 const int intOffset = static_cast<int>( 0.5f * ( size.height - actualSize.height ) ); // try to avoid pixel alignment.
1590 mImpl->mAlignmentOffset.y = static_cast<float>( intOffset );
1593 case LayoutEngine::VERTICAL_ALIGN_BOTTOM:
1595 mImpl->mAlignmentOffset.y = size.height - actualSize.height;
1601 View& Controller::GetView()
1603 return mImpl->mView;
1606 LayoutEngine& Controller::GetLayoutEngine()
1608 return mImpl->mLayoutEngine;
1611 void Controller::RequestRelayout()
1613 mImpl->mControlInterface.RequestTextRelayout();
1616 void Controller::KeyboardFocusGainEvent()
1618 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusGainEvent" );
1620 if( mImpl->mTextInput )
1622 TextInput::Event event( TextInput::KEYBOARD_FOCUS_GAIN_EVENT );
1623 mImpl->mTextInput->mEventQueue.push_back( event );
1629 void Controller::KeyboardFocusLostEvent()
1631 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusLostEvent" );
1633 if( mImpl->mTextInput )
1635 TextInput::Event event( TextInput::KEYBOARD_FOCUS_LOST_EVENT );
1636 mImpl->mTextInput->mEventQueue.push_back( event );
1642 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
1644 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyEvent" );
1646 if( mImpl->mTextInput &&
1647 keyEvent.state == KeyEvent::Down )
1649 int keyCode = keyEvent.keyCode;
1650 const std::string& keyString = keyEvent.keyPressed;
1652 // Pre-process to separate modifying events from non-modifying input events.
1653 if( Dali::DALI_KEY_ESCAPE == keyCode )
1655 // Escape key is a special case which causes focus loss
1656 KeyboardFocusLostEvent();
1658 else if( Dali::DALI_KEY_CURSOR_LEFT == keyCode ||
1659 Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
1660 Dali::DALI_KEY_CURSOR_UP == keyCode ||
1661 Dali::DALI_KEY_CURSOR_DOWN == keyCode )
1663 TextInput::Event event( TextInput::CURSOR_KEY_EVENT );
1664 event.p1.mInt = keyCode;
1665 mImpl->mTextInput->mEventQueue.push_back( event );
1667 else if( Dali::DALI_KEY_BACKSPACE == keyCode )
1669 // Queue a delete event
1671 event.type = DELETE_TEXT;
1672 mImpl->mModifyEvents.push_back( event );
1674 else if( !keyString.empty() )
1676 // Queue an insert event
1678 event.type = INSERT_TEXT;
1679 event.text = keyString;
1680 mImpl->mModifyEvents.push_back( event );
1683 mImpl->mTextInput->ChangeState( TextInput::EDITING ); // todo Confirm this is the best place to change the state of
1691 void Controller::TapEvent( unsigned int tapCount, float x, float y )
1693 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );
1695 if( mImpl->mTextInput )
1697 TextInput::Event event( TextInput::TAP_EVENT );
1698 event.p1.mUint = tapCount;
1699 event.p2.mFloat = x;
1700 event.p3.mFloat = y;
1701 mImpl->mTextInput->mEventQueue.push_back( event );
1707 void Controller::PanEvent( Gesture::State state, const Vector2& displacement )
1709 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected PanEvent" );
1711 if( mImpl->mTextInput )
1713 TextInput::Event event( TextInput::PAN_EVENT );
1714 event.p1.mInt = state;
1715 event.p2.mFloat = displacement.x;
1716 event.p3.mFloat = displacement.y;
1717 mImpl->mTextInput->mEventQueue.push_back( event );
1723 void Controller::GrabHandleEvent( GrabHandleState state, float x, float y )
1725 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" );
1727 if( mImpl->mTextInput )
1729 TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
1730 event.p1.mUint = state;
1731 event.p2.mFloat = x;
1732 event.p3.mFloat = y;
1733 mImpl->mTextInput->mEventQueue.push_back( event );
1739 Controller::~Controller()
1744 Controller::Controller( ControlInterface& controlInterface )
1747 mImpl = new Controller::Impl( controlInterface );
1752 } // namespace Toolkit