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 // 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 uint32_t oldCursorPos = mEventData->mPrimaryCursorPosition;
1264 mEventData->mPrimaryCursorPosition = std::min(index, length);
1265 // If there is no focus, only the value is updated.
1268 ChangeState(EventData::EDITING);
1269 mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1270 mEventData->mUpdateCursorPosition = true;
1271 ScrollTextToMatchCursor();
1274 if(nullptr != mEditableControlInterface)
1276 mEditableControlInterface->CursorPositionChanged(oldCursorPos, mEventData->mPrimaryCursorPosition);
1282 Uint32Pair Controller::Impl::GetTextSelectionRange() const
1288 range.first = mEventData->mLeftSelectionPosition;
1289 range.second = mEventData->mRightSelectionPosition;
1295 bool Controller::Impl::IsEditable() const
1297 return mEventData && mEventData->mEditingEnabled;
1300 void Controller::Impl::SetEditable(bool editable)
1304 mEventData->mEditingEnabled = editable;
1308 void Controller::Impl::RetrieveSelection(std::string& selectedText, bool deleteAfterRetrieval)
1310 if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
1312 // Nothing to select if handles are in the same place.
1313 selectedText.clear();
1317 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1319 //Get start and end position of selection
1320 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1321 const Length lengthOfSelectedText = (handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition) - startOfSelectedText;
1323 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1324 const Length numberOfCharacters = utf32Characters.Count();
1326 // Validate the start and end selection points
1327 if((startOfSelectedText + lengthOfSelectedText) <= numberOfCharacters)
1329 //Get text as a UTF8 string
1330 Utf32ToUtf8(&utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText);
1332 if(deleteAfterRetrieval) // Only delete text if copied successfully
1334 // Keep a copy of the current input style.
1335 InputStyle currentInputStyle;
1336 currentInputStyle.Copy(mEventData->mInputStyle);
1338 // Set as input style the style of the first deleted character.
1339 mModel->mLogicalModel->RetrieveStyle(startOfSelectedText, mEventData->mInputStyle);
1341 // Compare if the input style has changed.
1342 const bool hasInputStyleChanged = !currentInputStyle.Equal(mEventData->mInputStyle);
1344 if(hasInputStyleChanged)
1346 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask(mEventData->mInputStyle);
1347 // Queue the input style changed signal.
1348 mEventData->mInputStyleChangedQueue.PushBack(styleChangedMask);
1351 mModel->mLogicalModel->UpdateTextStyleRuns(startOfSelectedText, -static_cast<int>(lengthOfSelectedText));
1353 // Mark the paragraphs to be updated.
1354 if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1356 mTextUpdateInfo.mCharacterIndex = 0;
1357 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1358 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1359 mTextUpdateInfo.mClearAll = true;
1363 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1364 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1367 // Delete text between handles
1368 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1369 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1370 utf32Characters.Erase(first, last);
1372 // Will show the cursor at the first character of the selection.
1373 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1377 // Will show the cursor at the last character of the selection.
1378 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1381 mEventData->mDecoratorUpdated = true;
1385 void Controller::Impl::SetSelection(int start, int end)
1387 mEventData->mLeftSelectionPosition = start;
1388 mEventData->mRightSelectionPosition = end;
1389 mEventData->mUpdateCursorPosition = true;
1392 std::pair<int, int> Controller::Impl::GetSelectionIndexes() const
1394 return {mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition};
1397 void Controller::Impl::ShowClipboard()
1401 mClipboard.ShowClipboard();
1405 void Controller::Impl::HideClipboard()
1407 if(mClipboard && mClipboardHideEnabled)
1409 mClipboard.HideClipboard();
1413 void Controller::Impl::SetClipboardHideEnable(bool enable)
1415 mClipboardHideEnabled = enable;
1418 bool Controller::Impl::CopyStringToClipboard(const std::string& source)
1420 //Send string to clipboard
1421 return (mClipboard && mClipboard.SetItem(source));
1424 void Controller::Impl::SendSelectionToClipboard(bool deleteAfterSending)
1426 std::string selectedText;
1427 RetrieveSelection(selectedText, deleteAfterSending);
1428 CopyStringToClipboard(selectedText);
1429 ChangeState(EventData::EDITING);
1432 void Controller::Impl::RequestGetTextFromClipboard()
1436 mClipboard.RequestItem();
1440 void Controller::Impl::RepositionSelectionHandles()
1442 SelectionHandleController::Reposition(*this);
1444 void Controller::Impl::RepositionSelectionHandles(float visualX, float visualY, Controller::NoTextTap::Action action)
1446 SelectionHandleController::Reposition(*this, visualX, visualY, action);
1449 void Controller::Impl::SetPopupButtons()
1452 * Sets the Popup buttons to be shown depending on State.
1454 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1456 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1459 bool isEditable = IsEditable();
1460 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1462 if(EventData::SELECTING == mEventData->mState)
1464 buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::COPY);
1467 buttonsToShow = TextSelectionPopup::Buttons(buttonsToShow | TextSelectionPopup::CUT);
1470 if(!IsClipboardEmpty())
1474 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1476 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1479 if(!mEventData->mAllTextSelected)
1481 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::SELECT_ALL));
1484 else if(EventData::EDITING_WITH_POPUP == mEventData->mState)
1486 if(mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
1488 buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL);
1491 if(!IsClipboardEmpty())
1495 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1497 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1500 else if(EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState)
1502 if(!IsClipboardEmpty())
1506 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1508 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1512 mEventData->mDecorator->SetEnabledPopupButtons(buttonsToShow);
1515 void Controller::Impl::ChangeState(EventData::State newState)
1517 if(nullptr == mEventData)
1519 // Nothing to do if there is no text input.
1523 DALI_LOG_INFO(gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState);
1525 if(mEventData->mState != newState)
1527 mEventData->mPreviousState = mEventData->mState;
1528 mEventData->mState = newState;
1530 switch(mEventData->mState)
1532 case EventData::INACTIVE:
1534 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1535 mEventData->mDecorator->StopCursorBlink();
1536 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1537 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1538 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1539 mEventData->mDecorator->SetHighlightActive(false);
1540 mEventData->mDecorator->SetPopupActive(false);
1541 mEventData->mDecoratorUpdated = true;
1544 case EventData::INTERRUPTED:
1546 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1547 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1548 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1549 mEventData->mDecorator->SetHighlightActive(false);
1550 mEventData->mDecorator->SetPopupActive(false);
1551 mEventData->mDecoratorUpdated = true;
1554 case EventData::SELECTING:
1556 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1557 mEventData->mDecorator->StopCursorBlink();
1558 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1559 if(mEventData->mGrabHandleEnabled)
1561 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
1562 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
1564 mEventData->mDecorator->SetHighlightActive(true);
1565 if(mEventData->mGrabHandlePopupEnabled)
1568 mEventData->mDecorator->SetPopupActive(true);
1570 mEventData->mDecoratorUpdated = true;
1573 case EventData::EDITING:
1575 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1576 if(mEventData->mCursorBlinkEnabled)
1578 mEventData->mDecorator->StartCursorBlink();
1580 // Grab handle is not shown until a tap is received whilst EDITING
1581 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1582 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1583 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1584 mEventData->mDecorator->SetHighlightActive(false);
1585 if(mEventData->mGrabHandlePopupEnabled)
1587 mEventData->mDecorator->SetPopupActive(false);
1589 mEventData->mDecoratorUpdated = true;
1592 case EventData::EDITING_WITH_POPUP:
1594 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState);
1596 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1597 if(mEventData->mCursorBlinkEnabled)
1599 mEventData->mDecorator->StartCursorBlink();
1601 if(mEventData->mSelectionEnabled)
1603 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1604 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1605 mEventData->mDecorator->SetHighlightActive(false);
1607 else if(mEventData->mGrabHandleEnabled)
1609 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1611 if(mEventData->mGrabHandlePopupEnabled)
1614 mEventData->mDecorator->SetPopupActive(true);
1616 mEventData->mDecoratorUpdated = true;
1619 case EventData::EDITING_WITH_GRAB_HANDLE:
1621 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState);
1623 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1624 if(mEventData->mCursorBlinkEnabled)
1626 mEventData->mDecorator->StartCursorBlink();
1628 // Grab handle is not shown until a tap is received whilst EDITING
1629 if(mEventData->mGrabHandleEnabled)
1631 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1633 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1634 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1635 mEventData->mDecorator->SetHighlightActive(false);
1636 if(mEventData->mGrabHandlePopupEnabled)
1638 mEventData->mDecorator->SetPopupActive(false);
1640 mEventData->mDecoratorUpdated = true;
1643 case EventData::SELECTION_HANDLE_PANNING:
1645 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1646 mEventData->mDecorator->StopCursorBlink();
1647 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1648 if(mEventData->mGrabHandleEnabled)
1650 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
1651 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
1653 mEventData->mDecorator->SetHighlightActive(true);
1654 if(mEventData->mGrabHandlePopupEnabled)
1656 mEventData->mDecorator->SetPopupActive(false);
1658 mEventData->mDecoratorUpdated = true;
1661 case EventData::GRAB_HANDLE_PANNING:
1663 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState);
1665 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1666 if(mEventData->mCursorBlinkEnabled)
1668 mEventData->mDecorator->StartCursorBlink();
1670 if(mEventData->mGrabHandleEnabled)
1672 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1674 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1675 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1676 mEventData->mDecorator->SetHighlightActive(false);
1677 if(mEventData->mGrabHandlePopupEnabled)
1679 mEventData->mDecorator->SetPopupActive(false);
1681 mEventData->mDecoratorUpdated = true;
1684 case EventData::EDITING_WITH_PASTE_POPUP:
1686 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState);
1688 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1689 if(mEventData->mCursorBlinkEnabled)
1691 mEventData->mDecorator->StartCursorBlink();
1694 if(mEventData->mGrabHandleEnabled)
1696 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1698 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1699 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1700 mEventData->mDecorator->SetHighlightActive(false);
1702 if(mEventData->mGrabHandlePopupEnabled)
1705 mEventData->mDecorator->SetPopupActive(true);
1707 mEventData->mDecoratorUpdated = true;
1710 case EventData::TEXT_PANNING:
1712 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1713 mEventData->mDecorator->StopCursorBlink();
1714 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1715 if(mEventData->mDecorator->IsHandleActive(LEFT_SELECTION_HANDLE) ||
1716 mEventData->mDecorator->IsHandleActive(RIGHT_SELECTION_HANDLE))
1718 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1719 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1720 mEventData->mDecorator->SetHighlightActive(true);
1723 if(mEventData->mGrabHandlePopupEnabled)
1725 mEventData->mDecorator->SetPopupActive(false);
1728 mEventData->mDecoratorUpdated = true;
1735 void Controller::Impl::GetCursorPosition(CharacterIndex logical,
1736 CursorInfo& cursorInfo)
1738 if(!IsShowingRealText())
1740 // Do not want to use the place-holder text to set the cursor position.
1742 // Use the line's height of the font's family set to set the cursor's size.
1743 // If there is no font's family set, use the default font.
1744 // Use the current alignment to place the cursor at the beginning, center or end of the box.
1746 cursorInfo.lineOffset = 0.f;
1747 cursorInfo.lineHeight = GetDefaultFontLineHeight();
1748 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1751 if(mModel->mMatchLayoutDirection != DevelText::MatchLayoutDirection::CONTENTS)
1753 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1756 switch(mModel->mHorizontalAlignment)
1758 case Text::HorizontalAlignment::BEGIN:
1762 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1766 cursorInfo.primaryPosition.x = 0.f;
1770 case Text::HorizontalAlignment::CENTER:
1772 cursorInfo.primaryPosition.x = floorf(0.5f * mModel->mVisualModel->mControlSize.width);
1775 case Text::HorizontalAlignment::END:
1779 cursorInfo.primaryPosition.x = 0.f;
1783 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1789 // Nothing else to do.
1793 const bool isMultiLine = (Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout());
1794 GetCursorPositionParameters parameters;
1795 parameters.visualModel = mModel->mVisualModel;
1796 parameters.logicalModel = mModel->mLogicalModel;
1797 parameters.metrics = mMetrics;
1798 parameters.logical = logical;
1799 parameters.isMultiline = isMultiLine;
1801 Text::GetCursorPosition(parameters,
1804 // Adds Outline offset.
1805 const float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1806 cursorInfo.primaryPosition.x += outlineWidth;
1807 cursorInfo.primaryPosition.y += outlineWidth;
1808 cursorInfo.secondaryPosition.x += outlineWidth;
1809 cursorInfo.secondaryPosition.y += outlineWidth;
1813 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
1815 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
1816 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
1818 if(0.f > cursorInfo.primaryPosition.x)
1820 cursorInfo.primaryPosition.x = 0.f;
1823 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>(mEventData->mDecorator->GetCursorWidth());
1824 if(cursorInfo.primaryPosition.x > edgeWidth)
1826 cursorInfo.primaryPosition.x = edgeWidth;
1831 CharacterIndex Controller::Impl::CalculateNewCursorIndex(CharacterIndex index) const
1833 if(nullptr == mEventData)
1835 // Nothing to do if there is no text input.
1839 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1841 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1842 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1844 GlyphIndex glyphIndex = *(charactersToGlyphBuffer + index);
1845 Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1847 if(numberOfCharacters > 1u)
1849 const Script script = mModel->mLogicalModel->GetScript(index);
1850 if(HasLigatureMustBreak(script))
1852 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
1853 numberOfCharacters = 1u;
1858 while(0u == numberOfCharacters)
1861 numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1865 if(index < mEventData->mPrimaryCursorPosition)
1867 cursorIndex -= numberOfCharacters;
1871 cursorIndex += numberOfCharacters;
1874 // Will update the cursor hook position.
1875 mEventData->mUpdateCursorHookPosition = true;
1880 void Controller::Impl::UpdateCursorPosition(const CursorInfo& cursorInfo)
1882 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this);
1883 if(nullptr == mEventData)
1885 // Nothing to do if there is no text input.
1886 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n");
1890 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
1892 mEventData->mDecorator->SetGlyphOffset(PRIMARY_CURSOR, cursorInfo.glyphOffset);
1894 // Sets the cursor position.
1895 mEventData->mDecorator->SetPosition(PRIMARY_CURSOR,
1898 cursorInfo.primaryCursorHeight,
1899 cursorInfo.lineHeight);
1900 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y);
1902 if(mEventData->mUpdateGrabHandlePosition)
1904 // Sets the grab handle position.
1905 mEventData->mDecorator->SetPosition(GRAB_HANDLE,
1907 cursorInfo.lineOffset + mModel->mScrollPosition.y,
1908 cursorInfo.lineHeight);
1911 if(cursorInfo.isSecondaryCursor)
1913 mEventData->mDecorator->SetPosition(SECONDARY_CURSOR,
1914 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
1915 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
1916 cursorInfo.secondaryCursorHeight,
1917 cursorInfo.lineHeight);
1918 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y);
1921 // Set which cursors are active according the state.
1922 if(EventData::IsEditingState(mEventData->mState) || (EventData::GRAB_HANDLE_PANNING == mEventData->mState))
1924 if(cursorInfo.isSecondaryCursor)
1926 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_BOTH);
1930 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1935 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1938 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n");
1941 void Controller::Impl::UpdateSelectionHandle(HandleType handleType,
1942 const CursorInfo& cursorInfo)
1944 SelectionHandleController::Update(*this, handleType, cursorInfo);
1947 void Controller::Impl::ClampHorizontalScroll(const Vector2& layoutSize)
1949 // Clamp between -space & -alignment offset.
1951 if(layoutSize.width > mModel->mVisualModel->mControlSize.width)
1953 const float space = (layoutSize.width - mModel->mVisualModel->mControlSize.width) + mModel->mAlignmentOffset;
1954 mModel->mScrollPosition.x = (mModel->mScrollPosition.x < -space) ? -space : mModel->mScrollPosition.x;
1955 mModel->mScrollPosition.x = (mModel->mScrollPosition.x > -mModel->mAlignmentOffset) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
1957 mEventData->mDecoratorUpdated = true;
1961 mModel->mScrollPosition.x = 0.f;
1965 void Controller::Impl::ClampVerticalScroll(const Vector2& layoutSize)
1967 if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1969 // Nothing to do if the text is single line.
1973 // Clamp between -space & 0.
1974 if(layoutSize.height > mModel->mVisualModel->mControlSize.height)
1976 const float space = (layoutSize.height - mModel->mVisualModel->mControlSize.height);
1977 mModel->mScrollPosition.y = (mModel->mScrollPosition.y < -space) ? -space : mModel->mScrollPosition.y;
1978 mModel->mScrollPosition.y = (mModel->mScrollPosition.y > 0.f) ? 0.f : mModel->mScrollPosition.y;
1980 mEventData->mDecoratorUpdated = true;
1984 mModel->mScrollPosition.y = 0.f;
1988 void Controller::Impl::ScrollToMakePositionVisible(const Vector2& position, float lineHeight)
1990 const float cursorWidth = mEventData->mDecorator ? static_cast<float>(mEventData->mDecorator->GetCursorWidth()) : 0.f;
1992 // position is in actor's coords.
1993 const float positionEndX = position.x + cursorWidth;
1994 const float positionEndY = position.y + lineHeight;
1996 // Transform the position to decorator coords.
1997 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
1998 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
2000 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
2001 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
2003 if(decoratorPositionBeginX < 0.f)
2005 mModel->mScrollPosition.x = -position.x;
2007 else if(decoratorPositionEndX > mModel->mVisualModel->mControlSize.width)
2009 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
2012 if(Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout())
2014 if(decoratorPositionBeginY < 0.f)
2016 mModel->mScrollPosition.y = -position.y;
2018 else if(decoratorPositionEndY > mModel->mVisualModel->mControlSize.height)
2020 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
2025 void Controller::Impl::ScrollTextToMatchCursor(const CursorInfo& cursorInfo)
2027 // Get the current cursor position in decorator coords.
2028 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition(PRIMARY_CURSOR);
2030 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter(mEventData->mPrimaryCursorPosition);
2032 // Calculate the offset to match the cursor position before the character was deleted.
2033 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2035 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
2036 if(mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() - 1u)
2038 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset(PRIMARY_CURSOR);
2039 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
2042 ClampHorizontalScroll(mModel->mVisualModel->GetLayoutSize());
2043 ClampVerticalScroll(mModel->mVisualModel->GetLayoutSize());
2045 // Makes the new cursor position visible if needed.
2046 ScrollToMakePositionVisible(cursorInfo.primaryPosition, cursorInfo.lineHeight);
2049 void Controller::Impl::ScrollTextToMatchCursor()
2051 CursorInfo cursorInfo;
2052 GetCursorPosition(mEventData->mPrimaryCursorPosition, cursorInfo);
2053 ScrollTextToMatchCursor(cursorInfo);
2056 void Controller::Impl::RequestRelayout()
2058 if(nullptr != mControlInterface)
2060 mControlInterface->RequestTextRelayout();
2064 Actor Controller::Impl::CreateBackgroundActor()
2066 // NOTE: Currently we only support background color for left-to-right text.
2070 Length numberOfGlyphs = mView.GetNumberOfGlyphs();
2071 if(numberOfGlyphs > 0u)
2073 Vector<GlyphInfo> glyphs;
2074 glyphs.Resize(numberOfGlyphs);
2076 Vector<Vector2> positions;
2077 positions.Resize(numberOfGlyphs);
2079 // Get the line where the glyphs are laid-out.
2080 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
2081 float alignmentOffset = lineRun->alignmentOffset;
2082 numberOfGlyphs = mView.GetGlyphs(glyphs.Begin(),
2088 glyphs.Resize(numberOfGlyphs);
2089 positions.Resize(numberOfGlyphs);
2091 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
2092 const Vector2* const positionsBuffer = positions.Begin();
2094 BackgroundMesh mesh;
2095 mesh.mVertices.Reserve(4u * glyphs.Size());
2096 mesh.mIndices.Reserve(6u * glyphs.Size());
2098 const Vector2 textSize = mView.GetLayoutSize();
2100 const float offsetX = textSize.width * 0.5f;
2101 const float offsetY = textSize.height * 0.5f;
2103 const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
2104 const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
2105 const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
2108 uint32_t numberOfQuads = 0u;
2109 Length yLineOffset = 0;
2110 Length prevLineIndex = 0;
2111 LineIndex lineIndex;
2112 Length numberOfLines;
2114 for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i)
2116 const GlyphInfo& glyph = *(glyphsBuffer + i);
2118 // Get the background color of the character.
2119 // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
2120 const bool isMarkupBackground = mView.IsMarkupBackgroundColorSet();
2121 const ColorIndex backgroundColorIndex = isMarkupBackground ? *(backgroundColorIndicesBuffer + i) : 0u;
2122 const bool isDefaultBackgroundColor = (0u == backgroundColorIndex);
2123 const Vector4& backgroundColor = isDefaultBackgroundColor ? defaultBackgroundColor : *(backgroundColorsBuffer + backgroundColorIndex - 1u);
2125 mModel->mVisualModel->GetNumberOfLines(i, 1, lineIndex, numberOfLines);
2126 Length lineHeight = lineRun[lineIndex].ascender + -(lineRun[lineIndex].descender) + lineRun[lineIndex].lineSpacing;
2128 if(lineIndex != prevLineIndex)
2130 yLineOffset += lineHeight;
2133 // Only create quads for glyphs with a background color
2134 if(backgroundColor != Color::TRANSPARENT)
2136 const Vector2 position = *(positionsBuffer + i);
2138 if(i == 0u && glyphSize == 1u) // Only one glyph in the whole text
2140 quad.x = position.x;
2141 quad.y = yLineOffset;
2142 quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
2143 quad.w = lineHeight;
2145 else if((lineIndex != prevLineIndex) || (i == 0u)) // The first glyph in the line
2147 quad.x = position.x;
2148 quad.y = yLineOffset;
2149 quad.z = quad.x - glyph.xBearing + glyph.advance;
2150 quad.w = quad.y + lineHeight;
2152 else if(i == glyphSize - 1u) // The last glyph in the whole text
2154 quad.x = position.x - glyph.xBearing;
2155 quad.y = yLineOffset;
2156 quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
2157 quad.w = quad.y + lineHeight;
2159 else // The glyph in the middle of the text
2161 quad.x = position.x - glyph.xBearing;
2162 quad.y = yLineOffset;
2163 quad.z = quad.x + glyph.advance;
2164 quad.w = quad.y + lineHeight;
2167 BackgroundVertex vertex;
2170 vertex.mPosition.x = quad.x - offsetX;
2171 vertex.mPosition.y = quad.y - offsetY;
2172 vertex.mColor = backgroundColor;
2173 mesh.mVertices.PushBack(vertex);
2176 vertex.mPosition.x = quad.z - offsetX;
2177 vertex.mPosition.y = quad.y - offsetY;
2178 vertex.mColor = backgroundColor;
2179 mesh.mVertices.PushBack(vertex);
2182 vertex.mPosition.x = quad.x - offsetX;
2183 vertex.mPosition.y = quad.w - offsetY;
2184 vertex.mColor = backgroundColor;
2185 mesh.mVertices.PushBack(vertex);
2188 vertex.mPosition.x = quad.z - offsetX;
2189 vertex.mPosition.y = quad.w - offsetY;
2190 vertex.mColor = backgroundColor;
2191 mesh.mVertices.PushBack(vertex);
2193 // Six indices in counter clockwise winding
2194 mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
2195 mesh.mIndices.PushBack(0u + 4 * numberOfQuads);
2196 mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
2197 mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
2198 mesh.mIndices.PushBack(3u + 4 * numberOfQuads);
2199 mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
2204 if(lineIndex != prevLineIndex)
2206 prevLineIndex = lineIndex;
2210 // Only create the background actor if there are glyphs with background color
2211 if(mesh.mVertices.Count() > 0u)
2213 Property::Map quadVertexFormat;
2214 quadVertexFormat["aPosition"] = Property::VECTOR2;
2215 quadVertexFormat["aColor"] = Property::VECTOR4;
2217 VertexBuffer quadVertices = VertexBuffer::New(quadVertexFormat);
2218 quadVertices.SetData(&mesh.mVertices[0], mesh.mVertices.Size());
2220 Geometry quadGeometry = Geometry::New();
2221 quadGeometry.AddVertexBuffer(quadVertices);
2222 quadGeometry.SetIndexBuffer(&mesh.mIndices[0], mesh.mIndices.Size());
2224 if(!mShaderBackground)
2226 mShaderBackground = Shader::New(SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_VERT, SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_FRAG);
2229 Dali::Renderer renderer = Dali::Renderer::New(quadGeometry, mShaderBackground);
2230 renderer.SetProperty(Dali::Renderer::Property::BLEND_MODE, BlendMode::ON);
2231 renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT);
2233 actor = Actor::New();
2234 actor.SetProperty(Dali::Actor::Property::NAME, "TextBackgroundColorActor");
2235 actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
2236 actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
2237 actor.SetProperty(Actor::Property::SIZE, textSize);
2238 actor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR);
2239 actor.AddRenderer(renderer);
2246 void Controller::Impl::CopyUnderlinedFromLogicalToVisualModels(bool shouldClearPreUnderlineRuns)
2248 //Underlined character runs for markup-processor
2249 const Vector<UnderlinedCharacterRun>& underlinedCharacterRuns = mModel->mLogicalModel->mUnderlinedCharacterRuns;
2250 const Vector<GlyphIndex>& charactersToGlyph = mModel->mVisualModel->mCharactersToGlyph;
2251 const Vector<Length>& glyphsPerCharacter = mModel->mVisualModel->mGlyphsPerCharacter;
2253 if(shouldClearPreUnderlineRuns)
2255 mModel->mVisualModel->mUnderlineRuns.Clear();
2258 for(Vector<UnderlinedCharacterRun>::ConstIterator it = underlinedCharacterRuns.Begin(), endIt = underlinedCharacterRuns.End(); it != endIt; ++it)
2260 CharacterIndex characterIndex = it->characterRun.characterIndex;
2261 Length numberOfCharacters = it->characterRun.numberOfCharacters;
2262 for(Length index = 0u; index < numberOfCharacters; index++)
2264 GlyphRun underlineGlyphRun;
2265 underlineGlyphRun.glyphIndex = charactersToGlyph[characterIndex + index];
2266 underlineGlyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex + index];
2267 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineGlyphRun);
2274 } // namespace Toolkit