2 * Copyright (c) 2021 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-impl.h>
22 #include <dali/integration-api/debug.h>
23 #include <dali/public-api/rendering/renderer.h>
26 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
27 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.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/color-segmentation.h>
31 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
32 #include <dali-toolkit/internal/text/hyphenator.h>
33 #include <dali-toolkit/internal/text/multi-language-support.h>
34 #include <dali-toolkit/internal/text/segmentation.h>
35 #include <dali-toolkit/internal/text/shaper.h>
36 #include <dali-toolkit/internal/text/text-control-interface.h>
37 #include <dali-toolkit/internal/text/text-controller-impl-event-handler.h>
38 #include <dali-toolkit/internal/text/text-editable-control-interface.h>
39 #include <dali-toolkit/internal/text/text-enumerations-impl.h>
40 #include <dali-toolkit/internal/text/text-run-container.h>
41 #include <dali-toolkit/internal/text/text-selection-handle-controller.h>
47 #if defined(DEBUG_ENABLED)
48 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
51 struct BackgroundVertex
53 Vector2 mPosition; ///< Vertex posiiton
54 Vector4 mColor; ///< Vertex color
59 Vector<BackgroundVertex> mVertices; ///< container of vertices
60 Vector<unsigned short> mIndices; ///< container of indices
63 // The relative luminance of a color is defined as (L = 0.2126 * R + 0.7152 * G + 0.0722 * B)
64 // based on W3C Recommendations (https://www.w3.org/TR/WCAG20/)
65 const float BRIGHTNESS_THRESHOLD = 0.179f;
66 const float CONSTANT_R = 0.2126f;
67 const float CONSTANT_G = 0.7152f;
68 const float CONSTANT_B = 0.0722f;
69 const Dali::Vector4 BLACK(0.f, 0.f, 0.f, 1.f);
70 const Dali::Vector4 WHITE(1.f, 1.f, 1.f, 1.f);
71 const Dali::Vector4 LIGHT_BLUE(0.75f, 0.96f, 1.f, 1.f);
72 const Dali::Vector4 BACKGROUND_SUB4(0.58f, 0.87f, 0.96f, 1.f);
73 const Dali::Vector4 BACKGROUND_SUB5(0.83f, 0.94f, 0.98f, 1.f);
74 const Dali::Vector4 BACKGROUND_SUB6(1.f, 0.5f, 0.5f, 1.f);
75 const Dali::Vector4 BACKGROUND_SUB7(1.f, 0.8f, 0.8f, 1.f);
85 EventData::EventData(DecoratorPtr decorator, InputMethodContext& inputMethodContext)
86 : mDecorator(decorator),
87 mInputMethodContext(inputMethodContext),
88 mPlaceholderFont(nullptr),
89 mPlaceholderTextActive(),
90 mPlaceholderTextInactive(),
91 mPlaceholderTextColor(0.8f, 0.8f, 0.8f, 0.8f), // This color has been published in the Public API (placeholder-properties.h).
93 mInputStyleChangedQueue(),
94 mPreviousState(INACTIVE),
96 mPrimaryCursorPosition(0u),
97 mLeftSelectionPosition(0u),
98 mRightSelectionPosition(0u),
99 mPreEditStartPosition(0u),
101 mCursorHookPositionX(0.f),
102 mDoubleTapAction(Controller::NoTextTap::NO_ACTION),
103 mLongPressAction(Controller::NoTextTap::SHOW_SELECTION_POPUP),
104 mIsShowingPlaceholderText(false),
106 mDecoratorUpdated(false),
107 mCursorBlinkEnabled(true),
108 mGrabHandleEnabled(true),
109 mGrabHandlePopupEnabled(true),
110 mSelectionEnabled(true),
111 mUpdateCursorHookPosition(false),
112 mUpdateCursorPosition(false),
113 mUpdateGrabHandlePosition(false),
114 mUpdateLeftSelectionPosition(false),
115 mUpdateRightSelectionPosition(false),
116 mIsLeftHandleSelected(false),
117 mIsRightHandleSelected(false),
118 mUpdateHighlightBox(false),
119 mScrollAfterUpdatePosition(false),
120 mScrollAfterDelete(false),
121 mAllTextSelected(false),
122 mUpdateInputStyle(false),
123 mPasswordInput(false),
124 mCheckScrollAmount(false),
125 mIsPlaceholderPixelSize(false),
126 mIsPlaceholderElideEnabled(false),
127 mPlaceholderEllipsisFlag(false),
128 mShiftSelectionFlag(true),
129 mUpdateAlignment(false),
130 mEditingEnabled(true)
134 bool Controller::Impl::ProcessInputEvents()
136 return ControllerImplEventHandler::ProcessInputEvents(*this);
139 void Controller::Impl::NotifyInputMethodContext()
141 if(mEventData && mEventData->mInputMethodContext)
143 CharacterIndex cursorPosition = GetLogicalCursorPosition();
145 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces(0u);
147 // Update the cursor position by removing the initial white spaces.
148 if(cursorPosition < numberOfWhiteSpaces)
154 cursorPosition -= numberOfWhiteSpaces;
157 mEventData->mInputMethodContext.SetCursorPosition(cursorPosition);
158 mEventData->mInputMethodContext.NotifyCursorPosition();
162 void Controller::Impl::NotifyInputMethodContextMultiLineStatus()
164 if(mEventData && mEventData->mInputMethodContext)
166 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
167 mEventData->mInputMethodContext.NotifyTextInputMultiLine(layout == Text::Layout::Engine::MULTI_LINE_BOX);
171 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
173 CharacterIndex cursorPosition = 0u;
177 if((EventData::SELECTING == mEventData->mState) ||
178 (EventData::SELECTION_HANDLE_PANNING == mEventData->mState))
180 cursorPosition = std::min(mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition);
184 cursorPosition = mEventData->mPrimaryCursorPosition;
188 return cursorPosition;
191 Length Controller::Impl::GetNumberOfWhiteSpaces(CharacterIndex index) const
193 Length numberOfWhiteSpaces = 0u;
195 // Get the buffer to the text.
196 Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
198 const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
199 for(; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces)
201 if(!TextAbstraction::IsWhiteSpace(*(utf32CharacterBuffer + index)))
207 return numberOfWhiteSpaces;
210 void Controller::Impl::GetText(CharacterIndex index, std::string& text) const
212 // Get the total number of characters.
213 Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
215 // Retrieve the text.
216 if(0u != numberOfCharacters)
218 Utf32ToUtf8(mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text);
222 void Controller::Impl::CalculateTextUpdateIndices(Length& numberOfCharacters)
224 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
225 mTextUpdateInfo.mStartGlyphIndex = 0u;
226 mTextUpdateInfo.mStartLineIndex = 0u;
227 numberOfCharacters = 0u;
229 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
230 if(0u == numberOfParagraphs)
232 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
233 numberOfCharacters = 0u;
235 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
237 // Nothing else to do if there are no paragraphs.
241 // Find the paragraphs to be updated.
242 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
243 if(mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters)
245 // Text is being added at the end of the current text.
246 if(mTextUpdateInfo.mIsLastCharacterNewParagraph)
248 // Text is being added in a new paragraph after the last character of the text.
249 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
250 numberOfCharacters = 0u;
251 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
253 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
254 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
256 // Nothing else to do;
260 paragraphsToBeUpdated.PushBack(numberOfParagraphs - 1u);
264 Length numberOfCharactersToUpdate = 0u;
265 if(mTextUpdateInfo.mFullRelayoutNeeded)
267 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
271 numberOfCharactersToUpdate = (mTextUpdateInfo.mNumberOfCharactersToRemove > 0u) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
273 mModel->mLogicalModel->FindParagraphs(mTextUpdateInfo.mCharacterIndex,
274 numberOfCharactersToUpdate,
275 paragraphsToBeUpdated);
278 if(0u != paragraphsToBeUpdated.Count())
280 const ParagraphRunIndex firstParagraphIndex = *(paragraphsToBeUpdated.Begin());
281 const ParagraphRun& firstParagraph = *(mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex);
282 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
284 ParagraphRunIndex lastParagraphIndex = *(paragraphsToBeUpdated.End() - 1u);
285 const ParagraphRun& lastParagraph = *(mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex);
287 if((mTextUpdateInfo.mNumberOfCharactersToRemove > 0u) && // Some character are removed.
288 (lastParagraphIndex < numberOfParagraphs - 1u) && // There is a next paragraph.
289 ((lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters) == // The last removed character is the new paragraph character.
290 (mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove)))
292 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
293 const ParagraphRun& lastParagraph = *(mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u);
295 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
299 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
303 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
304 mTextUpdateInfo.mStartGlyphIndex = *(mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex);
307 void Controller::Impl::ClearFullModelData(OperationsMask operations)
309 if(NO_OPERATION != (GET_LINE_BREAKS & operations))
311 mModel->mLogicalModel->mLineBreakInfo.Clear();
312 mModel->mLogicalModel->mParagraphInfo.Clear();
315 if(NO_OPERATION != (GET_SCRIPTS & operations))
317 mModel->mLogicalModel->mScriptRuns.Clear();
320 if(NO_OPERATION != (VALIDATE_FONTS & operations))
322 mModel->mLogicalModel->mFontRuns.Clear();
325 if(0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count())
327 if(NO_OPERATION != (BIDI_INFO & operations))
329 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
330 mModel->mLogicalModel->mCharacterDirections.Clear();
333 if(NO_OPERATION != (REORDER & operations))
335 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
336 for(Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
337 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
341 BidirectionalLineInfoRun& bidiLineInfo = *it;
343 free(bidiLineInfo.visualToLogicalMap);
344 bidiLineInfo.visualToLogicalMap = NULL;
346 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
350 if(NO_OPERATION != (SHAPE_TEXT & operations))
352 mModel->mVisualModel->mGlyphs.Clear();
353 mModel->mVisualModel->mGlyphsToCharacters.Clear();
354 mModel->mVisualModel->mCharactersToGlyph.Clear();
355 mModel->mVisualModel->mCharactersPerGlyph.Clear();
356 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
357 mModel->mVisualModel->mGlyphPositions.Clear();
360 if(NO_OPERATION != (LAYOUT & operations))
362 mModel->mVisualModel->mLines.Clear();
365 if(NO_OPERATION != (COLOR & operations))
367 mModel->mVisualModel->mColorIndices.Clear();
368 mModel->mVisualModel->mBackgroundColorIndices.Clear();
372 void Controller::Impl::ClearCharacterModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
374 const CharacterIndex endIndexPlusOne = endIndex + 1u;
376 if(NO_OPERATION != (GET_LINE_BREAKS & operations))
378 // Clear the line break info.
379 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
381 mModel->mLogicalModel->mLineBreakInfo.Erase(lineBreakInfoBuffer + startIndex,
382 lineBreakInfoBuffer + endIndexPlusOne);
384 // Clear the paragraphs.
385 ClearCharacterRuns(startIndex,
387 mModel->mLogicalModel->mParagraphInfo);
390 if(NO_OPERATION != (GET_SCRIPTS & operations))
392 // Clear the scripts.
393 ClearCharacterRuns(startIndex,
395 mModel->mLogicalModel->mScriptRuns);
398 if(NO_OPERATION != (VALIDATE_FONTS & operations))
401 ClearCharacterRuns(startIndex,
403 mModel->mLogicalModel->mFontRuns);
406 if(0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count())
408 if(NO_OPERATION != (BIDI_INFO & operations))
410 // Clear the bidirectional paragraph info.
411 ClearCharacterRuns(startIndex,
413 mModel->mLogicalModel->mBidirectionalParagraphInfo);
415 // Clear the character's directions.
416 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
418 mModel->mLogicalModel->mCharacterDirections.Erase(characterDirectionsBuffer + startIndex,
419 characterDirectionsBuffer + endIndexPlusOne);
422 if(NO_OPERATION != (REORDER & operations))
424 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
425 uint32_t endRemoveIndex = startRemoveIndex;
426 ClearCharacterRuns(startIndex,
428 mModel->mLogicalModel->mBidirectionalLineInfo,
432 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
434 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
435 for(Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
436 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
440 BidirectionalLineInfoRun& bidiLineInfo = *it;
442 free(bidiLineInfo.visualToLogicalMap);
443 bidiLineInfo.visualToLogicalMap = NULL;
446 mModel->mLogicalModel->mBidirectionalLineInfo.Erase(bidirectionalLineInfoBuffer + startRemoveIndex,
447 bidirectionalLineInfoBuffer + endRemoveIndex);
452 void Controller::Impl::ClearGlyphModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
454 const CharacterIndex endIndexPlusOne = endIndex + 1u;
455 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
457 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
458 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
459 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
461 const GlyphIndex endGlyphIndexPlusOne = *(charactersToGlyphBuffer + endIndex) + *(glyphsPerCharacterBuffer + endIndex);
462 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
464 if(NO_OPERATION != (SHAPE_TEXT & operations))
466 // Update the character to glyph indices.
467 for(Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
468 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
472 CharacterIndex& index = *it;
473 index -= numberOfGlyphsRemoved;
476 // Clear the character to glyph conversion table.
477 mModel->mVisualModel->mCharactersToGlyph.Erase(charactersToGlyphBuffer + startIndex,
478 charactersToGlyphBuffer + endIndexPlusOne);
480 // Clear the glyphs per character table.
481 mModel->mVisualModel->mGlyphsPerCharacter.Erase(glyphsPerCharacterBuffer + startIndex,
482 glyphsPerCharacterBuffer + endIndexPlusOne);
484 // Clear the glyphs buffer.
485 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
486 mModel->mVisualModel->mGlyphs.Erase(glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
487 glyphsBuffer + endGlyphIndexPlusOne);
489 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
491 // Update the glyph to character indices.
492 for(Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
493 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
497 CharacterIndex& index = *it;
498 index -= numberOfCharactersRemoved;
501 // Clear the glyphs to characters buffer.
502 mModel->mVisualModel->mGlyphsToCharacters.Erase(glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
503 glyphsToCharactersBuffer + endGlyphIndexPlusOne);
505 // Clear the characters per glyph buffer.
506 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
507 mModel->mVisualModel->mCharactersPerGlyph.Erase(charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
508 charactersPerGlyphBuffer + endGlyphIndexPlusOne);
510 // Should pass if mGlyphPositions has already been cleared in Controller::Relayouter::Relayout
511 if(0u != mModel->mVisualModel->mGlyphPositions.Count())
513 // Clear the positions buffer.
514 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
515 mModel->mVisualModel->mGlyphPositions.Erase(positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
516 positionsBuffer + endGlyphIndexPlusOne);
520 if(NO_OPERATION != (LAYOUT & operations))
523 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
524 uint32_t endRemoveIndex = startRemoveIndex;
525 ClearCharacterRuns(startIndex,
527 mModel->mVisualModel->mLines,
531 // Will update the glyph runs.
532 startRemoveIndex = mModel->mVisualModel->mLines.Count();
533 endRemoveIndex = startRemoveIndex;
534 ClearGlyphRuns(mTextUpdateInfo.mStartGlyphIndex,
535 endGlyphIndexPlusOne - 1u,
536 mModel->mVisualModel->mLines,
540 // Set the line index from where to insert the new laid-out lines.
541 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
543 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
544 mModel->mVisualModel->mLines.Erase(linesBuffer + startRemoveIndex,
545 linesBuffer + endRemoveIndex);
548 if(NO_OPERATION != (COLOR & operations))
550 if(0u != mModel->mVisualModel->mColorIndices.Count())
552 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
553 mModel->mVisualModel->mColorIndices.Erase(colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
554 colorIndexBuffer + endGlyphIndexPlusOne);
557 if(0u != mModel->mVisualModel->mBackgroundColorIndices.Count())
559 ColorIndex* backgroundColorIndexBuffer = mModel->mVisualModel->mBackgroundColorIndices.Begin();
560 mModel->mVisualModel->mBackgroundColorIndices.Erase(backgroundColorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
561 backgroundColorIndexBuffer + endGlyphIndexPlusOne);
566 void Controller::Impl::ClearModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
568 if(mTextUpdateInfo.mClearAll ||
569 ((0u == startIndex) &&
570 (mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u)))
572 ClearFullModelData(operations);
576 // Clear the model data related with characters.
577 ClearCharacterModelData(startIndex, endIndex, operations);
579 // Clear the model data related with glyphs.
580 ClearGlyphModelData(startIndex, endIndex, operations);
583 // The estimated number of lines. Used to avoid reallocations when layouting.
584 mTextUpdateInfo.mEstimatedNumberOfLines = std::max(mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count());
586 mModel->mVisualModel->ClearCaches();
589 bool Controller::Impl::UpdateModel(OperationsMask operationsRequired)
591 DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::UpdateModel\n");
593 // Calculate the operations to be done.
594 const OperationsMask operations = static_cast<OperationsMask>(mOperationsPending & operationsRequired);
596 if(NO_OPERATION == operations)
598 // Nothing to do if no operations are pending and required.
602 Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
603 Vector<Character> displayCharacters;
604 bool useHiddenText = false;
605 if(mHiddenInput && mEventData != nullptr && !mEventData->mIsShowingPlaceholderText)
607 mHiddenInput->Substitute(srcCharacters, displayCharacters);
608 useHiddenText = true;
611 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
612 const Length numberOfCharacters = utf32Characters.Count();
614 // Index to the first character of the first paragraph to be updated.
615 CharacterIndex startIndex = 0u;
616 // Number of characters of the paragraphs to be removed.
617 Length paragraphCharacters = 0u;
619 CalculateTextUpdateIndices(paragraphCharacters);
621 // Check whether the indices for updating the text is valid
622 if(numberOfCharacters > 0u &&
623 (mTextUpdateInfo.mParagraphCharacterIndex > numberOfCharacters ||
624 mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters))
626 std::string currentText;
627 Utf32ToUtf8(mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText);
629 DALI_LOG_ERROR("Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n");
630 DALI_LOG_ERROR("Number of characters: %d, current text is: %s\n", numberOfCharacters, currentText.c_str());
632 // Dump mTextUpdateInfo
633 DALI_LOG_ERROR("Dump mTextUpdateInfo:\n");
634 DALI_LOG_ERROR(" mTextUpdateInfo.mCharacterIndex = %u\n", mTextUpdateInfo.mCharacterIndex);
635 DALI_LOG_ERROR(" mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", mTextUpdateInfo.mNumberOfCharactersToRemove);
636 DALI_LOG_ERROR(" mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", mTextUpdateInfo.mNumberOfCharactersToAdd);
637 DALI_LOG_ERROR(" mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", mTextUpdateInfo.mPreviousNumberOfCharacters);
638 DALI_LOG_ERROR(" mTextUpdateInfo.mParagraphCharacterIndex = %u\n", mTextUpdateInfo.mParagraphCharacterIndex);
639 DALI_LOG_ERROR(" mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", mTextUpdateInfo.mRequestedNumberOfCharacters);
640 DALI_LOG_ERROR(" mTextUpdateInfo.mStartGlyphIndex = %u\n", mTextUpdateInfo.mStartGlyphIndex);
641 DALI_LOG_ERROR(" mTextUpdateInfo.mStartLineIndex = %u\n", mTextUpdateInfo.mStartLineIndex);
642 DALI_LOG_ERROR(" mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", mTextUpdateInfo.mEstimatedNumberOfLines);
643 DALI_LOG_ERROR(" mTextUpdateInfo.mClearAll = %d\n", mTextUpdateInfo.mClearAll);
644 DALI_LOG_ERROR(" mTextUpdateInfo.mFullRelayoutNeeded = %d\n", mTextUpdateInfo.mFullRelayoutNeeded);
645 DALI_LOG_ERROR(" mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", mTextUpdateInfo.mIsLastCharacterNewParagraph);
650 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
652 if(mTextUpdateInfo.mClearAll ||
653 (0u != paragraphCharacters))
655 ClearModelData(startIndex, startIndex + ((paragraphCharacters > 0u) ? paragraphCharacters - 1u : 0u), operations);
658 mTextUpdateInfo.mClearAll = false;
660 // Whether the model is updated.
661 bool updated = false;
663 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
664 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
666 if(NO_OPERATION != (GET_LINE_BREAKS & operations))
668 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
669 // calculate the bidirectional info for each 'paragraph'.
670 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
671 // is not shaped together).
672 lineBreakInfo.Resize(numberOfCharacters, TextAbstraction::LINE_NO_BREAK);
674 SetLineBreakInfo(utf32Characters,
676 requestedNumberOfCharacters,
679 if(mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
680 mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::MIXED))
682 CharacterIndex end = startIndex + requestedNumberOfCharacters;
683 LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
685 for(CharacterIndex index = startIndex; index < end; index++)
687 CharacterIndex wordEnd = index;
688 while((*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_ALLOW_BREAK) && (*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_MUST_BREAK))
693 if((wordEnd + 1) == end) // add last char
698 Vector<bool> hyphens = GetWordHyphens(utf32Characters.Begin() + index, wordEnd - index, nullptr);
700 for(CharacterIndex i = 0; i < (wordEnd - index); i++)
704 *(lineBreakInfoBuffer + index + i) = TextAbstraction::LINE_HYPHENATION_BREAK;
712 // Create the paragraph info.
713 mModel->mLogicalModel->CreateParagraphInfo(startIndex,
714 requestedNumberOfCharacters);
718 const bool getScripts = NO_OPERATION != (GET_SCRIPTS & operations);
719 const bool validateFonts = NO_OPERATION != (VALIDATE_FONTS & operations);
721 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
722 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
724 if(getScripts || validateFonts)
726 // Validates the fonts assigned by the application or assigns default ones.
727 // It makes sure all the characters are going to be rendered by the correct font.
728 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
732 // Retrieves the scripts used in the text.
733 multilanguageSupport.SetScripts(utf32Characters,
735 requestedNumberOfCharacters,
741 // Validate the fonts set through the mark-up string.
742 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
744 // Get the default font's description.
745 TextAbstraction::FontDescription defaultFontDescription;
746 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale;
748 //Get the number of points per one unit of point-size
749 uint32_t numberOfPointsPerOneUnitOfPointSize = mFontClient.GetNumberOfPointsPerOneUnitOfPointSize();
751 if(IsShowingPlaceholderText() && mEventData && (nullptr != mEventData->mPlaceholderFont))
753 // If the placeholder font is set specifically, only placeholder font is changed.
754 defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
755 if(mEventData->mPlaceholderFont->sizeDefined)
757 defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * mFontSizeScale * numberOfPointsPerOneUnitOfPointSize;
760 else if(nullptr != mFontDefaults)
762 // Set the normal font and the placeholder font.
763 defaultFontDescription = mFontDefaults->mFontDescription;
767 defaultPointSize = mFontDefaults->mFitPointSize * numberOfPointsPerOneUnitOfPointSize;
771 defaultPointSize = mFontDefaults->mDefaultPointSize * mFontSizeScale * numberOfPointsPerOneUnitOfPointSize;
775 // Validates the fonts. If there is a character with no assigned font it sets a default one.
776 // After this call, fonts are validated.
777 multilanguageSupport.ValidateFonts(utf32Characters,
780 defaultFontDescription,
783 requestedNumberOfCharacters,
789 Vector<Character> mirroredUtf32Characters;
790 bool textMirrored = false;
791 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
792 if(NO_OPERATION != (BIDI_INFO & operations))
794 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
795 bidirectionalInfo.Reserve(numberOfParagraphs);
797 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
798 SetBidirectionalInfo(utf32Characters,
802 requestedNumberOfCharacters,
804 (mModel->mMatchLayoutDirection != DevelText::MatchLayoutDirection::CONTENTS),
807 if(0u != bidirectionalInfo.Count())
809 // Only set the character directions if there is right to left characters.
810 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
811 GetCharactersDirection(bidirectionalInfo,
814 requestedNumberOfCharacters,
817 // This paragraph has right to left text. Some characters may need to be mirrored.
818 // TODO: consider if the mirrored string can be stored as well.
820 textMirrored = GetMirroredText(utf32Characters,
824 requestedNumberOfCharacters,
825 mirroredUtf32Characters);
829 // There is no right to left characters. Clear the directions vector.
830 mModel->mLogicalModel->mCharacterDirections.Clear();
835 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
836 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
837 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
838 Vector<GlyphIndex> newParagraphGlyphs;
839 newParagraphGlyphs.Reserve(numberOfParagraphs);
841 const Length currentNumberOfGlyphs = glyphs.Count();
842 if(NO_OPERATION != (SHAPE_TEXT & operations))
844 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
846 ShapeText(textToShape,
851 mTextUpdateInfo.mStartGlyphIndex,
852 requestedNumberOfCharacters,
854 glyphsToCharactersMap,
858 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
859 mModel->mVisualModel->CreateGlyphsPerCharacterTable(startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters);
860 mModel->mVisualModel->CreateCharacterToGlyphTable(startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters);
865 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
867 if(NO_OPERATION != (GET_GLYPH_METRICS & operations))
869 GlyphInfo* glyphsBuffer = glyphs.Begin();
870 mMetrics->GetGlyphMetrics(glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs);
872 // Update the width and advance of all new paragraph characters.
873 for(Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it)
875 const GlyphIndex index = *it;
876 GlyphInfo& glyph = *(glyphsBuffer + index);
878 glyph.xBearing = 0.f;
885 if((nullptr != mEventData) &&
886 mEventData->mPreEditFlag &&
887 (0u != mModel->mVisualModel->mCharactersToGlyph.Count()))
889 Dali::InputMethodContext::PreEditAttributeDataContainer attrs;
890 mEventData->mInputMethodContext.GetPreeditStyle(attrs);
891 Dali::InputMethodContext::PreeditStyle type = Dali::InputMethodContext::PreeditStyle::NONE;
893 // Check the type of preedit and run it.
894 for(Dali::InputMethodContext::PreEditAttributeDataContainer::Iterator it = attrs.Begin(), endIt = attrs.End(); it != endIt; it++)
896 Dali::InputMethodContext::PreeditAttributeData attrData = *it;
897 DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::UpdateModel PreeditStyle type : %d start %d end %d \n", attrData.preeditType, attrData.startIndex, attrData.endIndex);
898 type = attrData.preeditType;
900 // Check the number of commit characters for the start position.
901 unsigned int numberOfCommit = mEventData->mPrimaryCursorPosition - mEventData->mPreEditLength;
902 Length numberOfIndices = attrData.endIndex - attrData.startIndex;
906 case Dali::InputMethodContext::PreeditStyle::UNDERLINE:
908 // Add the underline for the pre-edit text.
909 GlyphRun underlineRun;
910 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
911 underlineRun.numberOfGlyphs = numberOfIndices;
912 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
914 //Mark-up processor case
915 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
917 CopyUnderlinedFromLogicalToVisualModels(false);
921 case Dali::InputMethodContext::PreeditStyle::REVERSE:
923 Vector4 textColor = mModel->mVisualModel->GetTextColor();
924 ColorRun backgroundColorRun;
925 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
926 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
927 backgroundColorRun.color = textColor;
928 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
930 Vector4 backgroundColor = mModel->mVisualModel->GetBackgroundColor();
931 if(backgroundColor.a == 0) // There is no text background color.
933 // Try use the control's background color.
934 if(nullptr != mEditableControlInterface)
936 mEditableControlInterface->GetControlBackgroundColor(backgroundColor);
937 if(backgroundColor.a == 0) // There is no control background color.
939 // Determines black or white color according to text color.
940 // Based on W3C Recommendations (https://www.w3.org/TR/WCAG20/)
941 float L = CONSTANT_R * textColor.r + CONSTANT_G * textColor.g + CONSTANT_B * textColor.b;
942 backgroundColor = L > BRIGHTNESS_THRESHOLD ? BLACK : WHITE;
947 Vector<ColorRun> colorRuns;
948 colorRuns.Resize(1u);
949 ColorRun& colorRun = *(colorRuns.Begin());
950 colorRun.color = backgroundColor;
951 colorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
952 colorRun.characterRun.numberOfCharacters = numberOfIndices;
953 mModel->mLogicalModel->mColorRuns.PushBack(colorRun);
955 //Mark-up processor case
956 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
958 CopyUnderlinedFromLogicalToVisualModels(false);
962 case Dali::InputMethodContext::PreeditStyle::HIGHLIGHT:
964 ColorRun backgroundColorRun;
965 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
966 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
967 backgroundColorRun.color = LIGHT_BLUE;
968 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
970 //Mark-up processor case
971 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
973 CopyUnderlinedFromLogicalToVisualModels(false);
977 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_1:
979 // CUSTOM_PLATFORM_STYLE_1 should be drawn with background and underline together.
980 ColorRun backgroundColorRun;
981 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
982 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
983 backgroundColorRun.color = BACKGROUND_SUB4;
984 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
986 GlyphRun underlineRun;
987 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
988 underlineRun.numberOfGlyphs = numberOfIndices;
989 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
991 //Mark-up processor case
992 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
994 CopyUnderlinedFromLogicalToVisualModels(false);
998 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_2:
1000 // CUSTOM_PLATFORM_STYLE_2 should be drawn with background and underline together.
1001 ColorRun backgroundColorRun;
1002 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1003 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1004 backgroundColorRun.color = BACKGROUND_SUB5;
1005 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
1007 GlyphRun underlineRun;
1008 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1009 underlineRun.numberOfGlyphs = numberOfIndices;
1010 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
1012 //Mark-up processor case
1013 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
1015 CopyUnderlinedFromLogicalToVisualModels(false);
1019 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_3:
1021 // CUSTOM_PLATFORM_STYLE_3 should be drawn with background and underline together.
1022 ColorRun backgroundColorRun;
1023 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1024 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1025 backgroundColorRun.color = BACKGROUND_SUB6;
1026 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
1028 GlyphRun underlineRun;
1029 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1030 underlineRun.numberOfGlyphs = numberOfIndices;
1031 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
1033 //Mark-up processor case
1034 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
1036 CopyUnderlinedFromLogicalToVisualModels(false);
1040 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_4:
1042 // CUSTOM_PLATFORM_STYLE_4 should be drawn with background and underline together.
1043 ColorRun backgroundColorRun;
1044 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1045 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1046 backgroundColorRun.color = BACKGROUND_SUB7;
1047 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
1049 GlyphRun underlineRun;
1050 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1051 underlineRun.numberOfGlyphs = numberOfIndices;
1052 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
1054 //Mark-up processor case
1055 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
1057 CopyUnderlinedFromLogicalToVisualModels(false);
1061 case Dali::InputMethodContext::PreeditStyle::NONE:
1072 if(NO_OPERATION != (COLOR & operations))
1074 // Set the color runs in glyphs.
1075 SetColorSegmentationInfo(mModel->mLogicalModel->mColorRuns,
1076 mModel->mVisualModel->mCharactersToGlyph,
1077 mModel->mVisualModel->mGlyphsPerCharacter,
1079 mTextUpdateInfo.mStartGlyphIndex,
1080 requestedNumberOfCharacters,
1081 mModel->mVisualModel->mColors,
1082 mModel->mVisualModel->mColorIndices);
1084 // Set the background color runs in glyphs.
1085 SetColorSegmentationInfo(mModel->mLogicalModel->mBackgroundColorRuns,
1086 mModel->mVisualModel->mCharactersToGlyph,
1087 mModel->mVisualModel->mGlyphsPerCharacter,
1089 mTextUpdateInfo.mStartGlyphIndex,
1090 requestedNumberOfCharacters,
1091 mModel->mVisualModel->mBackgroundColors,
1092 mModel->mVisualModel->mBackgroundColorIndices);
1097 if((NO_OPERATION != (SHAPE_TEXT & operations)) &&
1098 !((nullptr != mEventData) &&
1099 mEventData->mPreEditFlag &&
1100 (0u != mModel->mVisualModel->mCharactersToGlyph.Count())))
1102 //Mark-up processor case
1103 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
1105 CopyUnderlinedFromLogicalToVisualModels(true);
1111 // The estimated number of lines. Used to avoid reallocations when layouting.
1112 mTextUpdateInfo.mEstimatedNumberOfLines = std::max(mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count());
1114 // Set the previous number of characters for the next time the text is updated.
1115 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1120 void Controller::Impl::RetrieveDefaultInputStyle(InputStyle& inputStyle)
1122 // Sets the default text's color.
1123 inputStyle.textColor = mTextColor;
1124 inputStyle.isDefaultColor = true;
1126 inputStyle.familyName.clear();
1127 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1128 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1129 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1130 inputStyle.size = 0.f;
1132 inputStyle.lineSpacing = 0.f;
1134 inputStyle.underlineProperties.clear();
1135 inputStyle.shadowProperties.clear();
1136 inputStyle.embossProperties.clear();
1137 inputStyle.outlineProperties.clear();
1139 inputStyle.isFamilyDefined = false;
1140 inputStyle.isWeightDefined = false;
1141 inputStyle.isWidthDefined = false;
1142 inputStyle.isSlantDefined = false;
1143 inputStyle.isSizeDefined = false;
1145 inputStyle.isLineSpacingDefined = false;
1147 inputStyle.isUnderlineDefined = false;
1148 inputStyle.isShadowDefined = false;
1149 inputStyle.isEmbossDefined = false;
1150 inputStyle.isOutlineDefined = false;
1152 // Sets the default font's family name, weight, width, slant and size.
1155 if(mFontDefaults->familyDefined)
1157 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1158 inputStyle.isFamilyDefined = true;
1161 if(mFontDefaults->weightDefined)
1163 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1164 inputStyle.isWeightDefined = true;
1167 if(mFontDefaults->widthDefined)
1169 inputStyle.width = mFontDefaults->mFontDescription.width;
1170 inputStyle.isWidthDefined = true;
1173 if(mFontDefaults->slantDefined)
1175 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1176 inputStyle.isSlantDefined = true;
1179 if(mFontDefaults->sizeDefined)
1181 inputStyle.size = mFontDefaults->mDefaultPointSize;
1182 inputStyle.isSizeDefined = true;
1187 float Controller::Impl::GetDefaultFontLineHeight()
1189 FontId defaultFontId = 0u;
1190 if(nullptr == mFontDefaults)
1192 TextAbstraction::FontDescription fontDescription;
1193 defaultFontId = mFontClient.GetFontId(fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale);
1197 defaultFontId = mFontDefaults->GetFontId(mFontClient, mFontDefaults->mDefaultPointSize * mFontSizeScale);
1200 Text::FontMetrics fontMetrics;
1201 mMetrics->GetFontMetrics(defaultFontId, fontMetrics);
1203 return (fontMetrics.ascender - fontMetrics.descender);
1206 void Controller::Impl::SetTextSelectionRange(const uint32_t* pStart, const uint32_t* pEnd)
1208 if(nullptr == mEventData)
1210 // Nothing to do if there is no text.
1214 if(mEventData->mSelectionEnabled && (pStart || pEnd))
1216 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
1217 uint32_t oldStart = mEventData->mLeftSelectionPosition;
1218 uint32_t oldEnd = mEventData->mRightSelectionPosition;
1222 mEventData->mLeftSelectionPosition = std::min(*pStart, length);
1226 mEventData->mRightSelectionPosition = std::min(*pEnd, length);
1229 if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
1231 ChangeState(EventData::EDITING);
1232 mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition;
1233 mEventData->mUpdateCursorPosition = true;
1237 ChangeState(EventData::SELECTING);
1238 mEventData->mUpdateHighlightBox = true;
1239 mEventData->mUpdateLeftSelectionPosition = true;
1240 mEventData->mUpdateRightSelectionPosition = true;
1243 if(mSelectableControlInterface != nullptr)
1245 mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
1250 CharacterIndex Controller::Impl::GetPrimaryCursorPosition() const
1252 if(nullptr == mEventData)
1256 return mEventData->mPrimaryCursorPosition;
1259 bool Controller::Impl::SetPrimaryCursorPosition(CharacterIndex index, bool focused)
1261 if(nullptr == mEventData)
1263 // Nothing to do if there is no text.
1267 if(mEventData->mPrimaryCursorPosition == index && mEventData->mState != EventData::SELECTING)
1269 // Nothing for same cursor position.
1273 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
1274 uint32_t oldCursorPos = mEventData->mPrimaryCursorPosition;
1275 mEventData->mPrimaryCursorPosition = std::min(index, length);
1276 // If there is no focus, only the value is updated.
1279 bool wasInSelectingState = mEventData->mState == EventData::SELECTING;
1280 uint32_t oldStart = mEventData->mLeftSelectionPosition;
1281 uint32_t oldEnd = mEventData->mRightSelectionPosition;
1282 ChangeState(EventData::EDITING);
1283 mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1284 mEventData->mUpdateCursorPosition = true;
1286 if(mSelectableControlInterface != nullptr && wasInSelectingState)
1288 mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
1291 ScrollTextToMatchCursor();
1294 if(nullptr != mEditableControlInterface)
1296 mEditableControlInterface->CursorPositionChanged(oldCursorPos, mEventData->mPrimaryCursorPosition);
1302 Uint32Pair Controller::Impl::GetTextSelectionRange() const
1308 range.first = mEventData->mLeftSelectionPosition;
1309 range.second = mEventData->mRightSelectionPosition;
1315 bool Controller::Impl::IsEditable() const
1317 return mEventData && mEventData->mEditingEnabled;
1320 void Controller::Impl::SetEditable(bool editable)
1324 mEventData->mEditingEnabled = editable;
1328 void Controller::Impl::RetrieveSelection(std::string& selectedText, bool deleteAfterRetrieval)
1330 if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
1332 // Nothing to select if handles are in the same place.
1333 selectedText.clear();
1337 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1339 //Get start and end position of selection
1340 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1341 const Length lengthOfSelectedText = (handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition) - startOfSelectedText;
1343 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1344 const Length numberOfCharacters = utf32Characters.Count();
1346 // Validate the start and end selection points
1347 if((startOfSelectedText + lengthOfSelectedText) <= numberOfCharacters)
1349 //Get text as a UTF8 string
1350 Utf32ToUtf8(&utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText);
1352 if(deleteAfterRetrieval) // Only delete text if copied successfully
1354 // Keep a copy of the current input style.
1355 InputStyle currentInputStyle;
1356 currentInputStyle.Copy(mEventData->mInputStyle);
1358 // Set as input style the style of the first deleted character.
1359 mModel->mLogicalModel->RetrieveStyle(startOfSelectedText, mEventData->mInputStyle);
1361 // Compare if the input style has changed.
1362 const bool hasInputStyleChanged = !currentInputStyle.Equal(mEventData->mInputStyle);
1364 if(hasInputStyleChanged)
1366 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask(mEventData->mInputStyle);
1367 // Queue the input style changed signal.
1368 mEventData->mInputStyleChangedQueue.PushBack(styleChangedMask);
1371 mModel->mLogicalModel->UpdateTextStyleRuns(startOfSelectedText, -static_cast<int>(lengthOfSelectedText));
1373 // Mark the paragraphs to be updated.
1374 if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1376 mTextUpdateInfo.mCharacterIndex = 0;
1377 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1378 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1379 mTextUpdateInfo.mClearAll = true;
1383 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1384 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1387 // Delete text between handles
1388 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1389 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1390 utf32Characters.Erase(first, last);
1392 // Will show the cursor at the first character of the selection.
1393 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1397 // Will show the cursor at the last character of the selection.
1398 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1401 mEventData->mDecoratorUpdated = true;
1405 void Controller::Impl::SetSelection(int start, int end)
1407 uint32_t oldStart = mEventData->mLeftSelectionPosition;
1408 uint32_t oldEnd = mEventData->mRightSelectionPosition;
1410 mEventData->mLeftSelectionPosition = start;
1411 mEventData->mRightSelectionPosition = end;
1412 mEventData->mUpdateCursorPosition = true;
1414 if(mSelectableControlInterface != nullptr)
1416 mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, start, end);
1420 std::pair<int, int> Controller::Impl::GetSelectionIndexes() const
1422 return {mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition};
1425 void Controller::Impl::ShowClipboard()
1429 mClipboard.ShowClipboard();
1433 void Controller::Impl::HideClipboard()
1435 if(mClipboard && mClipboardHideEnabled)
1437 mClipboard.HideClipboard();
1441 void Controller::Impl::SetClipboardHideEnable(bool enable)
1443 mClipboardHideEnabled = enable;
1446 bool Controller::Impl::CopyStringToClipboard(const std::string& source)
1448 //Send string to clipboard
1449 return (mClipboard && mClipboard.SetItem(source));
1452 void Controller::Impl::SendSelectionToClipboard(bool deleteAfterSending)
1454 std::string selectedText;
1455 RetrieveSelection(selectedText, deleteAfterSending);
1456 CopyStringToClipboard(selectedText);
1457 ChangeState(EventData::EDITING);
1460 void Controller::Impl::RequestGetTextFromClipboard()
1464 mClipboard.RequestItem();
1468 void Controller::Impl::RepositionSelectionHandles()
1470 SelectionHandleController::Reposition(*this);
1472 void Controller::Impl::RepositionSelectionHandles(float visualX, float visualY, Controller::NoTextTap::Action action)
1474 SelectionHandleController::Reposition(*this, visualX, visualY, action);
1477 void Controller::Impl::SetPopupButtons()
1480 * Sets the Popup buttons to be shown depending on State.
1482 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1484 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1487 bool isEditable = IsEditable();
1488 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1490 if(EventData::SELECTING == mEventData->mState)
1492 buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::COPY);
1495 buttonsToShow = TextSelectionPopup::Buttons(buttonsToShow | TextSelectionPopup::CUT);
1498 if(!IsClipboardEmpty())
1502 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1504 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1507 if(!mEventData->mAllTextSelected)
1509 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::SELECT_ALL));
1512 else if(EventData::EDITING_WITH_POPUP == mEventData->mState)
1514 if(mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
1516 buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL);
1519 if(!IsClipboardEmpty())
1523 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1525 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1528 else if(EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState)
1530 if(!IsClipboardEmpty())
1534 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1536 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1540 mEventData->mDecorator->SetEnabledPopupButtons(buttonsToShow);
1543 void Controller::Impl::ChangeState(EventData::State newState)
1545 if(nullptr == mEventData)
1547 // Nothing to do if there is no text input.
1551 DALI_LOG_INFO(gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState);
1553 if(mEventData->mState != newState)
1555 mEventData->mPreviousState = mEventData->mState;
1556 mEventData->mState = newState;
1558 switch(mEventData->mState)
1560 case EventData::INACTIVE:
1562 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1563 mEventData->mDecorator->StopCursorBlink();
1564 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1565 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1566 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1567 mEventData->mDecorator->SetHighlightActive(false);
1568 mEventData->mDecorator->SetPopupActive(false);
1569 mEventData->mDecoratorUpdated = true;
1572 case EventData::INTERRUPTED:
1574 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1575 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1576 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1577 mEventData->mDecorator->SetHighlightActive(false);
1578 mEventData->mDecorator->SetPopupActive(false);
1579 mEventData->mDecoratorUpdated = true;
1582 case EventData::SELECTING:
1584 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1585 mEventData->mDecorator->StopCursorBlink();
1586 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1587 if(mEventData->mGrabHandleEnabled)
1589 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
1590 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
1592 mEventData->mDecorator->SetHighlightActive(true);
1593 if(mEventData->mGrabHandlePopupEnabled)
1596 mEventData->mDecorator->SetPopupActive(true);
1598 mEventData->mDecoratorUpdated = true;
1601 case EventData::EDITING:
1603 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1604 if(mEventData->mCursorBlinkEnabled)
1606 mEventData->mDecorator->StartCursorBlink();
1608 // Grab handle is not shown until a tap is received whilst EDITING
1609 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1610 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1611 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1612 mEventData->mDecorator->SetHighlightActive(false);
1613 if(mEventData->mGrabHandlePopupEnabled)
1615 mEventData->mDecorator->SetPopupActive(false);
1617 mEventData->mDecoratorUpdated = true;
1620 case EventData::EDITING_WITH_POPUP:
1622 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState);
1624 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1625 if(mEventData->mCursorBlinkEnabled)
1627 mEventData->mDecorator->StartCursorBlink();
1629 if(mEventData->mSelectionEnabled)
1631 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1632 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1633 mEventData->mDecorator->SetHighlightActive(false);
1635 else if(mEventData->mGrabHandleEnabled)
1637 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1639 if(mEventData->mGrabHandlePopupEnabled)
1642 mEventData->mDecorator->SetPopupActive(true);
1644 mEventData->mDecoratorUpdated = true;
1647 case EventData::EDITING_WITH_GRAB_HANDLE:
1649 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState);
1651 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1652 if(mEventData->mCursorBlinkEnabled)
1654 mEventData->mDecorator->StartCursorBlink();
1656 // Grab handle is not shown until a tap is received whilst EDITING
1657 if(mEventData->mGrabHandleEnabled)
1659 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1661 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1662 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1663 mEventData->mDecorator->SetHighlightActive(false);
1664 if(mEventData->mGrabHandlePopupEnabled)
1666 mEventData->mDecorator->SetPopupActive(false);
1668 mEventData->mDecoratorUpdated = true;
1671 case EventData::SELECTION_HANDLE_PANNING:
1673 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1674 mEventData->mDecorator->StopCursorBlink();
1675 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1676 if(mEventData->mGrabHandleEnabled)
1678 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
1679 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
1681 mEventData->mDecorator->SetHighlightActive(true);
1682 if(mEventData->mGrabHandlePopupEnabled)
1684 mEventData->mDecorator->SetPopupActive(false);
1686 mEventData->mDecoratorUpdated = true;
1689 case EventData::GRAB_HANDLE_PANNING:
1691 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState);
1693 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1694 if(mEventData->mCursorBlinkEnabled)
1696 mEventData->mDecorator->StartCursorBlink();
1698 if(mEventData->mGrabHandleEnabled)
1700 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1702 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1703 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1704 mEventData->mDecorator->SetHighlightActive(false);
1705 if(mEventData->mGrabHandlePopupEnabled)
1707 mEventData->mDecorator->SetPopupActive(false);
1709 mEventData->mDecoratorUpdated = true;
1712 case EventData::EDITING_WITH_PASTE_POPUP:
1714 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState);
1716 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1717 if(mEventData->mCursorBlinkEnabled)
1719 mEventData->mDecorator->StartCursorBlink();
1722 if(mEventData->mGrabHandleEnabled)
1724 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1726 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1727 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1728 mEventData->mDecorator->SetHighlightActive(false);
1730 if(mEventData->mGrabHandlePopupEnabled)
1733 mEventData->mDecorator->SetPopupActive(true);
1735 mEventData->mDecoratorUpdated = true;
1738 case EventData::TEXT_PANNING:
1740 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1741 mEventData->mDecorator->StopCursorBlink();
1742 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1743 if(mEventData->mDecorator->IsHandleActive(LEFT_SELECTION_HANDLE) ||
1744 mEventData->mDecorator->IsHandleActive(RIGHT_SELECTION_HANDLE))
1746 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1747 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1748 mEventData->mDecorator->SetHighlightActive(true);
1751 if(mEventData->mGrabHandlePopupEnabled)
1753 mEventData->mDecorator->SetPopupActive(false);
1756 mEventData->mDecoratorUpdated = true;
1763 void Controller::Impl::GetCursorPosition(CharacterIndex logical,
1764 CursorInfo& cursorInfo)
1766 if(!IsShowingRealText())
1768 // Do not want to use the place-holder text to set the cursor position.
1770 // Use the line's height of the font's family set to set the cursor's size.
1771 // If there is no font's family set, use the default font.
1772 // Use the current alignment to place the cursor at the beginning, center or end of the box.
1774 cursorInfo.lineOffset = 0.f;
1775 cursorInfo.lineHeight = GetDefaultFontLineHeight();
1776 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1779 if(mModel->mMatchLayoutDirection != DevelText::MatchLayoutDirection::CONTENTS)
1781 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1784 switch(mModel->mHorizontalAlignment)
1786 case Text::HorizontalAlignment::BEGIN:
1790 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1794 cursorInfo.primaryPosition.x = 0.f;
1798 case Text::HorizontalAlignment::CENTER:
1800 cursorInfo.primaryPosition.x = floorf(0.5f * mModel->mVisualModel->mControlSize.width);
1803 case Text::HorizontalAlignment::END:
1807 cursorInfo.primaryPosition.x = 0.f;
1811 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1817 // Nothing else to do.
1821 const bool isMultiLine = (Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout());
1822 GetCursorPositionParameters parameters;
1823 parameters.visualModel = mModel->mVisualModel;
1824 parameters.logicalModel = mModel->mLogicalModel;
1825 parameters.metrics = mMetrics;
1826 parameters.logical = logical;
1827 parameters.isMultiline = isMultiLine;
1829 Text::GetCursorPosition(parameters,
1832 // Adds Outline offset.
1833 const float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1834 cursorInfo.primaryPosition.x += outlineWidth;
1835 cursorInfo.primaryPosition.y += outlineWidth;
1836 cursorInfo.secondaryPosition.x += outlineWidth;
1837 cursorInfo.secondaryPosition.y += outlineWidth;
1841 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
1843 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
1844 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
1846 if(0.f > cursorInfo.primaryPosition.x)
1848 cursorInfo.primaryPosition.x = 0.f;
1851 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>(mEventData->mDecorator->GetCursorWidth());
1852 if(cursorInfo.primaryPosition.x > edgeWidth)
1854 cursorInfo.primaryPosition.x = edgeWidth;
1859 CharacterIndex Controller::Impl::CalculateNewCursorIndex(CharacterIndex index) const
1861 if(nullptr == mEventData)
1863 // Nothing to do if there is no text input.
1867 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1869 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1870 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1872 GlyphIndex glyphIndex = *(charactersToGlyphBuffer + index);
1873 Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1875 if(numberOfCharacters > 1u)
1877 const Script script = mModel->mLogicalModel->GetScript(index);
1878 if(HasLigatureMustBreak(script))
1880 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
1881 numberOfCharacters = 1u;
1886 while(0u == numberOfCharacters)
1889 numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1893 if(index < mEventData->mPrimaryCursorPosition)
1895 cursorIndex -= numberOfCharacters;
1899 cursorIndex += numberOfCharacters;
1902 // Will update the cursor hook position.
1903 mEventData->mUpdateCursorHookPosition = true;
1908 void Controller::Impl::UpdateCursorPosition(const CursorInfo& cursorInfo)
1910 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this);
1911 if(nullptr == mEventData)
1913 // Nothing to do if there is no text input.
1914 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n");
1918 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
1920 mEventData->mDecorator->SetGlyphOffset(PRIMARY_CURSOR, cursorInfo.glyphOffset);
1922 // Sets the cursor position.
1923 mEventData->mDecorator->SetPosition(PRIMARY_CURSOR,
1926 cursorInfo.primaryCursorHeight,
1927 cursorInfo.lineHeight);
1928 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y);
1930 if(mEventData->mUpdateGrabHandlePosition)
1932 // Sets the grab handle position.
1933 mEventData->mDecorator->SetPosition(GRAB_HANDLE,
1935 cursorInfo.lineOffset + mModel->mScrollPosition.y,
1936 cursorInfo.lineHeight);
1939 if(cursorInfo.isSecondaryCursor)
1941 mEventData->mDecorator->SetPosition(SECONDARY_CURSOR,
1942 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
1943 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
1944 cursorInfo.secondaryCursorHeight,
1945 cursorInfo.lineHeight);
1946 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y);
1949 // Set which cursors are active according the state.
1950 if(EventData::IsEditingState(mEventData->mState) || (EventData::GRAB_HANDLE_PANNING == mEventData->mState))
1952 if(cursorInfo.isSecondaryCursor)
1954 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_BOTH);
1958 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1963 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1966 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n");
1969 void Controller::Impl::UpdateSelectionHandle(HandleType handleType,
1970 const CursorInfo& cursorInfo)
1972 SelectionHandleController::Update(*this, handleType, cursorInfo);
1975 void Controller::Impl::ClampHorizontalScroll(const Vector2& layoutSize)
1977 // Clamp between -space & -alignment offset.
1979 if(layoutSize.width > mModel->mVisualModel->mControlSize.width)
1981 const float space = (layoutSize.width - mModel->mVisualModel->mControlSize.width) + mModel->mAlignmentOffset;
1982 mModel->mScrollPosition.x = (mModel->mScrollPosition.x < -space) ? -space : mModel->mScrollPosition.x;
1983 mModel->mScrollPosition.x = (mModel->mScrollPosition.x > -mModel->mAlignmentOffset) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
1985 mEventData->mDecoratorUpdated = true;
1989 mModel->mScrollPosition.x = 0.f;
1993 void Controller::Impl::ClampVerticalScroll(const Vector2& layoutSize)
1995 if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1997 // Nothing to do if the text is single line.
2001 // Clamp between -space & 0.
2002 if(layoutSize.height > mModel->mVisualModel->mControlSize.height)
2004 const float space = (layoutSize.height - mModel->mVisualModel->mControlSize.height);
2005 mModel->mScrollPosition.y = (mModel->mScrollPosition.y < -space) ? -space : mModel->mScrollPosition.y;
2006 mModel->mScrollPosition.y = (mModel->mScrollPosition.y > 0.f) ? 0.f : mModel->mScrollPosition.y;
2008 mEventData->mDecoratorUpdated = true;
2012 mModel->mScrollPosition.y = 0.f;
2016 void Controller::Impl::ScrollToMakePositionVisible(const Vector2& position, float lineHeight)
2018 const float cursorWidth = mEventData->mDecorator ? static_cast<float>(mEventData->mDecorator->GetCursorWidth()) : 0.f;
2020 // position is in actor's coords.
2021 const float positionEndX = position.x + cursorWidth;
2022 const float positionEndY = position.y + lineHeight;
2024 // Transform the position to decorator coords.
2025 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
2026 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
2028 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
2029 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
2031 if(decoratorPositionBeginX < 0.f)
2033 mModel->mScrollPosition.x = -position.x;
2035 else if(decoratorPositionEndX > mModel->mVisualModel->mControlSize.width)
2037 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
2040 if(Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout())
2042 if(decoratorPositionBeginY < 0.f)
2044 mModel->mScrollPosition.y = -position.y;
2046 else if(decoratorPositionEndY > mModel->mVisualModel->mControlSize.height)
2048 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
2053 void Controller::Impl::ScrollTextToMatchCursor(const CursorInfo& cursorInfo)
2055 // Get the current cursor position in decorator coords.
2056 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition(PRIMARY_CURSOR);
2058 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter(mEventData->mPrimaryCursorPosition);
2060 // Calculate the offset to match the cursor position before the character was deleted.
2061 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2063 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
2064 if(mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() - 1u)
2066 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset(PRIMARY_CURSOR);
2067 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
2070 ClampHorizontalScroll(mModel->mVisualModel->GetLayoutSize());
2071 ClampVerticalScroll(mModel->mVisualModel->GetLayoutSize());
2073 // Makes the new cursor position visible if needed.
2074 ScrollToMakePositionVisible(cursorInfo.primaryPosition, cursorInfo.lineHeight);
2077 void Controller::Impl::ScrollTextToMatchCursor()
2079 CursorInfo cursorInfo;
2080 GetCursorPosition(mEventData->mPrimaryCursorPosition, cursorInfo);
2081 ScrollTextToMatchCursor(cursorInfo);
2084 void Controller::Impl::RequestRelayout()
2086 if(nullptr != mControlInterface)
2088 mControlInterface->RequestTextRelayout();
2092 Actor Controller::Impl::CreateBackgroundActor()
2094 // NOTE: Currently we only support background color for left-to-right text.
2098 Length numberOfGlyphs = mView.GetNumberOfGlyphs();
2099 if(numberOfGlyphs > 0u)
2101 Vector<GlyphInfo> glyphs;
2102 glyphs.Resize(numberOfGlyphs);
2104 Vector<Vector2> positions;
2105 positions.Resize(numberOfGlyphs);
2107 // Get the line where the glyphs are laid-out.
2108 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
2109 float alignmentOffset = lineRun->alignmentOffset;
2110 numberOfGlyphs = mView.GetGlyphs(glyphs.Begin(),
2116 glyphs.Resize(numberOfGlyphs);
2117 positions.Resize(numberOfGlyphs);
2119 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
2120 const Vector2* const positionsBuffer = positions.Begin();
2122 BackgroundMesh mesh;
2123 mesh.mVertices.Reserve(4u * glyphs.Size());
2124 mesh.mIndices.Reserve(6u * glyphs.Size());
2126 const Vector2 textSize = mView.GetLayoutSize();
2128 const float offsetX = textSize.width * 0.5f;
2129 const float offsetY = textSize.height * 0.5f;
2131 const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
2132 const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
2133 const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
2136 uint32_t numberOfQuads = 0u;
2137 Length yLineOffset = 0;
2138 Length prevLineIndex = 0;
2139 LineIndex lineIndex;
2140 Length numberOfLines;
2142 for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i)
2144 const GlyphInfo& glyph = *(glyphsBuffer + i);
2146 // Get the background color of the character.
2147 // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
2148 const bool isMarkupBackground = mView.IsMarkupBackgroundColorSet();
2149 const ColorIndex backgroundColorIndex = isMarkupBackground ? *(backgroundColorIndicesBuffer + i) : 0u;
2150 const bool isDefaultBackgroundColor = (0u == backgroundColorIndex);
2151 const Vector4& backgroundColor = isDefaultBackgroundColor ? defaultBackgroundColor : *(backgroundColorsBuffer + backgroundColorIndex - 1u);
2153 mModel->mVisualModel->GetNumberOfLines(i, 1, lineIndex, numberOfLines);
2154 Length lineHeight = lineRun[lineIndex].ascender + -(lineRun[lineIndex].descender) + lineRun[lineIndex].lineSpacing;
2156 if(lineIndex != prevLineIndex)
2158 yLineOffset += lineHeight;
2161 // Only create quads for glyphs with a background color
2162 if(backgroundColor != Color::TRANSPARENT)
2164 const Vector2 position = *(positionsBuffer + i);
2166 if(i == 0u && glyphSize == 1u) // Only one glyph in the whole text
2168 quad.x = position.x;
2169 quad.y = yLineOffset;
2170 quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
2171 quad.w = lineHeight;
2173 else if((lineIndex != prevLineIndex) || (i == 0u)) // The first glyph in the line
2175 quad.x = position.x;
2176 quad.y = yLineOffset;
2177 quad.z = quad.x - glyph.xBearing + glyph.advance;
2178 quad.w = quad.y + lineHeight;
2180 else if(i == glyphSize - 1u) // The last glyph in the whole text
2182 quad.x = position.x - glyph.xBearing;
2183 quad.y = yLineOffset;
2184 quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
2185 quad.w = quad.y + lineHeight;
2187 else // The glyph in the middle of the text
2189 quad.x = position.x - glyph.xBearing;
2190 quad.y = yLineOffset;
2191 quad.z = quad.x + glyph.advance;
2192 quad.w = quad.y + lineHeight;
2195 BackgroundVertex vertex;
2198 vertex.mPosition.x = quad.x - offsetX;
2199 vertex.mPosition.y = quad.y - offsetY;
2200 vertex.mColor = backgroundColor;
2201 mesh.mVertices.PushBack(vertex);
2204 vertex.mPosition.x = quad.z - offsetX;
2205 vertex.mPosition.y = quad.y - offsetY;
2206 vertex.mColor = backgroundColor;
2207 mesh.mVertices.PushBack(vertex);
2210 vertex.mPosition.x = quad.x - offsetX;
2211 vertex.mPosition.y = quad.w - offsetY;
2212 vertex.mColor = backgroundColor;
2213 mesh.mVertices.PushBack(vertex);
2216 vertex.mPosition.x = quad.z - offsetX;
2217 vertex.mPosition.y = quad.w - offsetY;
2218 vertex.mColor = backgroundColor;
2219 mesh.mVertices.PushBack(vertex);
2221 // Six indices in counter clockwise winding
2222 mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
2223 mesh.mIndices.PushBack(0u + 4 * numberOfQuads);
2224 mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
2225 mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
2226 mesh.mIndices.PushBack(3u + 4 * numberOfQuads);
2227 mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
2232 if(lineIndex != prevLineIndex)
2234 prevLineIndex = lineIndex;
2238 // Only create the background actor if there are glyphs with background color
2239 if(mesh.mVertices.Count() > 0u)
2241 Property::Map quadVertexFormat;
2242 quadVertexFormat["aPosition"] = Property::VECTOR2;
2243 quadVertexFormat["aColor"] = Property::VECTOR4;
2245 VertexBuffer quadVertices = VertexBuffer::New(quadVertexFormat);
2246 quadVertices.SetData(&mesh.mVertices[0], mesh.mVertices.Size());
2248 Geometry quadGeometry = Geometry::New();
2249 quadGeometry.AddVertexBuffer(quadVertices);
2250 quadGeometry.SetIndexBuffer(&mesh.mIndices[0], mesh.mIndices.Size());
2252 if(!mShaderBackground)
2254 mShaderBackground = Shader::New(SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_VERT, SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_FRAG);
2257 Dali::Renderer renderer = Dali::Renderer::New(quadGeometry, mShaderBackground);
2258 renderer.SetProperty(Dali::Renderer::Property::BLEND_MODE, BlendMode::ON);
2259 renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT);
2261 actor = Actor::New();
2262 actor.SetProperty(Dali::Actor::Property::NAME, "TextBackgroundColorActor");
2263 actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
2264 actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
2265 actor.SetProperty(Actor::Property::SIZE, textSize);
2266 actor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR);
2267 actor.AddRenderer(renderer);
2274 void Controller::Impl::CopyUnderlinedFromLogicalToVisualModels(bool shouldClearPreUnderlineRuns)
2276 //Underlined character runs for markup-processor
2277 const Vector<UnderlinedCharacterRun>& underlinedCharacterRuns = mModel->mLogicalModel->mUnderlinedCharacterRuns;
2278 const Vector<GlyphIndex>& charactersToGlyph = mModel->mVisualModel->mCharactersToGlyph;
2279 const Vector<Length>& glyphsPerCharacter = mModel->mVisualModel->mGlyphsPerCharacter;
2281 if(shouldClearPreUnderlineRuns)
2283 mModel->mVisualModel->mUnderlineRuns.Clear();
2286 for(Vector<UnderlinedCharacterRun>::ConstIterator it = underlinedCharacterRuns.Begin(), endIt = underlinedCharacterRuns.End(); it != endIt; ++it)
2288 CharacterIndex characterIndex = it->characterRun.characterIndex;
2289 Length numberOfCharacters = it->characterRun.numberOfCharacters;
2290 for(Length index = 0u; index < numberOfCharacters; index++)
2292 GlyphRun underlineGlyphRun;
2293 underlineGlyphRun.glyphIndex = charactersToGlyph[characterIndex + index];
2294 underlineGlyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex + index];
2295 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineGlyphRun);
2302 } // namespace Toolkit