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/multi-language-support.h>
33 #include <dali-toolkit/internal/text/segmentation.h>
34 #include <dali-toolkit/internal/text/shaper.h>
35 #include <dali-toolkit/internal/text/text-control-interface.h>
36 #include <dali-toolkit/internal/text/text-controller-impl-event-handler.h>
37 #include <dali-toolkit/internal/text/text-run-container.h>
38 #include <dali-toolkit/internal/text/text-selection-handle-controller.h>
44 #if defined(DEBUG_ENABLED)
45 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
48 struct BackgroundVertex
50 Vector2 mPosition; ///< Vertex posiiton
51 Vector4 mColor; ///< Vertex color
56 Vector<BackgroundVertex> mVertices; ///< container of vertices
57 Vector<unsigned short> mIndices; ///< container of indices
60 const Dali::Vector4 LIGHT_BLUE(0.75f, 0.96f, 1.f, 1.f);
61 const Dali::Vector4 BACKGROUND_SUB4(0.58f, 0.87f, 0.96f, 1.f);
62 const Dali::Vector4 BACKGROUND_SUB5(0.83f, 0.94f, 0.98f, 1.f);
63 const Dali::Vector4 BACKGROUND_SUB6(1.f, 0.5f, 0.5f, 1.f);
64 const Dali::Vector4 BACKGROUND_SUB7(1.f, 0.8f, 0.8f, 1.f);
74 EventData::EventData(DecoratorPtr decorator, InputMethodContext& inputMethodContext)
75 : mDecorator(decorator),
76 mInputMethodContext(inputMethodContext),
77 mPlaceholderFont(NULL),
78 mPlaceholderTextActive(),
79 mPlaceholderTextInactive(),
80 mPlaceholderTextColor(0.8f, 0.8f, 0.8f, 0.8f), // This color has been published in the Public API (placeholder-properties.h).
82 mInputStyleChangedQueue(),
83 mPreviousState(INACTIVE),
85 mPrimaryCursorPosition(0u),
86 mLeftSelectionPosition(0u),
87 mRightSelectionPosition(0u),
88 mPreEditStartPosition(0u),
90 mCursorHookPositionX(0.f),
91 mDoubleTapAction(Controller::NoTextTap::NO_ACTION),
92 mLongPressAction(Controller::NoTextTap::SHOW_SELECTION_POPUP),
93 mIsShowingPlaceholderText(false),
95 mDecoratorUpdated(false),
96 mCursorBlinkEnabled(true),
97 mGrabHandleEnabled(true),
98 mGrabHandlePopupEnabled(true),
99 mSelectionEnabled(true),
100 mUpdateCursorHookPosition(false),
101 mUpdateCursorPosition(false),
102 mUpdateGrabHandlePosition(false),
103 mUpdateLeftSelectionPosition(false),
104 mUpdateRightSelectionPosition(false),
105 mIsLeftHandleSelected(false),
106 mIsRightHandleSelected(false),
107 mUpdateHighlightBox(false),
108 mScrollAfterUpdatePosition(false),
109 mScrollAfterDelete(false),
110 mAllTextSelected(false),
111 mUpdateInputStyle(false),
112 mPasswordInput(false),
113 mCheckScrollAmount(false),
114 mIsPlaceholderPixelSize(false),
115 mIsPlaceholderElideEnabled(false),
116 mPlaceholderEllipsisFlag(false),
117 mShiftSelectionFlag(true),
118 mUpdateAlignment(false),
119 mEditingEnabled(true)
123 bool Controller::Impl::ProcessInputEvents()
125 return ControllerImplEventHandler::ProcessInputEvents(*this);
128 void Controller::Impl::NotifyInputMethodContext()
130 if(mEventData && mEventData->mInputMethodContext)
132 CharacterIndex cursorPosition = GetLogicalCursorPosition();
134 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces(0u);
136 // Update the cursor position by removing the initial white spaces.
137 if(cursorPosition < numberOfWhiteSpaces)
143 cursorPosition -= numberOfWhiteSpaces;
146 mEventData->mInputMethodContext.SetCursorPosition(cursorPosition);
147 mEventData->mInputMethodContext.NotifyCursorPosition();
151 void Controller::Impl::NotifyInputMethodContextMultiLineStatus()
153 if(mEventData && mEventData->mInputMethodContext)
155 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
156 mEventData->mInputMethodContext.NotifyTextInputMultiLine(layout == Text::Layout::Engine::MULTI_LINE_BOX);
160 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
162 CharacterIndex cursorPosition = 0u;
166 if((EventData::SELECTING == mEventData->mState) ||
167 (EventData::SELECTION_HANDLE_PANNING == mEventData->mState))
169 cursorPosition = std::min(mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition);
173 cursorPosition = mEventData->mPrimaryCursorPosition;
177 return cursorPosition;
180 Length Controller::Impl::GetNumberOfWhiteSpaces(CharacterIndex index) const
182 Length numberOfWhiteSpaces = 0u;
184 // Get the buffer to the text.
185 Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
187 const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
188 for(; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces)
190 if(!TextAbstraction::IsWhiteSpace(*(utf32CharacterBuffer + index)))
196 return numberOfWhiteSpaces;
199 void Controller::Impl::GetText(CharacterIndex index, std::string& text) const
201 // Get the total number of characters.
202 Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
204 // Retrieve the text.
205 if(0u != numberOfCharacters)
207 Utf32ToUtf8(mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text);
211 void Controller::Impl::CalculateTextUpdateIndices(Length& numberOfCharacters)
213 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
214 mTextUpdateInfo.mStartGlyphIndex = 0u;
215 mTextUpdateInfo.mStartLineIndex = 0u;
216 numberOfCharacters = 0u;
218 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
219 if(0u == numberOfParagraphs)
221 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
222 numberOfCharacters = 0u;
224 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
226 // Nothing else to do if there are no paragraphs.
230 // Find the paragraphs to be updated.
231 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
232 if(mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters)
234 // Text is being added at the end of the current text.
235 if(mTextUpdateInfo.mIsLastCharacterNewParagraph)
237 // Text is being added in a new paragraph after the last character of the text.
238 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
239 numberOfCharacters = 0u;
240 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
242 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
243 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
245 // Nothing else to do;
249 paragraphsToBeUpdated.PushBack(numberOfParagraphs - 1u);
253 Length numberOfCharactersToUpdate = 0u;
254 if(mTextUpdateInfo.mFullRelayoutNeeded)
256 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
260 numberOfCharactersToUpdate = (mTextUpdateInfo.mNumberOfCharactersToRemove > 0u) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
262 mModel->mLogicalModel->FindParagraphs(mTextUpdateInfo.mCharacterIndex,
263 numberOfCharactersToUpdate,
264 paragraphsToBeUpdated);
267 if(0u != paragraphsToBeUpdated.Count())
269 const ParagraphRunIndex firstParagraphIndex = *(paragraphsToBeUpdated.Begin());
270 const ParagraphRun& firstParagraph = *(mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex);
271 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
273 ParagraphRunIndex lastParagraphIndex = *(paragraphsToBeUpdated.End() - 1u);
274 const ParagraphRun& lastParagraph = *(mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex);
276 if((mTextUpdateInfo.mNumberOfCharactersToRemove > 0u) && // Some character are removed.
277 (lastParagraphIndex < numberOfParagraphs - 1u) && // There is a next paragraph.
278 ((lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters) == // The last removed character is the new paragraph character.
279 (mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove)))
281 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
282 const ParagraphRun& lastParagraph = *(mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u);
284 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
288 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
292 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
293 mTextUpdateInfo.mStartGlyphIndex = *(mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex);
296 void Controller::Impl::ClearFullModelData(OperationsMask operations)
298 if(NO_OPERATION != (GET_LINE_BREAKS & operations))
300 mModel->mLogicalModel->mLineBreakInfo.Clear();
301 mModel->mLogicalModel->mParagraphInfo.Clear();
304 if(NO_OPERATION != (GET_SCRIPTS & operations))
306 mModel->mLogicalModel->mScriptRuns.Clear();
309 if(NO_OPERATION != (VALIDATE_FONTS & operations))
311 mModel->mLogicalModel->mFontRuns.Clear();
314 if(0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count())
316 if(NO_OPERATION != (BIDI_INFO & operations))
318 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
319 mModel->mLogicalModel->mCharacterDirections.Clear();
322 if(NO_OPERATION != (REORDER & operations))
324 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
325 for(Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
326 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
330 BidirectionalLineInfoRun& bidiLineInfo = *it;
332 free(bidiLineInfo.visualToLogicalMap);
333 bidiLineInfo.visualToLogicalMap = NULL;
335 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
339 if(NO_OPERATION != (SHAPE_TEXT & operations))
341 mModel->mVisualModel->mGlyphs.Clear();
342 mModel->mVisualModel->mGlyphsToCharacters.Clear();
343 mModel->mVisualModel->mCharactersToGlyph.Clear();
344 mModel->mVisualModel->mCharactersPerGlyph.Clear();
345 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
346 mModel->mVisualModel->mGlyphPositions.Clear();
349 if(NO_OPERATION != (LAYOUT & operations))
351 mModel->mVisualModel->mLines.Clear();
354 if(NO_OPERATION != (COLOR & operations))
356 mModel->mVisualModel->mColorIndices.Clear();
357 mModel->mVisualModel->mBackgroundColorIndices.Clear();
361 void Controller::Impl::ClearCharacterModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
363 const CharacterIndex endIndexPlusOne = endIndex + 1u;
365 if(NO_OPERATION != (GET_LINE_BREAKS & operations))
367 // Clear the line break info.
368 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
370 mModel->mLogicalModel->mLineBreakInfo.Erase(lineBreakInfoBuffer + startIndex,
371 lineBreakInfoBuffer + endIndexPlusOne);
373 // Clear the paragraphs.
374 ClearCharacterRuns(startIndex,
376 mModel->mLogicalModel->mParagraphInfo);
379 if(NO_OPERATION != (GET_SCRIPTS & operations))
381 // Clear the scripts.
382 ClearCharacterRuns(startIndex,
384 mModel->mLogicalModel->mScriptRuns);
387 if(NO_OPERATION != (VALIDATE_FONTS & operations))
390 ClearCharacterRuns(startIndex,
392 mModel->mLogicalModel->mFontRuns);
395 if(0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count())
397 if(NO_OPERATION != (BIDI_INFO & operations))
399 // Clear the bidirectional paragraph info.
400 ClearCharacterRuns(startIndex,
402 mModel->mLogicalModel->mBidirectionalParagraphInfo);
404 // Clear the character's directions.
405 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
407 mModel->mLogicalModel->mCharacterDirections.Erase(characterDirectionsBuffer + startIndex,
408 characterDirectionsBuffer + endIndexPlusOne);
411 if(NO_OPERATION != (REORDER & operations))
413 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
414 uint32_t endRemoveIndex = startRemoveIndex;
415 ClearCharacterRuns(startIndex,
417 mModel->mLogicalModel->mBidirectionalLineInfo,
421 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
423 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
424 for(Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
425 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
429 BidirectionalLineInfoRun& bidiLineInfo = *it;
431 free(bidiLineInfo.visualToLogicalMap);
432 bidiLineInfo.visualToLogicalMap = NULL;
435 mModel->mLogicalModel->mBidirectionalLineInfo.Erase(bidirectionalLineInfoBuffer + startRemoveIndex,
436 bidirectionalLineInfoBuffer + endRemoveIndex);
441 void Controller::Impl::ClearGlyphModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
443 const CharacterIndex endIndexPlusOne = endIndex + 1u;
444 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
446 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
447 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
448 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
450 const GlyphIndex endGlyphIndexPlusOne = *(charactersToGlyphBuffer + endIndex) + *(glyphsPerCharacterBuffer + endIndex);
451 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
453 if(NO_OPERATION != (SHAPE_TEXT & operations))
455 // Update the character to glyph indices.
456 for(Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
457 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
461 CharacterIndex& index = *it;
462 index -= numberOfGlyphsRemoved;
465 // Clear the character to glyph conversion table.
466 mModel->mVisualModel->mCharactersToGlyph.Erase(charactersToGlyphBuffer + startIndex,
467 charactersToGlyphBuffer + endIndexPlusOne);
469 // Clear the glyphs per character table.
470 mModel->mVisualModel->mGlyphsPerCharacter.Erase(glyphsPerCharacterBuffer + startIndex,
471 glyphsPerCharacterBuffer + endIndexPlusOne);
473 // Clear the glyphs buffer.
474 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
475 mModel->mVisualModel->mGlyphs.Erase(glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
476 glyphsBuffer + endGlyphIndexPlusOne);
478 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
480 // Update the glyph to character indices.
481 for(Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
482 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
486 CharacterIndex& index = *it;
487 index -= numberOfCharactersRemoved;
490 // Clear the glyphs to characters buffer.
491 mModel->mVisualModel->mGlyphsToCharacters.Erase(glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
492 glyphsToCharactersBuffer + endGlyphIndexPlusOne);
494 // Clear the characters per glyph buffer.
495 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
496 mModel->mVisualModel->mCharactersPerGlyph.Erase(charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
497 charactersPerGlyphBuffer + endGlyphIndexPlusOne);
499 // Clear the positions buffer.
500 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
501 mModel->mVisualModel->mGlyphPositions.Erase(positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
502 positionsBuffer + endGlyphIndexPlusOne);
505 if(NO_OPERATION != (LAYOUT & operations))
508 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
509 uint32_t endRemoveIndex = startRemoveIndex;
510 ClearCharacterRuns(startIndex,
512 mModel->mVisualModel->mLines,
516 // Will update the glyph runs.
517 startRemoveIndex = mModel->mVisualModel->mLines.Count();
518 endRemoveIndex = startRemoveIndex;
519 ClearGlyphRuns(mTextUpdateInfo.mStartGlyphIndex,
520 endGlyphIndexPlusOne - 1u,
521 mModel->mVisualModel->mLines,
525 // Set the line index from where to insert the new laid-out lines.
526 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
528 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
529 mModel->mVisualModel->mLines.Erase(linesBuffer + startRemoveIndex,
530 linesBuffer + endRemoveIndex);
533 if(NO_OPERATION != (COLOR & operations))
535 if(0u != mModel->mVisualModel->mColorIndices.Count())
537 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
538 mModel->mVisualModel->mColorIndices.Erase(colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
539 colorIndexBuffer + endGlyphIndexPlusOne);
542 if(0u != mModel->mVisualModel->mBackgroundColorIndices.Count())
544 ColorIndex* backgroundColorIndexBuffer = mModel->mVisualModel->mBackgroundColorIndices.Begin();
545 mModel->mVisualModel->mBackgroundColorIndices.Erase(backgroundColorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
546 backgroundColorIndexBuffer + endGlyphIndexPlusOne);
551 void Controller::Impl::ClearModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
553 if(mTextUpdateInfo.mClearAll ||
554 ((0u == startIndex) &&
555 (mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u)))
557 ClearFullModelData(operations);
561 // Clear the model data related with characters.
562 ClearCharacterModelData(startIndex, endIndex, operations);
564 // Clear the model data related with glyphs.
565 ClearGlyphModelData(startIndex, endIndex, operations);
568 // The estimated number of lines. Used to avoid reallocations when layouting.
569 mTextUpdateInfo.mEstimatedNumberOfLines = std::max(mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count());
571 mModel->mVisualModel->ClearCaches();
574 bool Controller::Impl::UpdateModel(OperationsMask operationsRequired)
576 DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::UpdateModel\n");
578 // Calculate the operations to be done.
579 const OperationsMask operations = static_cast<OperationsMask>(mOperationsPending & operationsRequired);
581 if(NO_OPERATION == operations)
583 // Nothing to do if no operations are pending and required.
587 Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
588 Vector<Character> displayCharacters;
589 bool useHiddenText = false;
590 if(mHiddenInput && mEventData != nullptr && !mEventData->mIsShowingPlaceholderText)
592 mHiddenInput->Substitute(srcCharacters, displayCharacters);
593 useHiddenText = true;
596 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
597 const Length numberOfCharacters = utf32Characters.Count();
599 // Index to the first character of the first paragraph to be updated.
600 CharacterIndex startIndex = 0u;
601 // Number of characters of the paragraphs to be removed.
602 Length paragraphCharacters = 0u;
604 CalculateTextUpdateIndices(paragraphCharacters);
606 // Check whether the indices for updating the text is valid
607 if(numberOfCharacters > 0u &&
608 (mTextUpdateInfo.mParagraphCharacterIndex > numberOfCharacters ||
609 mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters))
611 std::string currentText;
612 Utf32ToUtf8(mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText);
614 DALI_LOG_ERROR("Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n");
615 DALI_LOG_ERROR("Number of characters: %d, current text is: %s\n", numberOfCharacters, currentText.c_str());
617 // Dump mTextUpdateInfo
618 DALI_LOG_ERROR("Dump mTextUpdateInfo:\n");
619 DALI_LOG_ERROR(" mTextUpdateInfo.mCharacterIndex = %u\n", mTextUpdateInfo.mCharacterIndex);
620 DALI_LOG_ERROR(" mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", mTextUpdateInfo.mNumberOfCharactersToRemove);
621 DALI_LOG_ERROR(" mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", mTextUpdateInfo.mNumberOfCharactersToAdd);
622 DALI_LOG_ERROR(" mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", mTextUpdateInfo.mPreviousNumberOfCharacters);
623 DALI_LOG_ERROR(" mTextUpdateInfo.mParagraphCharacterIndex = %u\n", mTextUpdateInfo.mParagraphCharacterIndex);
624 DALI_LOG_ERROR(" mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", mTextUpdateInfo.mRequestedNumberOfCharacters);
625 DALI_LOG_ERROR(" mTextUpdateInfo.mStartGlyphIndex = %u\n", mTextUpdateInfo.mStartGlyphIndex);
626 DALI_LOG_ERROR(" mTextUpdateInfo.mStartLineIndex = %u\n", mTextUpdateInfo.mStartLineIndex);
627 DALI_LOG_ERROR(" mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", mTextUpdateInfo.mEstimatedNumberOfLines);
628 DALI_LOG_ERROR(" mTextUpdateInfo.mClearAll = %d\n", mTextUpdateInfo.mClearAll);
629 DALI_LOG_ERROR(" mTextUpdateInfo.mFullRelayoutNeeded = %d\n", mTextUpdateInfo.mFullRelayoutNeeded);
630 DALI_LOG_ERROR(" mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", mTextUpdateInfo.mIsLastCharacterNewParagraph);
635 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
637 if(mTextUpdateInfo.mClearAll ||
638 (0u != paragraphCharacters))
640 ClearModelData(startIndex, startIndex + ((paragraphCharacters > 0u) ? paragraphCharacters - 1u : 0u), operations);
643 mTextUpdateInfo.mClearAll = false;
645 // Whether the model is updated.
646 bool updated = false;
648 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
649 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
651 if(NO_OPERATION != (GET_LINE_BREAKS & operations))
653 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
654 // calculate the bidirectional info for each 'paragraph'.
655 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
656 // is not shaped together).
657 lineBreakInfo.Resize(numberOfCharacters, TextAbstraction::LINE_NO_BREAK);
659 SetLineBreakInfo(utf32Characters,
661 requestedNumberOfCharacters,
664 // Create the paragraph info.
665 mModel->mLogicalModel->CreateParagraphInfo(startIndex,
666 requestedNumberOfCharacters);
670 const bool getScripts = NO_OPERATION != (GET_SCRIPTS & operations);
671 const bool validateFonts = NO_OPERATION != (VALIDATE_FONTS & operations);
673 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
674 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
676 if(getScripts || validateFonts)
678 // Validates the fonts assigned by the application or assigns default ones.
679 // It makes sure all the characters are going to be rendered by the correct font.
680 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
684 // Retrieves the scripts used in the text.
685 multilanguageSupport.SetScripts(utf32Characters,
687 requestedNumberOfCharacters,
693 // Validate the fonts set through the mark-up string.
694 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
696 // Get the default font's description.
697 TextAbstraction::FontDescription defaultFontDescription;
698 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale;
700 if(IsShowingPlaceholderText() && mEventData && (nullptr != mEventData->mPlaceholderFont))
702 // If the placeholder font is set specifically, only placeholder font is changed.
703 defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
704 if(mEventData->mPlaceholderFont->sizeDefined)
706 defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * mFontSizeScale * 64u;
709 else if(nullptr != mFontDefaults)
711 // Set the normal font and the placeholder font.
712 defaultFontDescription = mFontDefaults->mFontDescription;
716 defaultPointSize = mFontDefaults->mFitPointSize * 64u;
720 defaultPointSize = mFontDefaults->mDefaultPointSize * mFontSizeScale * 64u;
724 // Validates the fonts. If there is a character with no assigned font it sets a default one.
725 // After this call, fonts are validated.
726 multilanguageSupport.ValidateFonts(utf32Characters,
729 defaultFontDescription,
732 requestedNumberOfCharacters,
738 Vector<Character> mirroredUtf32Characters;
739 bool textMirrored = false;
740 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
741 if(NO_OPERATION != (BIDI_INFO & operations))
743 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
744 bidirectionalInfo.Reserve(numberOfParagraphs);
746 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
747 SetBidirectionalInfo(utf32Characters,
751 requestedNumberOfCharacters,
753 mModel->mMatchSystemLanguageDirection,
756 if(0u != bidirectionalInfo.Count())
758 // Only set the character directions if there is right to left characters.
759 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
760 GetCharactersDirection(bidirectionalInfo,
763 requestedNumberOfCharacters,
766 // This paragraph has right to left text. Some characters may need to be mirrored.
767 // TODO: consider if the mirrored string can be stored as well.
769 textMirrored = GetMirroredText(utf32Characters,
773 requestedNumberOfCharacters,
774 mirroredUtf32Characters);
778 // There is no right to left characters. Clear the directions vector.
779 mModel->mLogicalModel->mCharacterDirections.Clear();
784 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
785 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
786 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
787 Vector<GlyphIndex> newParagraphGlyphs;
788 newParagraphGlyphs.Reserve(numberOfParagraphs);
790 const Length currentNumberOfGlyphs = glyphs.Count();
791 if(NO_OPERATION != (SHAPE_TEXT & operations))
793 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
795 ShapeText(textToShape,
800 mTextUpdateInfo.mStartGlyphIndex,
801 requestedNumberOfCharacters,
803 glyphsToCharactersMap,
807 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
808 mModel->mVisualModel->CreateGlyphsPerCharacterTable(startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters);
809 mModel->mVisualModel->CreateCharacterToGlyphTable(startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters);
814 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
816 if(NO_OPERATION != (GET_GLYPH_METRICS & operations))
818 GlyphInfo* glyphsBuffer = glyphs.Begin();
819 mMetrics->GetGlyphMetrics(glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs);
821 // Update the width and advance of all new paragraph characters.
822 for(Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it)
824 const GlyphIndex index = *it;
825 GlyphInfo& glyph = *(glyphsBuffer + index);
827 glyph.xBearing = 0.f;
834 if((nullptr != mEventData) &&
835 mEventData->mPreEditFlag &&
836 (0u != mModel->mVisualModel->mCharactersToGlyph.Count()))
838 Dali::InputMethodContext::PreEditAttributeDataContainer attrs;
839 mEventData->mInputMethodContext.GetPreeditStyle(attrs);
840 Dali::InputMethodContext::PreeditStyle type = Dali::InputMethodContext::PreeditStyle::NONE;
842 // Check the type of preedit and run it.
843 for(Dali::InputMethodContext::PreEditAttributeDataContainer::Iterator it = attrs.Begin(), endIt = attrs.End(); it != endIt; it++)
845 Dali::InputMethodContext::PreeditAttributeData attrData = *it;
846 DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::UpdateModel PreeditStyle type : %d start %d end %d \n", attrData.preeditType, attrData.startIndex, attrData.endIndex);
847 type = attrData.preeditType;
849 // Check the number of commit characters for the start position.
850 unsigned int numberOfCommit = mEventData->mPrimaryCursorPosition - mEventData->mPreEditLength;
851 Length numberOfIndices = attrData.endIndex - attrData.startIndex;
855 case Dali::InputMethodContext::PreeditStyle::UNDERLINE:
857 // Add the underline for the pre-edit text.
858 GlyphRun underlineRun;
859 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
860 underlineRun.numberOfGlyphs = numberOfIndices;
861 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
863 //Mark-up processor case
864 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
866 CopyUnderlinedFromLogicalToVisualModels(false);
870 case Dali::InputMethodContext::PreeditStyle::REVERSE:
872 Vector4 textColor = mModel->mVisualModel->GetTextColor();
873 ColorRun backgroundColorRun;
874 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
875 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
876 backgroundColorRun.color = textColor;
877 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
879 Vector4 backgroundColor = mModel->mVisualModel->GetBackgroundColor();
880 Vector<ColorRun> colorRuns;
881 colorRuns.Resize(1u);
882 ColorRun& colorRun = *(colorRuns.Begin());
883 colorRun.color = backgroundColor;
884 colorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
885 colorRun.characterRun.numberOfCharacters = numberOfIndices;
887 mModel->mLogicalModel->mColorRuns.PushBack(colorRun);
890 case Dali::InputMethodContext::PreeditStyle::HIGHLIGHT:
892 ColorRun backgroundColorRun;
893 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
894 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
895 backgroundColorRun.color = LIGHT_BLUE;
896 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
899 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_1:
901 // CUSTOM_PLATFORM_STYLE_1 should be drawn with background and underline together.
902 ColorRun backgroundColorRun;
903 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
904 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
905 backgroundColorRun.color = BACKGROUND_SUB4;
906 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
908 GlyphRun underlineRun;
909 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
910 underlineRun.numberOfGlyphs = numberOfIndices;
911 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
913 //Mark-up processor case
914 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
916 CopyUnderlinedFromLogicalToVisualModels(false);
920 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_2:
922 // CUSTOM_PLATFORM_STYLE_2 should be drawn with background and underline together.
923 ColorRun backgroundColorRun;
924 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
925 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
926 backgroundColorRun.color = BACKGROUND_SUB5;
927 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
929 GlyphRun underlineRun;
930 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
931 underlineRun.numberOfGlyphs = numberOfIndices;
932 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
934 //Mark-up processor case
935 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
937 CopyUnderlinedFromLogicalToVisualModels(false);
941 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_3:
943 // CUSTOM_PLATFORM_STYLE_3 should be drawn with background and underline together.
944 ColorRun backgroundColorRun;
945 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
946 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
947 backgroundColorRun.color = BACKGROUND_SUB6;
948 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
950 GlyphRun underlineRun;
951 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
952 underlineRun.numberOfGlyphs = numberOfIndices;
953 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
955 //Mark-up processor case
956 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
958 CopyUnderlinedFromLogicalToVisualModels(false);
962 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_4:
964 // CUSTOM_PLATFORM_STYLE_4 should be drawn with background and underline together.
965 ColorRun backgroundColorRun;
966 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
967 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
968 backgroundColorRun.color = BACKGROUND_SUB7;
969 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
971 GlyphRun underlineRun;
972 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
973 underlineRun.numberOfGlyphs = numberOfIndices;
974 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
976 //Mark-up processor case
977 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
979 CopyUnderlinedFromLogicalToVisualModels(false);
983 case Dali::InputMethodContext::PreeditStyle::NONE:
994 if(NO_OPERATION != (COLOR & operations))
996 // Set the color runs in glyphs.
997 SetColorSegmentationInfo(mModel->mLogicalModel->mColorRuns,
998 mModel->mVisualModel->mCharactersToGlyph,
999 mModel->mVisualModel->mGlyphsPerCharacter,
1001 mTextUpdateInfo.mStartGlyphIndex,
1002 requestedNumberOfCharacters,
1003 mModel->mVisualModel->mColors,
1004 mModel->mVisualModel->mColorIndices);
1006 // Set the background color runs in glyphs.
1007 SetColorSegmentationInfo(mModel->mLogicalModel->mBackgroundColorRuns,
1008 mModel->mVisualModel->mCharactersToGlyph,
1009 mModel->mVisualModel->mGlyphsPerCharacter,
1011 mTextUpdateInfo.mStartGlyphIndex,
1012 requestedNumberOfCharacters,
1013 mModel->mVisualModel->mBackgroundColors,
1014 mModel->mVisualModel->mBackgroundColorIndices);
1019 if((NO_OPERATION != (SHAPE_TEXT & operations)) &&
1020 ! ((nullptr != mEventData) &&
1021 mEventData->mPreEditFlag &&
1022 (0u != mModel->mVisualModel->mCharactersToGlyph.Count())))
1024 //Mark-up processor case
1025 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
1027 CopyUnderlinedFromLogicalToVisualModels(true);
1034 // The estimated number of lines. Used to avoid reallocations when layouting.
1035 mTextUpdateInfo.mEstimatedNumberOfLines = std::max(mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count());
1037 // Set the previous number of characters for the next time the text is updated.
1038 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1043 void Controller::Impl::RetrieveDefaultInputStyle(InputStyle& inputStyle)
1045 // Sets the default text's color.
1046 inputStyle.textColor = mTextColor;
1047 inputStyle.isDefaultColor = true;
1049 inputStyle.familyName.clear();
1050 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1051 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1052 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1053 inputStyle.size = 0.f;
1055 inputStyle.lineSpacing = 0.f;
1057 inputStyle.underlineProperties.clear();
1058 inputStyle.shadowProperties.clear();
1059 inputStyle.embossProperties.clear();
1060 inputStyle.outlineProperties.clear();
1062 inputStyle.isFamilyDefined = false;
1063 inputStyle.isWeightDefined = false;
1064 inputStyle.isWidthDefined = false;
1065 inputStyle.isSlantDefined = false;
1066 inputStyle.isSizeDefined = false;
1068 inputStyle.isLineSpacingDefined = false;
1070 inputStyle.isUnderlineDefined = false;
1071 inputStyle.isShadowDefined = false;
1072 inputStyle.isEmbossDefined = false;
1073 inputStyle.isOutlineDefined = false;
1075 // Sets the default font's family name, weight, width, slant and size.
1078 if(mFontDefaults->familyDefined)
1080 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1081 inputStyle.isFamilyDefined = true;
1084 if(mFontDefaults->weightDefined)
1086 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1087 inputStyle.isWeightDefined = true;
1090 if(mFontDefaults->widthDefined)
1092 inputStyle.width = mFontDefaults->mFontDescription.width;
1093 inputStyle.isWidthDefined = true;
1096 if(mFontDefaults->slantDefined)
1098 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1099 inputStyle.isSlantDefined = true;
1102 if(mFontDefaults->sizeDefined)
1104 inputStyle.size = mFontDefaults->mDefaultPointSize;
1105 inputStyle.isSizeDefined = true;
1110 float Controller::Impl::GetDefaultFontLineHeight()
1112 FontId defaultFontId = 0u;
1113 if(nullptr == mFontDefaults)
1115 TextAbstraction::FontDescription fontDescription;
1116 defaultFontId = mFontClient.GetFontId(fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale);
1120 defaultFontId = mFontDefaults->GetFontId(mFontClient, mFontDefaults->mDefaultPointSize * mFontSizeScale);
1123 Text::FontMetrics fontMetrics;
1124 mMetrics->GetFontMetrics(defaultFontId, fontMetrics);
1126 return (fontMetrics.ascender - fontMetrics.descender);
1129 void Controller::Impl::SetTextSelectionRange(const uint32_t* pStart, const uint32_t* pEnd)
1131 if(nullptr == mEventData)
1133 // Nothing to do if there is no text.
1137 if(mEventData->mSelectionEnabled && (pStart || pEnd))
1139 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
1143 mEventData->mLeftSelectionPosition = std::min(*pStart, length);
1147 mEventData->mRightSelectionPosition = std::min(*pEnd, length);
1150 if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
1152 ChangeState(EventData::EDITING);
1153 mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition;
1154 mEventData->mUpdateCursorPosition = true;
1158 ChangeState(EventData::SELECTING);
1159 mEventData->mUpdateHighlightBox = true;
1160 mEventData->mUpdateLeftSelectionPosition = true;
1161 mEventData->mUpdateRightSelectionPosition = true;
1166 CharacterIndex Controller::Impl::GetPrimaryCursorPosition() const
1168 if(nullptr == mEventData)
1172 return mEventData->mPrimaryCursorPosition;
1175 bool Controller::Impl::SetPrimaryCursorPosition(CharacterIndex index)
1177 if(nullptr == mEventData)
1179 // Nothing to do if there is no text.
1183 if(mEventData->mPrimaryCursorPosition == index)
1185 // Nothing for same cursor position.
1189 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
1190 mEventData->mPrimaryCursorPosition = std::min(index, length);
1191 ChangeState(EventData::EDITING);
1192 mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1193 mEventData->mUpdateCursorPosition = true;
1194 ScrollTextToMatchCursor();
1198 Uint32Pair Controller::Impl::GetTextSelectionRange() const
1204 range.first = mEventData->mLeftSelectionPosition;
1205 range.second = mEventData->mRightSelectionPosition;
1211 bool Controller::Impl::IsEditable() const
1213 return mEventData && mEventData->mEditingEnabled;
1216 void Controller::Impl::SetEditable(bool editable)
1220 mEventData->mEditingEnabled = editable;
1224 void Controller::Impl::RetrieveSelection(std::string& selectedText, bool deleteAfterRetrieval)
1226 if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
1228 // Nothing to select if handles are in the same place.
1229 selectedText.clear();
1233 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1235 //Get start and end position of selection
1236 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1237 const Length lengthOfSelectedText = (handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition) - startOfSelectedText;
1239 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1240 const Length numberOfCharacters = utf32Characters.Count();
1242 // Validate the start and end selection points
1243 if((startOfSelectedText + lengthOfSelectedText) <= numberOfCharacters)
1245 //Get text as a UTF8 string
1246 Utf32ToUtf8(&utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText);
1248 if(deleteAfterRetrieval) // Only delete text if copied successfully
1250 // Keep a copy of the current input style.
1251 InputStyle currentInputStyle;
1252 currentInputStyle.Copy(mEventData->mInputStyle);
1254 // Set as input style the style of the first deleted character.
1255 mModel->mLogicalModel->RetrieveStyle(startOfSelectedText, mEventData->mInputStyle);
1257 // Compare if the input style has changed.
1258 const bool hasInputStyleChanged = !currentInputStyle.Equal(mEventData->mInputStyle);
1260 if(hasInputStyleChanged)
1262 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask(mEventData->mInputStyle);
1263 // Queue the input style changed signal.
1264 mEventData->mInputStyleChangedQueue.PushBack(styleChangedMask);
1267 mModel->mLogicalModel->UpdateTextStyleRuns(startOfSelectedText, -static_cast<int>(lengthOfSelectedText));
1269 // Mark the paragraphs to be updated.
1270 if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1272 mTextUpdateInfo.mCharacterIndex = 0;
1273 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1274 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1275 mTextUpdateInfo.mClearAll = true;
1279 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1280 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1283 // Delete text between handles
1284 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1285 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1286 utf32Characters.Erase(first, last);
1288 // Will show the cursor at the first character of the selection.
1289 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1293 // Will show the cursor at the last character of the selection.
1294 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1297 mEventData->mDecoratorUpdated = true;
1301 void Controller::Impl::SetSelection(int start, int end)
1303 mEventData->mLeftSelectionPosition = start;
1304 mEventData->mRightSelectionPosition = end;
1305 mEventData->mUpdateCursorPosition = true;
1308 std::pair<int, int> Controller::Impl::GetSelectionIndexes() const
1310 return {mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition};
1313 void Controller::Impl::ShowClipboard()
1317 mClipboard.ShowClipboard();
1321 void Controller::Impl::HideClipboard()
1323 if(mClipboard && mClipboardHideEnabled)
1325 mClipboard.HideClipboard();
1329 void Controller::Impl::SetClipboardHideEnable(bool enable)
1331 mClipboardHideEnabled = enable;
1334 bool Controller::Impl::CopyStringToClipboard(const std::string& source)
1336 //Send string to clipboard
1337 return (mClipboard && mClipboard.SetItem(source));
1340 void Controller::Impl::SendSelectionToClipboard(bool deleteAfterSending)
1342 std::string selectedText;
1343 RetrieveSelection(selectedText, deleteAfterSending);
1344 CopyStringToClipboard(selectedText);
1345 ChangeState(EventData::EDITING);
1348 void Controller::Impl::RequestGetTextFromClipboard()
1352 mClipboard.RequestItem();
1356 void Controller::Impl::RepositionSelectionHandles()
1358 SelectionHandleController::Reposition(*this);
1360 void Controller::Impl::RepositionSelectionHandles(float visualX, float visualY, Controller::NoTextTap::Action action)
1362 SelectionHandleController::Reposition(*this, visualX, visualY, action);
1365 void Controller::Impl::SetPopupButtons()
1368 * Sets the Popup buttons to be shown depending on State.
1370 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1372 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1375 bool isEditable = IsEditable();
1376 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1378 if(EventData::SELECTING == mEventData->mState)
1380 buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::COPY);
1383 buttonsToShow = TextSelectionPopup::Buttons(buttonsToShow | TextSelectionPopup::CUT);
1386 if(!IsClipboardEmpty())
1390 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1392 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1395 if(!mEventData->mAllTextSelected)
1397 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::SELECT_ALL));
1400 else if(EventData::EDITING_WITH_POPUP == mEventData->mState)
1402 if(mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
1404 buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL);
1407 if(!IsClipboardEmpty())
1411 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1413 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1416 else if(EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState)
1418 if(!IsClipboardEmpty())
1422 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1424 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1428 mEventData->mDecorator->SetEnabledPopupButtons(buttonsToShow);
1431 void Controller::Impl::ChangeState(EventData::State newState)
1433 if(nullptr == mEventData)
1435 // Nothing to do if there is no text input.
1439 DALI_LOG_INFO(gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState);
1441 if(mEventData->mState != newState)
1443 mEventData->mPreviousState = mEventData->mState;
1444 mEventData->mState = newState;
1446 switch(mEventData->mState)
1448 case EventData::INACTIVE:
1450 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1451 mEventData->mDecorator->StopCursorBlink();
1452 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1453 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1454 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1455 mEventData->mDecorator->SetHighlightActive(false);
1456 mEventData->mDecorator->SetPopupActive(false);
1457 mEventData->mDecoratorUpdated = true;
1460 case EventData::INTERRUPTED:
1462 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1463 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1464 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1465 mEventData->mDecorator->SetHighlightActive(false);
1466 mEventData->mDecorator->SetPopupActive(false);
1467 mEventData->mDecoratorUpdated = true;
1470 case EventData::SELECTING:
1472 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1473 mEventData->mDecorator->StopCursorBlink();
1474 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1475 if(mEventData->mGrabHandleEnabled)
1477 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
1478 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
1480 mEventData->mDecorator->SetHighlightActive(true);
1481 if(mEventData->mGrabHandlePopupEnabled)
1484 mEventData->mDecorator->SetPopupActive(true);
1486 mEventData->mDecoratorUpdated = true;
1489 case EventData::EDITING:
1491 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1492 if(mEventData->mCursorBlinkEnabled)
1494 mEventData->mDecorator->StartCursorBlink();
1496 // Grab handle is not shown until a tap is received whilst EDITING
1497 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1498 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1499 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1500 mEventData->mDecorator->SetHighlightActive(false);
1501 if(mEventData->mGrabHandlePopupEnabled)
1503 mEventData->mDecorator->SetPopupActive(false);
1505 mEventData->mDecoratorUpdated = true;
1508 case EventData::EDITING_WITH_POPUP:
1510 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState);
1512 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1513 if(mEventData->mCursorBlinkEnabled)
1515 mEventData->mDecorator->StartCursorBlink();
1517 if(mEventData->mSelectionEnabled)
1519 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1520 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1521 mEventData->mDecorator->SetHighlightActive(false);
1523 else if(mEventData->mGrabHandleEnabled)
1525 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1527 if(mEventData->mGrabHandlePopupEnabled)
1530 mEventData->mDecorator->SetPopupActive(true);
1532 mEventData->mDecoratorUpdated = true;
1535 case EventData::EDITING_WITH_GRAB_HANDLE:
1537 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState);
1539 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1540 if(mEventData->mCursorBlinkEnabled)
1542 mEventData->mDecorator->StartCursorBlink();
1544 // Grab handle is not shown until a tap is received whilst EDITING
1545 if(mEventData->mGrabHandleEnabled)
1547 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1549 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1550 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1551 mEventData->mDecorator->SetHighlightActive(false);
1552 if(mEventData->mGrabHandlePopupEnabled)
1554 mEventData->mDecorator->SetPopupActive(false);
1556 mEventData->mDecoratorUpdated = true;
1559 case EventData::SELECTION_HANDLE_PANNING:
1561 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1562 mEventData->mDecorator->StopCursorBlink();
1563 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1564 if(mEventData->mGrabHandleEnabled)
1566 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
1567 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
1569 mEventData->mDecorator->SetHighlightActive(true);
1570 if(mEventData->mGrabHandlePopupEnabled)
1572 mEventData->mDecorator->SetPopupActive(false);
1574 mEventData->mDecoratorUpdated = true;
1577 case EventData::GRAB_HANDLE_PANNING:
1579 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState);
1581 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1582 if(mEventData->mCursorBlinkEnabled)
1584 mEventData->mDecorator->StartCursorBlink();
1586 if(mEventData->mGrabHandleEnabled)
1588 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1590 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1591 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1592 mEventData->mDecorator->SetHighlightActive(false);
1593 if(mEventData->mGrabHandlePopupEnabled)
1595 mEventData->mDecorator->SetPopupActive(false);
1597 mEventData->mDecoratorUpdated = true;
1600 case EventData::EDITING_WITH_PASTE_POPUP:
1602 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState);
1604 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1605 if(mEventData->mCursorBlinkEnabled)
1607 mEventData->mDecorator->StartCursorBlink();
1610 if(mEventData->mGrabHandleEnabled)
1612 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1614 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1615 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1616 mEventData->mDecorator->SetHighlightActive(false);
1618 if(mEventData->mGrabHandlePopupEnabled)
1621 mEventData->mDecorator->SetPopupActive(true);
1623 mEventData->mDecoratorUpdated = true;
1626 case EventData::TEXT_PANNING:
1628 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1629 mEventData->mDecorator->StopCursorBlink();
1630 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1631 if(mEventData->mDecorator->IsHandleActive(LEFT_SELECTION_HANDLE) ||
1632 mEventData->mDecorator->IsHandleActive(RIGHT_SELECTION_HANDLE))
1634 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1635 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1636 mEventData->mDecorator->SetHighlightActive(true);
1639 if(mEventData->mGrabHandlePopupEnabled)
1641 mEventData->mDecorator->SetPopupActive(false);
1644 mEventData->mDecoratorUpdated = true;
1651 void Controller::Impl::GetCursorPosition(CharacterIndex logical,
1652 CursorInfo& cursorInfo)
1654 if(!IsShowingRealText())
1656 // Do not want to use the place-holder text to set the cursor position.
1658 // Use the line's height of the font's family set to set the cursor's size.
1659 // If there is no font's family set, use the default font.
1660 // Use the current alignment to place the cursor at the beginning, center or end of the box.
1662 cursorInfo.lineOffset = 0.f;
1663 cursorInfo.lineHeight = GetDefaultFontLineHeight();
1664 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1667 if(mModel->mMatchSystemLanguageDirection)
1669 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1672 switch(mModel->mHorizontalAlignment)
1674 case Text::HorizontalAlignment::BEGIN:
1678 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1682 cursorInfo.primaryPosition.x = 0.f;
1686 case Text::HorizontalAlignment::CENTER:
1688 cursorInfo.primaryPosition.x = floorf(0.5f * mModel->mVisualModel->mControlSize.width);
1691 case Text::HorizontalAlignment::END:
1695 cursorInfo.primaryPosition.x = 0.f;
1699 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1705 // Nothing else to do.
1709 const bool isMultiLine = (Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout());
1710 GetCursorPositionParameters parameters;
1711 parameters.visualModel = mModel->mVisualModel;
1712 parameters.logicalModel = mModel->mLogicalModel;
1713 parameters.metrics = mMetrics;
1714 parameters.logical = logical;
1715 parameters.isMultiline = isMultiLine;
1717 Text::GetCursorPosition(parameters,
1720 // Adds Outline offset.
1721 const float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1722 cursorInfo.primaryPosition.x += outlineWidth;
1723 cursorInfo.primaryPosition.y += outlineWidth;
1724 cursorInfo.secondaryPosition.x += outlineWidth;
1725 cursorInfo.secondaryPosition.y += outlineWidth;
1729 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
1731 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
1732 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
1734 if(0.f > cursorInfo.primaryPosition.x)
1736 cursorInfo.primaryPosition.x = 0.f;
1739 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>(mEventData->mDecorator->GetCursorWidth());
1740 if(cursorInfo.primaryPosition.x > edgeWidth)
1742 cursorInfo.primaryPosition.x = edgeWidth;
1747 CharacterIndex Controller::Impl::CalculateNewCursorIndex(CharacterIndex index) const
1749 if(nullptr == mEventData)
1751 // Nothing to do if there is no text input.
1755 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1757 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1758 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1760 GlyphIndex glyphIndex = *(charactersToGlyphBuffer + index);
1761 Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1763 if(numberOfCharacters > 1u)
1765 const Script script = mModel->mLogicalModel->GetScript(index);
1766 if(HasLigatureMustBreak(script))
1768 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
1769 numberOfCharacters = 1u;
1774 while(0u == numberOfCharacters)
1777 numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1781 if(index < mEventData->mPrimaryCursorPosition)
1783 cursorIndex -= numberOfCharacters;
1787 cursorIndex += numberOfCharacters;
1790 // Will update the cursor hook position.
1791 mEventData->mUpdateCursorHookPosition = true;
1796 void Controller::Impl::UpdateCursorPosition(const CursorInfo& cursorInfo)
1798 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this);
1799 if(nullptr == mEventData)
1801 // Nothing to do if there is no text input.
1802 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n");
1806 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
1808 mEventData->mDecorator->SetGlyphOffset(PRIMARY_CURSOR, cursorInfo.glyphOffset);
1810 // Sets the cursor position.
1811 mEventData->mDecorator->SetPosition(PRIMARY_CURSOR,
1814 cursorInfo.primaryCursorHeight,
1815 cursorInfo.lineHeight);
1816 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y);
1818 if(mEventData->mUpdateGrabHandlePosition)
1820 // Sets the grab handle position.
1821 mEventData->mDecorator->SetPosition(GRAB_HANDLE,
1823 cursorInfo.lineOffset + mModel->mScrollPosition.y,
1824 cursorInfo.lineHeight);
1827 if(cursorInfo.isSecondaryCursor)
1829 mEventData->mDecorator->SetPosition(SECONDARY_CURSOR,
1830 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
1831 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
1832 cursorInfo.secondaryCursorHeight,
1833 cursorInfo.lineHeight);
1834 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y);
1837 // Set which cursors are active according the state.
1838 if(EventData::IsEditingState(mEventData->mState) || (EventData::GRAB_HANDLE_PANNING == mEventData->mState))
1840 if(cursorInfo.isSecondaryCursor)
1842 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_BOTH);
1846 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1851 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1854 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n");
1857 void Controller::Impl::UpdateSelectionHandle(HandleType handleType,
1858 const CursorInfo& cursorInfo)
1860 SelectionHandleController::Update(*this, handleType, cursorInfo);
1863 void Controller::Impl::ClampHorizontalScroll(const Vector2& layoutSize)
1865 // Clamp between -space & -alignment offset.
1867 if(layoutSize.width > mModel->mVisualModel->mControlSize.width)
1869 const float space = (layoutSize.width - mModel->mVisualModel->mControlSize.width) + mModel->mAlignmentOffset;
1870 mModel->mScrollPosition.x = (mModel->mScrollPosition.x < -space) ? -space : mModel->mScrollPosition.x;
1871 mModel->mScrollPosition.x = (mModel->mScrollPosition.x > -mModel->mAlignmentOffset) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
1873 mEventData->mDecoratorUpdated = true;
1877 mModel->mScrollPosition.x = 0.f;
1881 void Controller::Impl::ClampVerticalScroll(const Vector2& layoutSize)
1883 if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1885 // Nothing to do if the text is single line.
1889 // Clamp between -space & 0.
1890 if(layoutSize.height > mModel->mVisualModel->mControlSize.height)
1892 const float space = (layoutSize.height - mModel->mVisualModel->mControlSize.height);
1893 mModel->mScrollPosition.y = (mModel->mScrollPosition.y < -space) ? -space : mModel->mScrollPosition.y;
1894 mModel->mScrollPosition.y = (mModel->mScrollPosition.y > 0.f) ? 0.f : mModel->mScrollPosition.y;
1896 mEventData->mDecoratorUpdated = true;
1900 mModel->mScrollPosition.y = 0.f;
1904 void Controller::Impl::ScrollToMakePositionVisible(const Vector2& position, float lineHeight)
1906 const float cursorWidth = mEventData->mDecorator ? static_cast<float>(mEventData->mDecorator->GetCursorWidth()) : 0.f;
1908 // position is in actor's coords.
1909 const float positionEndX = position.x + cursorWidth;
1910 const float positionEndY = position.y + lineHeight;
1912 // Transform the position to decorator coords.
1913 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
1914 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
1916 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
1917 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
1919 if(decoratorPositionBeginX < 0.f)
1921 mModel->mScrollPosition.x = -position.x;
1923 else if(decoratorPositionEndX > mModel->mVisualModel->mControlSize.width)
1925 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
1928 if(Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout())
1930 if(decoratorPositionBeginY < 0.f)
1932 mModel->mScrollPosition.y = -position.y;
1934 else if(decoratorPositionEndY > mModel->mVisualModel->mControlSize.height)
1936 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
1941 void Controller::Impl::ScrollTextToMatchCursor(const CursorInfo& cursorInfo)
1943 // Get the current cursor position in decorator coords.
1944 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition(PRIMARY_CURSOR);
1946 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter(mEventData->mPrimaryCursorPosition);
1948 // Calculate the offset to match the cursor position before the character was deleted.
1949 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
1951 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
1952 if(mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() - 1u)
1954 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset(PRIMARY_CURSOR);
1955 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
1958 ClampHorizontalScroll(mModel->mVisualModel->GetLayoutSize());
1959 ClampVerticalScroll(mModel->mVisualModel->GetLayoutSize());
1961 // Makes the new cursor position visible if needed.
1962 ScrollToMakePositionVisible(cursorInfo.primaryPosition, cursorInfo.lineHeight);
1965 void Controller::Impl::ScrollTextToMatchCursor()
1967 CursorInfo cursorInfo;
1968 GetCursorPosition(mEventData->mPrimaryCursorPosition, cursorInfo);
1969 ScrollTextToMatchCursor(cursorInfo);
1972 void Controller::Impl::RequestRelayout()
1974 if(nullptr != mControlInterface)
1976 mControlInterface->RequestTextRelayout();
1980 Actor Controller::Impl::CreateBackgroundActor()
1982 // NOTE: Currently we only support background color for one line left-to-right text,
1983 // so the following calculation is based on one line left-to-right text only!
1987 Length numberOfGlyphs = mView.GetNumberOfGlyphs();
1988 if(numberOfGlyphs > 0u)
1990 Vector<GlyphInfo> glyphs;
1991 glyphs.Resize(numberOfGlyphs);
1993 Vector<Vector2> positions;
1994 positions.Resize(numberOfGlyphs);
1996 // Get the line where the glyphs are laid-out.
1997 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
1998 float alignmentOffset = lineRun->alignmentOffset;
1999 numberOfGlyphs = mView.GetGlyphs(glyphs.Begin(),
2005 glyphs.Resize(numberOfGlyphs);
2006 positions.Resize(numberOfGlyphs);
2008 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
2009 const Vector2* const positionsBuffer = positions.Begin();
2011 BackgroundMesh mesh;
2012 mesh.mVertices.Reserve(4u * glyphs.Size());
2013 mesh.mIndices.Reserve(6u * glyphs.Size());
2015 const Vector2 textSize = mView.GetLayoutSize();
2017 const float offsetX = textSize.width * 0.5f;
2018 const float offsetY = textSize.height * 0.5f;
2020 const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
2021 const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
2022 const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
2025 uint32_t numberOfQuads = 0u;
2027 for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i)
2029 const GlyphInfo& glyph = *(glyphsBuffer + i);
2031 // Get the background color of the character.
2032 // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
2033 const ColorIndex backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + i);
2034 const Vector4& backgroundColor = (0u == backgroundColorIndex) ? defaultBackgroundColor : *(backgroundColorsBuffer + backgroundColorIndex - 1u);
2036 // Only create quads for glyphs with a background color
2037 if(backgroundColor != Color::TRANSPARENT)
2039 const Vector2 position = *(positionsBuffer + i);
2041 if(i == 0u && glyphSize == 1u) // Only one glyph in the whole text
2043 quad.x = position.x;
2045 quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
2046 quad.w = textSize.height;
2048 else if(i == 0u) // The first glyph in the whole text
2050 quad.x = position.x;
2052 quad.z = quad.x - glyph.xBearing + glyph.advance;
2053 quad.w = textSize.height;
2055 else if(i == glyphSize - 1u) // The last glyph in the whole text
2057 quad.x = position.x - glyph.xBearing;
2059 quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
2060 quad.w = textSize.height;
2062 else // The glyph in the middle of the text
2064 quad.x = position.x - glyph.xBearing;
2066 quad.z = quad.x + glyph.advance;
2067 quad.w = textSize.height;
2070 BackgroundVertex vertex;
2073 vertex.mPosition.x = quad.x - offsetX;
2074 vertex.mPosition.y = quad.y - offsetY;
2075 vertex.mColor = backgroundColor;
2076 mesh.mVertices.PushBack(vertex);
2079 vertex.mPosition.x = quad.z - offsetX;
2080 vertex.mPosition.y = quad.y - offsetY;
2081 vertex.mColor = backgroundColor;
2082 mesh.mVertices.PushBack(vertex);
2085 vertex.mPosition.x = quad.x - offsetX;
2086 vertex.mPosition.y = quad.w - offsetY;
2087 vertex.mColor = backgroundColor;
2088 mesh.mVertices.PushBack(vertex);
2091 vertex.mPosition.x = quad.z - offsetX;
2092 vertex.mPosition.y = quad.w - offsetY;
2093 vertex.mColor = backgroundColor;
2094 mesh.mVertices.PushBack(vertex);
2096 // Six indices in counter clockwise winding
2097 mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
2098 mesh.mIndices.PushBack(0u + 4 * numberOfQuads);
2099 mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
2100 mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
2101 mesh.mIndices.PushBack(3u + 4 * numberOfQuads);
2102 mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
2108 // Only create the background actor if there are glyphs with background color
2109 if(mesh.mVertices.Count() > 0u)
2111 Property::Map quadVertexFormat;
2112 quadVertexFormat["aPosition"] = Property::VECTOR2;
2113 quadVertexFormat["aColor"] = Property::VECTOR4;
2115 VertexBuffer quadVertices = VertexBuffer::New(quadVertexFormat);
2116 quadVertices.SetData(&mesh.mVertices[0], mesh.mVertices.Size());
2118 Geometry quadGeometry = Geometry::New();
2119 quadGeometry.AddVertexBuffer(quadVertices);
2120 quadGeometry.SetIndexBuffer(&mesh.mIndices[0], mesh.mIndices.Size());
2122 if(!mShaderBackground)
2124 mShaderBackground = Shader::New(SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_VERT, SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_FRAG);
2127 Dali::Renderer renderer = Dali::Renderer::New(quadGeometry, mShaderBackground);
2128 renderer.SetProperty(Dali::Renderer::Property::BLEND_MODE, BlendMode::ON);
2129 renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT);
2131 actor = Actor::New();
2132 actor.SetProperty(Dali::Actor::Property::NAME, "TextBackgroundColorActor");
2133 actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
2134 actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
2135 actor.SetProperty(Actor::Property::SIZE, textSize);
2136 actor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR);
2137 actor.AddRenderer(renderer);
2144 void Controller::Impl::CopyUnderlinedFromLogicalToVisualModels(bool shouldClearPreUnderlineRuns)
2146 //Underlined character runs for markup-processor
2147 const Vector<UnderlinedCharacterRun>& underlinedCharacterRuns = mModel->mLogicalModel->mUnderlinedCharacterRuns;
2148 const Vector<GlyphIndex>& charactersToGlyph = mModel->mVisualModel->mCharactersToGlyph;
2149 const Vector<Length>& glyphsPerCharacter = mModel->mVisualModel->mGlyphsPerCharacter;
2151 if(shouldClearPreUnderlineRuns)
2153 mModel->mVisualModel->mUnderlineRuns.Clear();
2156 for(Vector<UnderlinedCharacterRun>::ConstIterator it = underlinedCharacterRuns.Begin(), endIt = underlinedCharacterRuns.End(); it != endIt; ++it)
2158 CharacterIndex characterIndex = it->characterRun.characterIndex;
2159 Length numberOfCharacters = it->characterRun.numberOfCharacters;
2160 for(Length index=0u; index<numberOfCharacters; index++)
2162 GlyphRun underlineGlyphRun;
2163 underlineGlyphRun.glyphIndex = charactersToGlyph[characterIndex + index];
2164 underlineGlyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex + index];
2165 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineGlyphRun);
2172 } // namespace Toolkit