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/public-api/text/text-controller.h>
22 #include <dali-toolkit/public-api/text/character-set-conversion.h>
23 #include <dali-toolkit/public-api/text/layouts/layout-engine.h>
24 #include <dali-toolkit/public-api/text/logical-model.h>
25 #include <dali-toolkit/public-api/text/multi-language-support.h>
26 #include <dali-toolkit/public-api/text/script-run.h>
27 #include <dali-toolkit/public-api/text/segmentation.h>
28 #include <dali-toolkit/public-api/text/shaper.h>
29 #include <dali-toolkit/public-api/text/text-view.h>
30 #include <dali-toolkit/public-api/text/visual-model.h>
35 #include <dali/public-api/text-abstraction/font-client.h>
48 struct Controller::TextInput
50 // Used to queue input events until DoRelayout()
53 KEYBOARD_FOCUS_GAIN_EVENT,
54 KEYBOARD_FOCUS_LOST_EVENT,
68 Event( EventType eventType )
88 TextInput( LogicalModelPtr logicalModel,
89 VisualModelPtr visualModel,
90 DecoratorPtr decorator )
91 : mLogicalModel( logicalModel ),
92 mVisualModel( visualModel ),
93 mDecorator( decorator ),
99 * @brief Helper to move the cursor, grab handle etc.
101 bool ProcessTouchEvents()
103 mDecoratorUpdated = false;
107 for( vector<TextInput::Event>::iterator iter = mEventQueue.begin(); iter != mEventQueue.end(); ++iter )
111 case KEYBOARD_FOCUS_GAIN_EVENT:
113 OnKeyboardFocus( true );
116 case KEYBOARD_FOCUS_LOST_EVENT:
118 OnKeyboardFocus( false );
126 case GRAB_HANDLE_EVENT:
128 OnGrabHandleEvent( *iter );
137 return mDecoratorUpdated;
140 void OnKeyboardFocus( bool hasFocus )
145 void OnTapEvent( const Event& event )
147 if( 1u == event.p1.mUint )
149 mState = TextInput::EDITING;
150 mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
151 mDecorator->StartCursorBlink();
152 mDecorator->SetGrabHandleActive( true );
154 float xPosition = event.p2.mFloat;
155 float yPosition = event.p3.mFloat;
157 GetClosestCursorPosition( xPosition, yPosition, height );
158 mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height );
160 mDecoratorUpdated = true;
162 else if( 2u == event.p1.mUint )
164 mState = TextInput::SELECTING;
165 mDecorator->SetGrabHandleActive( false );
166 mDecorator->SetSelectionActive( true );
167 mDecoratorUpdated = true;
171 void OnGrabHandleEvent( const Event& event )
173 unsigned int state = event.p1.mUint;
175 if( GRAB_HANDLE_PRESSED == state )
177 float xPosition = event.p2.mFloat;
178 float yPosition = event.p3.mFloat;
181 GetClosestCursorPosition( xPosition, yPosition, height );
183 mDecorator->SetPosition( PRIMARY_CURSOR, xPosition, yPosition, height );
184 mDecoratorUpdated = true;
188 void GetClosestCursorPosition( float& x, float& y, float& height )
190 // TODO - Look at LineRuns first
192 Text::Length numberOfGlyphs = mVisualModel->GetNumberOfGlyphs();
193 if( 0 == numberOfGlyphs )
198 Vector<GlyphInfo> glyphs;
199 glyphs.Resize( numberOfGlyphs );
200 mVisualModel->GetGlyphs( &glyphs[0], 0, numberOfGlyphs );
202 std::vector<Vector2> positions;
203 positions.resize( numberOfGlyphs );
204 mVisualModel->GetGlyphPositions( &positions[0], 0, numberOfGlyphs );
206 unsigned int closestGlyph = 0;
207 float closestDistance = std::numeric_limits<float>::max();
209 for( unsigned int i=0; i<glyphs.Count(); ++i )
211 float glyphX = positions[i].x + glyphs[i].width*0.5f;
212 float glyphY = positions[i].y + glyphs[i].height*0.5f;
214 float distanceToGlyph = fabsf( glyphX - x ) + fabsf( glyphY - y );
216 if( distanceToGlyph < closestDistance )
218 closestDistance = distanceToGlyph;
223 // TODO - Consider RTL languages
224 x = positions[closestGlyph].x + glyphs[closestGlyph].width;
228 TextAbstraction::FontClient::Get().GetFontMetrics( glyphs[closestGlyph].fontId, metrics );
229 height = metrics.height; // TODO - Fix for multi-line
232 LogicalModelPtr mLogicalModel;
233 VisualModelPtr mVisualModel;
234 DecoratorPtr mDecorator;
239 * This is used to delay handling events until after the model has been updated.
240 * The number of updates to the model is minimized to improve performance.
242 vector<Event> mEventQueue; ///< The queue of touch events etc.
244 bool mDecoratorUpdated;
247 struct Controller::Impl
249 Impl( ControlInterface& controlInterface )
250 : mControlInterface( controlInterface ),
252 mOperations( NO_OPERATION ),
256 mLogicalModel = LogicalModel::New();
257 mVisualModel = VisualModel::New();
259 mView.SetVisualModel( mVisualModel );
261 mFontClient = TextAbstraction::FontClient::Get();
269 ControlInterface& mControlInterface;
271 std::string mNewText;
273 LogicalModelPtr mLogicalModel;
274 VisualModelPtr mVisualModel;
278 LayoutEngine mLayoutEngine;
280 TextAbstraction::FontClient mFontClient;
282 OperationsMask mOperations;
286 // Avoid allocating everything for text input until EnableTextInput()
287 Controller::TextInput* mTextInput;
290 ControllerPtr Controller::New( ControlInterface& controlInterface )
292 return ControllerPtr( new Controller( controlInterface ) );
295 void Controller::SetText( const std::string& text )
297 // Keep until size negotiation
298 mImpl->mNewText = text;
299 mImpl->mOperations = ALL_OPERATIONS;
301 if( mImpl->mTextInput )
303 // Cancel previously queued events
304 mImpl->mTextInput->mEventQueue.clear();
306 // TODO - Hide selection decorations
310 void Controller::EnableTextInput( DecoratorPtr decorator )
312 if( !mImpl->mTextInput )
314 mImpl->mTextInput = new TextInput( mImpl->mLogicalModel, mImpl->mVisualModel, decorator );
318 bool Controller::Relayout( const Vector2& size )
320 if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
322 // Not worth to relayout if width or height is equal to zero.
326 bool updated = false;
328 if( size != mImpl->mControlSize )
330 updated = DoRelayout( size, mImpl->mOperations );
332 // Do not re-do any operation until something changes.
333 mImpl->mOperations = NO_OPERATION;
335 mImpl->mControlSize = size;
338 if( mImpl->mTextInput )
340 // Move the cursor, grab handle etc.
341 updated = mImpl->mTextInput->ProcessTouchEvents() || updated;
347 bool Controller::DoRelayout( const Vector2& size, OperationsMask operations )
349 bool viewUpdated( false );
351 Vector<Character> utf32Characters;
352 Length characterCount = 0u;
353 if( CONVERT_TO_UTF32 & operations )
355 std::string& text = mImpl->mNewText;
357 // Convert text into UTF-32
358 utf32Characters.Resize( text.size() );
360 // This is a bit horrible but std::string returns a (signed) char*
361 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
363 // Transform a text array encoded in utf8 into an array encoded in utf32.
364 // It returns the actual number of characters.
365 characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
366 utf32Characters.Resize( characterCount );
368 // Sets the text into the model.
369 mImpl->mLogicalModel->SetText( utf32Characters.Begin(), characterCount );
371 // Discard temporary text
375 Vector<LineBreakInfo> lineBreakInfo;
376 if( GET_LINE_BREAKS & operations )
378 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
379 // calculate the bidirectional info for each 'paragraph'.
380 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
381 // is not shaped together).
382 lineBreakInfo.Resize( characterCount, TextAbstraction::LINE_NO_BREAK );
384 SetLineBreakInfo( utf32Characters,
387 mImpl->mLogicalModel->SetLineBreakInfo( lineBreakInfo.Begin(), characterCount );
390 const bool getScripts = GET_SCRIPTS & operations;
391 const bool validateFonts = VALIDATE_FONTS & operations;
393 Vector<ScriptRun> scripts;
394 Vector<FontRun> fonts;
395 if( getScripts || validateFonts )
397 // Validates the fonts assigned by the application or assigns default ones.
398 // It makes sure all the characters are going to be rendered by the correct font.
399 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
403 // Retrieves the scripts used in the text.
404 multilanguageSupport.SetScripts( utf32Characters,
408 // Sets the scripts into the model.
409 mImpl->mLogicalModel->SetScripts( scripts.Begin(), scripts.Count() );
414 // Validates the fonts. If there is a character with no assigned font it sets a default one.
415 // After this call, fonts are validated.
416 multilanguageSupport.ValidateFonts( utf32Characters,
420 // Sets the fonts into the model.
421 mImpl->mLogicalModel->SetFonts( fonts.Begin(), fonts.Count() );
425 Vector<GlyphInfo> glyphs;
426 Vector<CharacterIndex> characterIndices;
427 Vector<Length> charactersPerGlyph;
428 if( SHAPE_TEXT & operations )
431 ShapeText( utf32Characters,
437 charactersPerGlyph );
440 if( GET_GLYPH_METRICS & operations )
442 mImpl->mFontClient.GetGlyphMetrics( glyphs.Begin(), glyphs.Count() );
445 if( LAYOUT & operations )
447 if( 0u == glyphs.Count() )
449 const Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
451 glyphs.Resize( numberOfGlyphs );
452 characterIndices.Resize( numberOfGlyphs );
453 charactersPerGlyph.Resize( numberOfGlyphs );
455 mImpl->mVisualModel->GetGlyphs( glyphs.Begin(),
459 mImpl->mVisualModel->GetGlyphToCharacterMap( characterIndices.Begin(),
463 mImpl->mVisualModel->GetCharactersPerGlyphMap( charactersPerGlyph.Begin(),
468 // Update the visual model
469 mImpl->mLayoutEngine.UpdateVisualModel( size,
473 *mImpl->mVisualModel );
481 Vector3 Controller::GetNaturalSize()
483 // Operations that can be done only once until the text changes.
484 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
492 // Operations that need to be done if the size or the text changes.
493 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
496 const float maxFloat = std::numeric_limits<float>::max();
497 DoRelayout( Vector2( maxFloat, maxFloat ),
498 static_cast<OperationsMask>( onlyOnceOperations |
501 // Do not do again the only once operations.
502 mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations & ~onlyOnceOperations );
504 // Do the size related operations again.
505 mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations | sizeOperations );
507 return Vector3( mImpl->mVisualModel->GetNaturalSize() );
510 float Controller::GetHeightForWidth( float width )
512 // Operations that can be done only once until the text changes.
513 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
521 // Operations that need to be done if the size or the text changes.
522 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
525 DoRelayout( Size( width, 0.f ),
526 static_cast<OperationsMask>( onlyOnceOperations |
529 // Do not do again the only once operations.
530 mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations & ~onlyOnceOperations );
532 // Do the size related operations again.
533 mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations | sizeOperations );
535 return mImpl->mVisualModel->GetActualSize().height;
538 View& Controller::GetView()
543 LayoutEngine& Controller::GetLayoutEngine()
545 return mImpl->mLayoutEngine;
548 void Controller::RequestRelayout()
550 mImpl->mControlInterface.RequestTextRelayout();
553 void Controller::KeyboardFocusGainEvent()
555 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusGainEvent" );
557 if( mImpl->mTextInput )
559 TextInput::Event event( TextInput::KEYBOARD_FOCUS_GAIN_EVENT );
560 mImpl->mTextInput->mEventQueue.push_back( event );
566 void Controller::KeyboardFocusLostEvent()
568 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusLostEvent" );
570 if( mImpl->mTextInput )
572 TextInput::Event event( TextInput::KEYBOARD_FOCUS_LOST_EVENT );
573 mImpl->mTextInput->mEventQueue.push_back( event );
579 void Controller::TapEvent( unsigned int tapCount, float x, float y )
581 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );
583 if( mImpl->mTextInput )
585 TextInput::Event event( TextInput::TAP_EVENT );
586 event.p1.mUint = tapCount;
589 mImpl->mTextInput->mEventQueue.push_back( event );
595 void Controller::GrabHandleEvent( GrabHandleState state, float x, float y )
597 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" );
599 if( mImpl->mTextInput )
601 TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
602 event.p1.mUint = state;
605 mImpl->mTextInput->mEventQueue.push_back( event );
611 Controller::~Controller()
616 Controller::Controller( ControlInterface& controlInterface )
619 mImpl = new Controller::Impl( controlInterface );
624 } // namespace Toolkit