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(NULL),
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 // Clear the positions buffer.
511 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
512 mModel->mVisualModel->mGlyphPositions.Erase(positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
513 positionsBuffer + endGlyphIndexPlusOne);
516 if(NO_OPERATION != (LAYOUT & operations))
519 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
520 uint32_t endRemoveIndex = startRemoveIndex;
521 ClearCharacterRuns(startIndex,
523 mModel->mVisualModel->mLines,
527 // Will update the glyph runs.
528 startRemoveIndex = mModel->mVisualModel->mLines.Count();
529 endRemoveIndex = startRemoveIndex;
530 ClearGlyphRuns(mTextUpdateInfo.mStartGlyphIndex,
531 endGlyphIndexPlusOne - 1u,
532 mModel->mVisualModel->mLines,
536 // Set the line index from where to insert the new laid-out lines.
537 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
539 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
540 mModel->mVisualModel->mLines.Erase(linesBuffer + startRemoveIndex,
541 linesBuffer + endRemoveIndex);
544 if(NO_OPERATION != (COLOR & operations))
546 if(0u != mModel->mVisualModel->mColorIndices.Count())
548 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
549 mModel->mVisualModel->mColorIndices.Erase(colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
550 colorIndexBuffer + endGlyphIndexPlusOne);
553 if(0u != mModel->mVisualModel->mBackgroundColorIndices.Count())
555 ColorIndex* backgroundColorIndexBuffer = mModel->mVisualModel->mBackgroundColorIndices.Begin();
556 mModel->mVisualModel->mBackgroundColorIndices.Erase(backgroundColorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
557 backgroundColorIndexBuffer + endGlyphIndexPlusOne);
562 void Controller::Impl::ClearModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
564 if(mTextUpdateInfo.mClearAll ||
565 ((0u == startIndex) &&
566 (mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u)))
568 ClearFullModelData(operations);
572 // Clear the model data related with characters.
573 ClearCharacterModelData(startIndex, endIndex, operations);
575 // Clear the model data related with glyphs.
576 ClearGlyphModelData(startIndex, endIndex, operations);
579 // The estimated number of lines. Used to avoid reallocations when layouting.
580 mTextUpdateInfo.mEstimatedNumberOfLines = std::max(mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count());
582 mModel->mVisualModel->ClearCaches();
585 bool Controller::Impl::UpdateModel(OperationsMask operationsRequired)
587 DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::UpdateModel\n");
589 // Calculate the operations to be done.
590 const OperationsMask operations = static_cast<OperationsMask>(mOperationsPending & operationsRequired);
592 if(NO_OPERATION == operations)
594 // Nothing to do if no operations are pending and required.
598 Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
599 Vector<Character> displayCharacters;
600 bool useHiddenText = false;
601 if(mHiddenInput && mEventData != nullptr && !mEventData->mIsShowingPlaceholderText)
603 mHiddenInput->Substitute(srcCharacters, displayCharacters);
604 useHiddenText = true;
607 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
608 const Length numberOfCharacters = utf32Characters.Count();
610 // Index to the first character of the first paragraph to be updated.
611 CharacterIndex startIndex = 0u;
612 // Number of characters of the paragraphs to be removed.
613 Length paragraphCharacters = 0u;
615 CalculateTextUpdateIndices(paragraphCharacters);
617 // Check whether the indices for updating the text is valid
618 if(numberOfCharacters > 0u &&
619 (mTextUpdateInfo.mParagraphCharacterIndex > numberOfCharacters ||
620 mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters))
622 std::string currentText;
623 Utf32ToUtf8(mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText);
625 DALI_LOG_ERROR("Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n");
626 DALI_LOG_ERROR("Number of characters: %d, current text is: %s\n", numberOfCharacters, currentText.c_str());
628 // Dump mTextUpdateInfo
629 DALI_LOG_ERROR("Dump mTextUpdateInfo:\n");
630 DALI_LOG_ERROR(" mTextUpdateInfo.mCharacterIndex = %u\n", mTextUpdateInfo.mCharacterIndex);
631 DALI_LOG_ERROR(" mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", mTextUpdateInfo.mNumberOfCharactersToRemove);
632 DALI_LOG_ERROR(" mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", mTextUpdateInfo.mNumberOfCharactersToAdd);
633 DALI_LOG_ERROR(" mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", mTextUpdateInfo.mPreviousNumberOfCharacters);
634 DALI_LOG_ERROR(" mTextUpdateInfo.mParagraphCharacterIndex = %u\n", mTextUpdateInfo.mParagraphCharacterIndex);
635 DALI_LOG_ERROR(" mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", mTextUpdateInfo.mRequestedNumberOfCharacters);
636 DALI_LOG_ERROR(" mTextUpdateInfo.mStartGlyphIndex = %u\n", mTextUpdateInfo.mStartGlyphIndex);
637 DALI_LOG_ERROR(" mTextUpdateInfo.mStartLineIndex = %u\n", mTextUpdateInfo.mStartLineIndex);
638 DALI_LOG_ERROR(" mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", mTextUpdateInfo.mEstimatedNumberOfLines);
639 DALI_LOG_ERROR(" mTextUpdateInfo.mClearAll = %d\n", mTextUpdateInfo.mClearAll);
640 DALI_LOG_ERROR(" mTextUpdateInfo.mFullRelayoutNeeded = %d\n", mTextUpdateInfo.mFullRelayoutNeeded);
641 DALI_LOG_ERROR(" mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", mTextUpdateInfo.mIsLastCharacterNewParagraph);
646 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
648 if(mTextUpdateInfo.mClearAll ||
649 (0u != paragraphCharacters))
651 ClearModelData(startIndex, startIndex + ((paragraphCharacters > 0u) ? paragraphCharacters - 1u : 0u), operations);
654 mTextUpdateInfo.mClearAll = false;
656 // Whether the model is updated.
657 bool updated = false;
659 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
660 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
662 if(NO_OPERATION != (GET_LINE_BREAKS & operations))
664 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
665 // calculate the bidirectional info for each 'paragraph'.
666 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
667 // is not shaped together).
668 lineBreakInfo.Resize(numberOfCharacters, TextAbstraction::LINE_NO_BREAK);
670 SetLineBreakInfo(utf32Characters,
672 requestedNumberOfCharacters,
675 if(mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
676 mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::MIXED))
678 CharacterIndex end = startIndex + requestedNumberOfCharacters;
679 LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
681 for(CharacterIndex index = startIndex; index < end; index++)
683 CharacterIndex wordEnd = index;
684 while((*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_ALLOW_BREAK) && (*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_MUST_BREAK))
689 if((wordEnd + 1) == end) // add last char
694 Vector<bool> hyphens = GetWordHyphens(utf32Characters.Begin() + index, wordEnd - index, nullptr);
696 for(CharacterIndex i = 0; i < (wordEnd - index); i++)
700 *(lineBreakInfoBuffer + index + i) = TextAbstraction::LINE_HYPHENATION_BREAK;
708 // Create the paragraph info.
709 mModel->mLogicalModel->CreateParagraphInfo(startIndex,
710 requestedNumberOfCharacters);
714 const bool getScripts = NO_OPERATION != (GET_SCRIPTS & operations);
715 const bool validateFonts = NO_OPERATION != (VALIDATE_FONTS & operations);
717 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
718 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
720 if(getScripts || validateFonts)
722 // Validates the fonts assigned by the application or assigns default ones.
723 // It makes sure all the characters are going to be rendered by the correct font.
724 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
728 // Retrieves the scripts used in the text.
729 multilanguageSupport.SetScripts(utf32Characters,
731 requestedNumberOfCharacters,
737 // Validate the fonts set through the mark-up string.
738 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
740 // Get the default font's description.
741 TextAbstraction::FontDescription defaultFontDescription;
742 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale;
744 //Get the number of points per one unit of point-size
745 uint32_t numberOfPointsPerOneUnitOfPointSize = mFontClient.GetNumberOfPointsPerOneUnitOfPointSize();
747 if(IsShowingPlaceholderText() && mEventData && (nullptr != mEventData->mPlaceholderFont))
749 // If the placeholder font is set specifically, only placeholder font is changed.
750 defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
751 if(mEventData->mPlaceholderFont->sizeDefined)
753 defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * mFontSizeScale * numberOfPointsPerOneUnitOfPointSize;
756 else if(nullptr != mFontDefaults)
758 // Set the normal font and the placeholder font.
759 defaultFontDescription = mFontDefaults->mFontDescription;
763 defaultPointSize = mFontDefaults->mFitPointSize * numberOfPointsPerOneUnitOfPointSize;
767 defaultPointSize = mFontDefaults->mDefaultPointSize * mFontSizeScale * numberOfPointsPerOneUnitOfPointSize;
771 // Validates the fonts. If there is a character with no assigned font it sets a default one.
772 // After this call, fonts are validated.
773 multilanguageSupport.ValidateFonts(utf32Characters,
776 defaultFontDescription,
779 requestedNumberOfCharacters,
785 Vector<Character> mirroredUtf32Characters;
786 bool textMirrored = false;
787 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
788 if(NO_OPERATION != (BIDI_INFO & operations))
790 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
791 bidirectionalInfo.Reserve(numberOfParagraphs);
793 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
794 SetBidirectionalInfo(utf32Characters,
798 requestedNumberOfCharacters,
800 (mModel->mMatchLayoutDirection != DevelText::MatchLayoutDirection::CONTENTS),
803 if(0u != bidirectionalInfo.Count())
805 // Only set the character directions if there is right to left characters.
806 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
807 GetCharactersDirection(bidirectionalInfo,
810 requestedNumberOfCharacters,
813 // This paragraph has right to left text. Some characters may need to be mirrored.
814 // TODO: consider if the mirrored string can be stored as well.
816 textMirrored = GetMirroredText(utf32Characters,
820 requestedNumberOfCharacters,
821 mirroredUtf32Characters);
825 // There is no right to left characters. Clear the directions vector.
826 mModel->mLogicalModel->mCharacterDirections.Clear();
831 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
832 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
833 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
834 Vector<GlyphIndex> newParagraphGlyphs;
835 newParagraphGlyphs.Reserve(numberOfParagraphs);
837 const Length currentNumberOfGlyphs = glyphs.Count();
838 if(NO_OPERATION != (SHAPE_TEXT & operations))
840 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
842 ShapeText(textToShape,
847 mTextUpdateInfo.mStartGlyphIndex,
848 requestedNumberOfCharacters,
850 glyphsToCharactersMap,
854 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
855 mModel->mVisualModel->CreateGlyphsPerCharacterTable(startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters);
856 mModel->mVisualModel->CreateCharacterToGlyphTable(startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters);
861 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
863 if(NO_OPERATION != (GET_GLYPH_METRICS & operations))
865 GlyphInfo* glyphsBuffer = glyphs.Begin();
866 mMetrics->GetGlyphMetrics(glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs);
868 // Update the width and advance of all new paragraph characters.
869 for(Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it)
871 const GlyphIndex index = *it;
872 GlyphInfo& glyph = *(glyphsBuffer + index);
874 glyph.xBearing = 0.f;
881 if((nullptr != mEventData) &&
882 mEventData->mPreEditFlag &&
883 (0u != mModel->mVisualModel->mCharactersToGlyph.Count()))
885 Dali::InputMethodContext::PreEditAttributeDataContainer attrs;
886 mEventData->mInputMethodContext.GetPreeditStyle(attrs);
887 Dali::InputMethodContext::PreeditStyle type = Dali::InputMethodContext::PreeditStyle::NONE;
889 // Check the type of preedit and run it.
890 for(Dali::InputMethodContext::PreEditAttributeDataContainer::Iterator it = attrs.Begin(), endIt = attrs.End(); it != endIt; it++)
892 Dali::InputMethodContext::PreeditAttributeData attrData = *it;
893 DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::UpdateModel PreeditStyle type : %d start %d end %d \n", attrData.preeditType, attrData.startIndex, attrData.endIndex);
894 type = attrData.preeditType;
896 // Check the number of commit characters for the start position.
897 unsigned int numberOfCommit = mEventData->mPrimaryCursorPosition - mEventData->mPreEditLength;
898 Length numberOfIndices = attrData.endIndex - attrData.startIndex;
902 case Dali::InputMethodContext::PreeditStyle::UNDERLINE:
904 // Add the underline for the pre-edit text.
905 GlyphRun underlineRun;
906 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
907 underlineRun.numberOfGlyphs = numberOfIndices;
908 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
910 //Mark-up processor case
911 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
913 CopyUnderlinedFromLogicalToVisualModels(false);
917 case Dali::InputMethodContext::PreeditStyle::REVERSE:
919 Vector4 textColor = mModel->mVisualModel->GetTextColor();
920 ColorRun backgroundColorRun;
921 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
922 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
923 backgroundColorRun.color = textColor;
924 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
926 Vector4 backgroundColor = mModel->mVisualModel->GetBackgroundColor();
927 if(backgroundColor.a == 0) // There is no text background color.
929 // Try use the control's background color.
930 if(nullptr != mEditableControlInterface)
932 mEditableControlInterface->GetControlBackgroundColor(backgroundColor);
933 if(backgroundColor.a == 0) // There is no control background color.
935 // Determines black or white color according to text color.
936 // Based on W3C Recommendations (https://www.w3.org/TR/WCAG20/)
937 float L = CONSTANT_R * textColor.r + CONSTANT_G * textColor.g + CONSTANT_B * textColor.b;
938 backgroundColor = L > BRIGHTNESS_THRESHOLD ? BLACK : WHITE;
943 Vector<ColorRun> colorRuns;
944 colorRuns.Resize(1u);
945 ColorRun& colorRun = *(colorRuns.Begin());
946 colorRun.color = backgroundColor;
947 colorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
948 colorRun.characterRun.numberOfCharacters = numberOfIndices;
949 mModel->mLogicalModel->mColorRuns.PushBack(colorRun);
951 //Mark-up processor case
952 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
954 CopyUnderlinedFromLogicalToVisualModels(false);
958 case Dali::InputMethodContext::PreeditStyle::HIGHLIGHT:
960 ColorRun backgroundColorRun;
961 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
962 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
963 backgroundColorRun.color = LIGHT_BLUE;
964 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
966 //Mark-up processor case
967 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
969 CopyUnderlinedFromLogicalToVisualModels(false);
973 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_1:
975 // CUSTOM_PLATFORM_STYLE_1 should be drawn with background and underline together.
976 ColorRun backgroundColorRun;
977 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
978 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
979 backgroundColorRun.color = BACKGROUND_SUB4;
980 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
982 GlyphRun underlineRun;
983 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
984 underlineRun.numberOfGlyphs = numberOfIndices;
985 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
987 //Mark-up processor case
988 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
990 CopyUnderlinedFromLogicalToVisualModels(false);
994 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_2:
996 // CUSTOM_PLATFORM_STYLE_2 should be drawn with background and underline together.
997 ColorRun backgroundColorRun;
998 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
999 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1000 backgroundColorRun.color = BACKGROUND_SUB5;
1001 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
1003 GlyphRun underlineRun;
1004 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1005 underlineRun.numberOfGlyphs = numberOfIndices;
1006 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
1008 //Mark-up processor case
1009 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
1011 CopyUnderlinedFromLogicalToVisualModels(false);
1015 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_3:
1017 // CUSTOM_PLATFORM_STYLE_3 should be drawn with background and underline together.
1018 ColorRun backgroundColorRun;
1019 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1020 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1021 backgroundColorRun.color = BACKGROUND_SUB6;
1022 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
1024 GlyphRun underlineRun;
1025 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1026 underlineRun.numberOfGlyphs = numberOfIndices;
1027 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
1029 //Mark-up processor case
1030 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
1032 CopyUnderlinedFromLogicalToVisualModels(false);
1036 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_4:
1038 // CUSTOM_PLATFORM_STYLE_4 should be drawn with background and underline together.
1039 ColorRun backgroundColorRun;
1040 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1041 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1042 backgroundColorRun.color = BACKGROUND_SUB7;
1043 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
1045 GlyphRun underlineRun;
1046 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1047 underlineRun.numberOfGlyphs = numberOfIndices;
1048 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
1050 //Mark-up processor case
1051 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
1053 CopyUnderlinedFromLogicalToVisualModels(false);
1057 case Dali::InputMethodContext::PreeditStyle::NONE:
1068 if(NO_OPERATION != (COLOR & operations))
1070 // Set the color runs in glyphs.
1071 SetColorSegmentationInfo(mModel->mLogicalModel->mColorRuns,
1072 mModel->mVisualModel->mCharactersToGlyph,
1073 mModel->mVisualModel->mGlyphsPerCharacter,
1075 mTextUpdateInfo.mStartGlyphIndex,
1076 requestedNumberOfCharacters,
1077 mModel->mVisualModel->mColors,
1078 mModel->mVisualModel->mColorIndices);
1080 // Set the background color runs in glyphs.
1081 SetColorSegmentationInfo(mModel->mLogicalModel->mBackgroundColorRuns,
1082 mModel->mVisualModel->mCharactersToGlyph,
1083 mModel->mVisualModel->mGlyphsPerCharacter,
1085 mTextUpdateInfo.mStartGlyphIndex,
1086 requestedNumberOfCharacters,
1087 mModel->mVisualModel->mBackgroundColors,
1088 mModel->mVisualModel->mBackgroundColorIndices);
1093 if((NO_OPERATION != (SHAPE_TEXT & operations)) &&
1094 !((nullptr != mEventData) &&
1095 mEventData->mPreEditFlag &&
1096 (0u != mModel->mVisualModel->mCharactersToGlyph.Count())))
1098 //Mark-up processor case
1099 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
1101 CopyUnderlinedFromLogicalToVisualModels(true);
1107 // The estimated number of lines. Used to avoid reallocations when layouting.
1108 mTextUpdateInfo.mEstimatedNumberOfLines = std::max(mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count());
1110 // Set the previous number of characters for the next time the text is updated.
1111 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1116 void Controller::Impl::RetrieveDefaultInputStyle(InputStyle& inputStyle)
1118 // Sets the default text's color.
1119 inputStyle.textColor = mTextColor;
1120 inputStyle.isDefaultColor = true;
1122 inputStyle.familyName.clear();
1123 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1124 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1125 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1126 inputStyle.size = 0.f;
1128 inputStyle.lineSpacing = 0.f;
1130 inputStyle.underlineProperties.clear();
1131 inputStyle.shadowProperties.clear();
1132 inputStyle.embossProperties.clear();
1133 inputStyle.outlineProperties.clear();
1135 inputStyle.isFamilyDefined = false;
1136 inputStyle.isWeightDefined = false;
1137 inputStyle.isWidthDefined = false;
1138 inputStyle.isSlantDefined = false;
1139 inputStyle.isSizeDefined = false;
1141 inputStyle.isLineSpacingDefined = false;
1143 inputStyle.isUnderlineDefined = false;
1144 inputStyle.isShadowDefined = false;
1145 inputStyle.isEmbossDefined = false;
1146 inputStyle.isOutlineDefined = false;
1148 // Sets the default font's family name, weight, width, slant and size.
1151 if(mFontDefaults->familyDefined)
1153 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1154 inputStyle.isFamilyDefined = true;
1157 if(mFontDefaults->weightDefined)
1159 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1160 inputStyle.isWeightDefined = true;
1163 if(mFontDefaults->widthDefined)
1165 inputStyle.width = mFontDefaults->mFontDescription.width;
1166 inputStyle.isWidthDefined = true;
1169 if(mFontDefaults->slantDefined)
1171 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1172 inputStyle.isSlantDefined = true;
1175 if(mFontDefaults->sizeDefined)
1177 inputStyle.size = mFontDefaults->mDefaultPointSize;
1178 inputStyle.isSizeDefined = true;
1183 float Controller::Impl::GetDefaultFontLineHeight()
1185 FontId defaultFontId = 0u;
1186 if(nullptr == mFontDefaults)
1188 TextAbstraction::FontDescription fontDescription;
1189 defaultFontId = mFontClient.GetFontId(fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale);
1193 defaultFontId = mFontDefaults->GetFontId(mFontClient, mFontDefaults->mDefaultPointSize * mFontSizeScale);
1196 Text::FontMetrics fontMetrics;
1197 mMetrics->GetFontMetrics(defaultFontId, fontMetrics);
1199 return (fontMetrics.ascender - fontMetrics.descender);
1202 void Controller::Impl::SetTextSelectionRange(const uint32_t* pStart, const uint32_t* pEnd)
1204 if(nullptr == mEventData)
1206 // Nothing to do if there is no text.
1210 if(mEventData->mSelectionEnabled && (pStart || pEnd))
1212 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
1216 mEventData->mLeftSelectionPosition = std::min(*pStart, length);
1220 mEventData->mRightSelectionPosition = std::min(*pEnd, length);
1223 if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
1225 ChangeState(EventData::EDITING);
1226 mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition;
1227 mEventData->mUpdateCursorPosition = true;
1231 ChangeState(EventData::SELECTING);
1232 mEventData->mUpdateHighlightBox = true;
1233 mEventData->mUpdateLeftSelectionPosition = true;
1234 mEventData->mUpdateRightSelectionPosition = true;
1239 CharacterIndex Controller::Impl::GetPrimaryCursorPosition() const
1241 if(nullptr == mEventData)
1245 return mEventData->mPrimaryCursorPosition;
1248 bool Controller::Impl::SetPrimaryCursorPosition(CharacterIndex index, bool focused)
1250 if(nullptr == mEventData)
1252 // Nothing to do if there is no text.
1256 if(mEventData->mPrimaryCursorPosition == index)
1258 // Nothing for same cursor position.
1262 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
1263 mEventData->mPrimaryCursorPosition = std::min(index, length);
1264 // If there is no focus, only the value is updated.
1267 ChangeState(EventData::EDITING);
1268 mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1269 mEventData->mUpdateCursorPosition = true;
1270 ScrollTextToMatchCursor();
1275 Uint32Pair Controller::Impl::GetTextSelectionRange() const
1281 range.first = mEventData->mLeftSelectionPosition;
1282 range.second = mEventData->mRightSelectionPosition;
1288 bool Controller::Impl::IsEditable() const
1290 return mEventData && mEventData->mEditingEnabled;
1293 void Controller::Impl::SetEditable(bool editable)
1297 mEventData->mEditingEnabled = editable;
1301 void Controller::Impl::RetrieveSelection(std::string& selectedText, bool deleteAfterRetrieval)
1303 if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
1305 // Nothing to select if handles are in the same place.
1306 selectedText.clear();
1310 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1312 //Get start and end position of selection
1313 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1314 const Length lengthOfSelectedText = (handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition) - startOfSelectedText;
1316 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1317 const Length numberOfCharacters = utf32Characters.Count();
1319 // Validate the start and end selection points
1320 if((startOfSelectedText + lengthOfSelectedText) <= numberOfCharacters)
1322 //Get text as a UTF8 string
1323 Utf32ToUtf8(&utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText);
1325 if(deleteAfterRetrieval) // Only delete text if copied successfully
1327 // Keep a copy of the current input style.
1328 InputStyle currentInputStyle;
1329 currentInputStyle.Copy(mEventData->mInputStyle);
1331 // Set as input style the style of the first deleted character.
1332 mModel->mLogicalModel->RetrieveStyle(startOfSelectedText, mEventData->mInputStyle);
1334 // Compare if the input style has changed.
1335 const bool hasInputStyleChanged = !currentInputStyle.Equal(mEventData->mInputStyle);
1337 if(hasInputStyleChanged)
1339 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask(mEventData->mInputStyle);
1340 // Queue the input style changed signal.
1341 mEventData->mInputStyleChangedQueue.PushBack(styleChangedMask);
1344 mModel->mLogicalModel->UpdateTextStyleRuns(startOfSelectedText, -static_cast<int>(lengthOfSelectedText));
1346 // Mark the paragraphs to be updated.
1347 if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1349 mTextUpdateInfo.mCharacterIndex = 0;
1350 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1351 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1352 mTextUpdateInfo.mClearAll = true;
1356 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1357 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1360 // Delete text between handles
1361 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1362 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1363 utf32Characters.Erase(first, last);
1365 // Will show the cursor at the first character of the selection.
1366 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1370 // Will show the cursor at the last character of the selection.
1371 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1374 mEventData->mDecoratorUpdated = true;
1378 void Controller::Impl::SetSelection(int start, int end)
1380 mEventData->mLeftSelectionPosition = start;
1381 mEventData->mRightSelectionPosition = end;
1382 mEventData->mUpdateCursorPosition = true;
1385 std::pair<int, int> Controller::Impl::GetSelectionIndexes() const
1387 return {mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition};
1390 void Controller::Impl::ShowClipboard()
1394 mClipboard.ShowClipboard();
1398 void Controller::Impl::HideClipboard()
1400 if(mClipboard && mClipboardHideEnabled)
1402 mClipboard.HideClipboard();
1406 void Controller::Impl::SetClipboardHideEnable(bool enable)
1408 mClipboardHideEnabled = enable;
1411 bool Controller::Impl::CopyStringToClipboard(const std::string& source)
1413 //Send string to clipboard
1414 return (mClipboard && mClipboard.SetItem(source));
1417 void Controller::Impl::SendSelectionToClipboard(bool deleteAfterSending)
1419 std::string selectedText;
1420 RetrieveSelection(selectedText, deleteAfterSending);
1421 CopyStringToClipboard(selectedText);
1422 ChangeState(EventData::EDITING);
1425 void Controller::Impl::RequestGetTextFromClipboard()
1429 mClipboard.RequestItem();
1433 void Controller::Impl::RepositionSelectionHandles()
1435 SelectionHandleController::Reposition(*this);
1437 void Controller::Impl::RepositionSelectionHandles(float visualX, float visualY, Controller::NoTextTap::Action action)
1439 SelectionHandleController::Reposition(*this, visualX, visualY, action);
1442 void Controller::Impl::SetPopupButtons()
1445 * Sets the Popup buttons to be shown depending on State.
1447 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1449 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1452 bool isEditable = IsEditable();
1453 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1455 if(EventData::SELECTING == mEventData->mState)
1457 buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::COPY);
1460 buttonsToShow = TextSelectionPopup::Buttons(buttonsToShow | TextSelectionPopup::CUT);
1463 if(!IsClipboardEmpty())
1467 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1469 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1472 if(!mEventData->mAllTextSelected)
1474 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::SELECT_ALL));
1477 else if(EventData::EDITING_WITH_POPUP == mEventData->mState)
1479 if(mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
1481 buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL);
1484 if(!IsClipboardEmpty())
1488 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1490 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1493 else if(EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState)
1495 if(!IsClipboardEmpty())
1499 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1501 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1505 mEventData->mDecorator->SetEnabledPopupButtons(buttonsToShow);
1508 void Controller::Impl::ChangeState(EventData::State newState)
1510 if(nullptr == mEventData)
1512 // Nothing to do if there is no text input.
1516 DALI_LOG_INFO(gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState);
1518 if(mEventData->mState != newState)
1520 mEventData->mPreviousState = mEventData->mState;
1521 mEventData->mState = newState;
1523 switch(mEventData->mState)
1525 case EventData::INACTIVE:
1527 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1528 mEventData->mDecorator->StopCursorBlink();
1529 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1530 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1531 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1532 mEventData->mDecorator->SetHighlightActive(false);
1533 mEventData->mDecorator->SetPopupActive(false);
1534 mEventData->mDecoratorUpdated = true;
1537 case EventData::INTERRUPTED:
1539 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1540 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1541 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1542 mEventData->mDecorator->SetHighlightActive(false);
1543 mEventData->mDecorator->SetPopupActive(false);
1544 mEventData->mDecoratorUpdated = true;
1547 case EventData::SELECTING:
1549 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1550 mEventData->mDecorator->StopCursorBlink();
1551 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1552 if(mEventData->mGrabHandleEnabled)
1554 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
1555 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
1557 mEventData->mDecorator->SetHighlightActive(true);
1558 if(mEventData->mGrabHandlePopupEnabled)
1561 mEventData->mDecorator->SetPopupActive(true);
1563 mEventData->mDecoratorUpdated = true;
1566 case EventData::EDITING:
1568 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1569 if(mEventData->mCursorBlinkEnabled)
1571 mEventData->mDecorator->StartCursorBlink();
1573 // Grab handle is not shown until a tap is received whilst EDITING
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 if(mEventData->mGrabHandlePopupEnabled)
1580 mEventData->mDecorator->SetPopupActive(false);
1582 mEventData->mDecoratorUpdated = true;
1585 case EventData::EDITING_WITH_POPUP:
1587 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState);
1589 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1590 if(mEventData->mCursorBlinkEnabled)
1592 mEventData->mDecorator->StartCursorBlink();
1594 if(mEventData->mSelectionEnabled)
1596 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1597 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1598 mEventData->mDecorator->SetHighlightActive(false);
1600 else if(mEventData->mGrabHandleEnabled)
1602 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1604 if(mEventData->mGrabHandlePopupEnabled)
1607 mEventData->mDecorator->SetPopupActive(true);
1609 mEventData->mDecoratorUpdated = true;
1612 case EventData::EDITING_WITH_GRAB_HANDLE:
1614 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState);
1616 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1617 if(mEventData->mCursorBlinkEnabled)
1619 mEventData->mDecorator->StartCursorBlink();
1621 // Grab handle is not shown until a tap is received whilst EDITING
1622 if(mEventData->mGrabHandleEnabled)
1624 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1626 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1627 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1628 mEventData->mDecorator->SetHighlightActive(false);
1629 if(mEventData->mGrabHandlePopupEnabled)
1631 mEventData->mDecorator->SetPopupActive(false);
1633 mEventData->mDecoratorUpdated = true;
1636 case EventData::SELECTION_HANDLE_PANNING:
1638 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1639 mEventData->mDecorator->StopCursorBlink();
1640 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1641 if(mEventData->mGrabHandleEnabled)
1643 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
1644 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
1646 mEventData->mDecorator->SetHighlightActive(true);
1647 if(mEventData->mGrabHandlePopupEnabled)
1649 mEventData->mDecorator->SetPopupActive(false);
1651 mEventData->mDecoratorUpdated = true;
1654 case EventData::GRAB_HANDLE_PANNING:
1656 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState);
1658 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1659 if(mEventData->mCursorBlinkEnabled)
1661 mEventData->mDecorator->StartCursorBlink();
1663 if(mEventData->mGrabHandleEnabled)
1665 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1667 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1668 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1669 mEventData->mDecorator->SetHighlightActive(false);
1670 if(mEventData->mGrabHandlePopupEnabled)
1672 mEventData->mDecorator->SetPopupActive(false);
1674 mEventData->mDecoratorUpdated = true;
1677 case EventData::EDITING_WITH_PASTE_POPUP:
1679 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState);
1681 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1682 if(mEventData->mCursorBlinkEnabled)
1684 mEventData->mDecorator->StartCursorBlink();
1687 if(mEventData->mGrabHandleEnabled)
1689 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1691 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1692 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1693 mEventData->mDecorator->SetHighlightActive(false);
1695 if(mEventData->mGrabHandlePopupEnabled)
1698 mEventData->mDecorator->SetPopupActive(true);
1700 mEventData->mDecoratorUpdated = true;
1703 case EventData::TEXT_PANNING:
1705 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1706 mEventData->mDecorator->StopCursorBlink();
1707 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1708 if(mEventData->mDecorator->IsHandleActive(LEFT_SELECTION_HANDLE) ||
1709 mEventData->mDecorator->IsHandleActive(RIGHT_SELECTION_HANDLE))
1711 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1712 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1713 mEventData->mDecorator->SetHighlightActive(true);
1716 if(mEventData->mGrabHandlePopupEnabled)
1718 mEventData->mDecorator->SetPopupActive(false);
1721 mEventData->mDecoratorUpdated = true;
1728 void Controller::Impl::GetCursorPosition(CharacterIndex logical,
1729 CursorInfo& cursorInfo)
1731 if(!IsShowingRealText())
1733 // Do not want to use the place-holder text to set the cursor position.
1735 // Use the line's height of the font's family set to set the cursor's size.
1736 // If there is no font's family set, use the default font.
1737 // Use the current alignment to place the cursor at the beginning, center or end of the box.
1739 cursorInfo.lineOffset = 0.f;
1740 cursorInfo.lineHeight = GetDefaultFontLineHeight();
1741 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1744 if(mModel->mMatchLayoutDirection != DevelText::MatchLayoutDirection::CONTENTS)
1746 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1749 switch(mModel->mHorizontalAlignment)
1751 case Text::HorizontalAlignment::BEGIN:
1755 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1759 cursorInfo.primaryPosition.x = 0.f;
1763 case Text::HorizontalAlignment::CENTER:
1765 cursorInfo.primaryPosition.x = floorf(0.5f * mModel->mVisualModel->mControlSize.width);
1768 case Text::HorizontalAlignment::END:
1772 cursorInfo.primaryPosition.x = 0.f;
1776 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1782 // Nothing else to do.
1786 const bool isMultiLine = (Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout());
1787 GetCursorPositionParameters parameters;
1788 parameters.visualModel = mModel->mVisualModel;
1789 parameters.logicalModel = mModel->mLogicalModel;
1790 parameters.metrics = mMetrics;
1791 parameters.logical = logical;
1792 parameters.isMultiline = isMultiLine;
1794 Text::GetCursorPosition(parameters,
1797 // Adds Outline offset.
1798 const float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1799 cursorInfo.primaryPosition.x += outlineWidth;
1800 cursorInfo.primaryPosition.y += outlineWidth;
1801 cursorInfo.secondaryPosition.x += outlineWidth;
1802 cursorInfo.secondaryPosition.y += outlineWidth;
1806 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
1808 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
1809 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
1811 if(0.f > cursorInfo.primaryPosition.x)
1813 cursorInfo.primaryPosition.x = 0.f;
1816 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>(mEventData->mDecorator->GetCursorWidth());
1817 if(cursorInfo.primaryPosition.x > edgeWidth)
1819 cursorInfo.primaryPosition.x = edgeWidth;
1824 CharacterIndex Controller::Impl::CalculateNewCursorIndex(CharacterIndex index) const
1826 if(nullptr == mEventData)
1828 // Nothing to do if there is no text input.
1832 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1834 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1835 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1837 GlyphIndex glyphIndex = *(charactersToGlyphBuffer + index);
1838 Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1840 if(numberOfCharacters > 1u)
1842 const Script script = mModel->mLogicalModel->GetScript(index);
1843 if(HasLigatureMustBreak(script))
1845 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
1846 numberOfCharacters = 1u;
1851 while(0u == numberOfCharacters)
1854 numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1858 if(index < mEventData->mPrimaryCursorPosition)
1860 cursorIndex -= numberOfCharacters;
1864 cursorIndex += numberOfCharacters;
1867 // Will update the cursor hook position.
1868 mEventData->mUpdateCursorHookPosition = true;
1873 void Controller::Impl::UpdateCursorPosition(const CursorInfo& cursorInfo)
1875 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this);
1876 if(nullptr == mEventData)
1878 // Nothing to do if there is no text input.
1879 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n");
1883 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
1885 mEventData->mDecorator->SetGlyphOffset(PRIMARY_CURSOR, cursorInfo.glyphOffset);
1887 // Sets the cursor position.
1888 mEventData->mDecorator->SetPosition(PRIMARY_CURSOR,
1891 cursorInfo.primaryCursorHeight,
1892 cursorInfo.lineHeight);
1893 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y);
1895 if(mEventData->mUpdateGrabHandlePosition)
1897 // Sets the grab handle position.
1898 mEventData->mDecorator->SetPosition(GRAB_HANDLE,
1900 cursorInfo.lineOffset + mModel->mScrollPosition.y,
1901 cursorInfo.lineHeight);
1904 if(cursorInfo.isSecondaryCursor)
1906 mEventData->mDecorator->SetPosition(SECONDARY_CURSOR,
1907 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
1908 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
1909 cursorInfo.secondaryCursorHeight,
1910 cursorInfo.lineHeight);
1911 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y);
1914 // Set which cursors are active according the state.
1915 if(EventData::IsEditingState(mEventData->mState) || (EventData::GRAB_HANDLE_PANNING == mEventData->mState))
1917 if(cursorInfo.isSecondaryCursor)
1919 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_BOTH);
1923 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1928 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1931 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n");
1934 void Controller::Impl::UpdateSelectionHandle(HandleType handleType,
1935 const CursorInfo& cursorInfo)
1937 SelectionHandleController::Update(*this, handleType, cursorInfo);
1940 void Controller::Impl::ClampHorizontalScroll(const Vector2& layoutSize)
1942 // Clamp between -space & -alignment offset.
1944 if(layoutSize.width > mModel->mVisualModel->mControlSize.width)
1946 const float space = (layoutSize.width - mModel->mVisualModel->mControlSize.width) + mModel->mAlignmentOffset;
1947 mModel->mScrollPosition.x = (mModel->mScrollPosition.x < -space) ? -space : mModel->mScrollPosition.x;
1948 mModel->mScrollPosition.x = (mModel->mScrollPosition.x > -mModel->mAlignmentOffset) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
1950 mEventData->mDecoratorUpdated = true;
1954 mModel->mScrollPosition.x = 0.f;
1958 void Controller::Impl::ClampVerticalScroll(const Vector2& layoutSize)
1960 if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1962 // Nothing to do if the text is single line.
1966 // Clamp between -space & 0.
1967 if(layoutSize.height > mModel->mVisualModel->mControlSize.height)
1969 const float space = (layoutSize.height - mModel->mVisualModel->mControlSize.height);
1970 mModel->mScrollPosition.y = (mModel->mScrollPosition.y < -space) ? -space : mModel->mScrollPosition.y;
1971 mModel->mScrollPosition.y = (mModel->mScrollPosition.y > 0.f) ? 0.f : mModel->mScrollPosition.y;
1973 mEventData->mDecoratorUpdated = true;
1977 mModel->mScrollPosition.y = 0.f;
1981 void Controller::Impl::ScrollToMakePositionVisible(const Vector2& position, float lineHeight)
1983 const float cursorWidth = mEventData->mDecorator ? static_cast<float>(mEventData->mDecorator->GetCursorWidth()) : 0.f;
1985 // position is in actor's coords.
1986 const float positionEndX = position.x + cursorWidth;
1987 const float positionEndY = position.y + lineHeight;
1989 // Transform the position to decorator coords.
1990 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
1991 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
1993 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
1994 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
1996 if(decoratorPositionBeginX < 0.f)
1998 mModel->mScrollPosition.x = -position.x;
2000 else if(decoratorPositionEndX > mModel->mVisualModel->mControlSize.width)
2002 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
2005 if(Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout())
2007 if(decoratorPositionBeginY < 0.f)
2009 mModel->mScrollPosition.y = -position.y;
2011 else if(decoratorPositionEndY > mModel->mVisualModel->mControlSize.height)
2013 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
2018 void Controller::Impl::ScrollTextToMatchCursor(const CursorInfo& cursorInfo)
2020 // Get the current cursor position in decorator coords.
2021 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition(PRIMARY_CURSOR);
2023 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter(mEventData->mPrimaryCursorPosition);
2025 // Calculate the offset to match the cursor position before the character was deleted.
2026 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2028 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
2029 if(mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() - 1u)
2031 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset(PRIMARY_CURSOR);
2032 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
2035 ClampHorizontalScroll(mModel->mVisualModel->GetLayoutSize());
2036 ClampVerticalScroll(mModel->mVisualModel->GetLayoutSize());
2038 // Makes the new cursor position visible if needed.
2039 ScrollToMakePositionVisible(cursorInfo.primaryPosition, cursorInfo.lineHeight);
2042 void Controller::Impl::ScrollTextToMatchCursor()
2044 CursorInfo cursorInfo;
2045 GetCursorPosition(mEventData->mPrimaryCursorPosition, cursorInfo);
2046 ScrollTextToMatchCursor(cursorInfo);
2049 void Controller::Impl::RequestRelayout()
2051 if(nullptr != mControlInterface)
2053 mControlInterface->RequestTextRelayout();
2057 Actor Controller::Impl::CreateBackgroundActor()
2059 // NOTE: Currently we only support background color for left-to-right text.
2063 Length numberOfGlyphs = mView.GetNumberOfGlyphs();
2064 if(numberOfGlyphs > 0u)
2066 Vector<GlyphInfo> glyphs;
2067 glyphs.Resize(numberOfGlyphs);
2069 Vector<Vector2> positions;
2070 positions.Resize(numberOfGlyphs);
2072 // Get the line where the glyphs are laid-out.
2073 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
2074 float alignmentOffset = lineRun->alignmentOffset;
2075 numberOfGlyphs = mView.GetGlyphs(glyphs.Begin(),
2081 glyphs.Resize(numberOfGlyphs);
2082 positions.Resize(numberOfGlyphs);
2084 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
2085 const Vector2* const positionsBuffer = positions.Begin();
2087 BackgroundMesh mesh;
2088 mesh.mVertices.Reserve(4u * glyphs.Size());
2089 mesh.mIndices.Reserve(6u * glyphs.Size());
2091 const Vector2 textSize = mView.GetLayoutSize();
2093 const float offsetX = textSize.width * 0.5f;
2094 const float offsetY = textSize.height * 0.5f;
2096 const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
2097 const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
2098 const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
2101 uint32_t numberOfQuads = 0u;
2102 Length yLineOffset = 0;
2103 Length prevLineIndex = 0;
2104 LineIndex lineIndex;
2105 Length numberOfLines;
2107 for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i)
2109 const GlyphInfo& glyph = *(glyphsBuffer + i);
2111 // Get the background color of the character.
2112 // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
2113 const bool isMarkupBackground = mView.IsMarkupBackgroundColorSet();
2114 const ColorIndex backgroundColorIndex = isMarkupBackground ? *(backgroundColorIndicesBuffer + i) : 0u;
2115 const bool isDefaultBackgroundColor = (0u == backgroundColorIndex);
2116 const Vector4& backgroundColor = isDefaultBackgroundColor ? defaultBackgroundColor : *(backgroundColorsBuffer + backgroundColorIndex - 1u);
2118 mModel->mVisualModel->GetNumberOfLines(i, 1, lineIndex, numberOfLines);
2119 Length lineHeight = lineRun[lineIndex].ascender + -(lineRun[lineIndex].descender) + lineRun[lineIndex].lineSpacing;
2121 if(lineIndex != prevLineIndex)
2123 yLineOffset += lineHeight;
2126 // Only create quads for glyphs with a background color
2127 if(backgroundColor != Color::TRANSPARENT)
2129 const Vector2 position = *(positionsBuffer + i);
2131 if(i == 0u && glyphSize == 1u) // Only one glyph in the whole text
2133 quad.x = position.x;
2134 quad.y = yLineOffset;
2135 quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
2136 quad.w = lineHeight;
2138 else if((lineIndex != prevLineIndex) || (i == 0u)) // The first glyph in the line
2140 quad.x = position.x;
2141 quad.y = yLineOffset;
2142 quad.z = quad.x - glyph.xBearing + glyph.advance;
2143 quad.w = quad.y + lineHeight;
2145 else if(i == glyphSize - 1u) // The last glyph in the whole text
2147 quad.x = position.x - glyph.xBearing;
2148 quad.y = yLineOffset;
2149 quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
2150 quad.w = quad.y + lineHeight;
2152 else // The glyph in the middle of the text
2154 quad.x = position.x - glyph.xBearing;
2155 quad.y = yLineOffset;
2156 quad.z = quad.x + glyph.advance;
2157 quad.w = quad.y + lineHeight;
2160 BackgroundVertex vertex;
2163 vertex.mPosition.x = quad.x - offsetX;
2164 vertex.mPosition.y = quad.y - offsetY;
2165 vertex.mColor = backgroundColor;
2166 mesh.mVertices.PushBack(vertex);
2169 vertex.mPosition.x = quad.z - offsetX;
2170 vertex.mPosition.y = quad.y - offsetY;
2171 vertex.mColor = backgroundColor;
2172 mesh.mVertices.PushBack(vertex);
2175 vertex.mPosition.x = quad.x - offsetX;
2176 vertex.mPosition.y = quad.w - offsetY;
2177 vertex.mColor = backgroundColor;
2178 mesh.mVertices.PushBack(vertex);
2181 vertex.mPosition.x = quad.z - offsetX;
2182 vertex.mPosition.y = quad.w - offsetY;
2183 vertex.mColor = backgroundColor;
2184 mesh.mVertices.PushBack(vertex);
2186 // Six indices in counter clockwise winding
2187 mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
2188 mesh.mIndices.PushBack(0u + 4 * numberOfQuads);
2189 mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
2190 mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
2191 mesh.mIndices.PushBack(3u + 4 * numberOfQuads);
2192 mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
2197 if(lineIndex != prevLineIndex)
2199 prevLineIndex = lineIndex;
2203 // Only create the background actor if there are glyphs with background color
2204 if(mesh.mVertices.Count() > 0u)
2206 Property::Map quadVertexFormat;
2207 quadVertexFormat["aPosition"] = Property::VECTOR2;
2208 quadVertexFormat["aColor"] = Property::VECTOR4;
2210 VertexBuffer quadVertices = VertexBuffer::New(quadVertexFormat);
2211 quadVertices.SetData(&mesh.mVertices[0], mesh.mVertices.Size());
2213 Geometry quadGeometry = Geometry::New();
2214 quadGeometry.AddVertexBuffer(quadVertices);
2215 quadGeometry.SetIndexBuffer(&mesh.mIndices[0], mesh.mIndices.Size());
2217 if(!mShaderBackground)
2219 mShaderBackground = Shader::New(SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_VERT, SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_FRAG);
2222 Dali::Renderer renderer = Dali::Renderer::New(quadGeometry, mShaderBackground);
2223 renderer.SetProperty(Dali::Renderer::Property::BLEND_MODE, BlendMode::ON);
2224 renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT);
2226 actor = Actor::New();
2227 actor.SetProperty(Dali::Actor::Property::NAME, "TextBackgroundColorActor");
2228 actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
2229 actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
2230 actor.SetProperty(Actor::Property::SIZE, textSize);
2231 actor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR);
2232 actor.AddRenderer(renderer);
2239 void Controller::Impl::CopyUnderlinedFromLogicalToVisualModels(bool shouldClearPreUnderlineRuns)
2241 //Underlined character runs for markup-processor
2242 const Vector<UnderlinedCharacterRun>& underlinedCharacterRuns = mModel->mLogicalModel->mUnderlinedCharacterRuns;
2243 const Vector<GlyphIndex>& charactersToGlyph = mModel->mVisualModel->mCharactersToGlyph;
2244 const Vector<Length>& glyphsPerCharacter = mModel->mVisualModel->mGlyphsPerCharacter;
2246 if(shouldClearPreUnderlineRuns)
2248 mModel->mVisualModel->mUnderlineRuns.Clear();
2251 for(Vector<UnderlinedCharacterRun>::ConstIterator it = underlinedCharacterRuns.Begin(), endIt = underlinedCharacterRuns.End(); it != endIt; ++it)
2253 CharacterIndex characterIndex = it->characterRun.characterIndex;
2254 Length numberOfCharacters = it->characterRun.numberOfCharacters;
2255 for(Length index = 0u; index < numberOfCharacters; index++)
2257 GlyphRun underlineGlyphRun;
2258 underlineGlyphRun.glyphIndex = charactersToGlyph[characterIndex + index];
2259 underlineGlyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex + index];
2260 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineGlyphRun);
2267 } // namespace Toolkit