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 //Get the number of points per one unit of point-size
701 uint32_t numberOfPointsPerOneUnitOfPointSize = mFontClient.GetNumberOfPointsPerOneUnitOfPointSize();
703 if(IsShowingPlaceholderText() && mEventData && (nullptr != mEventData->mPlaceholderFont))
705 // If the placeholder font is set specifically, only placeholder font is changed.
706 defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
707 if(mEventData->mPlaceholderFont->sizeDefined)
709 defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * mFontSizeScale * numberOfPointsPerOneUnitOfPointSize;
712 else if(nullptr != mFontDefaults)
714 // Set the normal font and the placeholder font.
715 defaultFontDescription = mFontDefaults->mFontDescription;
719 defaultPointSize = mFontDefaults->mFitPointSize * numberOfPointsPerOneUnitOfPointSize;
723 defaultPointSize = mFontDefaults->mDefaultPointSize * mFontSizeScale * numberOfPointsPerOneUnitOfPointSize;
727 // Validates the fonts. If there is a character with no assigned font it sets a default one.
728 // After this call, fonts are validated.
729 multilanguageSupport.ValidateFonts(utf32Characters,
732 defaultFontDescription,
735 requestedNumberOfCharacters,
741 Vector<Character> mirroredUtf32Characters;
742 bool textMirrored = false;
743 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
744 if(NO_OPERATION != (BIDI_INFO & operations))
746 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
747 bidirectionalInfo.Reserve(numberOfParagraphs);
749 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
750 SetBidirectionalInfo(utf32Characters,
754 requestedNumberOfCharacters,
756 mModel->mMatchSystemLanguageDirection,
759 if(0u != bidirectionalInfo.Count())
761 // Only set the character directions if there is right to left characters.
762 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
763 GetCharactersDirection(bidirectionalInfo,
766 requestedNumberOfCharacters,
769 // This paragraph has right to left text. Some characters may need to be mirrored.
770 // TODO: consider if the mirrored string can be stored as well.
772 textMirrored = GetMirroredText(utf32Characters,
776 requestedNumberOfCharacters,
777 mirroredUtf32Characters);
781 // There is no right to left characters. Clear the directions vector.
782 mModel->mLogicalModel->mCharacterDirections.Clear();
787 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
788 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
789 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
790 Vector<GlyphIndex> newParagraphGlyphs;
791 newParagraphGlyphs.Reserve(numberOfParagraphs);
793 const Length currentNumberOfGlyphs = glyphs.Count();
794 if(NO_OPERATION != (SHAPE_TEXT & operations))
796 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
798 ShapeText(textToShape,
803 mTextUpdateInfo.mStartGlyphIndex,
804 requestedNumberOfCharacters,
806 glyphsToCharactersMap,
810 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
811 mModel->mVisualModel->CreateGlyphsPerCharacterTable(startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters);
812 mModel->mVisualModel->CreateCharacterToGlyphTable(startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters);
817 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
819 if(NO_OPERATION != (GET_GLYPH_METRICS & operations))
821 GlyphInfo* glyphsBuffer = glyphs.Begin();
822 mMetrics->GetGlyphMetrics(glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs);
824 // Update the width and advance of all new paragraph characters.
825 for(Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it)
827 const GlyphIndex index = *it;
828 GlyphInfo& glyph = *(glyphsBuffer + index);
830 glyph.xBearing = 0.f;
837 if((nullptr != mEventData) &&
838 mEventData->mPreEditFlag &&
839 (0u != mModel->mVisualModel->mCharactersToGlyph.Count()))
841 Dali::InputMethodContext::PreEditAttributeDataContainer attrs;
842 mEventData->mInputMethodContext.GetPreeditStyle(attrs);
843 Dali::InputMethodContext::PreeditStyle type = Dali::InputMethodContext::PreeditStyle::NONE;
845 // Check the type of preedit and run it.
846 for(Dali::InputMethodContext::PreEditAttributeDataContainer::Iterator it = attrs.Begin(), endIt = attrs.End(); it != endIt; it++)
848 Dali::InputMethodContext::PreeditAttributeData attrData = *it;
849 DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::UpdateModel PreeditStyle type : %d start %d end %d \n", attrData.preeditType, attrData.startIndex, attrData.endIndex);
850 type = attrData.preeditType;
852 // Check the number of commit characters for the start position.
853 unsigned int numberOfCommit = mEventData->mPrimaryCursorPosition - mEventData->mPreEditLength;
854 Length numberOfIndices = attrData.endIndex - attrData.startIndex;
858 case Dali::InputMethodContext::PreeditStyle::UNDERLINE:
860 // Add the underline for the pre-edit text.
861 GlyphRun underlineRun;
862 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
863 underlineRun.numberOfGlyphs = numberOfIndices;
864 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
866 //Mark-up processor case
867 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
869 CopyUnderlinedFromLogicalToVisualModels(false);
873 case Dali::InputMethodContext::PreeditStyle::REVERSE:
875 Vector4 textColor = mModel->mVisualModel->GetTextColor();
876 ColorRun backgroundColorRun;
877 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
878 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
879 backgroundColorRun.color = textColor;
880 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
882 Vector4 backgroundColor = mModel->mVisualModel->GetBackgroundColor();
883 Vector<ColorRun> colorRuns;
884 colorRuns.Resize(1u);
885 ColorRun& colorRun = *(colorRuns.Begin());
886 colorRun.color = backgroundColor;
887 colorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
888 colorRun.characterRun.numberOfCharacters = numberOfIndices;
890 mModel->mLogicalModel->mColorRuns.PushBack(colorRun);
893 case Dali::InputMethodContext::PreeditStyle::HIGHLIGHT:
895 ColorRun backgroundColorRun;
896 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
897 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
898 backgroundColorRun.color = LIGHT_BLUE;
899 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
902 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_1:
904 // CUSTOM_PLATFORM_STYLE_1 should be drawn with background and underline together.
905 ColorRun backgroundColorRun;
906 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
907 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
908 backgroundColorRun.color = BACKGROUND_SUB4;
909 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
911 GlyphRun underlineRun;
912 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
913 underlineRun.numberOfGlyphs = numberOfIndices;
914 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
916 //Mark-up processor case
917 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
919 CopyUnderlinedFromLogicalToVisualModels(false);
923 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_2:
925 // CUSTOM_PLATFORM_STYLE_2 should be drawn with background and underline together.
926 ColorRun backgroundColorRun;
927 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
928 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
929 backgroundColorRun.color = BACKGROUND_SUB5;
930 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
932 GlyphRun underlineRun;
933 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
934 underlineRun.numberOfGlyphs = numberOfIndices;
935 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
937 //Mark-up processor case
938 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
940 CopyUnderlinedFromLogicalToVisualModels(false);
944 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_3:
946 // CUSTOM_PLATFORM_STYLE_3 should be drawn with background and underline together.
947 ColorRun backgroundColorRun;
948 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
949 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
950 backgroundColorRun.color = BACKGROUND_SUB6;
951 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
953 GlyphRun underlineRun;
954 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
955 underlineRun.numberOfGlyphs = numberOfIndices;
956 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
958 //Mark-up processor case
959 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
961 CopyUnderlinedFromLogicalToVisualModels(false);
965 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_4:
967 // CUSTOM_PLATFORM_STYLE_4 should be drawn with background and underline together.
968 ColorRun backgroundColorRun;
969 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
970 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
971 backgroundColorRun.color = BACKGROUND_SUB7;
972 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
974 GlyphRun underlineRun;
975 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
976 underlineRun.numberOfGlyphs = numberOfIndices;
977 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
979 //Mark-up processor case
980 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
982 CopyUnderlinedFromLogicalToVisualModels(false);
986 case Dali::InputMethodContext::PreeditStyle::NONE:
997 if(NO_OPERATION != (COLOR & operations))
999 // Set the color runs in glyphs.
1000 SetColorSegmentationInfo(mModel->mLogicalModel->mColorRuns,
1001 mModel->mVisualModel->mCharactersToGlyph,
1002 mModel->mVisualModel->mGlyphsPerCharacter,
1004 mTextUpdateInfo.mStartGlyphIndex,
1005 requestedNumberOfCharacters,
1006 mModel->mVisualModel->mColors,
1007 mModel->mVisualModel->mColorIndices);
1009 // Set the background color runs in glyphs.
1010 SetColorSegmentationInfo(mModel->mLogicalModel->mBackgroundColorRuns,
1011 mModel->mVisualModel->mCharactersToGlyph,
1012 mModel->mVisualModel->mGlyphsPerCharacter,
1014 mTextUpdateInfo.mStartGlyphIndex,
1015 requestedNumberOfCharacters,
1016 mModel->mVisualModel->mBackgroundColors,
1017 mModel->mVisualModel->mBackgroundColorIndices);
1022 if((NO_OPERATION != (SHAPE_TEXT & operations)) &&
1023 ! ((nullptr != mEventData) &&
1024 mEventData->mPreEditFlag &&
1025 (0u != mModel->mVisualModel->mCharactersToGlyph.Count())))
1027 //Mark-up processor case
1028 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
1030 CopyUnderlinedFromLogicalToVisualModels(true);
1037 // The estimated number of lines. Used to avoid reallocations when layouting.
1038 mTextUpdateInfo.mEstimatedNumberOfLines = std::max(mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count());
1040 // Set the previous number of characters for the next time the text is updated.
1041 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1046 void Controller::Impl::RetrieveDefaultInputStyle(InputStyle& inputStyle)
1048 // Sets the default text's color.
1049 inputStyle.textColor = mTextColor;
1050 inputStyle.isDefaultColor = true;
1052 inputStyle.familyName.clear();
1053 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1054 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1055 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1056 inputStyle.size = 0.f;
1058 inputStyle.lineSpacing = 0.f;
1060 inputStyle.underlineProperties.clear();
1061 inputStyle.shadowProperties.clear();
1062 inputStyle.embossProperties.clear();
1063 inputStyle.outlineProperties.clear();
1065 inputStyle.isFamilyDefined = false;
1066 inputStyle.isWeightDefined = false;
1067 inputStyle.isWidthDefined = false;
1068 inputStyle.isSlantDefined = false;
1069 inputStyle.isSizeDefined = false;
1071 inputStyle.isLineSpacingDefined = false;
1073 inputStyle.isUnderlineDefined = false;
1074 inputStyle.isShadowDefined = false;
1075 inputStyle.isEmbossDefined = false;
1076 inputStyle.isOutlineDefined = false;
1078 // Sets the default font's family name, weight, width, slant and size.
1081 if(mFontDefaults->familyDefined)
1083 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1084 inputStyle.isFamilyDefined = true;
1087 if(mFontDefaults->weightDefined)
1089 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1090 inputStyle.isWeightDefined = true;
1093 if(mFontDefaults->widthDefined)
1095 inputStyle.width = mFontDefaults->mFontDescription.width;
1096 inputStyle.isWidthDefined = true;
1099 if(mFontDefaults->slantDefined)
1101 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1102 inputStyle.isSlantDefined = true;
1105 if(mFontDefaults->sizeDefined)
1107 inputStyle.size = mFontDefaults->mDefaultPointSize;
1108 inputStyle.isSizeDefined = true;
1113 float Controller::Impl::GetDefaultFontLineHeight()
1115 FontId defaultFontId = 0u;
1116 if(nullptr == mFontDefaults)
1118 TextAbstraction::FontDescription fontDescription;
1119 defaultFontId = mFontClient.GetFontId(fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale);
1123 defaultFontId = mFontDefaults->GetFontId(mFontClient, mFontDefaults->mDefaultPointSize * mFontSizeScale);
1126 Text::FontMetrics fontMetrics;
1127 mMetrics->GetFontMetrics(defaultFontId, fontMetrics);
1129 return (fontMetrics.ascender - fontMetrics.descender);
1132 void Controller::Impl::SetTextSelectionRange(const uint32_t* pStart, const uint32_t* pEnd)
1134 if(nullptr == mEventData)
1136 // Nothing to do if there is no text.
1140 if(mEventData->mSelectionEnabled && (pStart || pEnd))
1142 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
1146 mEventData->mLeftSelectionPosition = std::min(*pStart, length);
1150 mEventData->mRightSelectionPosition = std::min(*pEnd, length);
1153 if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
1155 ChangeState(EventData::EDITING);
1156 mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition;
1157 mEventData->mUpdateCursorPosition = true;
1161 ChangeState(EventData::SELECTING);
1162 mEventData->mUpdateHighlightBox = true;
1163 mEventData->mUpdateLeftSelectionPosition = true;
1164 mEventData->mUpdateRightSelectionPosition = true;
1169 CharacterIndex Controller::Impl::GetPrimaryCursorPosition() const
1171 if(nullptr == mEventData)
1175 return mEventData->mPrimaryCursorPosition;
1178 bool Controller::Impl::SetPrimaryCursorPosition(CharacterIndex index)
1180 if(nullptr == mEventData)
1182 // Nothing to do if there is no text.
1186 if(mEventData->mPrimaryCursorPosition == index)
1188 // Nothing for same cursor position.
1192 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
1193 mEventData->mPrimaryCursorPosition = std::min(index, length);
1194 ChangeState(EventData::EDITING);
1195 mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1196 mEventData->mUpdateCursorPosition = true;
1197 ScrollTextToMatchCursor();
1201 Uint32Pair Controller::Impl::GetTextSelectionRange() const
1207 range.first = mEventData->mLeftSelectionPosition;
1208 range.second = mEventData->mRightSelectionPosition;
1214 bool Controller::Impl::IsEditable() const
1216 return mEventData && mEventData->mEditingEnabled;
1219 void Controller::Impl::SetEditable(bool editable)
1223 mEventData->mEditingEnabled = editable;
1227 void Controller::Impl::RetrieveSelection(std::string& selectedText, bool deleteAfterRetrieval)
1229 if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
1231 // Nothing to select if handles are in the same place.
1232 selectedText.clear();
1236 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1238 //Get start and end position of selection
1239 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1240 const Length lengthOfSelectedText = (handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition) - startOfSelectedText;
1242 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1243 const Length numberOfCharacters = utf32Characters.Count();
1245 // Validate the start and end selection points
1246 if((startOfSelectedText + lengthOfSelectedText) <= numberOfCharacters)
1248 //Get text as a UTF8 string
1249 Utf32ToUtf8(&utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText);
1251 if(deleteAfterRetrieval) // Only delete text if copied successfully
1253 // Keep a copy of the current input style.
1254 InputStyle currentInputStyle;
1255 currentInputStyle.Copy(mEventData->mInputStyle);
1257 // Set as input style the style of the first deleted character.
1258 mModel->mLogicalModel->RetrieveStyle(startOfSelectedText, mEventData->mInputStyle);
1260 // Compare if the input style has changed.
1261 const bool hasInputStyleChanged = !currentInputStyle.Equal(mEventData->mInputStyle);
1263 if(hasInputStyleChanged)
1265 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask(mEventData->mInputStyle);
1266 // Queue the input style changed signal.
1267 mEventData->mInputStyleChangedQueue.PushBack(styleChangedMask);
1270 mModel->mLogicalModel->UpdateTextStyleRuns(startOfSelectedText, -static_cast<int>(lengthOfSelectedText));
1272 // Mark the paragraphs to be updated.
1273 if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1275 mTextUpdateInfo.mCharacterIndex = 0;
1276 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1277 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1278 mTextUpdateInfo.mClearAll = true;
1282 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1283 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1286 // Delete text between handles
1287 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1288 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1289 utf32Characters.Erase(first, last);
1291 // Will show the cursor at the first character of the selection.
1292 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1296 // Will show the cursor at the last character of the selection.
1297 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1300 mEventData->mDecoratorUpdated = true;
1304 void Controller::Impl::SetSelection(int start, int end)
1306 mEventData->mLeftSelectionPosition = start;
1307 mEventData->mRightSelectionPosition = end;
1308 mEventData->mUpdateCursorPosition = true;
1311 std::pair<int, int> Controller::Impl::GetSelectionIndexes() const
1313 return {mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition};
1316 void Controller::Impl::ShowClipboard()
1320 mClipboard.ShowClipboard();
1324 void Controller::Impl::HideClipboard()
1326 if(mClipboard && mClipboardHideEnabled)
1328 mClipboard.HideClipboard();
1332 void Controller::Impl::SetClipboardHideEnable(bool enable)
1334 mClipboardHideEnabled = enable;
1337 bool Controller::Impl::CopyStringToClipboard(const std::string& source)
1339 //Send string to clipboard
1340 return (mClipboard && mClipboard.SetItem(source));
1343 void Controller::Impl::SendSelectionToClipboard(bool deleteAfterSending)
1345 std::string selectedText;
1346 RetrieveSelection(selectedText, deleteAfterSending);
1347 CopyStringToClipboard(selectedText);
1348 ChangeState(EventData::EDITING);
1351 void Controller::Impl::RequestGetTextFromClipboard()
1355 mClipboard.RequestItem();
1359 void Controller::Impl::RepositionSelectionHandles()
1361 SelectionHandleController::Reposition(*this);
1363 void Controller::Impl::RepositionSelectionHandles(float visualX, float visualY, Controller::NoTextTap::Action action)
1365 SelectionHandleController::Reposition(*this, visualX, visualY, action);
1368 void Controller::Impl::SetPopupButtons()
1371 * Sets the Popup buttons to be shown depending on State.
1373 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1375 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1378 bool isEditable = IsEditable();
1379 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1381 if(EventData::SELECTING == mEventData->mState)
1383 buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::COPY);
1386 buttonsToShow = TextSelectionPopup::Buttons(buttonsToShow | TextSelectionPopup::CUT);
1389 if(!IsClipboardEmpty())
1393 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1395 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1398 if(!mEventData->mAllTextSelected)
1400 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::SELECT_ALL));
1403 else if(EventData::EDITING_WITH_POPUP == mEventData->mState)
1405 if(mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
1407 buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL);
1410 if(!IsClipboardEmpty())
1414 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1416 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1419 else if(EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState)
1421 if(!IsClipboardEmpty())
1425 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1427 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1431 mEventData->mDecorator->SetEnabledPopupButtons(buttonsToShow);
1434 void Controller::Impl::ChangeState(EventData::State newState)
1436 if(nullptr == mEventData)
1438 // Nothing to do if there is no text input.
1442 DALI_LOG_INFO(gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState);
1444 if(mEventData->mState != newState)
1446 mEventData->mPreviousState = mEventData->mState;
1447 mEventData->mState = newState;
1449 switch(mEventData->mState)
1451 case EventData::INACTIVE:
1453 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1454 mEventData->mDecorator->StopCursorBlink();
1455 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1456 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1457 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1458 mEventData->mDecorator->SetHighlightActive(false);
1459 mEventData->mDecorator->SetPopupActive(false);
1460 mEventData->mDecoratorUpdated = true;
1463 case EventData::INTERRUPTED:
1465 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1466 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1467 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1468 mEventData->mDecorator->SetHighlightActive(false);
1469 mEventData->mDecorator->SetPopupActive(false);
1470 mEventData->mDecoratorUpdated = true;
1473 case EventData::SELECTING:
1475 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1476 mEventData->mDecorator->StopCursorBlink();
1477 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1478 if(mEventData->mGrabHandleEnabled)
1480 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
1481 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
1483 mEventData->mDecorator->SetHighlightActive(true);
1484 if(mEventData->mGrabHandlePopupEnabled)
1487 mEventData->mDecorator->SetPopupActive(true);
1489 mEventData->mDecoratorUpdated = true;
1492 case EventData::EDITING:
1494 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1495 if(mEventData->mCursorBlinkEnabled)
1497 mEventData->mDecorator->StartCursorBlink();
1499 // Grab handle is not shown until a tap is received whilst EDITING
1500 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1501 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1502 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1503 mEventData->mDecorator->SetHighlightActive(false);
1504 if(mEventData->mGrabHandlePopupEnabled)
1506 mEventData->mDecorator->SetPopupActive(false);
1508 mEventData->mDecoratorUpdated = true;
1511 case EventData::EDITING_WITH_POPUP:
1513 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState);
1515 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1516 if(mEventData->mCursorBlinkEnabled)
1518 mEventData->mDecorator->StartCursorBlink();
1520 if(mEventData->mSelectionEnabled)
1522 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1523 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1524 mEventData->mDecorator->SetHighlightActive(false);
1526 else if(mEventData->mGrabHandleEnabled)
1528 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1530 if(mEventData->mGrabHandlePopupEnabled)
1533 mEventData->mDecorator->SetPopupActive(true);
1535 mEventData->mDecoratorUpdated = true;
1538 case EventData::EDITING_WITH_GRAB_HANDLE:
1540 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState);
1542 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1543 if(mEventData->mCursorBlinkEnabled)
1545 mEventData->mDecorator->StartCursorBlink();
1547 // Grab handle is not shown until a tap is received whilst EDITING
1548 if(mEventData->mGrabHandleEnabled)
1550 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1552 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1553 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1554 mEventData->mDecorator->SetHighlightActive(false);
1555 if(mEventData->mGrabHandlePopupEnabled)
1557 mEventData->mDecorator->SetPopupActive(false);
1559 mEventData->mDecoratorUpdated = true;
1562 case EventData::SELECTION_HANDLE_PANNING:
1564 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1565 mEventData->mDecorator->StopCursorBlink();
1566 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1567 if(mEventData->mGrabHandleEnabled)
1569 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
1570 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
1572 mEventData->mDecorator->SetHighlightActive(true);
1573 if(mEventData->mGrabHandlePopupEnabled)
1575 mEventData->mDecorator->SetPopupActive(false);
1577 mEventData->mDecoratorUpdated = true;
1580 case EventData::GRAB_HANDLE_PANNING:
1582 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState);
1584 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1585 if(mEventData->mCursorBlinkEnabled)
1587 mEventData->mDecorator->StartCursorBlink();
1589 if(mEventData->mGrabHandleEnabled)
1591 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1593 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1594 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1595 mEventData->mDecorator->SetHighlightActive(false);
1596 if(mEventData->mGrabHandlePopupEnabled)
1598 mEventData->mDecorator->SetPopupActive(false);
1600 mEventData->mDecoratorUpdated = true;
1603 case EventData::EDITING_WITH_PASTE_POPUP:
1605 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState);
1607 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1608 if(mEventData->mCursorBlinkEnabled)
1610 mEventData->mDecorator->StartCursorBlink();
1613 if(mEventData->mGrabHandleEnabled)
1615 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1617 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1618 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1619 mEventData->mDecorator->SetHighlightActive(false);
1621 if(mEventData->mGrabHandlePopupEnabled)
1624 mEventData->mDecorator->SetPopupActive(true);
1626 mEventData->mDecoratorUpdated = true;
1629 case EventData::TEXT_PANNING:
1631 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1632 mEventData->mDecorator->StopCursorBlink();
1633 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1634 if(mEventData->mDecorator->IsHandleActive(LEFT_SELECTION_HANDLE) ||
1635 mEventData->mDecorator->IsHandleActive(RIGHT_SELECTION_HANDLE))
1637 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1638 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1639 mEventData->mDecorator->SetHighlightActive(true);
1642 if(mEventData->mGrabHandlePopupEnabled)
1644 mEventData->mDecorator->SetPopupActive(false);
1647 mEventData->mDecoratorUpdated = true;
1654 void Controller::Impl::GetCursorPosition(CharacterIndex logical,
1655 CursorInfo& cursorInfo)
1657 if(!IsShowingRealText())
1659 // Do not want to use the place-holder text to set the cursor position.
1661 // Use the line's height of the font's family set to set the cursor's size.
1662 // If there is no font's family set, use the default font.
1663 // Use the current alignment to place the cursor at the beginning, center or end of the box.
1665 cursorInfo.lineOffset = 0.f;
1666 cursorInfo.lineHeight = GetDefaultFontLineHeight();
1667 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1670 if(mModel->mMatchSystemLanguageDirection)
1672 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1675 switch(mModel->mHorizontalAlignment)
1677 case Text::HorizontalAlignment::BEGIN:
1681 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1685 cursorInfo.primaryPosition.x = 0.f;
1689 case Text::HorizontalAlignment::CENTER:
1691 cursorInfo.primaryPosition.x = floorf(0.5f * mModel->mVisualModel->mControlSize.width);
1694 case Text::HorizontalAlignment::END:
1698 cursorInfo.primaryPosition.x = 0.f;
1702 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1708 // Nothing else to do.
1712 const bool isMultiLine = (Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout());
1713 GetCursorPositionParameters parameters;
1714 parameters.visualModel = mModel->mVisualModel;
1715 parameters.logicalModel = mModel->mLogicalModel;
1716 parameters.metrics = mMetrics;
1717 parameters.logical = logical;
1718 parameters.isMultiline = isMultiLine;
1720 Text::GetCursorPosition(parameters,
1723 // Adds Outline offset.
1724 const float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1725 cursorInfo.primaryPosition.x += outlineWidth;
1726 cursorInfo.primaryPosition.y += outlineWidth;
1727 cursorInfo.secondaryPosition.x += outlineWidth;
1728 cursorInfo.secondaryPosition.y += outlineWidth;
1732 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
1734 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
1735 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
1737 if(0.f > cursorInfo.primaryPosition.x)
1739 cursorInfo.primaryPosition.x = 0.f;
1742 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>(mEventData->mDecorator->GetCursorWidth());
1743 if(cursorInfo.primaryPosition.x > edgeWidth)
1745 cursorInfo.primaryPosition.x = edgeWidth;
1750 CharacterIndex Controller::Impl::CalculateNewCursorIndex(CharacterIndex index) const
1752 if(nullptr == mEventData)
1754 // Nothing to do if there is no text input.
1758 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1760 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1761 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1763 GlyphIndex glyphIndex = *(charactersToGlyphBuffer + index);
1764 Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1766 if(numberOfCharacters > 1u)
1768 const Script script = mModel->mLogicalModel->GetScript(index);
1769 if(HasLigatureMustBreak(script))
1771 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
1772 numberOfCharacters = 1u;
1777 while(0u == numberOfCharacters)
1780 numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1784 if(index < mEventData->mPrimaryCursorPosition)
1786 cursorIndex -= numberOfCharacters;
1790 cursorIndex += numberOfCharacters;
1793 // Will update the cursor hook position.
1794 mEventData->mUpdateCursorHookPosition = true;
1799 void Controller::Impl::UpdateCursorPosition(const CursorInfo& cursorInfo)
1801 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this);
1802 if(nullptr == mEventData)
1804 // Nothing to do if there is no text input.
1805 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n");
1809 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
1811 mEventData->mDecorator->SetGlyphOffset(PRIMARY_CURSOR, cursorInfo.glyphOffset);
1813 // Sets the cursor position.
1814 mEventData->mDecorator->SetPosition(PRIMARY_CURSOR,
1817 cursorInfo.primaryCursorHeight,
1818 cursorInfo.lineHeight);
1819 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y);
1821 if(mEventData->mUpdateGrabHandlePosition)
1823 // Sets the grab handle position.
1824 mEventData->mDecorator->SetPosition(GRAB_HANDLE,
1826 cursorInfo.lineOffset + mModel->mScrollPosition.y,
1827 cursorInfo.lineHeight);
1830 if(cursorInfo.isSecondaryCursor)
1832 mEventData->mDecorator->SetPosition(SECONDARY_CURSOR,
1833 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
1834 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
1835 cursorInfo.secondaryCursorHeight,
1836 cursorInfo.lineHeight);
1837 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y);
1840 // Set which cursors are active according the state.
1841 if(EventData::IsEditingState(mEventData->mState) || (EventData::GRAB_HANDLE_PANNING == mEventData->mState))
1843 if(cursorInfo.isSecondaryCursor)
1845 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_BOTH);
1849 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1854 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1857 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n");
1860 void Controller::Impl::UpdateSelectionHandle(HandleType handleType,
1861 const CursorInfo& cursorInfo)
1863 SelectionHandleController::Update(*this, handleType, cursorInfo);
1866 void Controller::Impl::ClampHorizontalScroll(const Vector2& layoutSize)
1868 // Clamp between -space & -alignment offset.
1870 if(layoutSize.width > mModel->mVisualModel->mControlSize.width)
1872 const float space = (layoutSize.width - mModel->mVisualModel->mControlSize.width) + mModel->mAlignmentOffset;
1873 mModel->mScrollPosition.x = (mModel->mScrollPosition.x < -space) ? -space : mModel->mScrollPosition.x;
1874 mModel->mScrollPosition.x = (mModel->mScrollPosition.x > -mModel->mAlignmentOffset) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
1876 mEventData->mDecoratorUpdated = true;
1880 mModel->mScrollPosition.x = 0.f;
1884 void Controller::Impl::ClampVerticalScroll(const Vector2& layoutSize)
1886 if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1888 // Nothing to do if the text is single line.
1892 // Clamp between -space & 0.
1893 if(layoutSize.height > mModel->mVisualModel->mControlSize.height)
1895 const float space = (layoutSize.height - mModel->mVisualModel->mControlSize.height);
1896 mModel->mScrollPosition.y = (mModel->mScrollPosition.y < -space) ? -space : mModel->mScrollPosition.y;
1897 mModel->mScrollPosition.y = (mModel->mScrollPosition.y > 0.f) ? 0.f : mModel->mScrollPosition.y;
1899 mEventData->mDecoratorUpdated = true;
1903 mModel->mScrollPosition.y = 0.f;
1907 void Controller::Impl::ScrollToMakePositionVisible(const Vector2& position, float lineHeight)
1909 const float cursorWidth = mEventData->mDecorator ? static_cast<float>(mEventData->mDecorator->GetCursorWidth()) : 0.f;
1911 // position is in actor's coords.
1912 const float positionEndX = position.x + cursorWidth;
1913 const float positionEndY = position.y + lineHeight;
1915 // Transform the position to decorator coords.
1916 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
1917 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
1919 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
1920 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
1922 if(decoratorPositionBeginX < 0.f)
1924 mModel->mScrollPosition.x = -position.x;
1926 else if(decoratorPositionEndX > mModel->mVisualModel->mControlSize.width)
1928 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
1931 if(Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout())
1933 if(decoratorPositionBeginY < 0.f)
1935 mModel->mScrollPosition.y = -position.y;
1937 else if(decoratorPositionEndY > mModel->mVisualModel->mControlSize.height)
1939 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
1944 void Controller::Impl::ScrollTextToMatchCursor(const CursorInfo& cursorInfo)
1946 // Get the current cursor position in decorator coords.
1947 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition(PRIMARY_CURSOR);
1949 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter(mEventData->mPrimaryCursorPosition);
1951 // Calculate the offset to match the cursor position before the character was deleted.
1952 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
1954 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
1955 if(mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() - 1u)
1957 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset(PRIMARY_CURSOR);
1958 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
1961 ClampHorizontalScroll(mModel->mVisualModel->GetLayoutSize());
1962 ClampVerticalScroll(mModel->mVisualModel->GetLayoutSize());
1964 // Makes the new cursor position visible if needed.
1965 ScrollToMakePositionVisible(cursorInfo.primaryPosition, cursorInfo.lineHeight);
1968 void Controller::Impl::ScrollTextToMatchCursor()
1970 CursorInfo cursorInfo;
1971 GetCursorPosition(mEventData->mPrimaryCursorPosition, cursorInfo);
1972 ScrollTextToMatchCursor(cursorInfo);
1975 void Controller::Impl::RequestRelayout()
1977 if(nullptr != mControlInterface)
1979 mControlInterface->RequestTextRelayout();
1983 Actor Controller::Impl::CreateBackgroundActor()
1985 // NOTE: Currently we only support background color for one line left-to-right text,
1986 // so the following calculation is based on one line left-to-right text only!
1990 Length numberOfGlyphs = mView.GetNumberOfGlyphs();
1991 if(numberOfGlyphs > 0u)
1993 Vector<GlyphInfo> glyphs;
1994 glyphs.Resize(numberOfGlyphs);
1996 Vector<Vector2> positions;
1997 positions.Resize(numberOfGlyphs);
1999 // Get the line where the glyphs are laid-out.
2000 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
2001 float alignmentOffset = lineRun->alignmentOffset;
2002 numberOfGlyphs = mView.GetGlyphs(glyphs.Begin(),
2008 glyphs.Resize(numberOfGlyphs);
2009 positions.Resize(numberOfGlyphs);
2011 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
2012 const Vector2* const positionsBuffer = positions.Begin();
2014 BackgroundMesh mesh;
2015 mesh.mVertices.Reserve(4u * glyphs.Size());
2016 mesh.mIndices.Reserve(6u * glyphs.Size());
2018 const Vector2 textSize = mView.GetLayoutSize();
2020 const float offsetX = textSize.width * 0.5f;
2021 const float offsetY = textSize.height * 0.5f;
2023 const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
2024 const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
2025 const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
2028 uint32_t numberOfQuads = 0u;
2030 for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i)
2032 const GlyphInfo& glyph = *(glyphsBuffer + i);
2034 // Get the background color of the character.
2035 // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
2036 const ColorIndex backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + i);
2037 const Vector4& backgroundColor = (0u == backgroundColorIndex) ? defaultBackgroundColor : *(backgroundColorsBuffer + backgroundColorIndex - 1u);
2039 // Only create quads for glyphs with a background color
2040 if(backgroundColor != Color::TRANSPARENT)
2042 const Vector2 position = *(positionsBuffer + i);
2044 if(i == 0u && glyphSize == 1u) // Only one glyph in the whole text
2046 quad.x = position.x;
2048 quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
2049 quad.w = textSize.height;
2051 else if(i == 0u) // The first glyph in the whole text
2053 quad.x = position.x;
2055 quad.z = quad.x - glyph.xBearing + glyph.advance;
2056 quad.w = textSize.height;
2058 else if(i == glyphSize - 1u) // The last glyph in the whole text
2060 quad.x = position.x - glyph.xBearing;
2062 quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
2063 quad.w = textSize.height;
2065 else // The glyph in the middle of the text
2067 quad.x = position.x - glyph.xBearing;
2069 quad.z = quad.x + glyph.advance;
2070 quad.w = textSize.height;
2073 BackgroundVertex vertex;
2076 vertex.mPosition.x = quad.x - offsetX;
2077 vertex.mPosition.y = quad.y - offsetY;
2078 vertex.mColor = backgroundColor;
2079 mesh.mVertices.PushBack(vertex);
2082 vertex.mPosition.x = quad.z - offsetX;
2083 vertex.mPosition.y = quad.y - offsetY;
2084 vertex.mColor = backgroundColor;
2085 mesh.mVertices.PushBack(vertex);
2088 vertex.mPosition.x = quad.x - offsetX;
2089 vertex.mPosition.y = quad.w - offsetY;
2090 vertex.mColor = backgroundColor;
2091 mesh.mVertices.PushBack(vertex);
2094 vertex.mPosition.x = quad.z - offsetX;
2095 vertex.mPosition.y = quad.w - offsetY;
2096 vertex.mColor = backgroundColor;
2097 mesh.mVertices.PushBack(vertex);
2099 // Six indices in counter clockwise winding
2100 mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
2101 mesh.mIndices.PushBack(0u + 4 * numberOfQuads);
2102 mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
2103 mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
2104 mesh.mIndices.PushBack(3u + 4 * numberOfQuads);
2105 mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
2111 // Only create the background actor if there are glyphs with background color
2112 if(mesh.mVertices.Count() > 0u)
2114 Property::Map quadVertexFormat;
2115 quadVertexFormat["aPosition"] = Property::VECTOR2;
2116 quadVertexFormat["aColor"] = Property::VECTOR4;
2118 VertexBuffer quadVertices = VertexBuffer::New(quadVertexFormat);
2119 quadVertices.SetData(&mesh.mVertices[0], mesh.mVertices.Size());
2121 Geometry quadGeometry = Geometry::New();
2122 quadGeometry.AddVertexBuffer(quadVertices);
2123 quadGeometry.SetIndexBuffer(&mesh.mIndices[0], mesh.mIndices.Size());
2125 if(!mShaderBackground)
2127 mShaderBackground = Shader::New(SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_VERT, SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_FRAG);
2130 Dali::Renderer renderer = Dali::Renderer::New(quadGeometry, mShaderBackground);
2131 renderer.SetProperty(Dali::Renderer::Property::BLEND_MODE, BlendMode::ON);
2132 renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT);
2134 actor = Actor::New();
2135 actor.SetProperty(Dali::Actor::Property::NAME, "TextBackgroundColorActor");
2136 actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
2137 actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
2138 actor.SetProperty(Actor::Property::SIZE, textSize);
2139 actor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR);
2140 actor.AddRenderer(renderer);
2147 void Controller::Impl::CopyUnderlinedFromLogicalToVisualModels(bool shouldClearPreUnderlineRuns)
2149 //Underlined character runs for markup-processor
2150 const Vector<UnderlinedCharacterRun>& underlinedCharacterRuns = mModel->mLogicalModel->mUnderlinedCharacterRuns;
2151 const Vector<GlyphIndex>& charactersToGlyph = mModel->mVisualModel->mCharactersToGlyph;
2152 const Vector<Length>& glyphsPerCharacter = mModel->mVisualModel->mGlyphsPerCharacter;
2154 if(shouldClearPreUnderlineRuns)
2156 mModel->mVisualModel->mUnderlineRuns.Clear();
2159 for(Vector<UnderlinedCharacterRun>::ConstIterator it = underlinedCharacterRuns.Begin(), endIt = underlinedCharacterRuns.End(); it != endIt; ++it)
2161 CharacterIndex characterIndex = it->characterRun.characterIndex;
2162 Length numberOfCharacters = it->characterRun.numberOfCharacters;
2163 for(Length index=0u; index<numberOfCharacters; index++)
2165 GlyphRun underlineGlyphRun;
2166 underlineGlyphRun.glyphIndex = charactersToGlyph[characterIndex + index];
2167 underlineGlyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex + index];
2168 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineGlyphRun);
2175 } // namespace Toolkit