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->mMatchSystemLanguageDirection,
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)
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 ChangeState(EventData::EDITING);
1265 mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1266 mEventData->mUpdateCursorPosition = true;
1267 ScrollTextToMatchCursor();
1271 Uint32Pair Controller::Impl::GetTextSelectionRange() const
1277 range.first = mEventData->mLeftSelectionPosition;
1278 range.second = mEventData->mRightSelectionPosition;
1284 bool Controller::Impl::IsEditable() const
1286 return mEventData && mEventData->mEditingEnabled;
1289 void Controller::Impl::SetEditable(bool editable)
1293 mEventData->mEditingEnabled = editable;
1297 void Controller::Impl::RetrieveSelection(std::string& selectedText, bool deleteAfterRetrieval)
1299 if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
1301 // Nothing to select if handles are in the same place.
1302 selectedText.clear();
1306 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1308 //Get start and end position of selection
1309 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1310 const Length lengthOfSelectedText = (handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition) - startOfSelectedText;
1312 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1313 const Length numberOfCharacters = utf32Characters.Count();
1315 // Validate the start and end selection points
1316 if((startOfSelectedText + lengthOfSelectedText) <= numberOfCharacters)
1318 //Get text as a UTF8 string
1319 Utf32ToUtf8(&utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText);
1321 if(deleteAfterRetrieval) // Only delete text if copied successfully
1323 // Keep a copy of the current input style.
1324 InputStyle currentInputStyle;
1325 currentInputStyle.Copy(mEventData->mInputStyle);
1327 // Set as input style the style of the first deleted character.
1328 mModel->mLogicalModel->RetrieveStyle(startOfSelectedText, mEventData->mInputStyle);
1330 // Compare if the input style has changed.
1331 const bool hasInputStyleChanged = !currentInputStyle.Equal(mEventData->mInputStyle);
1333 if(hasInputStyleChanged)
1335 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask(mEventData->mInputStyle);
1336 // Queue the input style changed signal.
1337 mEventData->mInputStyleChangedQueue.PushBack(styleChangedMask);
1340 mModel->mLogicalModel->UpdateTextStyleRuns(startOfSelectedText, -static_cast<int>(lengthOfSelectedText));
1342 // Mark the paragraphs to be updated.
1343 if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1345 mTextUpdateInfo.mCharacterIndex = 0;
1346 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1347 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1348 mTextUpdateInfo.mClearAll = true;
1352 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1353 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1356 // Delete text between handles
1357 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1358 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1359 utf32Characters.Erase(first, last);
1361 // Will show the cursor at the first character of the selection.
1362 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1366 // Will show the cursor at the last character of the selection.
1367 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1370 mEventData->mDecoratorUpdated = true;
1374 void Controller::Impl::SetSelection(int start, int end)
1376 mEventData->mLeftSelectionPosition = start;
1377 mEventData->mRightSelectionPosition = end;
1378 mEventData->mUpdateCursorPosition = true;
1381 std::pair<int, int> Controller::Impl::GetSelectionIndexes() const
1383 return {mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition};
1386 void Controller::Impl::ShowClipboard()
1390 mClipboard.ShowClipboard();
1394 void Controller::Impl::HideClipboard()
1396 if(mClipboard && mClipboardHideEnabled)
1398 mClipboard.HideClipboard();
1402 void Controller::Impl::SetClipboardHideEnable(bool enable)
1404 mClipboardHideEnabled = enable;
1407 bool Controller::Impl::CopyStringToClipboard(const std::string& source)
1409 //Send string to clipboard
1410 return (mClipboard && mClipboard.SetItem(source));
1413 void Controller::Impl::SendSelectionToClipboard(bool deleteAfterSending)
1415 std::string selectedText;
1416 RetrieveSelection(selectedText, deleteAfterSending);
1417 CopyStringToClipboard(selectedText);
1418 ChangeState(EventData::EDITING);
1421 void Controller::Impl::RequestGetTextFromClipboard()
1425 mClipboard.RequestItem();
1429 void Controller::Impl::RepositionSelectionHandles()
1431 SelectionHandleController::Reposition(*this);
1433 void Controller::Impl::RepositionSelectionHandles(float visualX, float visualY, Controller::NoTextTap::Action action)
1435 SelectionHandleController::Reposition(*this, visualX, visualY, action);
1438 void Controller::Impl::SetPopupButtons()
1441 * Sets the Popup buttons to be shown depending on State.
1443 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1445 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1448 bool isEditable = IsEditable();
1449 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1451 if(EventData::SELECTING == mEventData->mState)
1453 buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::COPY);
1456 buttonsToShow = TextSelectionPopup::Buttons(buttonsToShow | TextSelectionPopup::CUT);
1459 if(!IsClipboardEmpty())
1463 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1465 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1468 if(!mEventData->mAllTextSelected)
1470 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::SELECT_ALL));
1473 else if(EventData::EDITING_WITH_POPUP == mEventData->mState)
1475 if(mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
1477 buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL);
1480 if(!IsClipboardEmpty())
1484 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1486 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1489 else if(EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState)
1491 if(!IsClipboardEmpty())
1495 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1497 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1501 mEventData->mDecorator->SetEnabledPopupButtons(buttonsToShow);
1504 void Controller::Impl::ChangeState(EventData::State newState)
1506 if(nullptr == mEventData)
1508 // Nothing to do if there is no text input.
1512 DALI_LOG_INFO(gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState);
1514 if(mEventData->mState != newState)
1516 mEventData->mPreviousState = mEventData->mState;
1517 mEventData->mState = newState;
1519 switch(mEventData->mState)
1521 case EventData::INACTIVE:
1523 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1524 mEventData->mDecorator->StopCursorBlink();
1525 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1526 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1527 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1528 mEventData->mDecorator->SetHighlightActive(false);
1529 mEventData->mDecorator->SetPopupActive(false);
1530 mEventData->mDecoratorUpdated = true;
1533 case EventData::INTERRUPTED:
1535 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1536 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1537 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1538 mEventData->mDecorator->SetHighlightActive(false);
1539 mEventData->mDecorator->SetPopupActive(false);
1540 mEventData->mDecoratorUpdated = true;
1543 case EventData::SELECTING:
1545 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1546 mEventData->mDecorator->StopCursorBlink();
1547 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1548 if(mEventData->mGrabHandleEnabled)
1550 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
1551 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
1553 mEventData->mDecorator->SetHighlightActive(true);
1554 if(mEventData->mGrabHandlePopupEnabled)
1557 mEventData->mDecorator->SetPopupActive(true);
1559 mEventData->mDecoratorUpdated = true;
1562 case EventData::EDITING:
1564 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1565 if(mEventData->mCursorBlinkEnabled)
1567 mEventData->mDecorator->StartCursorBlink();
1569 // Grab handle is not shown until a tap is received whilst EDITING
1570 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1571 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1572 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1573 mEventData->mDecorator->SetHighlightActive(false);
1574 if(mEventData->mGrabHandlePopupEnabled)
1576 mEventData->mDecorator->SetPopupActive(false);
1578 mEventData->mDecoratorUpdated = true;
1581 case EventData::EDITING_WITH_POPUP:
1583 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState);
1585 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1586 if(mEventData->mCursorBlinkEnabled)
1588 mEventData->mDecorator->StartCursorBlink();
1590 if(mEventData->mSelectionEnabled)
1592 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1593 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1594 mEventData->mDecorator->SetHighlightActive(false);
1596 else if(mEventData->mGrabHandleEnabled)
1598 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1600 if(mEventData->mGrabHandlePopupEnabled)
1603 mEventData->mDecorator->SetPopupActive(true);
1605 mEventData->mDecoratorUpdated = true;
1608 case EventData::EDITING_WITH_GRAB_HANDLE:
1610 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState);
1612 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1613 if(mEventData->mCursorBlinkEnabled)
1615 mEventData->mDecorator->StartCursorBlink();
1617 // Grab handle is not shown until a tap is received whilst EDITING
1618 if(mEventData->mGrabHandleEnabled)
1620 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1622 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1623 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1624 mEventData->mDecorator->SetHighlightActive(false);
1625 if(mEventData->mGrabHandlePopupEnabled)
1627 mEventData->mDecorator->SetPopupActive(false);
1629 mEventData->mDecoratorUpdated = true;
1632 case EventData::SELECTION_HANDLE_PANNING:
1634 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1635 mEventData->mDecorator->StopCursorBlink();
1636 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1637 if(mEventData->mGrabHandleEnabled)
1639 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
1640 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
1642 mEventData->mDecorator->SetHighlightActive(true);
1643 if(mEventData->mGrabHandlePopupEnabled)
1645 mEventData->mDecorator->SetPopupActive(false);
1647 mEventData->mDecoratorUpdated = true;
1650 case EventData::GRAB_HANDLE_PANNING:
1652 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState);
1654 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1655 if(mEventData->mCursorBlinkEnabled)
1657 mEventData->mDecorator->StartCursorBlink();
1659 if(mEventData->mGrabHandleEnabled)
1661 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1663 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1664 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1665 mEventData->mDecorator->SetHighlightActive(false);
1666 if(mEventData->mGrabHandlePopupEnabled)
1668 mEventData->mDecorator->SetPopupActive(false);
1670 mEventData->mDecoratorUpdated = true;
1673 case EventData::EDITING_WITH_PASTE_POPUP:
1675 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState);
1677 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1678 if(mEventData->mCursorBlinkEnabled)
1680 mEventData->mDecorator->StartCursorBlink();
1683 if(mEventData->mGrabHandleEnabled)
1685 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1687 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1688 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1689 mEventData->mDecorator->SetHighlightActive(false);
1691 if(mEventData->mGrabHandlePopupEnabled)
1694 mEventData->mDecorator->SetPopupActive(true);
1696 mEventData->mDecoratorUpdated = true;
1699 case EventData::TEXT_PANNING:
1701 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1702 mEventData->mDecorator->StopCursorBlink();
1703 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1704 if(mEventData->mDecorator->IsHandleActive(LEFT_SELECTION_HANDLE) ||
1705 mEventData->mDecorator->IsHandleActive(RIGHT_SELECTION_HANDLE))
1707 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1708 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1709 mEventData->mDecorator->SetHighlightActive(true);
1712 if(mEventData->mGrabHandlePopupEnabled)
1714 mEventData->mDecorator->SetPopupActive(false);
1717 mEventData->mDecoratorUpdated = true;
1724 void Controller::Impl::GetCursorPosition(CharacterIndex logical,
1725 CursorInfo& cursorInfo)
1727 if(!IsShowingRealText())
1729 // Do not want to use the place-holder text to set the cursor position.
1731 // Use the line's height of the font's family set to set the cursor's size.
1732 // If there is no font's family set, use the default font.
1733 // Use the current alignment to place the cursor at the beginning, center or end of the box.
1735 cursorInfo.lineOffset = 0.f;
1736 cursorInfo.lineHeight = GetDefaultFontLineHeight();
1737 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1740 if(mModel->mMatchSystemLanguageDirection)
1742 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1745 switch(mModel->mHorizontalAlignment)
1747 case Text::HorizontalAlignment::BEGIN:
1751 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1755 cursorInfo.primaryPosition.x = 0.f;
1759 case Text::HorizontalAlignment::CENTER:
1761 cursorInfo.primaryPosition.x = floorf(0.5f * mModel->mVisualModel->mControlSize.width);
1764 case Text::HorizontalAlignment::END:
1768 cursorInfo.primaryPosition.x = 0.f;
1772 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1778 // Nothing else to do.
1782 const bool isMultiLine = (Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout());
1783 GetCursorPositionParameters parameters;
1784 parameters.visualModel = mModel->mVisualModel;
1785 parameters.logicalModel = mModel->mLogicalModel;
1786 parameters.metrics = mMetrics;
1787 parameters.logical = logical;
1788 parameters.isMultiline = isMultiLine;
1790 Text::GetCursorPosition(parameters,
1793 // Adds Outline offset.
1794 const float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1795 cursorInfo.primaryPosition.x += outlineWidth;
1796 cursorInfo.primaryPosition.y += outlineWidth;
1797 cursorInfo.secondaryPosition.x += outlineWidth;
1798 cursorInfo.secondaryPosition.y += outlineWidth;
1802 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
1804 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
1805 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
1807 if(0.f > cursorInfo.primaryPosition.x)
1809 cursorInfo.primaryPosition.x = 0.f;
1812 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>(mEventData->mDecorator->GetCursorWidth());
1813 if(cursorInfo.primaryPosition.x > edgeWidth)
1815 cursorInfo.primaryPosition.x = edgeWidth;
1820 CharacterIndex Controller::Impl::CalculateNewCursorIndex(CharacterIndex index) const
1822 if(nullptr == mEventData)
1824 // Nothing to do if there is no text input.
1828 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1830 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1831 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1833 GlyphIndex glyphIndex = *(charactersToGlyphBuffer + index);
1834 Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1836 if(numberOfCharacters > 1u)
1838 const Script script = mModel->mLogicalModel->GetScript(index);
1839 if(HasLigatureMustBreak(script))
1841 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
1842 numberOfCharacters = 1u;
1847 while(0u == numberOfCharacters)
1850 numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1854 if(index < mEventData->mPrimaryCursorPosition)
1856 cursorIndex -= numberOfCharacters;
1860 cursorIndex += numberOfCharacters;
1863 // Will update the cursor hook position.
1864 mEventData->mUpdateCursorHookPosition = true;
1869 void Controller::Impl::UpdateCursorPosition(const CursorInfo& cursorInfo)
1871 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this);
1872 if(nullptr == mEventData)
1874 // Nothing to do if there is no text input.
1875 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n");
1879 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
1881 mEventData->mDecorator->SetGlyphOffset(PRIMARY_CURSOR, cursorInfo.glyphOffset);
1883 // Sets the cursor position.
1884 mEventData->mDecorator->SetPosition(PRIMARY_CURSOR,
1887 cursorInfo.primaryCursorHeight,
1888 cursorInfo.lineHeight);
1889 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y);
1891 if(mEventData->mUpdateGrabHandlePosition)
1893 // Sets the grab handle position.
1894 mEventData->mDecorator->SetPosition(GRAB_HANDLE,
1896 cursorInfo.lineOffset + mModel->mScrollPosition.y,
1897 cursorInfo.lineHeight);
1900 if(cursorInfo.isSecondaryCursor)
1902 mEventData->mDecorator->SetPosition(SECONDARY_CURSOR,
1903 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
1904 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
1905 cursorInfo.secondaryCursorHeight,
1906 cursorInfo.lineHeight);
1907 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y);
1910 // Set which cursors are active according the state.
1911 if(EventData::IsEditingState(mEventData->mState) || (EventData::GRAB_HANDLE_PANNING == mEventData->mState))
1913 if(cursorInfo.isSecondaryCursor)
1915 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_BOTH);
1919 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1924 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1927 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n");
1930 void Controller::Impl::UpdateSelectionHandle(HandleType handleType,
1931 const CursorInfo& cursorInfo)
1933 SelectionHandleController::Update(*this, handleType, cursorInfo);
1936 void Controller::Impl::ClampHorizontalScroll(const Vector2& layoutSize)
1938 // Clamp between -space & -alignment offset.
1940 if(layoutSize.width > mModel->mVisualModel->mControlSize.width)
1942 const float space = (layoutSize.width - mModel->mVisualModel->mControlSize.width) + mModel->mAlignmentOffset;
1943 mModel->mScrollPosition.x = (mModel->mScrollPosition.x < -space) ? -space : mModel->mScrollPosition.x;
1944 mModel->mScrollPosition.x = (mModel->mScrollPosition.x > -mModel->mAlignmentOffset) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
1946 mEventData->mDecoratorUpdated = true;
1950 mModel->mScrollPosition.x = 0.f;
1954 void Controller::Impl::ClampVerticalScroll(const Vector2& layoutSize)
1956 if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1958 // Nothing to do if the text is single line.
1962 // Clamp between -space & 0.
1963 if(layoutSize.height > mModel->mVisualModel->mControlSize.height)
1965 const float space = (layoutSize.height - mModel->mVisualModel->mControlSize.height);
1966 mModel->mScrollPosition.y = (mModel->mScrollPosition.y < -space) ? -space : mModel->mScrollPosition.y;
1967 mModel->mScrollPosition.y = (mModel->mScrollPosition.y > 0.f) ? 0.f : mModel->mScrollPosition.y;
1969 mEventData->mDecoratorUpdated = true;
1973 mModel->mScrollPosition.y = 0.f;
1977 void Controller::Impl::ScrollToMakePositionVisible(const Vector2& position, float lineHeight)
1979 const float cursorWidth = mEventData->mDecorator ? static_cast<float>(mEventData->mDecorator->GetCursorWidth()) : 0.f;
1981 // position is in actor's coords.
1982 const float positionEndX = position.x + cursorWidth;
1983 const float positionEndY = position.y + lineHeight;
1985 // Transform the position to decorator coords.
1986 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
1987 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
1989 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
1990 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
1992 if(decoratorPositionBeginX < 0.f)
1994 mModel->mScrollPosition.x = -position.x;
1996 else if(decoratorPositionEndX > mModel->mVisualModel->mControlSize.width)
1998 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
2001 if(Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout())
2003 if(decoratorPositionBeginY < 0.f)
2005 mModel->mScrollPosition.y = -position.y;
2007 else if(decoratorPositionEndY > mModel->mVisualModel->mControlSize.height)
2009 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
2014 void Controller::Impl::ScrollTextToMatchCursor(const CursorInfo& cursorInfo)
2016 // Get the current cursor position in decorator coords.
2017 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition(PRIMARY_CURSOR);
2019 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter(mEventData->mPrimaryCursorPosition);
2021 // Calculate the offset to match the cursor position before the character was deleted.
2022 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2024 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
2025 if(mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() - 1u)
2027 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset(PRIMARY_CURSOR);
2028 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
2031 ClampHorizontalScroll(mModel->mVisualModel->GetLayoutSize());
2032 ClampVerticalScroll(mModel->mVisualModel->GetLayoutSize());
2034 // Makes the new cursor position visible if needed.
2035 ScrollToMakePositionVisible(cursorInfo.primaryPosition, cursorInfo.lineHeight);
2038 void Controller::Impl::ScrollTextToMatchCursor()
2040 CursorInfo cursorInfo;
2041 GetCursorPosition(mEventData->mPrimaryCursorPosition, cursorInfo);
2042 ScrollTextToMatchCursor(cursorInfo);
2045 void Controller::Impl::RequestRelayout()
2047 if(nullptr != mControlInterface)
2049 mControlInterface->RequestTextRelayout();
2053 Actor Controller::Impl::CreateBackgroundActor()
2055 // NOTE: Currently we only support background color for left-to-right text.
2059 Length numberOfGlyphs = mView.GetNumberOfGlyphs();
2060 if(numberOfGlyphs > 0u)
2062 Vector<GlyphInfo> glyphs;
2063 glyphs.Resize(numberOfGlyphs);
2065 Vector<Vector2> positions;
2066 positions.Resize(numberOfGlyphs);
2068 // Get the line where the glyphs are laid-out.
2069 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
2070 float alignmentOffset = lineRun->alignmentOffset;
2071 numberOfGlyphs = mView.GetGlyphs(glyphs.Begin(),
2077 glyphs.Resize(numberOfGlyphs);
2078 positions.Resize(numberOfGlyphs);
2080 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
2081 const Vector2* const positionsBuffer = positions.Begin();
2083 BackgroundMesh mesh;
2084 mesh.mVertices.Reserve(4u * glyphs.Size());
2085 mesh.mIndices.Reserve(6u * glyphs.Size());
2087 const Vector2 textSize = mView.GetLayoutSize();
2089 const float offsetX = textSize.width * 0.5f;
2090 const float offsetY = textSize.height * 0.5f;
2092 const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
2093 const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
2094 const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
2097 uint32_t numberOfQuads = 0u;
2098 Length yLineOffset = 0;
2099 Length prevLineIndex = 0;
2100 LineIndex lineIndex;
2101 Length numberOfLines;
2103 for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i)
2105 const GlyphInfo& glyph = *(glyphsBuffer + i);
2107 // Get the background color of the character.
2108 // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
2109 const ColorIndex backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + i);
2110 const Vector4& backgroundColor = (0u == backgroundColorIndex) ? defaultBackgroundColor : *(backgroundColorsBuffer + backgroundColorIndex - 1u);
2112 mModel->mVisualModel->GetNumberOfLines(i, 1, lineIndex, numberOfLines);
2113 Length lineHeight = lineRun[lineIndex].ascender + -(lineRun[lineIndex].descender) + lineRun[lineIndex].lineSpacing;
2115 if(lineIndex != prevLineIndex)
2117 yLineOffset += lineHeight;
2120 // Only create quads for glyphs with a background color
2121 if(backgroundColor != Color::TRANSPARENT)
2123 const Vector2 position = *(positionsBuffer + i);
2125 if(i == 0u && glyphSize == 1u) // Only one glyph in the whole text
2127 quad.x = position.x;
2128 quad.y = yLineOffset;
2129 quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
2130 quad.w = lineHeight;
2132 else if((lineIndex != prevLineIndex) || (i == 0u)) // The first glyph in the line
2134 quad.x = position.x;
2135 quad.y = yLineOffset;
2136 quad.z = quad.x - glyph.xBearing + glyph.advance;
2137 quad.w = quad.y + lineHeight;
2139 else if(i == glyphSize - 1u) // The last glyph in the whole text
2141 quad.x = position.x - glyph.xBearing;
2142 quad.y = yLineOffset;
2143 quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
2144 quad.w = quad.y + lineHeight;
2146 else // The glyph in the middle of the text
2148 quad.x = position.x - glyph.xBearing;
2149 quad.y = yLineOffset;
2150 quad.z = quad.x + glyph.advance;
2151 quad.w = quad.y + lineHeight;
2154 BackgroundVertex vertex;
2157 vertex.mPosition.x = quad.x - offsetX;
2158 vertex.mPosition.y = quad.y - offsetY;
2159 vertex.mColor = backgroundColor;
2160 mesh.mVertices.PushBack(vertex);
2163 vertex.mPosition.x = quad.z - offsetX;
2164 vertex.mPosition.y = quad.y - offsetY;
2165 vertex.mColor = backgroundColor;
2166 mesh.mVertices.PushBack(vertex);
2169 vertex.mPosition.x = quad.x - offsetX;
2170 vertex.mPosition.y = quad.w - offsetY;
2171 vertex.mColor = backgroundColor;
2172 mesh.mVertices.PushBack(vertex);
2175 vertex.mPosition.x = quad.z - offsetX;
2176 vertex.mPosition.y = quad.w - offsetY;
2177 vertex.mColor = backgroundColor;
2178 mesh.mVertices.PushBack(vertex);
2180 // Six indices in counter clockwise winding
2181 mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
2182 mesh.mIndices.PushBack(0u + 4 * numberOfQuads);
2183 mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
2184 mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
2185 mesh.mIndices.PushBack(3u + 4 * numberOfQuads);
2186 mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
2191 if(lineIndex != prevLineIndex)
2193 prevLineIndex = lineIndex;
2197 // Only create the background actor if there are glyphs with background color
2198 if(mesh.mVertices.Count() > 0u)
2200 Property::Map quadVertexFormat;
2201 quadVertexFormat["aPosition"] = Property::VECTOR2;
2202 quadVertexFormat["aColor"] = Property::VECTOR4;
2204 VertexBuffer quadVertices = VertexBuffer::New(quadVertexFormat);
2205 quadVertices.SetData(&mesh.mVertices[0], mesh.mVertices.Size());
2207 Geometry quadGeometry = Geometry::New();
2208 quadGeometry.AddVertexBuffer(quadVertices);
2209 quadGeometry.SetIndexBuffer(&mesh.mIndices[0], mesh.mIndices.Size());
2211 if(!mShaderBackground)
2213 mShaderBackground = Shader::New(SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_VERT, SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_FRAG);
2216 Dali::Renderer renderer = Dali::Renderer::New(quadGeometry, mShaderBackground);
2217 renderer.SetProperty(Dali::Renderer::Property::BLEND_MODE, BlendMode::ON);
2218 renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT);
2220 actor = Actor::New();
2221 actor.SetProperty(Dali::Actor::Property::NAME, "TextBackgroundColorActor");
2222 actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
2223 actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
2224 actor.SetProperty(Actor::Property::SIZE, textSize);
2225 actor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR);
2226 actor.AddRenderer(renderer);
2233 void Controller::Impl::CopyUnderlinedFromLogicalToVisualModels(bool shouldClearPreUnderlineRuns)
2235 //Underlined character runs for markup-processor
2236 const Vector<UnderlinedCharacterRun>& underlinedCharacterRuns = mModel->mLogicalModel->mUnderlinedCharacterRuns;
2237 const Vector<GlyphIndex>& charactersToGlyph = mModel->mVisualModel->mCharactersToGlyph;
2238 const Vector<Length>& glyphsPerCharacter = mModel->mVisualModel->mGlyphsPerCharacter;
2240 if(shouldClearPreUnderlineRuns)
2242 mModel->mVisualModel->mUnderlineRuns.Clear();
2245 for(Vector<UnderlinedCharacterRun>::ConstIterator it = underlinedCharacterRuns.Begin(), endIt = underlinedCharacterRuns.End(); it != endIt; ++it)
2247 CharacterIndex characterIndex = it->characterRun.characterIndex;
2248 Length numberOfCharacters = it->characterRun.numberOfCharacters;
2249 for(Length index = 0u; index < numberOfCharacters; index++)
2251 GlyphRun underlineGlyphRun;
2252 underlineGlyphRun.glyphIndex = charactersToGlyph[characterIndex + index];
2253 underlineGlyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex + index];
2254 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineGlyphRun);
2261 } // namespace Toolkit