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);
1073 // The estimated number of lines. Used to avoid reallocations when layouting.
1074 mTextUpdateInfo.mEstimatedNumberOfLines = std::max(mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count());
1076 // Set the previous number of characters for the next time the text is updated.
1077 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1082 void Controller::Impl::RetrieveDefaultInputStyle(InputStyle& inputStyle)
1084 // Sets the default text's color.
1085 inputStyle.textColor = mTextColor;
1086 inputStyle.isDefaultColor = true;
1088 inputStyle.familyName.clear();
1089 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1090 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1091 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1092 inputStyle.size = 0.f;
1094 inputStyle.lineSpacing = 0.f;
1096 inputStyle.underlineProperties.clear();
1097 inputStyle.shadowProperties.clear();
1098 inputStyle.embossProperties.clear();
1099 inputStyle.outlineProperties.clear();
1101 inputStyle.isFamilyDefined = false;
1102 inputStyle.isWeightDefined = false;
1103 inputStyle.isWidthDefined = false;
1104 inputStyle.isSlantDefined = false;
1105 inputStyle.isSizeDefined = false;
1107 inputStyle.isLineSpacingDefined = false;
1109 inputStyle.isUnderlineDefined = false;
1110 inputStyle.isShadowDefined = false;
1111 inputStyle.isEmbossDefined = false;
1112 inputStyle.isOutlineDefined = false;
1114 // Sets the default font's family name, weight, width, slant and size.
1117 if(mFontDefaults->familyDefined)
1119 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1120 inputStyle.isFamilyDefined = true;
1123 if(mFontDefaults->weightDefined)
1125 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1126 inputStyle.isWeightDefined = true;
1129 if(mFontDefaults->widthDefined)
1131 inputStyle.width = mFontDefaults->mFontDescription.width;
1132 inputStyle.isWidthDefined = true;
1135 if(mFontDefaults->slantDefined)
1137 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1138 inputStyle.isSlantDefined = true;
1141 if(mFontDefaults->sizeDefined)
1143 inputStyle.size = mFontDefaults->mDefaultPointSize;
1144 inputStyle.isSizeDefined = true;
1149 float Controller::Impl::GetDefaultFontLineHeight()
1151 FontId defaultFontId = 0u;
1152 if(nullptr == mFontDefaults)
1154 TextAbstraction::FontDescription fontDescription;
1155 defaultFontId = mFontClient.GetFontId(fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale);
1159 defaultFontId = mFontDefaults->GetFontId(mFontClient, mFontDefaults->mDefaultPointSize * mFontSizeScale);
1162 Text::FontMetrics fontMetrics;
1163 mMetrics->GetFontMetrics(defaultFontId, fontMetrics);
1165 return (fontMetrics.ascender - fontMetrics.descender);
1168 void Controller::Impl::SetTextSelectionRange(const uint32_t* pStart, const uint32_t* pEnd)
1170 if(nullptr == mEventData)
1172 // Nothing to do if there is no text.
1176 if(mEventData->mSelectionEnabled && (pStart || pEnd))
1178 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
1182 mEventData->mLeftSelectionPosition = std::min(*pStart, length);
1186 mEventData->mRightSelectionPosition = std::min(*pEnd, length);
1189 if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
1191 ChangeState(EventData::EDITING);
1192 mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition;
1193 mEventData->mUpdateCursorPosition = true;
1197 ChangeState(EventData::SELECTING);
1198 mEventData->mUpdateHighlightBox = true;
1199 mEventData->mUpdateLeftSelectionPosition = true;
1200 mEventData->mUpdateRightSelectionPosition = true;
1205 CharacterIndex Controller::Impl::GetPrimaryCursorPosition() const
1207 if(nullptr == mEventData)
1211 return mEventData->mPrimaryCursorPosition;
1214 bool Controller::Impl::SetPrimaryCursorPosition(CharacterIndex index)
1216 if(nullptr == mEventData)
1218 // Nothing to do if there is no text.
1222 if(mEventData->mPrimaryCursorPosition == index)
1224 // Nothing for same cursor position.
1228 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
1229 mEventData->mPrimaryCursorPosition = std::min(index, length);
1230 ChangeState(EventData::EDITING);
1231 mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1232 mEventData->mUpdateCursorPosition = true;
1233 ScrollTextToMatchCursor();
1237 Uint32Pair Controller::Impl::GetTextSelectionRange() const
1243 range.first = mEventData->mLeftSelectionPosition;
1244 range.second = mEventData->mRightSelectionPosition;
1250 bool Controller::Impl::IsEditable() const
1252 return mEventData && mEventData->mEditingEnabled;
1255 void Controller::Impl::SetEditable(bool editable)
1259 mEventData->mEditingEnabled = editable;
1263 void Controller::Impl::RetrieveSelection(std::string& selectedText, bool deleteAfterRetrieval)
1265 if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
1267 // Nothing to select if handles are in the same place.
1268 selectedText.clear();
1272 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1274 //Get start and end position of selection
1275 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1276 const Length lengthOfSelectedText = (handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition) - startOfSelectedText;
1278 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1279 const Length numberOfCharacters = utf32Characters.Count();
1281 // Validate the start and end selection points
1282 if((startOfSelectedText + lengthOfSelectedText) <= numberOfCharacters)
1284 //Get text as a UTF8 string
1285 Utf32ToUtf8(&utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText);
1287 if(deleteAfterRetrieval) // Only delete text if copied successfully
1289 // Keep a copy of the current input style.
1290 InputStyle currentInputStyle;
1291 currentInputStyle.Copy(mEventData->mInputStyle);
1293 // Set as input style the style of the first deleted character.
1294 mModel->mLogicalModel->RetrieveStyle(startOfSelectedText, mEventData->mInputStyle);
1296 // Compare if the input style has changed.
1297 const bool hasInputStyleChanged = !currentInputStyle.Equal(mEventData->mInputStyle);
1299 if(hasInputStyleChanged)
1301 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask(mEventData->mInputStyle);
1302 // Queue the input style changed signal.
1303 mEventData->mInputStyleChangedQueue.PushBack(styleChangedMask);
1306 mModel->mLogicalModel->UpdateTextStyleRuns(startOfSelectedText, -static_cast<int>(lengthOfSelectedText));
1308 // Mark the paragraphs to be updated.
1309 if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1311 mTextUpdateInfo.mCharacterIndex = 0;
1312 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1313 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1314 mTextUpdateInfo.mClearAll = true;
1318 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1319 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1322 // Delete text between handles
1323 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1324 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1325 utf32Characters.Erase(first, last);
1327 // Will show the cursor at the first character of the selection.
1328 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1332 // Will show the cursor at the last character of the selection.
1333 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1336 mEventData->mDecoratorUpdated = true;
1340 void Controller::Impl::SetSelection(int start, int end)
1342 mEventData->mLeftSelectionPosition = start;
1343 mEventData->mRightSelectionPosition = end;
1344 mEventData->mUpdateCursorPosition = true;
1347 std::pair<int, int> Controller::Impl::GetSelectionIndexes() const
1349 return {mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition};
1352 void Controller::Impl::ShowClipboard()
1356 mClipboard.ShowClipboard();
1360 void Controller::Impl::HideClipboard()
1362 if(mClipboard && mClipboardHideEnabled)
1364 mClipboard.HideClipboard();
1368 void Controller::Impl::SetClipboardHideEnable(bool enable)
1370 mClipboardHideEnabled = enable;
1373 bool Controller::Impl::CopyStringToClipboard(const std::string& source)
1375 //Send string to clipboard
1376 return (mClipboard && mClipboard.SetItem(source));
1379 void Controller::Impl::SendSelectionToClipboard(bool deleteAfterSending)
1381 std::string selectedText;
1382 RetrieveSelection(selectedText, deleteAfterSending);
1383 CopyStringToClipboard(selectedText);
1384 ChangeState(EventData::EDITING);
1387 void Controller::Impl::RequestGetTextFromClipboard()
1391 mClipboard.RequestItem();
1395 void Controller::Impl::RepositionSelectionHandles()
1397 SelectionHandleController::Reposition(*this);
1399 void Controller::Impl::RepositionSelectionHandles(float visualX, float visualY, Controller::NoTextTap::Action action)
1401 SelectionHandleController::Reposition(*this, visualX, visualY, action);
1404 void Controller::Impl::SetPopupButtons()
1407 * Sets the Popup buttons to be shown depending on State.
1409 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1411 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1414 bool isEditable = IsEditable();
1415 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1417 if(EventData::SELECTING == mEventData->mState)
1419 buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::COPY);
1422 buttonsToShow = TextSelectionPopup::Buttons(buttonsToShow | TextSelectionPopup::CUT);
1425 if(!IsClipboardEmpty())
1429 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1431 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1434 if(!mEventData->mAllTextSelected)
1436 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::SELECT_ALL));
1439 else if(EventData::EDITING_WITH_POPUP == mEventData->mState)
1441 if(mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
1443 buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL);
1446 if(!IsClipboardEmpty())
1450 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1452 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1455 else if(EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState)
1457 if(!IsClipboardEmpty())
1461 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1463 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1467 mEventData->mDecorator->SetEnabledPopupButtons(buttonsToShow);
1470 void Controller::Impl::ChangeState(EventData::State newState)
1472 if(nullptr == mEventData)
1474 // Nothing to do if there is no text input.
1478 DALI_LOG_INFO(gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState);
1480 if(mEventData->mState != newState)
1482 mEventData->mPreviousState = mEventData->mState;
1483 mEventData->mState = newState;
1485 switch(mEventData->mState)
1487 case EventData::INACTIVE:
1489 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1490 mEventData->mDecorator->StopCursorBlink();
1491 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1492 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1493 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1494 mEventData->mDecorator->SetHighlightActive(false);
1495 mEventData->mDecorator->SetPopupActive(false);
1496 mEventData->mDecoratorUpdated = true;
1499 case EventData::INTERRUPTED:
1501 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1502 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1503 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1504 mEventData->mDecorator->SetHighlightActive(false);
1505 mEventData->mDecorator->SetPopupActive(false);
1506 mEventData->mDecoratorUpdated = true;
1509 case EventData::SELECTING:
1511 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1512 mEventData->mDecorator->StopCursorBlink();
1513 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1514 if(mEventData->mGrabHandleEnabled)
1516 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
1517 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
1519 mEventData->mDecorator->SetHighlightActive(true);
1520 if(mEventData->mGrabHandlePopupEnabled)
1523 mEventData->mDecorator->SetPopupActive(true);
1525 mEventData->mDecoratorUpdated = true;
1528 case EventData::EDITING:
1530 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1531 if(mEventData->mCursorBlinkEnabled)
1533 mEventData->mDecorator->StartCursorBlink();
1535 // Grab handle is not shown until a tap is received whilst EDITING
1536 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1537 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1538 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1539 mEventData->mDecorator->SetHighlightActive(false);
1540 if(mEventData->mGrabHandlePopupEnabled)
1542 mEventData->mDecorator->SetPopupActive(false);
1544 mEventData->mDecoratorUpdated = true;
1547 case EventData::EDITING_WITH_POPUP:
1549 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState);
1551 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1552 if(mEventData->mCursorBlinkEnabled)
1554 mEventData->mDecorator->StartCursorBlink();
1556 if(mEventData->mSelectionEnabled)
1558 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1559 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1560 mEventData->mDecorator->SetHighlightActive(false);
1562 else if(mEventData->mGrabHandleEnabled)
1564 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1566 if(mEventData->mGrabHandlePopupEnabled)
1569 mEventData->mDecorator->SetPopupActive(true);
1571 mEventData->mDecoratorUpdated = true;
1574 case EventData::EDITING_WITH_GRAB_HANDLE:
1576 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState);
1578 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1579 if(mEventData->mCursorBlinkEnabled)
1581 mEventData->mDecorator->StartCursorBlink();
1583 // Grab handle is not shown until a tap is received whilst EDITING
1584 if(mEventData->mGrabHandleEnabled)
1586 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1588 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1589 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1590 mEventData->mDecorator->SetHighlightActive(false);
1591 if(mEventData->mGrabHandlePopupEnabled)
1593 mEventData->mDecorator->SetPopupActive(false);
1595 mEventData->mDecoratorUpdated = true;
1598 case EventData::SELECTION_HANDLE_PANNING:
1600 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1601 mEventData->mDecorator->StopCursorBlink();
1602 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1603 if(mEventData->mGrabHandleEnabled)
1605 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
1606 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
1608 mEventData->mDecorator->SetHighlightActive(true);
1609 if(mEventData->mGrabHandlePopupEnabled)
1611 mEventData->mDecorator->SetPopupActive(false);
1613 mEventData->mDecoratorUpdated = true;
1616 case EventData::GRAB_HANDLE_PANNING:
1618 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState);
1620 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1621 if(mEventData->mCursorBlinkEnabled)
1623 mEventData->mDecorator->StartCursorBlink();
1625 if(mEventData->mGrabHandleEnabled)
1627 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1629 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1630 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1631 mEventData->mDecorator->SetHighlightActive(false);
1632 if(mEventData->mGrabHandlePopupEnabled)
1634 mEventData->mDecorator->SetPopupActive(false);
1636 mEventData->mDecoratorUpdated = true;
1639 case EventData::EDITING_WITH_PASTE_POPUP:
1641 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState);
1643 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1644 if(mEventData->mCursorBlinkEnabled)
1646 mEventData->mDecorator->StartCursorBlink();
1649 if(mEventData->mGrabHandleEnabled)
1651 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1653 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1654 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1655 mEventData->mDecorator->SetHighlightActive(false);
1657 if(mEventData->mGrabHandlePopupEnabled)
1660 mEventData->mDecorator->SetPopupActive(true);
1662 mEventData->mDecoratorUpdated = true;
1665 case EventData::TEXT_PANNING:
1667 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1668 mEventData->mDecorator->StopCursorBlink();
1669 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1670 if(mEventData->mDecorator->IsHandleActive(LEFT_SELECTION_HANDLE) ||
1671 mEventData->mDecorator->IsHandleActive(RIGHT_SELECTION_HANDLE))
1673 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1674 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1675 mEventData->mDecorator->SetHighlightActive(true);
1678 if(mEventData->mGrabHandlePopupEnabled)
1680 mEventData->mDecorator->SetPopupActive(false);
1683 mEventData->mDecoratorUpdated = true;
1690 void Controller::Impl::GetCursorPosition(CharacterIndex logical,
1691 CursorInfo& cursorInfo)
1693 if(!IsShowingRealText())
1695 // Do not want to use the place-holder text to set the cursor position.
1697 // Use the line's height of the font's family set to set the cursor's size.
1698 // If there is no font's family set, use the default font.
1699 // Use the current alignment to place the cursor at the beginning, center or end of the box.
1701 cursorInfo.lineOffset = 0.f;
1702 cursorInfo.lineHeight = GetDefaultFontLineHeight();
1703 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1706 if(mModel->mMatchSystemLanguageDirection)
1708 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1711 switch(mModel->mHorizontalAlignment)
1713 case Text::HorizontalAlignment::BEGIN:
1717 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1721 cursorInfo.primaryPosition.x = 0.f;
1725 case Text::HorizontalAlignment::CENTER:
1727 cursorInfo.primaryPosition.x = floorf(0.5f * mModel->mVisualModel->mControlSize.width);
1730 case Text::HorizontalAlignment::END:
1734 cursorInfo.primaryPosition.x = 0.f;
1738 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1744 // Nothing else to do.
1748 const bool isMultiLine = (Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout());
1749 GetCursorPositionParameters parameters;
1750 parameters.visualModel = mModel->mVisualModel;
1751 parameters.logicalModel = mModel->mLogicalModel;
1752 parameters.metrics = mMetrics;
1753 parameters.logical = logical;
1754 parameters.isMultiline = isMultiLine;
1756 Text::GetCursorPosition(parameters,
1759 // Adds Outline offset.
1760 const float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1761 cursorInfo.primaryPosition.x += outlineWidth;
1762 cursorInfo.primaryPosition.y += outlineWidth;
1763 cursorInfo.secondaryPosition.x += outlineWidth;
1764 cursorInfo.secondaryPosition.y += outlineWidth;
1768 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
1770 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
1771 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
1773 if(0.f > cursorInfo.primaryPosition.x)
1775 cursorInfo.primaryPosition.x = 0.f;
1778 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>(mEventData->mDecorator->GetCursorWidth());
1779 if(cursorInfo.primaryPosition.x > edgeWidth)
1781 cursorInfo.primaryPosition.x = edgeWidth;
1786 CharacterIndex Controller::Impl::CalculateNewCursorIndex(CharacterIndex index) const
1788 if(nullptr == mEventData)
1790 // Nothing to do if there is no text input.
1794 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1796 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1797 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1799 GlyphIndex glyphIndex = *(charactersToGlyphBuffer + index);
1800 Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1802 if(numberOfCharacters > 1u)
1804 const Script script = mModel->mLogicalModel->GetScript(index);
1805 if(HasLigatureMustBreak(script))
1807 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
1808 numberOfCharacters = 1u;
1813 while(0u == numberOfCharacters)
1816 numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1820 if(index < mEventData->mPrimaryCursorPosition)
1822 cursorIndex -= numberOfCharacters;
1826 cursorIndex += numberOfCharacters;
1829 // Will update the cursor hook position.
1830 mEventData->mUpdateCursorHookPosition = true;
1835 void Controller::Impl::UpdateCursorPosition(const CursorInfo& cursorInfo)
1837 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this);
1838 if(nullptr == mEventData)
1840 // Nothing to do if there is no text input.
1841 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n");
1845 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
1847 mEventData->mDecorator->SetGlyphOffset(PRIMARY_CURSOR, cursorInfo.glyphOffset);
1849 // Sets the cursor position.
1850 mEventData->mDecorator->SetPosition(PRIMARY_CURSOR,
1853 cursorInfo.primaryCursorHeight,
1854 cursorInfo.lineHeight);
1855 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y);
1857 if(mEventData->mUpdateGrabHandlePosition)
1859 // Sets the grab handle position.
1860 mEventData->mDecorator->SetPosition(GRAB_HANDLE,
1862 cursorInfo.lineOffset + mModel->mScrollPosition.y,
1863 cursorInfo.lineHeight);
1866 if(cursorInfo.isSecondaryCursor)
1868 mEventData->mDecorator->SetPosition(SECONDARY_CURSOR,
1869 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
1870 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
1871 cursorInfo.secondaryCursorHeight,
1872 cursorInfo.lineHeight);
1873 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y);
1876 // Set which cursors are active according the state.
1877 if(EventData::IsEditingState(mEventData->mState) || (EventData::GRAB_HANDLE_PANNING == mEventData->mState))
1879 if(cursorInfo.isSecondaryCursor)
1881 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_BOTH);
1885 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1890 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1893 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n");
1896 void Controller::Impl::UpdateSelectionHandle(HandleType handleType,
1897 const CursorInfo& cursorInfo)
1899 SelectionHandleController::Update(*this, handleType, cursorInfo);
1902 void Controller::Impl::ClampHorizontalScroll(const Vector2& layoutSize)
1904 // Clamp between -space & -alignment offset.
1906 if(layoutSize.width > mModel->mVisualModel->mControlSize.width)
1908 const float space = (layoutSize.width - mModel->mVisualModel->mControlSize.width) + mModel->mAlignmentOffset;
1909 mModel->mScrollPosition.x = (mModel->mScrollPosition.x < -space) ? -space : mModel->mScrollPosition.x;
1910 mModel->mScrollPosition.x = (mModel->mScrollPosition.x > -mModel->mAlignmentOffset) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
1912 mEventData->mDecoratorUpdated = true;
1916 mModel->mScrollPosition.x = 0.f;
1920 void Controller::Impl::ClampVerticalScroll(const Vector2& layoutSize)
1922 if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1924 // Nothing to do if the text is single line.
1928 // Clamp between -space & 0.
1929 if(layoutSize.height > mModel->mVisualModel->mControlSize.height)
1931 const float space = (layoutSize.height - mModel->mVisualModel->mControlSize.height);
1932 mModel->mScrollPosition.y = (mModel->mScrollPosition.y < -space) ? -space : mModel->mScrollPosition.y;
1933 mModel->mScrollPosition.y = (mModel->mScrollPosition.y > 0.f) ? 0.f : mModel->mScrollPosition.y;
1935 mEventData->mDecoratorUpdated = true;
1939 mModel->mScrollPosition.y = 0.f;
1943 void Controller::Impl::ScrollToMakePositionVisible(const Vector2& position, float lineHeight)
1945 const float cursorWidth = mEventData->mDecorator ? static_cast<float>(mEventData->mDecorator->GetCursorWidth()) : 0.f;
1947 // position is in actor's coords.
1948 const float positionEndX = position.x + cursorWidth;
1949 const float positionEndY = position.y + lineHeight;
1951 // Transform the position to decorator coords.
1952 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
1953 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
1955 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
1956 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
1958 if(decoratorPositionBeginX < 0.f)
1960 mModel->mScrollPosition.x = -position.x;
1962 else if(decoratorPositionEndX > mModel->mVisualModel->mControlSize.width)
1964 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
1967 if(Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout())
1969 if(decoratorPositionBeginY < 0.f)
1971 mModel->mScrollPosition.y = -position.y;
1973 else if(decoratorPositionEndY > mModel->mVisualModel->mControlSize.height)
1975 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
1980 void Controller::Impl::ScrollTextToMatchCursor(const CursorInfo& cursorInfo)
1982 // Get the current cursor position in decorator coords.
1983 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition(PRIMARY_CURSOR);
1985 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter(mEventData->mPrimaryCursorPosition);
1987 // Calculate the offset to match the cursor position before the character was deleted.
1988 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
1990 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
1991 if(mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() - 1u)
1993 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset(PRIMARY_CURSOR);
1994 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
1997 ClampHorizontalScroll(mModel->mVisualModel->GetLayoutSize());
1998 ClampVerticalScroll(mModel->mVisualModel->GetLayoutSize());
2000 // Makes the new cursor position visible if needed.
2001 ScrollToMakePositionVisible(cursorInfo.primaryPosition, cursorInfo.lineHeight);
2004 void Controller::Impl::ScrollTextToMatchCursor()
2006 CursorInfo cursorInfo;
2007 GetCursorPosition(mEventData->mPrimaryCursorPosition, cursorInfo);
2008 ScrollTextToMatchCursor(cursorInfo);
2011 void Controller::Impl::RequestRelayout()
2013 if(nullptr != mControlInterface)
2015 mControlInterface->RequestTextRelayout();
2019 Actor Controller::Impl::CreateBackgroundActor()
2021 // NOTE: Currently we only support background color for one line left-to-right text,
2022 // so the following calculation is based on one line left-to-right text only!
2026 Length numberOfGlyphs = mView.GetNumberOfGlyphs();
2027 if(numberOfGlyphs > 0u)
2029 Vector<GlyphInfo> glyphs;
2030 glyphs.Resize(numberOfGlyphs);
2032 Vector<Vector2> positions;
2033 positions.Resize(numberOfGlyphs);
2035 // Get the line where the glyphs are laid-out.
2036 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
2037 float alignmentOffset = lineRun->alignmentOffset;
2038 numberOfGlyphs = mView.GetGlyphs(glyphs.Begin(),
2044 glyphs.Resize(numberOfGlyphs);
2045 positions.Resize(numberOfGlyphs);
2047 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
2048 const Vector2* const positionsBuffer = positions.Begin();
2050 BackgroundMesh mesh;
2051 mesh.mVertices.Reserve(4u * glyphs.Size());
2052 mesh.mIndices.Reserve(6u * glyphs.Size());
2054 const Vector2 textSize = mView.GetLayoutSize();
2056 const float offsetX = textSize.width * 0.5f;
2057 const float offsetY = textSize.height * 0.5f;
2059 const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
2060 const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
2061 const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
2064 uint32_t numberOfQuads = 0u;
2066 for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i)
2068 const GlyphInfo& glyph = *(glyphsBuffer + i);
2070 // Get the background color of the character.
2071 // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
2072 const ColorIndex backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + i);
2073 const Vector4& backgroundColor = (0u == backgroundColorIndex) ? defaultBackgroundColor : *(backgroundColorsBuffer + backgroundColorIndex - 1u);
2075 // Only create quads for glyphs with a background color
2076 if(backgroundColor != Color::TRANSPARENT)
2078 const Vector2 position = *(positionsBuffer + i);
2080 if(i == 0u && glyphSize == 1u) // Only one glyph in the whole text
2082 quad.x = position.x;
2084 quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
2085 quad.w = textSize.height;
2087 else if(i == 0u) // The first glyph in the whole text
2089 quad.x = position.x;
2091 quad.z = quad.x - glyph.xBearing + glyph.advance;
2092 quad.w = textSize.height;
2094 else if(i == glyphSize - 1u) // The last glyph in the whole text
2096 quad.x = position.x - glyph.xBearing;
2098 quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
2099 quad.w = textSize.height;
2101 else // The glyph in the middle of the text
2103 quad.x = position.x - glyph.xBearing;
2105 quad.z = quad.x + glyph.advance;
2106 quad.w = textSize.height;
2109 BackgroundVertex vertex;
2112 vertex.mPosition.x = quad.x - offsetX;
2113 vertex.mPosition.y = quad.y - offsetY;
2114 vertex.mColor = backgroundColor;
2115 mesh.mVertices.PushBack(vertex);
2118 vertex.mPosition.x = quad.z - offsetX;
2119 vertex.mPosition.y = quad.y - offsetY;
2120 vertex.mColor = backgroundColor;
2121 mesh.mVertices.PushBack(vertex);
2124 vertex.mPosition.x = quad.x - offsetX;
2125 vertex.mPosition.y = quad.w - offsetY;
2126 vertex.mColor = backgroundColor;
2127 mesh.mVertices.PushBack(vertex);
2130 vertex.mPosition.x = quad.z - offsetX;
2131 vertex.mPosition.y = quad.w - offsetY;
2132 vertex.mColor = backgroundColor;
2133 mesh.mVertices.PushBack(vertex);
2135 // Six indices in counter clockwise winding
2136 mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
2137 mesh.mIndices.PushBack(0u + 4 * numberOfQuads);
2138 mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
2139 mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
2140 mesh.mIndices.PushBack(3u + 4 * numberOfQuads);
2141 mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
2147 // Only create the background actor if there are glyphs with background color
2148 if(mesh.mVertices.Count() > 0u)
2150 Property::Map quadVertexFormat;
2151 quadVertexFormat["aPosition"] = Property::VECTOR2;
2152 quadVertexFormat["aColor"] = Property::VECTOR4;
2154 VertexBuffer quadVertices = VertexBuffer::New(quadVertexFormat);
2155 quadVertices.SetData(&mesh.mVertices[0], mesh.mVertices.Size());
2157 Geometry quadGeometry = Geometry::New();
2158 quadGeometry.AddVertexBuffer(quadVertices);
2159 quadGeometry.SetIndexBuffer(&mesh.mIndices[0], mesh.mIndices.Size());
2161 if(!mShaderBackground)
2163 mShaderBackground = Shader::New(SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_VERT, SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_FRAG);
2166 Dali::Renderer renderer = Dali::Renderer::New(quadGeometry, mShaderBackground);
2167 renderer.SetProperty(Dali::Renderer::Property::BLEND_MODE, BlendMode::ON);
2168 renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT);
2170 actor = Actor::New();
2171 actor.SetProperty(Dali::Actor::Property::NAME, "TextBackgroundColorActor");
2172 actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
2173 actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
2174 actor.SetProperty(Actor::Property::SIZE, textSize);
2175 actor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR);
2176 actor.AddRenderer(renderer);
2183 void Controller::Impl::CopyUnderlinedFromLogicalToVisualModels(bool shouldClearPreUnderlineRuns)
2185 //Underlined character runs for markup-processor
2186 const Vector<UnderlinedCharacterRun>& underlinedCharacterRuns = mModel->mLogicalModel->mUnderlinedCharacterRuns;
2187 const Vector<GlyphIndex>& charactersToGlyph = mModel->mVisualModel->mCharactersToGlyph;
2188 const Vector<Length>& glyphsPerCharacter = mModel->mVisualModel->mGlyphsPerCharacter;
2190 if(shouldClearPreUnderlineRuns)
2192 mModel->mVisualModel->mUnderlineRuns.Clear();
2195 for(Vector<UnderlinedCharacterRun>::ConstIterator it = underlinedCharacterRuns.Begin(), endIt = underlinedCharacterRuns.End(); it != endIt; ++it)
2197 CharacterIndex characterIndex = it->characterRun.characterIndex;
2198 Length numberOfCharacters = it->characterRun.numberOfCharacters;
2199 for(Length index=0u; index<numberOfCharacters; index++)
2201 GlyphRun underlineGlyphRun;
2202 underlineGlyphRun.glyphIndex = charactersToGlyph[characterIndex + index];
2203 underlineGlyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex + index];
2204 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineGlyphRun);
2211 } // namespace Toolkit