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/text-abstraction/font-client.h>
49 struct Controller::TextInput
51 // Used to queue input events until DoRelayout()
54 KEYBOARD_FOCUS_GAIN_EVENT,
55 KEYBOARD_FOCUS_LOST_EVENT,
69 Event( EventType eventType )
89 TextInput( LogicalModelPtr logicalModel,
90 VisualModelPtr visualModel,
91 DecoratorPtr decorator )
92 : mLogicalModel( logicalModel ),
93 mVisualModel( visualModel ),
94 mDecorator( decorator ),
100 * @brief Helper to move the cursor, grab handle etc.
102 bool ProcessTouchEvents()
104 mDecoratorUpdated = false;
108 for( vector<TextInput::Event>::iterator iter = mEventQueue.begin(); iter != mEventQueue.end(); ++iter )
112 case KEYBOARD_FOCUS_GAIN_EVENT:
114 OnKeyboardFocus( true );
117 case KEYBOARD_FOCUS_LOST_EVENT:
119 OnKeyboardFocus( false );
127 case GRAB_HANDLE_EVENT:
129 OnGrabHandleEvent( *iter );
138 return mDecoratorUpdated;
141 void OnKeyboardFocus( bool hasFocus )
146 void OnTapEvent( const Event& event )
148 if( 1u == event.p1.mUint )
150 mState = TextInput::EDITING;
151 mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
152 mDecorator->StartCursorBlink();
153 mDecorator->SetGrabHandleActive( true );
155 float xPosition = event.p2.mFloat;
156 float yPosition = event.p3.mFloat;
158 GetClosestCursorPosition( xPosition, yPosition, height );
159 mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height );
161 mDecoratorUpdated = true;
163 else if( 2u == event.p1.mUint )
165 mState = TextInput::SELECTING;
166 mDecorator->SetGrabHandleActive( false );
167 mDecorator->SetSelectionActive( true );
168 mDecoratorUpdated = true;
172 void OnGrabHandleEvent( const Event& event )
174 unsigned int state = event.p1.mUint;
176 if( GRAB_HANDLE_PRESSED == state )
178 float xPosition = event.p2.mFloat;
179 float yPosition = event.p3.mFloat;
182 GetClosestCursorPosition( xPosition, yPosition, height );
184 mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height );
185 mDecoratorUpdated = true;
189 void GetClosestCursorPosition( float& x, float& y, float& height )
191 // TODO - Look at LineRuns first
193 Text::Length numberOfGlyphs = mVisualModel->GetNumberOfGlyphs();
194 if( 0 == numberOfGlyphs )
199 Vector<GlyphInfo> glyphs;
200 glyphs.Resize( numberOfGlyphs );
201 mVisualModel->GetGlyphs( &glyphs[0], 0, numberOfGlyphs );
203 std::vector<Vector2> positions;
204 positions.resize( numberOfGlyphs );
205 mVisualModel->GetGlyphPositions( &positions[0], 0, numberOfGlyphs );
207 unsigned int closestGlyph = 0;
208 float closestDistance = std::numeric_limits<float>::max();
210 for( unsigned int i=0; i<glyphs.Count(); ++i )
212 float glyphX = positions[i].x + glyphs[i].width*0.5f;
213 float glyphY = positions[i].y + glyphs[i].height*0.5f;
215 float distanceToGlyph = fabsf( glyphX - x ) + fabsf( glyphY - y );
217 if( distanceToGlyph < closestDistance )
219 closestDistance = distanceToGlyph;
224 // TODO - Consider RTL languages
225 x = positions[closestGlyph].x + glyphs[closestGlyph].width;
229 TextAbstraction::FontClient::Get().GetFontMetrics( glyphs[closestGlyph].fontId, metrics );
230 height = metrics.height; // TODO - Fix for multi-line
233 LogicalModelPtr mLogicalModel;
234 VisualModelPtr mVisualModel;
235 DecoratorPtr mDecorator;
240 * This is used to delay handling events until after the model has been updated.
241 * The number of updates to the model is minimized to improve performance.
243 vector<Event> mEventQueue; ///< The queue of touch events etc.
245 bool mDecoratorUpdated;
248 struct Controller::Impl
250 Impl( ControlInterface& controlInterface )
251 : mControlInterface( controlInterface ),
253 mOperations( NO_OPERATION ),
257 mLogicalModel = LogicalModel::New();
258 mVisualModel = VisualModel::New();
260 mView.SetVisualModel( mVisualModel );
262 mFontClient = TextAbstraction::FontClient::Get();
270 ControlInterface& mControlInterface;
272 std::string mNewText;
274 LogicalModelPtr mLogicalModel;
275 VisualModelPtr mVisualModel;
279 LayoutEngine mLayoutEngine;
281 TextAbstraction::FontClient mFontClient;
283 OperationsMask mOperations;
287 // Avoid allocating everything for text input until EnableTextInput()
288 Controller::TextInput* mTextInput;
291 ControllerPtr Controller::New( ControlInterface& controlInterface )
293 return ControllerPtr( new Controller( controlInterface ) );
296 void Controller::SetText( const std::string& text )
298 // Keep until size negotiation
299 mImpl->mNewText = text;
300 mImpl->mOperations = ALL_OPERATIONS;
302 if( mImpl->mTextInput )
304 // Cancel previously queued events
305 mImpl->mTextInput->mEventQueue.clear();
307 // TODO - Hide selection decorations
311 void Controller::GetText( std::string& text )
313 if( !mImpl->mNewText.empty() )
315 text = mImpl->mNewText;
319 // TODO - Convert from UTF-32
323 void Controller::EnableTextInput( DecoratorPtr decorator )
325 if( !mImpl->mTextInput )
327 mImpl->mTextInput = new TextInput( mImpl->mLogicalModel, mImpl->mVisualModel, decorator );
331 bool Controller::Relayout( const Vector2& size )
333 if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
335 // Not worth to relayout if width or height is equal to zero.
339 bool updated = false;
341 if( size != mImpl->mControlSize )
343 updated = DoRelayout( size, mImpl->mOperations );
345 // Do not re-do any operation until something changes.
346 mImpl->mOperations = NO_OPERATION;
348 mImpl->mControlSize = size;
351 if( mImpl->mTextInput )
353 // Move the cursor, grab handle etc.
354 updated = mImpl->mTextInput->ProcessTouchEvents() || updated;
360 bool Controller::DoRelayout( const Vector2& size, OperationsMask operations )
362 bool viewUpdated( false );
364 Vector<Character> utf32Characters;
365 Length characterCount = 0u;
366 if( CONVERT_TO_UTF32 & operations )
368 std::string& text = mImpl->mNewText;
370 // Convert text into UTF-32
371 utf32Characters.Resize( text.size() );
373 // This is a bit horrible but std::string returns a (signed) char*
374 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
376 // Transform a text array encoded in utf8 into an array encoded in utf32.
377 // It returns the actual number of characters.
378 characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
379 utf32Characters.Resize( characterCount );
381 // Sets the text into the model.
382 mImpl->mLogicalModel->SetText( utf32Characters.Begin(), characterCount );
384 // Discard temporary text
385 //text.clear(); temporary keep the text. will be fixed in the next patch.
388 Vector<LineBreakInfo> lineBreakInfo;
389 if( GET_LINE_BREAKS & operations )
391 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
392 // calculate the bidirectional info for each 'paragraph'.
393 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
394 // is not shaped together).
395 lineBreakInfo.Resize( characterCount, TextAbstraction::LINE_NO_BREAK );
397 SetLineBreakInfo( utf32Characters,
400 mImpl->mLogicalModel->SetLineBreakInfo( lineBreakInfo.Begin(), characterCount );
403 Vector<WordBreakInfo> wordBreakInfo;
404 if( GET_WORD_BREAKS & operations )
406 // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines).
407 wordBreakInfo.Resize( characterCount, TextAbstraction::WORD_NO_BREAK );
409 SetWordBreakInfo( utf32Characters,
412 mImpl->mLogicalModel->SetWordBreakInfo( wordBreakInfo.Begin(), characterCount );
415 const bool getScripts = GET_SCRIPTS & operations;
416 const bool validateFonts = VALIDATE_FONTS & operations;
418 Vector<ScriptRun> scripts;
419 Vector<FontRun> fonts;
420 if( getScripts || validateFonts )
422 // Validates the fonts assigned by the application or assigns default ones.
423 // It makes sure all the characters are going to be rendered by the correct font.
424 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
428 // Retrieves the scripts used in the text.
429 multilanguageSupport.SetScripts( utf32Characters,
433 // Sets the scripts into the model.
434 mImpl->mLogicalModel->SetScripts( scripts.Begin(), scripts.Count() );
439 // Validates the fonts. If there is a character with no assigned font it sets a default one.
440 // After this call, fonts are validated.
441 multilanguageSupport.ValidateFonts( utf32Characters,
445 // Sets the fonts into the model.
446 mImpl->mLogicalModel->SetFonts( fonts.Begin(), fonts.Count() );
450 Vector<GlyphInfo> glyphs;
451 Vector<CharacterIndex> glyphsToCharactersMap;
452 Vector<Length> charactersPerGlyph;
453 if( SHAPE_TEXT & operations )
456 ShapeText( utf32Characters,
461 glyphsToCharactersMap,
462 charactersPerGlyph );
465 if( GET_GLYPH_METRICS & operations )
467 mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), glyphs.Count() );
470 Length numberOfGlyphs = glyphs.Count();
471 if( 0u != numberOfGlyphs )
473 // Sets the glyphs into the model.
474 mImpl->mVisualModel->SetGlyphs( glyphs.Begin(),
475 glyphsToCharactersMap.Begin(),
476 charactersPerGlyph.Begin(),
480 if( LAYOUT & operations )
482 if( 0u == numberOfGlyphs )
484 const Length numberOfCharacters = mImpl->mLogicalModel->GetNumberOfCharacters();
485 numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
487 lineBreakInfo.Resize( numberOfCharacters );
488 wordBreakInfo.Resize( numberOfCharacters );
489 glyphs.Resize( numberOfGlyphs );
490 glyphsToCharactersMap.Resize( numberOfGlyphs );
491 charactersPerGlyph.Resize( numberOfGlyphs );
493 mImpl->mLogicalModel->GetLineBreakInfo( lineBreakInfo.Begin(),
495 numberOfCharacters );
497 mImpl->mLogicalModel->GetWordBreakInfo( wordBreakInfo.Begin(),
499 numberOfCharacters );
501 mImpl->mVisualModel->GetGlyphs( glyphs.Begin(),
505 mImpl->mVisualModel->GetGlyphToCharacterMap( glyphsToCharactersMap.Begin(),
509 mImpl->mVisualModel->GetCharactersPerGlyphMap( charactersPerGlyph.Begin(),
514 // Set the layout parameters.
515 LayoutParameters layoutParameters( size,
516 lineBreakInfo.Begin(),
517 wordBreakInfo.Begin(),
520 glyphsToCharactersMap.Begin(),
521 charactersPerGlyph.Begin() );
523 // Reserve space to set the positions of the glyphs.
524 Vector<Vector2> glyphPositions;
525 glyphPositions.Resize( numberOfGlyphs );
529 // Update the visual model
530 viewUpdated = mImpl->mLayoutEngine.LayoutText( layoutParameters,
534 // Sets the positions into the model.
535 mImpl->mVisualModel->SetGlyphPositions( glyphPositions.Begin(),
538 // Sets the actual size.
539 mImpl->mVisualModel->SetActualSize( layoutSize );
545 Vector3 Controller::GetNaturalSize()
547 // TODO - Finish implementing
548 return Vector3::ZERO;
550 // Operations that can be done only once until the text changes.
551 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
559 // Operations that need to be done if the size or the text changes.
560 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
563 const float maxFloat = std::numeric_limits<float>::max();
564 DoRelayout( Vector2( maxFloat, maxFloat ),
565 static_cast<OperationsMask>( onlyOnceOperations |
568 // Do not do again the only once operations.
569 mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations & ~onlyOnceOperations );
571 // Do the size related operations again.
572 mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations | sizeOperations );
574 return Vector3( mImpl->mVisualModel->GetNaturalSize() );
577 float Controller::GetHeightForWidth( float width )
579 // Operations that can be done only once until the text changes.
580 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
588 // Operations that need to be done if the size or the text changes.
589 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
592 DoRelayout( Size( width, 0.f ),
593 static_cast<OperationsMask>( onlyOnceOperations |
596 // Do not do again the only once operations.
597 mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations & ~onlyOnceOperations );
599 // Do the size related operations again.
600 mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations | sizeOperations );
602 return mImpl->mVisualModel->GetActualSize().height;
605 View& Controller::GetView()
610 LayoutEngine& Controller::GetLayoutEngine()
612 return mImpl->mLayoutEngine;
615 void Controller::RequestRelayout()
617 mImpl->mControlInterface.RequestTextRelayout();
620 void Controller::KeyboardFocusGainEvent()
622 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusGainEvent" );
624 if( mImpl->mTextInput )
626 TextInput::Event event( TextInput::KEYBOARD_FOCUS_GAIN_EVENT );
627 mImpl->mTextInput->mEventQueue.push_back( event );
633 void Controller::KeyboardFocusLostEvent()
635 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusLostEvent" );
637 if( mImpl->mTextInput )
639 TextInput::Event event( TextInput::KEYBOARD_FOCUS_LOST_EVENT );
640 mImpl->mTextInput->mEventQueue.push_back( event );
646 void Controller::TapEvent( unsigned int tapCount, float x, float y )
648 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );
650 if( mImpl->mTextInput )
652 TextInput::Event event( TextInput::TAP_EVENT );
653 event.p1.mUint = tapCount;
656 mImpl->mTextInput->mEventQueue.push_back( event );
662 void Controller::GrabHandleEvent( GrabHandleState state, float x, float y )
664 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" );
666 if( mImpl->mTextInput )
668 TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
669 event.p1.mUint = state;
672 mImpl->mTextInput->mEventQueue.push_back( event );
678 Controller::~Controller()
683 Controller::Controller( ControlInterface& controlInterface )
686 mImpl = new Controller::Impl( controlInterface );
691 } // namespace Toolkit