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-run-container.h>
39 #include <dali-toolkit/internal/text/text-selection-handle-controller.h>
41 #include <dali-toolkit/internal/text/text-enumerations-impl.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 const Dali::Vector4 LIGHT_BLUE(0.75f, 0.96f, 1.f, 1.f);
64 const Dali::Vector4 BACKGROUND_SUB4(0.58f, 0.87f, 0.96f, 1.f);
65 const Dali::Vector4 BACKGROUND_SUB5(0.83f, 0.94f, 0.98f, 1.f);
66 const Dali::Vector4 BACKGROUND_SUB6(1.f, 0.5f, 0.5f, 1.f);
67 const Dali::Vector4 BACKGROUND_SUB7(1.f, 0.8f, 0.8f, 1.f);
77 EventData::EventData(DecoratorPtr decorator, InputMethodContext& inputMethodContext)
78 : mDecorator(decorator),
79 mInputMethodContext(inputMethodContext),
80 mPlaceholderFont(NULL),
81 mPlaceholderTextActive(),
82 mPlaceholderTextInactive(),
83 mPlaceholderTextColor(0.8f, 0.8f, 0.8f, 0.8f), // This color has been published in the Public API (placeholder-properties.h).
85 mInputStyleChangedQueue(),
86 mPreviousState(INACTIVE),
88 mPrimaryCursorPosition(0u),
89 mLeftSelectionPosition(0u),
90 mRightSelectionPosition(0u),
91 mPreEditStartPosition(0u),
93 mCursorHookPositionX(0.f),
94 mDoubleTapAction(Controller::NoTextTap::NO_ACTION),
95 mLongPressAction(Controller::NoTextTap::SHOW_SELECTION_POPUP),
96 mIsShowingPlaceholderText(false),
98 mDecoratorUpdated(false),
99 mCursorBlinkEnabled(true),
100 mGrabHandleEnabled(true),
101 mGrabHandlePopupEnabled(true),
102 mSelectionEnabled(true),
103 mUpdateCursorHookPosition(false),
104 mUpdateCursorPosition(false),
105 mUpdateGrabHandlePosition(false),
106 mUpdateLeftSelectionPosition(false),
107 mUpdateRightSelectionPosition(false),
108 mIsLeftHandleSelected(false),
109 mIsRightHandleSelected(false),
110 mUpdateHighlightBox(false),
111 mScrollAfterUpdatePosition(false),
112 mScrollAfterDelete(false),
113 mAllTextSelected(false),
114 mUpdateInputStyle(false),
115 mPasswordInput(false),
116 mCheckScrollAmount(false),
117 mIsPlaceholderPixelSize(false),
118 mIsPlaceholderElideEnabled(false),
119 mPlaceholderEllipsisFlag(false),
120 mShiftSelectionFlag(true),
121 mUpdateAlignment(false),
122 mEditingEnabled(true)
126 bool Controller::Impl::ProcessInputEvents()
128 return ControllerImplEventHandler::ProcessInputEvents(*this);
131 void Controller::Impl::NotifyInputMethodContext()
133 if(mEventData && mEventData->mInputMethodContext)
135 CharacterIndex cursorPosition = GetLogicalCursorPosition();
137 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces(0u);
139 // Update the cursor position by removing the initial white spaces.
140 if(cursorPosition < numberOfWhiteSpaces)
146 cursorPosition -= numberOfWhiteSpaces;
149 mEventData->mInputMethodContext.SetCursorPosition(cursorPosition);
150 mEventData->mInputMethodContext.NotifyCursorPosition();
154 void Controller::Impl::NotifyInputMethodContextMultiLineStatus()
156 if(mEventData && mEventData->mInputMethodContext)
158 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
159 mEventData->mInputMethodContext.NotifyTextInputMultiLine(layout == Text::Layout::Engine::MULTI_LINE_BOX);
163 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
165 CharacterIndex cursorPosition = 0u;
169 if((EventData::SELECTING == mEventData->mState) ||
170 (EventData::SELECTION_HANDLE_PANNING == mEventData->mState))
172 cursorPosition = std::min(mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition);
176 cursorPosition = mEventData->mPrimaryCursorPosition;
180 return cursorPosition;
183 Length Controller::Impl::GetNumberOfWhiteSpaces(CharacterIndex index) const
185 Length numberOfWhiteSpaces = 0u;
187 // Get the buffer to the text.
188 Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
190 const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
191 for(; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces)
193 if(!TextAbstraction::IsWhiteSpace(*(utf32CharacterBuffer + index)))
199 return numberOfWhiteSpaces;
202 void Controller::Impl::GetText(CharacterIndex index, std::string& text) const
204 // Get the total number of characters.
205 Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
207 // Retrieve the text.
208 if(0u != numberOfCharacters)
210 Utf32ToUtf8(mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text);
214 void Controller::Impl::CalculateTextUpdateIndices(Length& numberOfCharacters)
216 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
217 mTextUpdateInfo.mStartGlyphIndex = 0u;
218 mTextUpdateInfo.mStartLineIndex = 0u;
219 numberOfCharacters = 0u;
221 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
222 if(0u == numberOfParagraphs)
224 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
225 numberOfCharacters = 0u;
227 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
229 // Nothing else to do if there are no paragraphs.
233 // Find the paragraphs to be updated.
234 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
235 if(mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters)
237 // Text is being added at the end of the current text.
238 if(mTextUpdateInfo.mIsLastCharacterNewParagraph)
240 // Text is being added in a new paragraph after the last character of the text.
241 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
242 numberOfCharacters = 0u;
243 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
245 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
246 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
248 // Nothing else to do;
252 paragraphsToBeUpdated.PushBack(numberOfParagraphs - 1u);
256 Length numberOfCharactersToUpdate = 0u;
257 if(mTextUpdateInfo.mFullRelayoutNeeded)
259 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
263 numberOfCharactersToUpdate = (mTextUpdateInfo.mNumberOfCharactersToRemove > 0u) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
265 mModel->mLogicalModel->FindParagraphs(mTextUpdateInfo.mCharacterIndex,
266 numberOfCharactersToUpdate,
267 paragraphsToBeUpdated);
270 if(0u != paragraphsToBeUpdated.Count())
272 const ParagraphRunIndex firstParagraphIndex = *(paragraphsToBeUpdated.Begin());
273 const ParagraphRun& firstParagraph = *(mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex);
274 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
276 ParagraphRunIndex lastParagraphIndex = *(paragraphsToBeUpdated.End() - 1u);
277 const ParagraphRun& lastParagraph = *(mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex);
279 if((mTextUpdateInfo.mNumberOfCharactersToRemove > 0u) && // Some character are removed.
280 (lastParagraphIndex < numberOfParagraphs - 1u) && // There is a next paragraph.
281 ((lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters) == // The last removed character is the new paragraph character.
282 (mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove)))
284 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
285 const ParagraphRun& lastParagraph = *(mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u);
287 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
291 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
295 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
296 mTextUpdateInfo.mStartGlyphIndex = *(mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex);
299 void Controller::Impl::ClearFullModelData(OperationsMask operations)
301 if(NO_OPERATION != (GET_LINE_BREAKS & operations))
303 mModel->mLogicalModel->mLineBreakInfo.Clear();
304 mModel->mLogicalModel->mParagraphInfo.Clear();
307 if(NO_OPERATION != (GET_SCRIPTS & operations))
309 mModel->mLogicalModel->mScriptRuns.Clear();
312 if(NO_OPERATION != (VALIDATE_FONTS & operations))
314 mModel->mLogicalModel->mFontRuns.Clear();
317 if(0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count())
319 if(NO_OPERATION != (BIDI_INFO & operations))
321 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
322 mModel->mLogicalModel->mCharacterDirections.Clear();
325 if(NO_OPERATION != (REORDER & operations))
327 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
328 for(Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
329 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
333 BidirectionalLineInfoRun& bidiLineInfo = *it;
335 free(bidiLineInfo.visualToLogicalMap);
336 bidiLineInfo.visualToLogicalMap = NULL;
338 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
342 if(NO_OPERATION != (SHAPE_TEXT & operations))
344 mModel->mVisualModel->mGlyphs.Clear();
345 mModel->mVisualModel->mGlyphsToCharacters.Clear();
346 mModel->mVisualModel->mCharactersToGlyph.Clear();
347 mModel->mVisualModel->mCharactersPerGlyph.Clear();
348 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
349 mModel->mVisualModel->mGlyphPositions.Clear();
352 if(NO_OPERATION != (LAYOUT & operations))
354 mModel->mVisualModel->mLines.Clear();
357 if(NO_OPERATION != (COLOR & operations))
359 mModel->mVisualModel->mColorIndices.Clear();
360 mModel->mVisualModel->mBackgroundColorIndices.Clear();
364 void Controller::Impl::ClearCharacterModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
366 const CharacterIndex endIndexPlusOne = endIndex + 1u;
368 if(NO_OPERATION != (GET_LINE_BREAKS & operations))
370 // Clear the line break info.
371 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
373 mModel->mLogicalModel->mLineBreakInfo.Erase(lineBreakInfoBuffer + startIndex,
374 lineBreakInfoBuffer + endIndexPlusOne);
376 // Clear the paragraphs.
377 ClearCharacterRuns(startIndex,
379 mModel->mLogicalModel->mParagraphInfo);
382 if(NO_OPERATION != (GET_SCRIPTS & operations))
384 // Clear the scripts.
385 ClearCharacterRuns(startIndex,
387 mModel->mLogicalModel->mScriptRuns);
390 if(NO_OPERATION != (VALIDATE_FONTS & operations))
393 ClearCharacterRuns(startIndex,
395 mModel->mLogicalModel->mFontRuns);
398 if(0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count())
400 if(NO_OPERATION != (BIDI_INFO & operations))
402 // Clear the bidirectional paragraph info.
403 ClearCharacterRuns(startIndex,
405 mModel->mLogicalModel->mBidirectionalParagraphInfo);
407 // Clear the character's directions.
408 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
410 mModel->mLogicalModel->mCharacterDirections.Erase(characterDirectionsBuffer + startIndex,
411 characterDirectionsBuffer + endIndexPlusOne);
414 if(NO_OPERATION != (REORDER & operations))
416 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
417 uint32_t endRemoveIndex = startRemoveIndex;
418 ClearCharacterRuns(startIndex,
420 mModel->mLogicalModel->mBidirectionalLineInfo,
424 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
426 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
427 for(Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
428 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
432 BidirectionalLineInfoRun& bidiLineInfo = *it;
434 free(bidiLineInfo.visualToLogicalMap);
435 bidiLineInfo.visualToLogicalMap = NULL;
438 mModel->mLogicalModel->mBidirectionalLineInfo.Erase(bidirectionalLineInfoBuffer + startRemoveIndex,
439 bidirectionalLineInfoBuffer + endRemoveIndex);
444 void Controller::Impl::ClearGlyphModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
446 const CharacterIndex endIndexPlusOne = endIndex + 1u;
447 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
449 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
450 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
451 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
453 const GlyphIndex endGlyphIndexPlusOne = *(charactersToGlyphBuffer + endIndex) + *(glyphsPerCharacterBuffer + endIndex);
454 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
456 if(NO_OPERATION != (SHAPE_TEXT & operations))
458 // Update the character to glyph indices.
459 for(Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
460 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
464 CharacterIndex& index = *it;
465 index -= numberOfGlyphsRemoved;
468 // Clear the character to glyph conversion table.
469 mModel->mVisualModel->mCharactersToGlyph.Erase(charactersToGlyphBuffer + startIndex,
470 charactersToGlyphBuffer + endIndexPlusOne);
472 // Clear the glyphs per character table.
473 mModel->mVisualModel->mGlyphsPerCharacter.Erase(glyphsPerCharacterBuffer + startIndex,
474 glyphsPerCharacterBuffer + endIndexPlusOne);
476 // Clear the glyphs buffer.
477 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
478 mModel->mVisualModel->mGlyphs.Erase(glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
479 glyphsBuffer + endGlyphIndexPlusOne);
481 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
483 // Update the glyph to character indices.
484 for(Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
485 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
489 CharacterIndex& index = *it;
490 index -= numberOfCharactersRemoved;
493 // Clear the glyphs to characters buffer.
494 mModel->mVisualModel->mGlyphsToCharacters.Erase(glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
495 glyphsToCharactersBuffer + endGlyphIndexPlusOne);
497 // Clear the characters per glyph buffer.
498 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
499 mModel->mVisualModel->mCharactersPerGlyph.Erase(charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
500 charactersPerGlyphBuffer + endGlyphIndexPlusOne);
502 // Clear the positions buffer.
503 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
504 mModel->mVisualModel->mGlyphPositions.Erase(positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
505 positionsBuffer + endGlyphIndexPlusOne);
508 if(NO_OPERATION != (LAYOUT & operations))
511 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
512 uint32_t endRemoveIndex = startRemoveIndex;
513 ClearCharacterRuns(startIndex,
515 mModel->mVisualModel->mLines,
519 // Will update the glyph runs.
520 startRemoveIndex = mModel->mVisualModel->mLines.Count();
521 endRemoveIndex = startRemoveIndex;
522 ClearGlyphRuns(mTextUpdateInfo.mStartGlyphIndex,
523 endGlyphIndexPlusOne - 1u,
524 mModel->mVisualModel->mLines,
528 // Set the line index from where to insert the new laid-out lines.
529 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
531 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
532 mModel->mVisualModel->mLines.Erase(linesBuffer + startRemoveIndex,
533 linesBuffer + endRemoveIndex);
536 if(NO_OPERATION != (COLOR & operations))
538 if(0u != mModel->mVisualModel->mColorIndices.Count())
540 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
541 mModel->mVisualModel->mColorIndices.Erase(colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
542 colorIndexBuffer + endGlyphIndexPlusOne);
545 if(0u != mModel->mVisualModel->mBackgroundColorIndices.Count())
547 ColorIndex* backgroundColorIndexBuffer = mModel->mVisualModel->mBackgroundColorIndices.Begin();
548 mModel->mVisualModel->mBackgroundColorIndices.Erase(backgroundColorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
549 backgroundColorIndexBuffer + endGlyphIndexPlusOne);
554 void Controller::Impl::ClearModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
556 if(mTextUpdateInfo.mClearAll ||
557 ((0u == startIndex) &&
558 (mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u)))
560 ClearFullModelData(operations);
564 // Clear the model data related with characters.
565 ClearCharacterModelData(startIndex, endIndex, operations);
567 // Clear the model data related with glyphs.
568 ClearGlyphModelData(startIndex, endIndex, operations);
571 // The estimated number of lines. Used to avoid reallocations when layouting.
572 mTextUpdateInfo.mEstimatedNumberOfLines = std::max(mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count());
574 mModel->mVisualModel->ClearCaches();
577 bool Controller::Impl::UpdateModel(OperationsMask operationsRequired)
579 DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::UpdateModel\n");
581 // Calculate the operations to be done.
582 const OperationsMask operations = static_cast<OperationsMask>(mOperationsPending & operationsRequired);
584 if(NO_OPERATION == operations)
586 // Nothing to do if no operations are pending and required.
590 Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
591 Vector<Character> displayCharacters;
592 bool useHiddenText = false;
593 if(mHiddenInput && mEventData != nullptr && !mEventData->mIsShowingPlaceholderText)
595 mHiddenInput->Substitute(srcCharacters, displayCharacters);
596 useHiddenText = true;
599 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
600 const Length numberOfCharacters = utf32Characters.Count();
602 // Index to the first character of the first paragraph to be updated.
603 CharacterIndex startIndex = 0u;
604 // Number of characters of the paragraphs to be removed.
605 Length paragraphCharacters = 0u;
607 CalculateTextUpdateIndices(paragraphCharacters);
609 // Check whether the indices for updating the text is valid
610 if(numberOfCharacters > 0u &&
611 (mTextUpdateInfo.mParagraphCharacterIndex > numberOfCharacters ||
612 mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters))
614 std::string currentText;
615 Utf32ToUtf8(mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText);
617 DALI_LOG_ERROR("Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n");
618 DALI_LOG_ERROR("Number of characters: %d, current text is: %s\n", numberOfCharacters, currentText.c_str());
620 // Dump mTextUpdateInfo
621 DALI_LOG_ERROR("Dump mTextUpdateInfo:\n");
622 DALI_LOG_ERROR(" mTextUpdateInfo.mCharacterIndex = %u\n", mTextUpdateInfo.mCharacterIndex);
623 DALI_LOG_ERROR(" mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", mTextUpdateInfo.mNumberOfCharactersToRemove);
624 DALI_LOG_ERROR(" mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", mTextUpdateInfo.mNumberOfCharactersToAdd);
625 DALI_LOG_ERROR(" mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", mTextUpdateInfo.mPreviousNumberOfCharacters);
626 DALI_LOG_ERROR(" mTextUpdateInfo.mParagraphCharacterIndex = %u\n", mTextUpdateInfo.mParagraphCharacterIndex);
627 DALI_LOG_ERROR(" mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", mTextUpdateInfo.mRequestedNumberOfCharacters);
628 DALI_LOG_ERROR(" mTextUpdateInfo.mStartGlyphIndex = %u\n", mTextUpdateInfo.mStartGlyphIndex);
629 DALI_LOG_ERROR(" mTextUpdateInfo.mStartLineIndex = %u\n", mTextUpdateInfo.mStartLineIndex);
630 DALI_LOG_ERROR(" mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", mTextUpdateInfo.mEstimatedNumberOfLines);
631 DALI_LOG_ERROR(" mTextUpdateInfo.mClearAll = %d\n", mTextUpdateInfo.mClearAll);
632 DALI_LOG_ERROR(" mTextUpdateInfo.mFullRelayoutNeeded = %d\n", mTextUpdateInfo.mFullRelayoutNeeded);
633 DALI_LOG_ERROR(" mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", mTextUpdateInfo.mIsLastCharacterNewParagraph);
638 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
640 if(mTextUpdateInfo.mClearAll ||
641 (0u != paragraphCharacters))
643 ClearModelData(startIndex, startIndex + ((paragraphCharacters > 0u) ? paragraphCharacters - 1u : 0u), operations);
646 mTextUpdateInfo.mClearAll = false;
648 // Whether the model is updated.
649 bool updated = false;
651 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
652 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
654 if(NO_OPERATION != (GET_LINE_BREAKS & operations))
656 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
657 // calculate the bidirectional info for each 'paragraph'.
658 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
659 // is not shaped together).
660 lineBreakInfo.Resize(numberOfCharacters, TextAbstraction::LINE_NO_BREAK);
662 SetLineBreakInfo(utf32Characters,
664 requestedNumberOfCharacters,
667 if(mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
668 mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::MIXED))
670 CharacterIndex end = startIndex + requestedNumberOfCharacters;
671 LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
673 for(CharacterIndex index = startIndex; index < end; index++)
675 CharacterIndex wordEnd = index;
676 while((*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_ALLOW_BREAK) && (*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_MUST_BREAK))
681 if((wordEnd + 1) == end) // add last char
686 Vector<bool> hyphens = GetWordHyphens(utf32Characters.Begin() + index, wordEnd - index, nullptr);
688 for(CharacterIndex i = 0; i < (wordEnd - index); i++)
692 *(lineBreakInfoBuffer + index + i) = TextAbstraction::LINE_HYPHENATION_BREAK;
700 // Create the paragraph info.
701 mModel->mLogicalModel->CreateParagraphInfo(startIndex,
702 requestedNumberOfCharacters);
706 const bool getScripts = NO_OPERATION != (GET_SCRIPTS & operations);
707 const bool validateFonts = NO_OPERATION != (VALIDATE_FONTS & operations);
709 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
710 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
712 if(getScripts || validateFonts)
714 // Validates the fonts assigned by the application or assigns default ones.
715 // It makes sure all the characters are going to be rendered by the correct font.
716 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
720 // Retrieves the scripts used in the text.
721 multilanguageSupport.SetScripts(utf32Characters,
723 requestedNumberOfCharacters,
729 // Validate the fonts set through the mark-up string.
730 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
732 // Get the default font's description.
733 TextAbstraction::FontDescription defaultFontDescription;
734 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale;
736 //Get the number of points per one unit of point-size
737 uint32_t numberOfPointsPerOneUnitOfPointSize = mFontClient.GetNumberOfPointsPerOneUnitOfPointSize();
739 if(IsShowingPlaceholderText() && mEventData && (nullptr != mEventData->mPlaceholderFont))
741 // If the placeholder font is set specifically, only placeholder font is changed.
742 defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
743 if(mEventData->mPlaceholderFont->sizeDefined)
745 defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * mFontSizeScale * numberOfPointsPerOneUnitOfPointSize;
748 else if(nullptr != mFontDefaults)
750 // Set the normal font and the placeholder font.
751 defaultFontDescription = mFontDefaults->mFontDescription;
755 defaultPointSize = mFontDefaults->mFitPointSize * numberOfPointsPerOneUnitOfPointSize;
759 defaultPointSize = mFontDefaults->mDefaultPointSize * mFontSizeScale * numberOfPointsPerOneUnitOfPointSize;
763 // Validates the fonts. If there is a character with no assigned font it sets a default one.
764 // After this call, fonts are validated.
765 multilanguageSupport.ValidateFonts(utf32Characters,
768 defaultFontDescription,
771 requestedNumberOfCharacters,
777 Vector<Character> mirroredUtf32Characters;
778 bool textMirrored = false;
779 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
780 if(NO_OPERATION != (BIDI_INFO & operations))
782 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
783 bidirectionalInfo.Reserve(numberOfParagraphs);
785 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
786 SetBidirectionalInfo(utf32Characters,
790 requestedNumberOfCharacters,
792 mModel->mMatchSystemLanguageDirection,
795 if(0u != bidirectionalInfo.Count())
797 // Only set the character directions if there is right to left characters.
798 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
799 GetCharactersDirection(bidirectionalInfo,
802 requestedNumberOfCharacters,
805 // This paragraph has right to left text. Some characters may need to be mirrored.
806 // TODO: consider if the mirrored string can be stored as well.
808 textMirrored = GetMirroredText(utf32Characters,
812 requestedNumberOfCharacters,
813 mirroredUtf32Characters);
817 // There is no right to left characters. Clear the directions vector.
818 mModel->mLogicalModel->mCharacterDirections.Clear();
823 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
824 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
825 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
826 Vector<GlyphIndex> newParagraphGlyphs;
827 newParagraphGlyphs.Reserve(numberOfParagraphs);
829 const Length currentNumberOfGlyphs = glyphs.Count();
830 if(NO_OPERATION != (SHAPE_TEXT & operations))
832 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
834 ShapeText(textToShape,
839 mTextUpdateInfo.mStartGlyphIndex,
840 requestedNumberOfCharacters,
842 glyphsToCharactersMap,
846 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
847 mModel->mVisualModel->CreateGlyphsPerCharacterTable(startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters);
848 mModel->mVisualModel->CreateCharacterToGlyphTable(startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters);
853 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
855 if(NO_OPERATION != (GET_GLYPH_METRICS & operations))
857 GlyphInfo* glyphsBuffer = glyphs.Begin();
858 mMetrics->GetGlyphMetrics(glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs);
860 // Update the width and advance of all new paragraph characters.
861 for(Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it)
863 const GlyphIndex index = *it;
864 GlyphInfo& glyph = *(glyphsBuffer + index);
866 glyph.xBearing = 0.f;
873 if((nullptr != mEventData) &&
874 mEventData->mPreEditFlag &&
875 (0u != mModel->mVisualModel->mCharactersToGlyph.Count()))
877 Dali::InputMethodContext::PreEditAttributeDataContainer attrs;
878 mEventData->mInputMethodContext.GetPreeditStyle(attrs);
879 Dali::InputMethodContext::PreeditStyle type = Dali::InputMethodContext::PreeditStyle::NONE;
881 // Check the type of preedit and run it.
882 for(Dali::InputMethodContext::PreEditAttributeDataContainer::Iterator it = attrs.Begin(), endIt = attrs.End(); it != endIt; it++)
884 Dali::InputMethodContext::PreeditAttributeData attrData = *it;
885 DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::UpdateModel PreeditStyle type : %d start %d end %d \n", attrData.preeditType, attrData.startIndex, attrData.endIndex);
886 type = attrData.preeditType;
888 // Check the number of commit characters for the start position.
889 unsigned int numberOfCommit = mEventData->mPrimaryCursorPosition - mEventData->mPreEditLength;
890 Length numberOfIndices = attrData.endIndex - attrData.startIndex;
894 case Dali::InputMethodContext::PreeditStyle::UNDERLINE:
896 // Add the underline for the pre-edit text.
897 GlyphRun underlineRun;
898 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
899 underlineRun.numberOfGlyphs = numberOfIndices;
900 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
902 //Mark-up processor case
903 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
905 CopyUnderlinedFromLogicalToVisualModels(false);
909 case Dali::InputMethodContext::PreeditStyle::REVERSE:
911 Vector4 textColor = mModel->mVisualModel->GetTextColor();
912 ColorRun backgroundColorRun;
913 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
914 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
915 backgroundColorRun.color = textColor;
916 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
918 Vector4 backgroundColor = mModel->mVisualModel->GetBackgroundColor();
919 Vector<ColorRun> colorRuns;
920 colorRuns.Resize(1u);
921 ColorRun& colorRun = *(colorRuns.Begin());
922 colorRun.color = backgroundColor;
923 colorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
924 colorRun.characterRun.numberOfCharacters = numberOfIndices;
926 mModel->mLogicalModel->mColorRuns.PushBack(colorRun);
929 case Dali::InputMethodContext::PreeditStyle::HIGHLIGHT:
931 ColorRun backgroundColorRun;
932 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
933 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
934 backgroundColorRun.color = LIGHT_BLUE;
935 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
938 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_1:
940 // CUSTOM_PLATFORM_STYLE_1 should be drawn with background and underline together.
941 ColorRun backgroundColorRun;
942 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
943 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
944 backgroundColorRun.color = BACKGROUND_SUB4;
945 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
947 GlyphRun underlineRun;
948 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
949 underlineRun.numberOfGlyphs = numberOfIndices;
950 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
952 //Mark-up processor case
953 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
955 CopyUnderlinedFromLogicalToVisualModels(false);
959 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_2:
961 // CUSTOM_PLATFORM_STYLE_2 should be drawn with background and underline together.
962 ColorRun backgroundColorRun;
963 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
964 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
965 backgroundColorRun.color = BACKGROUND_SUB5;
966 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
968 GlyphRun underlineRun;
969 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
970 underlineRun.numberOfGlyphs = numberOfIndices;
971 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
973 //Mark-up processor case
974 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
976 CopyUnderlinedFromLogicalToVisualModels(false);
980 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_3:
982 // CUSTOM_PLATFORM_STYLE_3 should be drawn with background and underline together.
983 ColorRun backgroundColorRun;
984 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
985 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
986 backgroundColorRun.color = BACKGROUND_SUB6;
987 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
989 GlyphRun underlineRun;
990 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
991 underlineRun.numberOfGlyphs = numberOfIndices;
992 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
994 //Mark-up processor case
995 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
997 CopyUnderlinedFromLogicalToVisualModels(false);
1001 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_4:
1003 // CUSTOM_PLATFORM_STYLE_4 should be drawn with background and underline together.
1004 ColorRun backgroundColorRun;
1005 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1006 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1007 backgroundColorRun.color = BACKGROUND_SUB7;
1008 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
1010 GlyphRun underlineRun;
1011 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1012 underlineRun.numberOfGlyphs = numberOfIndices;
1013 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
1015 //Mark-up processor case
1016 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
1018 CopyUnderlinedFromLogicalToVisualModels(false);
1022 case Dali::InputMethodContext::PreeditStyle::NONE:
1033 if(NO_OPERATION != (COLOR & operations))
1035 // Set the color runs in glyphs.
1036 SetColorSegmentationInfo(mModel->mLogicalModel->mColorRuns,
1037 mModel->mVisualModel->mCharactersToGlyph,
1038 mModel->mVisualModel->mGlyphsPerCharacter,
1040 mTextUpdateInfo.mStartGlyphIndex,
1041 requestedNumberOfCharacters,
1042 mModel->mVisualModel->mColors,
1043 mModel->mVisualModel->mColorIndices);
1045 // Set the background color runs in glyphs.
1046 SetColorSegmentationInfo(mModel->mLogicalModel->mBackgroundColorRuns,
1047 mModel->mVisualModel->mCharactersToGlyph,
1048 mModel->mVisualModel->mGlyphsPerCharacter,
1050 mTextUpdateInfo.mStartGlyphIndex,
1051 requestedNumberOfCharacters,
1052 mModel->mVisualModel->mBackgroundColors,
1053 mModel->mVisualModel->mBackgroundColorIndices);
1058 if((NO_OPERATION != (SHAPE_TEXT & operations)) &&
1059 !((nullptr != mEventData) &&
1060 mEventData->mPreEditFlag &&
1061 (0u != mModel->mVisualModel->mCharactersToGlyph.Count())))
1063 //Mark-up processor case
1064 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
1066 CopyUnderlinedFromLogicalToVisualModels(true);
1072 // The estimated number of lines. Used to avoid reallocations when layouting.
1073 mTextUpdateInfo.mEstimatedNumberOfLines = std::max(mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count());
1075 // Set the previous number of characters for the next time the text is updated.
1076 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1081 void Controller::Impl::RetrieveDefaultInputStyle(InputStyle& inputStyle)
1083 // Sets the default text's color.
1084 inputStyle.textColor = mTextColor;
1085 inputStyle.isDefaultColor = true;
1087 inputStyle.familyName.clear();
1088 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1089 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1090 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1091 inputStyle.size = 0.f;
1093 inputStyle.lineSpacing = 0.f;
1095 inputStyle.underlineProperties.clear();
1096 inputStyle.shadowProperties.clear();
1097 inputStyle.embossProperties.clear();
1098 inputStyle.outlineProperties.clear();
1100 inputStyle.isFamilyDefined = false;
1101 inputStyle.isWeightDefined = false;
1102 inputStyle.isWidthDefined = false;
1103 inputStyle.isSlantDefined = false;
1104 inputStyle.isSizeDefined = false;
1106 inputStyle.isLineSpacingDefined = false;
1108 inputStyle.isUnderlineDefined = false;
1109 inputStyle.isShadowDefined = false;
1110 inputStyle.isEmbossDefined = false;
1111 inputStyle.isOutlineDefined = false;
1113 // Sets the default font's family name, weight, width, slant and size.
1116 if(mFontDefaults->familyDefined)
1118 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1119 inputStyle.isFamilyDefined = true;
1122 if(mFontDefaults->weightDefined)
1124 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1125 inputStyle.isWeightDefined = true;
1128 if(mFontDefaults->widthDefined)
1130 inputStyle.width = mFontDefaults->mFontDescription.width;
1131 inputStyle.isWidthDefined = true;
1134 if(mFontDefaults->slantDefined)
1136 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1137 inputStyle.isSlantDefined = true;
1140 if(mFontDefaults->sizeDefined)
1142 inputStyle.size = mFontDefaults->mDefaultPointSize;
1143 inputStyle.isSizeDefined = true;
1148 float Controller::Impl::GetDefaultFontLineHeight()
1150 FontId defaultFontId = 0u;
1151 if(nullptr == mFontDefaults)
1153 TextAbstraction::FontDescription fontDescription;
1154 defaultFontId = mFontClient.GetFontId(fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale);
1158 defaultFontId = mFontDefaults->GetFontId(mFontClient, mFontDefaults->mDefaultPointSize * mFontSizeScale);
1161 Text::FontMetrics fontMetrics;
1162 mMetrics->GetFontMetrics(defaultFontId, fontMetrics);
1164 return (fontMetrics.ascender - fontMetrics.descender);
1167 void Controller::Impl::SetTextSelectionRange(const uint32_t* pStart, const uint32_t* pEnd)
1169 if(nullptr == mEventData)
1171 // Nothing to do if there is no text.
1175 if(mEventData->mSelectionEnabled && (pStart || pEnd))
1177 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
1181 mEventData->mLeftSelectionPosition = std::min(*pStart, length);
1185 mEventData->mRightSelectionPosition = std::min(*pEnd, length);
1188 if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
1190 ChangeState(EventData::EDITING);
1191 mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition;
1192 mEventData->mUpdateCursorPosition = true;
1196 ChangeState(EventData::SELECTING);
1197 mEventData->mUpdateHighlightBox = true;
1198 mEventData->mUpdateLeftSelectionPosition = true;
1199 mEventData->mUpdateRightSelectionPosition = true;
1204 CharacterIndex Controller::Impl::GetPrimaryCursorPosition() const
1206 if(nullptr == mEventData)
1210 return mEventData->mPrimaryCursorPosition;
1213 bool Controller::Impl::SetPrimaryCursorPosition(CharacterIndex index)
1215 if(nullptr == mEventData)
1217 // Nothing to do if there is no text.
1221 if(mEventData->mPrimaryCursorPosition == index)
1223 // Nothing for same cursor position.
1227 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
1228 mEventData->mPrimaryCursorPosition = std::min(index, length);
1229 ChangeState(EventData::EDITING);
1230 mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1231 mEventData->mUpdateCursorPosition = true;
1232 ScrollTextToMatchCursor();
1236 Uint32Pair Controller::Impl::GetTextSelectionRange() const
1242 range.first = mEventData->mLeftSelectionPosition;
1243 range.second = mEventData->mRightSelectionPosition;
1249 bool Controller::Impl::IsEditable() const
1251 return mEventData && mEventData->mEditingEnabled;
1254 void Controller::Impl::SetEditable(bool editable)
1258 mEventData->mEditingEnabled = editable;
1262 void Controller::Impl::RetrieveSelection(std::string& selectedText, bool deleteAfterRetrieval)
1264 if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
1266 // Nothing to select if handles are in the same place.
1267 selectedText.clear();
1271 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1273 //Get start and end position of selection
1274 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1275 const Length lengthOfSelectedText = (handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition) - startOfSelectedText;
1277 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1278 const Length numberOfCharacters = utf32Characters.Count();
1280 // Validate the start and end selection points
1281 if((startOfSelectedText + lengthOfSelectedText) <= numberOfCharacters)
1283 //Get text as a UTF8 string
1284 Utf32ToUtf8(&utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText);
1286 if(deleteAfterRetrieval) // Only delete text if copied successfully
1288 // Keep a copy of the current input style.
1289 InputStyle currentInputStyle;
1290 currentInputStyle.Copy(mEventData->mInputStyle);
1292 // Set as input style the style of the first deleted character.
1293 mModel->mLogicalModel->RetrieveStyle(startOfSelectedText, mEventData->mInputStyle);
1295 // Compare if the input style has changed.
1296 const bool hasInputStyleChanged = !currentInputStyle.Equal(mEventData->mInputStyle);
1298 if(hasInputStyleChanged)
1300 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask(mEventData->mInputStyle);
1301 // Queue the input style changed signal.
1302 mEventData->mInputStyleChangedQueue.PushBack(styleChangedMask);
1305 mModel->mLogicalModel->UpdateTextStyleRuns(startOfSelectedText, -static_cast<int>(lengthOfSelectedText));
1307 // Mark the paragraphs to be updated.
1308 if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1310 mTextUpdateInfo.mCharacterIndex = 0;
1311 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1312 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1313 mTextUpdateInfo.mClearAll = true;
1317 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1318 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1321 // Delete text between handles
1322 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1323 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1324 utf32Characters.Erase(first, last);
1326 // Will show the cursor at the first character of the selection.
1327 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1331 // Will show the cursor at the last character of the selection.
1332 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1335 mEventData->mDecoratorUpdated = true;
1339 void Controller::Impl::SetSelection(int start, int end)
1341 mEventData->mLeftSelectionPosition = start;
1342 mEventData->mRightSelectionPosition = end;
1343 mEventData->mUpdateCursorPosition = true;
1346 std::pair<int, int> Controller::Impl::GetSelectionIndexes() const
1348 return {mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition};
1351 void Controller::Impl::ShowClipboard()
1355 mClipboard.ShowClipboard();
1359 void Controller::Impl::HideClipboard()
1361 if(mClipboard && mClipboardHideEnabled)
1363 mClipboard.HideClipboard();
1367 void Controller::Impl::SetClipboardHideEnable(bool enable)
1369 mClipboardHideEnabled = enable;
1372 bool Controller::Impl::CopyStringToClipboard(const std::string& source)
1374 //Send string to clipboard
1375 return (mClipboard && mClipboard.SetItem(source));
1378 void Controller::Impl::SendSelectionToClipboard(bool deleteAfterSending)
1380 std::string selectedText;
1381 RetrieveSelection(selectedText, deleteAfterSending);
1382 CopyStringToClipboard(selectedText);
1383 ChangeState(EventData::EDITING);
1386 void Controller::Impl::RequestGetTextFromClipboard()
1390 mClipboard.RequestItem();
1394 void Controller::Impl::RepositionSelectionHandles()
1396 SelectionHandleController::Reposition(*this);
1398 void Controller::Impl::RepositionSelectionHandles(float visualX, float visualY, Controller::NoTextTap::Action action)
1400 SelectionHandleController::Reposition(*this, visualX, visualY, action);
1403 void Controller::Impl::SetPopupButtons()
1406 * Sets the Popup buttons to be shown depending on State.
1408 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1410 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1413 bool isEditable = IsEditable();
1414 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1416 if(EventData::SELECTING == mEventData->mState)
1418 buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::COPY);
1421 buttonsToShow = TextSelectionPopup::Buttons(buttonsToShow | TextSelectionPopup::CUT);
1424 if(!IsClipboardEmpty())
1428 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1430 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1433 if(!mEventData->mAllTextSelected)
1435 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::SELECT_ALL));
1438 else if(EventData::EDITING_WITH_POPUP == mEventData->mState)
1440 if(mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
1442 buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL);
1445 if(!IsClipboardEmpty())
1449 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1451 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1454 else if(EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState)
1456 if(!IsClipboardEmpty())
1460 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1462 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1466 mEventData->mDecorator->SetEnabledPopupButtons(buttonsToShow);
1469 void Controller::Impl::ChangeState(EventData::State newState)
1471 if(nullptr == mEventData)
1473 // Nothing to do if there is no text input.
1477 DALI_LOG_INFO(gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState);
1479 if(mEventData->mState != newState)
1481 mEventData->mPreviousState = mEventData->mState;
1482 mEventData->mState = newState;
1484 switch(mEventData->mState)
1486 case EventData::INACTIVE:
1488 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1489 mEventData->mDecorator->StopCursorBlink();
1490 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1491 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1492 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1493 mEventData->mDecorator->SetHighlightActive(false);
1494 mEventData->mDecorator->SetPopupActive(false);
1495 mEventData->mDecoratorUpdated = true;
1498 case EventData::INTERRUPTED:
1500 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1501 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1502 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1503 mEventData->mDecorator->SetHighlightActive(false);
1504 mEventData->mDecorator->SetPopupActive(false);
1505 mEventData->mDecoratorUpdated = true;
1508 case EventData::SELECTING:
1510 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1511 mEventData->mDecorator->StopCursorBlink();
1512 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1513 if(mEventData->mGrabHandleEnabled)
1515 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
1516 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
1518 mEventData->mDecorator->SetHighlightActive(true);
1519 if(mEventData->mGrabHandlePopupEnabled)
1522 mEventData->mDecorator->SetPopupActive(true);
1524 mEventData->mDecoratorUpdated = true;
1527 case EventData::EDITING:
1529 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1530 if(mEventData->mCursorBlinkEnabled)
1532 mEventData->mDecorator->StartCursorBlink();
1534 // Grab handle is not shown until a tap is received whilst EDITING
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 if(mEventData->mGrabHandlePopupEnabled)
1541 mEventData->mDecorator->SetPopupActive(false);
1543 mEventData->mDecoratorUpdated = true;
1546 case EventData::EDITING_WITH_POPUP:
1548 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState);
1550 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1551 if(mEventData->mCursorBlinkEnabled)
1553 mEventData->mDecorator->StartCursorBlink();
1555 if(mEventData->mSelectionEnabled)
1557 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1558 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1559 mEventData->mDecorator->SetHighlightActive(false);
1561 else if(mEventData->mGrabHandleEnabled)
1563 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1565 if(mEventData->mGrabHandlePopupEnabled)
1568 mEventData->mDecorator->SetPopupActive(true);
1570 mEventData->mDecoratorUpdated = true;
1573 case EventData::EDITING_WITH_GRAB_HANDLE:
1575 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState);
1577 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1578 if(mEventData->mCursorBlinkEnabled)
1580 mEventData->mDecorator->StartCursorBlink();
1582 // Grab handle is not shown until a tap is received whilst EDITING
1583 if(mEventData->mGrabHandleEnabled)
1585 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1587 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1588 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1589 mEventData->mDecorator->SetHighlightActive(false);
1590 if(mEventData->mGrabHandlePopupEnabled)
1592 mEventData->mDecorator->SetPopupActive(false);
1594 mEventData->mDecoratorUpdated = true;
1597 case EventData::SELECTION_HANDLE_PANNING:
1599 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1600 mEventData->mDecorator->StopCursorBlink();
1601 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1602 if(mEventData->mGrabHandleEnabled)
1604 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
1605 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
1607 mEventData->mDecorator->SetHighlightActive(true);
1608 if(mEventData->mGrabHandlePopupEnabled)
1610 mEventData->mDecorator->SetPopupActive(false);
1612 mEventData->mDecoratorUpdated = true;
1615 case EventData::GRAB_HANDLE_PANNING:
1617 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState);
1619 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1620 if(mEventData->mCursorBlinkEnabled)
1622 mEventData->mDecorator->StartCursorBlink();
1624 if(mEventData->mGrabHandleEnabled)
1626 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1628 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1629 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1630 mEventData->mDecorator->SetHighlightActive(false);
1631 if(mEventData->mGrabHandlePopupEnabled)
1633 mEventData->mDecorator->SetPopupActive(false);
1635 mEventData->mDecoratorUpdated = true;
1638 case EventData::EDITING_WITH_PASTE_POPUP:
1640 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState);
1642 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1643 if(mEventData->mCursorBlinkEnabled)
1645 mEventData->mDecorator->StartCursorBlink();
1648 if(mEventData->mGrabHandleEnabled)
1650 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1652 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1653 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1654 mEventData->mDecorator->SetHighlightActive(false);
1656 if(mEventData->mGrabHandlePopupEnabled)
1659 mEventData->mDecorator->SetPopupActive(true);
1661 mEventData->mDecoratorUpdated = true;
1664 case EventData::TEXT_PANNING:
1666 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1667 mEventData->mDecorator->StopCursorBlink();
1668 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1669 if(mEventData->mDecorator->IsHandleActive(LEFT_SELECTION_HANDLE) ||
1670 mEventData->mDecorator->IsHandleActive(RIGHT_SELECTION_HANDLE))
1672 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1673 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1674 mEventData->mDecorator->SetHighlightActive(true);
1677 if(mEventData->mGrabHandlePopupEnabled)
1679 mEventData->mDecorator->SetPopupActive(false);
1682 mEventData->mDecoratorUpdated = true;
1689 void Controller::Impl::GetCursorPosition(CharacterIndex logical,
1690 CursorInfo& cursorInfo)
1692 if(!IsShowingRealText())
1694 // Do not want to use the place-holder text to set the cursor position.
1696 // Use the line's height of the font's family set to set the cursor's size.
1697 // If there is no font's family set, use the default font.
1698 // Use the current alignment to place the cursor at the beginning, center or end of the box.
1700 cursorInfo.lineOffset = 0.f;
1701 cursorInfo.lineHeight = GetDefaultFontLineHeight();
1702 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1705 if(mModel->mMatchSystemLanguageDirection)
1707 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1710 switch(mModel->mHorizontalAlignment)
1712 case Text::HorizontalAlignment::BEGIN:
1716 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1720 cursorInfo.primaryPosition.x = 0.f;
1724 case Text::HorizontalAlignment::CENTER:
1726 cursorInfo.primaryPosition.x = floorf(0.5f * mModel->mVisualModel->mControlSize.width);
1729 case Text::HorizontalAlignment::END:
1733 cursorInfo.primaryPosition.x = 0.f;
1737 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1743 // Nothing else to do.
1747 const bool isMultiLine = (Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout());
1748 GetCursorPositionParameters parameters;
1749 parameters.visualModel = mModel->mVisualModel;
1750 parameters.logicalModel = mModel->mLogicalModel;
1751 parameters.metrics = mMetrics;
1752 parameters.logical = logical;
1753 parameters.isMultiline = isMultiLine;
1755 Text::GetCursorPosition(parameters,
1758 // Adds Outline offset.
1759 const float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1760 cursorInfo.primaryPosition.x += outlineWidth;
1761 cursorInfo.primaryPosition.y += outlineWidth;
1762 cursorInfo.secondaryPosition.x += outlineWidth;
1763 cursorInfo.secondaryPosition.y += outlineWidth;
1767 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
1769 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
1770 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
1772 if(0.f > cursorInfo.primaryPosition.x)
1774 cursorInfo.primaryPosition.x = 0.f;
1777 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>(mEventData->mDecorator->GetCursorWidth());
1778 if(cursorInfo.primaryPosition.x > edgeWidth)
1780 cursorInfo.primaryPosition.x = edgeWidth;
1785 CharacterIndex Controller::Impl::CalculateNewCursorIndex(CharacterIndex index) const
1787 if(nullptr == mEventData)
1789 // Nothing to do if there is no text input.
1793 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1795 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1796 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1798 GlyphIndex glyphIndex = *(charactersToGlyphBuffer + index);
1799 Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1801 if(numberOfCharacters > 1u)
1803 const Script script = mModel->mLogicalModel->GetScript(index);
1804 if(HasLigatureMustBreak(script))
1806 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
1807 numberOfCharacters = 1u;
1812 while(0u == numberOfCharacters)
1815 numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1819 if(index < mEventData->mPrimaryCursorPosition)
1821 cursorIndex -= numberOfCharacters;
1825 cursorIndex += numberOfCharacters;
1828 // Will update the cursor hook position.
1829 mEventData->mUpdateCursorHookPosition = true;
1834 void Controller::Impl::UpdateCursorPosition(const CursorInfo& cursorInfo)
1836 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this);
1837 if(nullptr == mEventData)
1839 // Nothing to do if there is no text input.
1840 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n");
1844 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
1846 mEventData->mDecorator->SetGlyphOffset(PRIMARY_CURSOR, cursorInfo.glyphOffset);
1848 // Sets the cursor position.
1849 mEventData->mDecorator->SetPosition(PRIMARY_CURSOR,
1852 cursorInfo.primaryCursorHeight,
1853 cursorInfo.lineHeight);
1854 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y);
1856 if(mEventData->mUpdateGrabHandlePosition)
1858 // Sets the grab handle position.
1859 mEventData->mDecorator->SetPosition(GRAB_HANDLE,
1861 cursorInfo.lineOffset + mModel->mScrollPosition.y,
1862 cursorInfo.lineHeight);
1865 if(cursorInfo.isSecondaryCursor)
1867 mEventData->mDecorator->SetPosition(SECONDARY_CURSOR,
1868 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
1869 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
1870 cursorInfo.secondaryCursorHeight,
1871 cursorInfo.lineHeight);
1872 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y);
1875 // Set which cursors are active according the state.
1876 if(EventData::IsEditingState(mEventData->mState) || (EventData::GRAB_HANDLE_PANNING == mEventData->mState))
1878 if(cursorInfo.isSecondaryCursor)
1880 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_BOTH);
1884 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1889 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1892 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n");
1895 void Controller::Impl::UpdateSelectionHandle(HandleType handleType,
1896 const CursorInfo& cursorInfo)
1898 SelectionHandleController::Update(*this, handleType, cursorInfo);
1901 void Controller::Impl::ClampHorizontalScroll(const Vector2& layoutSize)
1903 // Clamp between -space & -alignment offset.
1905 if(layoutSize.width > mModel->mVisualModel->mControlSize.width)
1907 const float space = (layoutSize.width - mModel->mVisualModel->mControlSize.width) + mModel->mAlignmentOffset;
1908 mModel->mScrollPosition.x = (mModel->mScrollPosition.x < -space) ? -space : mModel->mScrollPosition.x;
1909 mModel->mScrollPosition.x = (mModel->mScrollPosition.x > -mModel->mAlignmentOffset) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
1911 mEventData->mDecoratorUpdated = true;
1915 mModel->mScrollPosition.x = 0.f;
1919 void Controller::Impl::ClampVerticalScroll(const Vector2& layoutSize)
1921 if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1923 // Nothing to do if the text is single line.
1927 // Clamp between -space & 0.
1928 if(layoutSize.height > mModel->mVisualModel->mControlSize.height)
1930 const float space = (layoutSize.height - mModel->mVisualModel->mControlSize.height);
1931 mModel->mScrollPosition.y = (mModel->mScrollPosition.y < -space) ? -space : mModel->mScrollPosition.y;
1932 mModel->mScrollPosition.y = (mModel->mScrollPosition.y > 0.f) ? 0.f : mModel->mScrollPosition.y;
1934 mEventData->mDecoratorUpdated = true;
1938 mModel->mScrollPosition.y = 0.f;
1942 void Controller::Impl::ScrollToMakePositionVisible(const Vector2& position, float lineHeight)
1944 const float cursorWidth = mEventData->mDecorator ? static_cast<float>(mEventData->mDecorator->GetCursorWidth()) : 0.f;
1946 // position is in actor's coords.
1947 const float positionEndX = position.x + cursorWidth;
1948 const float positionEndY = position.y + lineHeight;
1950 // Transform the position to decorator coords.
1951 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
1952 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
1954 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
1955 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
1957 if(decoratorPositionBeginX < 0.f)
1959 mModel->mScrollPosition.x = -position.x;
1961 else if(decoratorPositionEndX > mModel->mVisualModel->mControlSize.width)
1963 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
1966 if(Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout())
1968 if(decoratorPositionBeginY < 0.f)
1970 mModel->mScrollPosition.y = -position.y;
1972 else if(decoratorPositionEndY > mModel->mVisualModel->mControlSize.height)
1974 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
1979 void Controller::Impl::ScrollTextToMatchCursor(const CursorInfo& cursorInfo)
1981 // Get the current cursor position in decorator coords.
1982 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition(PRIMARY_CURSOR);
1984 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter(mEventData->mPrimaryCursorPosition);
1986 // Calculate the offset to match the cursor position before the character was deleted.
1987 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
1989 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
1990 if(mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() - 1u)
1992 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset(PRIMARY_CURSOR);
1993 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
1996 ClampHorizontalScroll(mModel->mVisualModel->GetLayoutSize());
1997 ClampVerticalScroll(mModel->mVisualModel->GetLayoutSize());
1999 // Makes the new cursor position visible if needed.
2000 ScrollToMakePositionVisible(cursorInfo.primaryPosition, cursorInfo.lineHeight);
2003 void Controller::Impl::ScrollTextToMatchCursor()
2005 CursorInfo cursorInfo;
2006 GetCursorPosition(mEventData->mPrimaryCursorPosition, cursorInfo);
2007 ScrollTextToMatchCursor(cursorInfo);
2010 void Controller::Impl::RequestRelayout()
2012 if(nullptr != mControlInterface)
2014 mControlInterface->RequestTextRelayout();
2018 Actor Controller::Impl::CreateBackgroundActor()
2020 // NOTE: Currently we only support background color for left-to-right text.
2024 Length numberOfGlyphs = mView.GetNumberOfGlyphs();
2025 if(numberOfGlyphs > 0u)
2027 Vector<GlyphInfo> glyphs;
2028 glyphs.Resize(numberOfGlyphs);
2030 Vector<Vector2> positions;
2031 positions.Resize(numberOfGlyphs);
2033 // Get the line where the glyphs are laid-out.
2034 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
2035 float alignmentOffset = lineRun->alignmentOffset;
2036 numberOfGlyphs = mView.GetGlyphs(glyphs.Begin(),
2042 glyphs.Resize(numberOfGlyphs);
2043 positions.Resize(numberOfGlyphs);
2045 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
2046 const Vector2* const positionsBuffer = positions.Begin();
2048 BackgroundMesh mesh;
2049 mesh.mVertices.Reserve(4u * glyphs.Size());
2050 mesh.mIndices.Reserve(6u * glyphs.Size());
2052 const Vector2 textSize = mView.GetLayoutSize();
2054 const float offsetX = textSize.width * 0.5f;
2055 const float offsetY = textSize.height * 0.5f;
2057 const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
2058 const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
2059 const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
2062 uint32_t numberOfQuads = 0u;
2063 Length yLineOffset = 0;
2064 Length prevLineIndex = 0;
2065 LineIndex lineIndex;
2066 Length numberOfLines;
2068 for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i)
2070 const GlyphInfo& glyph = *(glyphsBuffer + i);
2072 // Get the background color of the character.
2073 // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
2074 const ColorIndex backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + i);
2075 const Vector4& backgroundColor = (0u == backgroundColorIndex) ? defaultBackgroundColor : *(backgroundColorsBuffer + backgroundColorIndex - 1u);
2077 mModel->mVisualModel->GetNumberOfLines(i, 1, lineIndex, numberOfLines);
2078 Length lineHeight = lineRun[lineIndex].ascender + -(lineRun[lineIndex].descender) + lineRun[lineIndex].lineSpacing;
2080 if(lineIndex != prevLineIndex)
2082 yLineOffset += lineHeight;
2085 // Only create quads for glyphs with a background color
2086 if(backgroundColor != Color::TRANSPARENT)
2088 const Vector2 position = *(positionsBuffer + i);
2090 if(i == 0u && glyphSize == 1u) // Only one glyph in the whole text
2092 quad.x = position.x;
2093 quad.y = yLineOffset;
2094 quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
2095 quad.w = lineHeight;
2097 else if((lineIndex != prevLineIndex) || (i == 0u)) // The first glyph in the line
2099 quad.x = position.x;
2100 quad.y = yLineOffset;
2101 quad.z = quad.x - glyph.xBearing + glyph.advance;
2102 quad.w = quad.y + lineHeight;
2104 else if(i == glyphSize - 1u) // The last glyph in the whole text
2106 quad.x = position.x - glyph.xBearing;
2107 quad.y = yLineOffset;
2108 quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
2109 quad.w = quad.y + lineHeight;
2111 else // The glyph in the middle of the text
2113 quad.x = position.x - glyph.xBearing;
2114 quad.y = yLineOffset;
2115 quad.z = quad.x + glyph.advance;
2116 quad.w = quad.y + lineHeight;
2119 BackgroundVertex vertex;
2122 vertex.mPosition.x = quad.x - offsetX;
2123 vertex.mPosition.y = quad.y - offsetY;
2124 vertex.mColor = backgroundColor;
2125 mesh.mVertices.PushBack(vertex);
2128 vertex.mPosition.x = quad.z - offsetX;
2129 vertex.mPosition.y = quad.y - offsetY;
2130 vertex.mColor = backgroundColor;
2131 mesh.mVertices.PushBack(vertex);
2134 vertex.mPosition.x = quad.x - offsetX;
2135 vertex.mPosition.y = quad.w - offsetY;
2136 vertex.mColor = backgroundColor;
2137 mesh.mVertices.PushBack(vertex);
2140 vertex.mPosition.x = quad.z - offsetX;
2141 vertex.mPosition.y = quad.w - offsetY;
2142 vertex.mColor = backgroundColor;
2143 mesh.mVertices.PushBack(vertex);
2145 // Six indices in counter clockwise winding
2146 mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
2147 mesh.mIndices.PushBack(0u + 4 * numberOfQuads);
2148 mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
2149 mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
2150 mesh.mIndices.PushBack(3u + 4 * numberOfQuads);
2151 mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
2156 if(lineIndex != prevLineIndex)
2158 prevLineIndex = lineIndex;
2162 // Only create the background actor if there are glyphs with background color
2163 if(mesh.mVertices.Count() > 0u)
2165 Property::Map quadVertexFormat;
2166 quadVertexFormat["aPosition"] = Property::VECTOR2;
2167 quadVertexFormat["aColor"] = Property::VECTOR4;
2169 VertexBuffer quadVertices = VertexBuffer::New(quadVertexFormat);
2170 quadVertices.SetData(&mesh.mVertices[0], mesh.mVertices.Size());
2172 Geometry quadGeometry = Geometry::New();
2173 quadGeometry.AddVertexBuffer(quadVertices);
2174 quadGeometry.SetIndexBuffer(&mesh.mIndices[0], mesh.mIndices.Size());
2176 if(!mShaderBackground)
2178 mShaderBackground = Shader::New(SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_VERT, SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_FRAG);
2181 Dali::Renderer renderer = Dali::Renderer::New(quadGeometry, mShaderBackground);
2182 renderer.SetProperty(Dali::Renderer::Property::BLEND_MODE, BlendMode::ON);
2183 renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT);
2185 actor = Actor::New();
2186 actor.SetProperty(Dali::Actor::Property::NAME, "TextBackgroundColorActor");
2187 actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
2188 actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
2189 actor.SetProperty(Actor::Property::SIZE, textSize);
2190 actor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR);
2191 actor.AddRenderer(renderer);
2198 void Controller::Impl::CopyUnderlinedFromLogicalToVisualModels(bool shouldClearPreUnderlineRuns)
2200 //Underlined character runs for markup-processor
2201 const Vector<UnderlinedCharacterRun>& underlinedCharacterRuns = mModel->mLogicalModel->mUnderlinedCharacterRuns;
2202 const Vector<GlyphIndex>& charactersToGlyph = mModel->mVisualModel->mCharactersToGlyph;
2203 const Vector<Length>& glyphsPerCharacter = mModel->mVisualModel->mGlyphsPerCharacter;
2205 if(shouldClearPreUnderlineRuns)
2207 mModel->mVisualModel->mUnderlineRuns.Clear();
2210 for(Vector<UnderlinedCharacterRun>::ConstIterator it = underlinedCharacterRuns.Begin(), endIt = underlinedCharacterRuns.End(); it != endIt; ++it)
2212 CharacterIndex characterIndex = it->characterRun.characterIndex;
2213 Length numberOfCharacters = it->characterRun.numberOfCharacters;
2214 for(Length index = 0u; index < numberOfCharacters; index++)
2216 GlyphRun underlineGlyphRun;
2217 underlineGlyphRun.glyphIndex = charactersToGlyph[characterIndex + index];
2218 underlineGlyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex + index];
2219 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineGlyphRun);
2226 } // namespace Toolkit