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( DecoratorPtr decorator )
89 : mDecorator( decorator ),
95 * @brief Helper to move the cursor, grab handle etc.
97 bool ProcessTouchEvents()
99 mDecoratorUpdated = false;
103 for( vector<TextInput::Event>::iterator iter = mEventQueue.begin(); iter != mEventQueue.end(); ++iter )
107 case KEYBOARD_FOCUS_GAIN_EVENT:
109 OnKeyboardFocus( true );
112 case KEYBOARD_FOCUS_LOST_EVENT:
114 OnKeyboardFocus( false );
122 case GRAB_HANDLE_EVENT:
124 OnGrabHandleEvent( *iter );
133 return mDecoratorUpdated;
136 void OnKeyboardFocus( bool hasFocus )
141 void OnTapEvent( const Event& event )
143 if( 1u == event.p1.mUint )
145 mState = TextInput::EDITING;
146 mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY );
147 mDecorator->StartCursorBlink();
148 mDecorator->SetGrabHandleActive( true );
149 mDecorator->SetPosition( PRIMARY_CURSOR, 10, 0, 18 );
150 mDecoratorUpdated = true;
152 else if( 2u == event.p1.mUint )
154 mState = TextInput::SELECTING;
155 mDecorator->SetGrabHandleActive( false );
156 mDecorator->SetSelectionActive( true );
157 mDecoratorUpdated = true;
161 void OnGrabHandleEvent( const Event& event )
166 DecoratorPtr mDecorator;
167 bool mDecoratorUpdated;
172 * This is used to delay handling events until after the model has been updated.
173 * The number of updates to the model is minimized to improve performance.
175 vector<Event> mEventQueue; ///< The queue of touch events etc.
178 struct Controller::Impl
180 Impl( ControlInterface& controlInterface )
181 : mControlInterface( controlInterface ),
183 mOperations( NO_OPERATION ),
187 mLogicalModel = LogicalModel::New();
188 mVisualModel = VisualModel::New();
190 mView.SetVisualModel( mVisualModel );
192 mFontClient = TextAbstraction::FontClient::Get();
200 ControlInterface& mControlInterface;
202 std::string mNewText;
204 LogicalModelPtr mLogicalModel;
205 VisualModelPtr mVisualModel;
209 LayoutEngine mLayoutEngine;
211 TextAbstraction::FontClient mFontClient;
213 OperationsMask mOperations;
217 // Avoid allocating everything for text input until EnableTextInput()
218 Controller::TextInput* mTextInput;
221 ControllerPtr Controller::New( ControlInterface& controlInterface )
223 return ControllerPtr( new Controller( controlInterface ) );
226 void Controller::SetText( const std::string& text )
228 // Keep until size negotiation
229 mImpl->mNewText = text;
230 mImpl->mOperations = ALL_OPERATIONS;
232 if( mImpl->mTextInput )
234 // Cancel previously queued events
235 mImpl->mTextInput->mEventQueue.clear();
237 // TODO - Hide selection decorations
241 void Controller::EnableTextInput( DecoratorPtr decorator )
243 if( !mImpl->mTextInput )
245 mImpl->mTextInput = new TextInput( decorator );
249 bool Controller::Relayout( const Vector2& size )
251 if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
253 // Not worth to relayout if width or height is equal to zero.
257 bool updated = false;
259 if( size != mImpl->mControlSize )
261 updated = DoRelayout( size, mImpl->mOperations );
263 // Do not re-do any operation until something changes.
264 mImpl->mOperations = NO_OPERATION;
266 mImpl->mControlSize = size;
269 if( mImpl->mTextInput )
271 // Move the cursor, grab handle etc.
272 updated = mImpl->mTextInput->ProcessTouchEvents() || updated;
278 bool Controller::DoRelayout( const Vector2& size, OperationsMask operations )
280 bool viewUpdated( false );
282 Vector<Character> utf32Characters;
283 Length characterCount = 0u;
284 if( CONVERT_TO_UTF32 & operations )
286 std::string& text = mImpl->mNewText;
288 // Convert text into UTF-32
289 utf32Characters.Resize( text.size() );
291 // This is a bit horrible but std::string returns a (signed) char*
292 const uint8_t* utf8 = reinterpret_cast<const uint8_t*>( text.c_str() );
294 // Transform a text array encoded in utf8 into an array encoded in utf32.
295 // It returns the actual number of characters.
296 characterCount = Utf8ToUtf32( utf8, text.size(), utf32Characters.Begin() );
297 utf32Characters.Resize( characterCount );
299 // Sets the text into the model.
300 mImpl->mLogicalModel->SetText( utf32Characters.Begin(), characterCount );
302 // Discard temporary text
306 Vector<LineBreakInfo> lineBreakInfo;
307 if( GET_LINE_BREAKS & operations )
309 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
310 // calculate the bidirectional info for each 'paragraph'.
311 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
312 // is not shaped together).
313 lineBreakInfo.Resize( characterCount, TextAbstraction::LINE_NO_BREAK );
315 SetLineBreakInfo( utf32Characters,
318 mImpl->mLogicalModel->SetLineBreakInfo( lineBreakInfo.Begin(), characterCount );
321 const bool getScripts = GET_SCRIPTS & operations;
322 const bool validateFonts = VALIDATE_FONTS & operations;
324 Vector<ScriptRun> scripts;
325 Vector<FontRun> fonts;
326 if( getScripts || validateFonts )
328 // Validates the fonts assigned by the application or assigns default ones.
329 // It makes sure all the characters are going to be rendered by the correct font.
330 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
334 // Retrieves the scripts used in the text.
335 multilanguageSupport.SetScripts( utf32Characters,
339 // Sets the scripts into the model.
340 mImpl->mLogicalModel->SetScripts( scripts.Begin(), scripts.Count() );
345 // Validates the fonts. If there is a character with no assigned font it sets a default one.
346 // After this call, fonts are validated.
347 multilanguageSupport.ValidateFonts( utf32Characters,
351 // Sets the fonts into the model.
352 mImpl->mLogicalModel->SetFonts( fonts.Begin(), fonts.Count() );
356 Vector<GlyphInfo> glyphs;
357 Vector<CharacterIndex> characterIndices;
358 Vector<Length> charactersPerGlyph;
359 if( SHAPE_TEXT & operations )
362 ShapeText( utf32Characters,
368 charactersPerGlyph );
371 if( GET_GLYPH_METRICS & operations )
373 TextAbstraction::FontClient::Get().GetGlyphMetrics( glyphs.Begin(), glyphs.Count() );
376 if( LAYOUT & operations )
378 if( 0u == glyphs.Count() )
380 const Length numberOfGlyphs = mImpl->mVisualModel->GetNumberOfGlyphs();
382 glyphs.Resize( numberOfGlyphs );
383 characterIndices.Resize( numberOfGlyphs );
384 charactersPerGlyph.Resize( numberOfGlyphs );
386 mImpl->mVisualModel->GetGlyphs( glyphs.Begin(),
390 mImpl->mVisualModel->GetGlyphToCharacterMap( characterIndices.Begin(),
394 mImpl->mVisualModel->GetCharactersPerGlyphMap( charactersPerGlyph.Begin(),
399 // Update the visual model
400 mImpl->mLayoutEngine.UpdateVisualModel( size,
404 *mImpl->mVisualModel );
412 Vector3 Controller::GetNaturalSize()
414 // Operations that can be done only once until the text changes.
415 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
423 // Operations that need to be done if the size or the text changes.
424 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
427 const float maxFloat = std::numeric_limits<float>::max();
428 DoRelayout( Vector2( maxFloat, maxFloat ),
429 static_cast<OperationsMask>( onlyOnceOperations |
432 // Do not do again the only once operations.
433 mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations & ~onlyOnceOperations );
435 // Do the size related operations again.
436 mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations | sizeOperations );
438 return Vector3( mImpl->mVisualModel->GetNaturalSize() );
441 float Controller::GetHeightForWidth( float width )
443 // Operations that can be done only once until the text changes.
444 const OperationsMask onlyOnceOperations = static_cast<OperationsMask>( CONVERT_TO_UTF32 |
452 // Operations that need to be done if the size or the text changes.
453 const OperationsMask sizeOperations = static_cast<OperationsMask>( LAYOUT |
456 DoRelayout( Size( width, 0.f ),
457 static_cast<OperationsMask>( onlyOnceOperations |
460 // Do not do again the only once operations.
461 mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations & ~onlyOnceOperations );
463 // Do the size related operations again.
464 mImpl->mOperations = static_cast<OperationsMask>( mImpl->mOperations | sizeOperations );
466 return mImpl->mVisualModel->GetActualSize().height;
469 View& Controller::GetView()
474 LayoutEngine& Controller::GetLayoutEngine()
476 return mImpl->mLayoutEngine;
479 void Controller::RequestRelayout()
481 mImpl->mControlInterface.RequestTextRelayout();
484 void Controller::KeyboardFocusGainEvent()
486 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusGainEvent" );
488 if( mImpl->mTextInput )
490 TextInput::Event event( TextInput::KEYBOARD_FOCUS_GAIN_EVENT );
491 mImpl->mTextInput->mEventQueue.push_back( event );
497 void Controller::KeyboardFocusLostEvent()
499 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected KeyboardFocusLostEvent" );
501 if( mImpl->mTextInput )
503 TextInput::Event event( TextInput::KEYBOARD_FOCUS_LOST_EVENT );
504 mImpl->mTextInput->mEventQueue.push_back( event );
510 void Controller::TapEvent( unsigned int tapCount, float x, float y )
512 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected TapEvent" );
514 if( mImpl->mTextInput )
516 TextInput::Event event( TextInput::TAP_EVENT );
517 event.p1.mUint = tapCount;
520 mImpl->mTextInput->mEventQueue.push_back( event );
526 void Controller::GrabHandleEvent( GrabHandleState state, float x )
528 DALI_ASSERT_DEBUG( mImpl->mTextInput && "Unexpected GrabHandleEvent" );
530 if( mImpl->mTextInput )
532 TextInput::Event event( TextInput::GRAB_HANDLE_EVENT );
533 event.p1.mInt = state;
535 mImpl->mTextInput->mEventQueue.push_back( event );
541 Controller::~Controller()
546 Controller::Controller( ControlInterface& controlInterface )
549 mImpl = new Controller::Impl( controlInterface );
554 } // namespace Toolkit