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>
22 #include <dali-toolkit/internal/text/character-set-conversion.h>
23 #include <dali-toolkit/internal/text/layouts/layout-engine.h>
24 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
25 #include <dali-toolkit/internal/text/logical-model.h>
26 #include <dali-toolkit/internal/text/multi-language-support.h>
27 #include <dali-toolkit/internal/text/script-run.h>
28 #include <dali-toolkit/internal/text/segmentation.h>
29 #include <dali-toolkit/internal/text/shaper.h>
30 #include <dali-toolkit/internal/text/text-view.h>
31 #include <dali-toolkit/internal/text/visual-model.h>
36 #include <dali/public-api/adaptor-framework/key.h>
37 #include <dali/public-api/text-abstraction/font-client.h>
50 struct Controller::TextInput
52 // Used to queue input events until DoRelayout()
55 KEYBOARD_FOCUS_GAIN_EVENT,
56 KEYBOARD_FOCUS_LOST_EVENT,
72 Event( EventType eventType )
92 TextInput( LogicalModelPtr logicalModel,
93 VisualModelPtr visualModel,
94 DecoratorPtr decorator )
95 : mLogicalModel( logicalModel ),
96 mVisualModel( visualModel ),
97 mDecorator( decorator ),
103 * @brief Helper to move the cursor, grab handle etc.
105 bool ProcessInputEvents()
107 mDecoratorUpdated = false;
111 for( vector<TextInput::Event>::iterator iter = mEventQueue.begin(); iter != mEventQueue.end(); ++iter )
115 case KEYBOARD_FOCUS_GAIN_EVENT:
117 OnKeyboardFocus( true );
120 case KEYBOARD_FOCUS_LOST_EVENT:
122 OnKeyboardFocus( false );
135 case GRAB_HANDLE_EVENT:
137 OnGrabHandleEvent( *iter );
146 return mDecoratorUpdated;
149 void OnKeyboardFocus( bool hasFocus )
153 void OnKeyEvent( const Event& event )
155 int keyCode = event.p1.mInt;
157 // Handle state changes
158 if( Dali::DALI_KEY_ESCAPE == keyCode )
160 ChangeState( INACTIVE ); // Escape key ends edit mode
162 else if ( event.p2.mString )
164 // Some text may be selected, hiding keyboard causes an empty keystring to be sent, we don't want to delete highlight in this case
165 ChangeState( EDITING );
168 // Handle the actual key event
169 if( Dali::DALI_KEY_BACKSPACE == keyCode )
171 HandleBackspaceKey();
173 else if( Dali::DALI_KEY_CURSOR_LEFT == keyCode ||
174 Dali::DALI_KEY_CURSOR_RIGHT == keyCode ||
175 Dali::DALI_KEY_CURSOR_UP == keyCode ||
176 Dali::DALI_KEY_CURSOR_DOWN == keyCode )
178 HandleCursorKey( keyCode );
180 else if ( event.p2.mString )
182 HandleKeyString( event.p2.mString );
184 delete [] event.p2.mString;
188 void HandleBackspaceKey()
193 void HandleCursorKey( int keyCode )
198 void HandleKeyString( const char* keyString )
203 void OnTapEvent( const Event& event )
205 unsigned int tapCount = event.p1.mUint;
209 ChangeState( EDITING );
211 float xPosition = event.p2.mFloat;
212 float yPosition = event.p3.mFloat;
214 GetClosestCursorPosition( xPosition, yPosition, height );
215 mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height );
217 mDecoratorUpdated = true;
219 else if( 2u == tapCount )
221 ChangeState( SELECTING );
225 void OnGrabHandleEvent( const Event& event )
227 unsigned int state = event.p1.mUint;
229 if( GRAB_HANDLE_PRESSED == state )
231 float xPosition = event.p2.mFloat;
232 float yPosition = event.p3.mFloat;
235 GetClosestCursorPosition( xPosition, yPosition, height );
237 mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height );
238 mDecoratorUpdated = true;
242 void ChangeState( State newState )
244 if( mState != newState )
248 if( INACTIVE == mState )
250 mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
251 mDecorator->StopCursorBlink();
252 mDecorator->SetGrabHandleActive( false );
253 mDecorator->SetSelectionActive( false );
254 mDecoratorUpdated = true;
256 else if ( SELECTING == mState )
258 mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE );
259 mDecorator->StopCursorBlink();
260 mDecorator->SetGrabHandleActive( false );
261 mDecorator->SetSelectionActive( true );
262 mDecoratorUpdated = true;
264 else if( EDITING == mState )
266 mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
267 mDecorator->StartCursorBlink();
268 mDecorator->SetGrabHandleActive( true );
269 mDecorator->SetSelectionActive( false );
270 mDecoratorUpdated = true;
275 void GetClosestCursorPosition( float& x, float& y, float& height )
277 // TODO - Look at LineRuns first
279 Text::Length numberOfGlyphs = mVisualModel->GetNumberOfGlyphs();
280 if( 0 == numberOfGlyphs )
285 Vector<GlyphInfo> glyphs;
286 glyphs.Resize( numberOfGlyphs );
287 mVisualModel->GetGlyphs( &glyphs[0], 0, numberOfGlyphs );
289 std::vector<Vector2> positions;
290 positions.resize( numberOfGlyphs );
291 mVisualModel->GetGlyphPositions( &positions[0], 0, numberOfGlyphs );
293 unsigned int closestGlyph = 0;
294 float closestDistance = std::numeric_limits<float>::max();
296 for( unsigned int i=0; i<glyphs.Count(); ++i )
298 float glyphX = positions[i].x + glyphs[i].width*0.5f;
299 float glyphY = positions[i].y + glyphs[i].height*0.5f;
301 float distanceToGlyph = fabsf( glyphX - x ) + fabsf( glyphY - y );
303 if( distanceToGlyph < closestDistance )
305 closestDistance = distanceToGlyph;
310 // TODO - Consider RTL languages
311 x = positions[closestGlyph].x + glyphs[closestGlyph].width;
315 TextAbstraction::FontClient::Get().GetFontMetrics( glyphs[closestGlyph].fontId, metrics );
316 height = metrics.height; // TODO - Fix for multi-line
319 LogicalModelPtr mLogicalModel;
320 VisualModelPtr mVisualModel;
321 DecoratorPtr mDecorator;
323 std::string mPlaceholderText;
326 * This is used to delay handling events until after the model has been updated.
327 * The number of updates to the model is minimized to improve performance.
329 vector<Event> mEventQueue; ///< The queue of touch events etc.
333 bool mDecoratorUpdated;
336 struct Controller::FontDefaults
339 : mDefaultPointSize(0.0f),
344 FontId GetFontId( TextAbstraction::FontClient& fontClient )
348 Dali::TextAbstraction::PointSize26Dot6 pointSize = mDefaultPointSize*64;
349 mFontId = fontClient.GetFontId( mDefaultFontFamily, mDefaultFontStyle, pointSize );
355 std::string mDefaultFontFamily;
356 std::string mDefaultFontStyle;
357 float mDefaultPointSize;
361 struct Controller::Impl
363 Impl( ControlInterface& controlInterface )
364 : mControlInterface( controlInterface ),
366 mOperations( NO_OPERATION ),
368 mFontDefaults( NULL ),
371 mLogicalModel = LogicalModel::New();
372 mVisualModel = VisualModel::New();
374 mView.SetVisualModel( mVisualModel );
376 mFontClient = TextAbstraction::FontClient::Get();
384 ControlInterface& mControlInterface;
386 std::string mNewText;
388 LogicalModelPtr mLogicalModel;
389 VisualModelPtr mVisualModel;
393 LayoutEngine mLayoutEngine;
395 TextAbstraction::FontClient mFontClient;
397 OperationsMask mOperations;
401 // Avoid allocating this when the user does not specify a font
402 FontDefaults* mFontDefaults;
404 // Avoid allocating everything for text input until EnableTextInput()
405 Controller::TextInput* mTextInput;
408 ControllerPtr Controller::New( ControlInterface& controlInterface )
410 return ControllerPtr( new Controller( controlInterface ) );
413 void Controller::SetText( const std::string& text )
415 // Keep until size negotiation
416 mImpl->mNewText = text;
417 mImpl->mOperations = ALL_OPERATIONS;
419 if( mImpl->mTextInput )
421 // Cancel previously queued events
422 mImpl->mTextInput->mEventQueue.clear();
424 // TODO - Hide selection decorations
428 void Controller::GetText( std::string& text ) const
430 if( !mImpl->mNewText.empty() )
432 text = mImpl->mNewText;
436 // TODO - Convert from UTF-32
440 void Controller::SetPlaceholderText( const std::string& text )
442 if( !mImpl->mTextInput )
444 mImpl->mTextInput->mPlaceholderText = text;
448 void Controller::GetPlaceholderText( std::string& text ) const
450 if( !mImpl->mTextInput )
452 text = mImpl->mTextInput->mPlaceholderText;
456 void Controller::SetDefaultFontFamily( const std::string& defaultFontFamily )
458 if( !mImpl->mFontDefaults )
460 mImpl->mFontDefaults = new Controller::FontDefaults();
463 mImpl->mFontDefaults->mDefaultFontFamily = defaultFontFamily;
464 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
465 mImpl->mOperations = ALL_OPERATIONS;
468 const std::string& Controller::GetDefaultFontFamily() const
470 if( mImpl->mFontDefaults )
472 return mImpl->mFontDefaults->mDefaultFontFamily;
475 return Dali::String::EMPTY;
478 void Controller::SetDefaultFontStyle( const std::string& defaultFontStyle )
480 if( !mImpl->mFontDefaults )
482 mImpl->mFontDefaults = new Controller::FontDefaults();
485 mImpl->mFontDefaults->mDefaultFontStyle = defaultFontStyle;
486 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
487 mImpl->mOperations = ALL_OPERATIONS;
490 const std::string& Controller::GetDefaultFontStyle() const
492 if( mImpl->mFontDefaults )
494 return mImpl->mFontDefaults->mDefaultFontStyle;
497 return Dali::String::EMPTY;
500 void Controller::SetDefaultPointSize( float pointSize )
502 if( !mImpl->mFontDefaults )
504 mImpl->mFontDefaults = new Controller::FontDefaults();
507 mImpl->mFontDefaults->mDefaultPointSize = pointSize;
508 mImpl->mFontDefaults->mFontId = 0u; // Remove old font ID
509 mImpl->mOperations = ALL_OPERATIONS;
512 float Controller::GetDefaultPointSize() const
514 if( mImpl->mFontDefaults )
516 return mImpl->mFontDefaults->mDefaultPointSize;
522 void Controller::EnableTextInput( DecoratorPtr decorator )
524 if( !mImpl->mTextInput )
526 mImpl->mTextInput = new TextInput( mImpl->mLogicalModel, mImpl->mVisualModel, decorator );
530 bool Controller::Relayout( const Vector2& size )
532 if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
534 // Not worth to relayout if width or height is equal to zero.
538 bool updated = false;
540 if( size != mImpl->mControlSize )
542 updated = DoRelayout( size, mImpl->mOperations );
544 // Do not re-do any operation until something changes.
545 mImpl->mOperations = NO_OPERATION;
547 mImpl->mControlSize = size;
550 if( mImpl->mTextInput )
552 // Move the cursor, grab handle etc.
553 updated = mImpl->mTextInput->ProcessInputEvents() || updated;
559 bool Controller::DoRelayout( const Vector2& size, OperationsMask operations )
561 bool viewUpdated( false );
563 Vector<Character> utf32Characters;
564 Length characterCount = 0u;
565 if( CONVERT_TO_UTF32 & operations )
567 std::string& text = mImpl->mNewText;
569 // Convert text into UTF-32
570 utf32Characters.Resize( text.size() );
572 // This is a bit horrible but std::string returns a (signed) char*
573 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
575 // Transform a text array encoded in utf8 into an array encoded in utf32.
576 // It returns the actual number of characters.
577 characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
578 utf32Characters.Resize( characterCount );
580 // Sets the text into the model.
581 mImpl->mLogicalModel->SetText( utf32Characters.Begin(), characterCount );
583 // Discard temporary text
584 //text.clear(); temporary keep the text. will be fixed in the next patch.
587 Vector<LineBreakInfo> lineBreakInfo;
588 if( GET_LINE_BREAKS & operations )
590 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
591 // calculate the bidirectional info for each 'paragraph'.
592 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
593 // is not shaped together).
594 lineBreakInfo.Resize( characterCount, TextAbstraction::LINE_NO_BREAK );
596 SetLineBreakInfo( utf32Characters,
599 mImpl->mLogicalModel->SetLineBreakInfo( lineBreakInfo.Begin(), characterCount );
602 Vector<WordBreakInfo> wordBreakInfo;
603 if( GET_WORD_BREAKS & operations )
605 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
606 wordBreakInfo.Resize( characterCount, TextAbstraction::WORD_NO_BREAK );
608 SetWordBreakInfo( utf32Characters,
611 mImpl->mLogicalModel->SetWordBreakInfo( wordBreakInfo.Begin(), characterCount );
614 const bool getScripts = GET_SCRIPTS & operations;
615 const bool validateFonts = VALIDATE_FONTS & operations;
617 Vector<ScriptRun> scripts;
618 Vector<FontRun> fonts;
620 if( mImpl->mFontDefaults )
622 // TODO - pass into ValidateFonts
625 if( getScripts || validateFonts )
627 // Validates the fonts assigned by the application or assigns default ones.
628 // It makes sure all the characters are going to be rendered by the correct font.
629 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
633 // Retrieves the scripts used in the text.
634 multilanguageSupport.SetScripts( utf32Characters,
638 // Sets the scripts into the model.
639 mImpl->mLogicalModel->SetScripts( scripts.Begin(), scripts.Count() );
644 // Validates the fonts. If there is a character with no assigned font it sets a default one.
645 // After this call, fonts are validated.
646 multilanguageSupport.ValidateFonts( utf32Characters,
650 // Sets the fonts into the model.
651 mImpl->mLogicalModel->SetFonts( fonts.Begin(), fonts.Count() );
655 Vector<GlyphInfo> glyphs;
656 Vector<CharacterIndex> glyphsToCharactersMap;
657 Vector<Length> charactersPerGlyph;
658 if( SHAPE_TEXT & operations )
661 ShapeText( utf32Characters,
666 glyphsToCharactersMap,
667 charactersPerGlyph );
670 if( GET_GLYPH_METRICS & operations )
672 mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), glyphs.Count() );
675 Length numberOfGlyphs = glyphs.Count();
676 if( 0u != numberOfGlyphs )
678 // Sets the glyphs into the model.
679 mImpl->mVisualModel->SetGlyphs( glyphs.Begin(),
680 glyphsToCharactersMap.Begin(),
681 charactersPerGlyph.Begin(),
685 if( LAYOUT & operations )
687 if( 0u == numberOfGlyphs )
689 const Length numberOfCharacters = mImpl->mLogicalModel->GetNumberOfCharacters();
690 numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
692 lineBreakInfo.Resize( numberOfCharacters );
693 wordBreakInfo.Resize( numberOfCharacters );
694 glyphs.Resize( numberOfGlyphs );
695 glyphsToCharactersMap.Resize( numberOfGlyphs );
696 charactersPerGlyph.Resize( numberOfGlyphs );
698 mImpl->mLogicalModel->GetLineBreakInfo( lineBreakInfo.Begin(),
700 numberOfCharacters );
702 mImpl->mLogicalModel->GetWordBreakInfo( wordBreakInfo.Begin(),
704 numberOfCharacters );
706 mImpl->mVisualModel->GetGlyphs( glyphs.Begin(),
710 mImpl->mVisualModel->GetGlyphToCharacterMap( glyphsToCharactersMap.Begin(),
714 mImpl->mVisualModel->GetCharactersPerGlyphMap( charactersPerGlyph.Begin(),
719 // Set the layout parameters.
720 LayoutParameters layoutParameters( size,
721 lineBreakInfo.Begin(),
722 wordBreakInfo.Begin(),
725 glyphsToCharactersMap.Begin(),
726 charactersPerGlyph.Begin() );
728 // Reserve space to set the positions of the glyphs.
729 Vector<Vector2> glyphPositions;
730 glyphPositions.Resize( numberOfGlyphs );
734 // Update the visual model
735 viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
739 // Sets the positions into the model.
740 mImpl->mVisualModel->SetGlyphPositions( glyphPositions.Begin(),
743 // Sets the actual size.
744 mImpl->mVisualModel->SetActualSize( layoutSize );
750 Vector3 Controller::GetNaturalSize()
752 // TODO - Finish implementing
753 return Vector3::ZERO;
755 // Operations that can be done only once until the text changes.
756 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
764 // Operations that need to be done if the size or the text changes.
765 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
768 const float maxFloat = std::numeric_limits<float>::max();
769 DoRelayout( Vector2( maxFloat, maxFloat ),
770 static_cast<OperationsMask>( onlyOnceOperations |
773 // Do not do again the only once operations.
774 mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations & ~onlyOnceOperations );
776 // Do the size related operations again.
777 mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations | sizeOperations );
779 return Vector3( mImpl->mVisualModel->GetNaturalSize() );
782 float Controller::GetHeightForWidth( float width )
784 // Operations that can be done only once until the text changes.
785 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
793 // Operations that need to be done if the size or the text changes.
794 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
797 DoRelayout( Size( width, 0.f ),
798 static_cast<OperationsMask>( onlyOnceOperations |
801 // Do not do again the only once operations.
802 mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations & ~onlyOnceOperations );
804 // Do the size related operations again.
805 mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations | sizeOperations );
807 return mImpl->mVisualModel->GetActualSize().height;
810 View& Controller::GetView()
815 LayoutEngine& Controller::GetLayoutEngine()
817 return mImpl->mLayoutEngine;
820 void Controller::RequestRelayout()
822 mImpl->mControlInterface.RequestTextRelayout();
825 void Controller::KeyboardFocusGainEvent()
827 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusGainEvent" );
829 if( mImpl->mTextInput )
831 TextInput::Event event( TextInput::KEYBOARD_FOCUS_GAIN_EVENT );
832 mImpl->mTextInput->mEventQueue.push_back( event );
838 void Controller::KeyboardFocusLostEvent()
840 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusLostEvent" );
842 if( mImpl->mTextInput )
844 TextInput::Event event( TextInput::KEYBOARD_FOCUS_LOST_EVENT );
845 mImpl->mTextInput->mEventQueue.push_back( event );
851 bool Controller::KeyEvent( const Dali::KeyEvent& keyEvent )
853 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyEvent" );
855 if( mImpl->mTextInput )
857 TextInput::Event event( TextInput::KEY_EVENT );
858 event.p1.mInt = keyEvent.keyCode;
859 event.p2.mString = NULL;
861 const std::string& keyString = keyEvent.keyPressed;
862 if ( !keyString.empty() )
864 event.p2.mString = new char[keyString.size() + 1];
865 std::copy(keyString.begin(), keyString.end(), event.p2.mString);
866 event.p2.mString[keyString.size()] = '\0';
869 mImpl->mTextInput->mEventQueue.push_back( event );
877 void Controller::TapEvent( unsigned int tapCount, float x, float y )
879 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );
881 if( mImpl->mTextInput )
883 TextInput::Event event( TextInput::TAP_EVENT );
884 event.p1.mUint = tapCount;
887 mImpl->mTextInput->mEventQueue.push_back( event );
893 void Controller::GrabHandleEvent( GrabHandleState state, float x, float y )
895 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" );
897 if( mImpl->mTextInput )
899 TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
900 event.p1.mUint = state;
903 mImpl->mTextInput->mEventQueue.push_back( event );
909 Controller::~Controller()
914 Controller::Controller( ControlInterface& controlInterface )
917 mImpl = new Controller::Impl( controlInterface );
922 } // namespace Toolkit