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 != NULL && !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 && (NULL != 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(NULL != 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);
813 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
815 if(NO_OPERATION != (GET_GLYPH_METRICS & operations))
817 GlyphInfo* glyphsBuffer = glyphs.Begin();
818 mMetrics->GetGlyphMetrics(glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs);
820 // Update the width and advance of all new paragraph characters.
821 for(Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it)
823 const GlyphIndex index = *it;
824 GlyphInfo& glyph = *(glyphsBuffer + index);
826 glyph.xBearing = 0.f;
833 if((NULL != mEventData) &&
834 mEventData->mPreEditFlag &&
835 (0u != mModel->mVisualModel->mCharactersToGlyph.Count()))
837 Dali::InputMethodContext::PreEditAttributeDataContainer attrs;
838 mEventData->mInputMethodContext.GetPreeditStyle(attrs);
839 Dali::InputMethodContext::PreeditStyle type = Dali::InputMethodContext::PreeditStyle::NONE;
841 // Check the type of preedit and run it.
842 for(Dali::InputMethodContext::PreEditAttributeDataContainer::Iterator it = attrs.Begin(), endIt = attrs.End(); it != endIt; it++)
844 Dali::InputMethodContext::PreeditAttributeData attrData = *it;
845 DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::UpdateModel PreeditStyle type : %d start %d end %d \n", attrData.preeditType, attrData.startIndex, attrData.endIndex);
846 type = attrData.preeditType;
848 // Check the number of commit characters for the start position.
849 unsigned int numberOfCommit = mEventData->mPrimaryCursorPosition - mEventData->mPreEditLength;
850 Length numberOfIndices = attrData.endIndex - attrData.startIndex;
854 case Dali::InputMethodContext::PreeditStyle::UNDERLINE:
856 // Add the underline for the pre-edit text.
857 GlyphRun underlineRun;
858 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
859 underlineRun.numberOfGlyphs = numberOfIndices;
860 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
863 case Dali::InputMethodContext::PreeditStyle::REVERSE:
865 Vector4 textColor = mModel->mVisualModel->GetTextColor();
866 ColorRun backgroundColorRun;
867 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
868 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
869 backgroundColorRun.color = textColor;
870 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
872 Vector4 backgroundColor = mModel->mVisualModel->GetBackgroundColor();
873 Vector<ColorRun> colorRuns;
874 colorRuns.Resize(1u);
875 ColorRun& colorRun = *(colorRuns.Begin());
876 colorRun.color = backgroundColor;
877 colorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
878 colorRun.characterRun.numberOfCharacters = numberOfIndices;
880 mModel->mLogicalModel->mColorRuns.PushBack(colorRun);
883 case Dali::InputMethodContext::PreeditStyle::HIGHLIGHT:
885 ColorRun backgroundColorRun;
886 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
887 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
888 backgroundColorRun.color = LIGHT_BLUE;
889 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
892 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_1:
894 // CUSTOM_PLATFORM_STYLE_1 should be drawn with background and underline together.
895 ColorRun backgroundColorRun;
896 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
897 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
898 backgroundColorRun.color = BACKGROUND_SUB4;
899 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
901 GlyphRun underlineRun;
902 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
903 underlineRun.numberOfGlyphs = numberOfIndices;
904 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
907 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_2:
909 // CUSTOM_PLATFORM_STYLE_2 should be drawn with background and underline together.
910 ColorRun backgroundColorRun;
911 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
912 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
913 backgroundColorRun.color = BACKGROUND_SUB5;
914 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
916 GlyphRun underlineRun;
917 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
918 underlineRun.numberOfGlyphs = numberOfIndices;
919 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
922 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_3:
924 // CUSTOM_PLATFORM_STYLE_3 should be drawn with background and underline together.
925 ColorRun backgroundColorRun;
926 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
927 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
928 backgroundColorRun.color = BACKGROUND_SUB6;
929 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
931 GlyphRun underlineRun;
932 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
933 underlineRun.numberOfGlyphs = numberOfIndices;
934 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
937 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_4:
939 // CUSTOM_PLATFORM_STYLE_4 should be drawn with background and underline together.
940 ColorRun backgroundColorRun;
941 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
942 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
943 backgroundColorRun.color = BACKGROUND_SUB7;
944 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
946 GlyphRun underlineRun;
947 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
948 underlineRun.numberOfGlyphs = numberOfIndices;
949 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
952 case Dali::InputMethodContext::PreeditStyle::NONE:
963 if(NO_OPERATION != (COLOR & operations))
965 // Set the color runs in glyphs.
966 SetColorSegmentationInfo(mModel->mLogicalModel->mColorRuns,
967 mModel->mVisualModel->mCharactersToGlyph,
968 mModel->mVisualModel->mGlyphsPerCharacter,
970 mTextUpdateInfo.mStartGlyphIndex,
971 requestedNumberOfCharacters,
972 mModel->mVisualModel->mColors,
973 mModel->mVisualModel->mColorIndices);
975 // Set the background color runs in glyphs.
976 SetColorSegmentationInfo(mModel->mLogicalModel->mBackgroundColorRuns,
977 mModel->mVisualModel->mCharactersToGlyph,
978 mModel->mVisualModel->mGlyphsPerCharacter,
980 mTextUpdateInfo.mStartGlyphIndex,
981 requestedNumberOfCharacters,
982 mModel->mVisualModel->mBackgroundColors,
983 mModel->mVisualModel->mBackgroundColorIndices);
988 // The estimated number of lines. Used to avoid reallocations when layouting.
989 mTextUpdateInfo.mEstimatedNumberOfLines = std::max(mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count());
991 // Set the previous number of characters for the next time the text is updated.
992 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
997 void Controller::Impl::RetrieveDefaultInputStyle(InputStyle& inputStyle)
999 // Sets the default text's color.
1000 inputStyle.textColor = mTextColor;
1001 inputStyle.isDefaultColor = true;
1003 inputStyle.familyName.clear();
1004 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1005 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1006 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1007 inputStyle.size = 0.f;
1009 inputStyle.lineSpacing = 0.f;
1011 inputStyle.underlineProperties.clear();
1012 inputStyle.shadowProperties.clear();
1013 inputStyle.embossProperties.clear();
1014 inputStyle.outlineProperties.clear();
1016 inputStyle.isFamilyDefined = false;
1017 inputStyle.isWeightDefined = false;
1018 inputStyle.isWidthDefined = false;
1019 inputStyle.isSlantDefined = false;
1020 inputStyle.isSizeDefined = false;
1022 inputStyle.isLineSpacingDefined = false;
1024 inputStyle.isUnderlineDefined = false;
1025 inputStyle.isShadowDefined = false;
1026 inputStyle.isEmbossDefined = false;
1027 inputStyle.isOutlineDefined = false;
1029 // Sets the default font's family name, weight, width, slant and size.
1032 if(mFontDefaults->familyDefined)
1034 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1035 inputStyle.isFamilyDefined = true;
1038 if(mFontDefaults->weightDefined)
1040 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1041 inputStyle.isWeightDefined = true;
1044 if(mFontDefaults->widthDefined)
1046 inputStyle.width = mFontDefaults->mFontDescription.width;
1047 inputStyle.isWidthDefined = true;
1050 if(mFontDefaults->slantDefined)
1052 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1053 inputStyle.isSlantDefined = true;
1056 if(mFontDefaults->sizeDefined)
1058 inputStyle.size = mFontDefaults->mDefaultPointSize;
1059 inputStyle.isSizeDefined = true;
1064 float Controller::Impl::GetDefaultFontLineHeight()
1066 FontId defaultFontId = 0u;
1067 if(NULL == mFontDefaults)
1069 TextAbstraction::FontDescription fontDescription;
1070 defaultFontId = mFontClient.GetFontId(fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale);
1074 defaultFontId = mFontDefaults->GetFontId(mFontClient, mFontDefaults->mDefaultPointSize * mFontSizeScale);
1077 Text::FontMetrics fontMetrics;
1078 mMetrics->GetFontMetrics(defaultFontId, fontMetrics);
1080 return (fontMetrics.ascender - fontMetrics.descender);
1083 void Controller::Impl::SetTextSelectionRange(const uint32_t* pStart, const uint32_t* pEnd)
1085 if(nullptr == mEventData)
1087 // Nothing to do if there is no text.
1091 if(mEventData->mSelectionEnabled && (pStart || pEnd))
1093 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
1097 mEventData->mLeftSelectionPosition = std::min(*pStart, length);
1101 mEventData->mRightSelectionPosition = std::min(*pEnd, length);
1104 if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
1106 ChangeState(EventData::EDITING);
1107 mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition;
1108 mEventData->mUpdateCursorPosition = true;
1112 ChangeState(EventData::SELECTING);
1113 mEventData->mUpdateHighlightBox = true;
1114 mEventData->mUpdateLeftSelectionPosition = true;
1115 mEventData->mUpdateRightSelectionPosition = true;
1120 CharacterIndex Controller::Impl::GetPrimaryCursorPosition() const
1122 if(nullptr == mEventData)
1126 return mEventData->mPrimaryCursorPosition;
1129 bool Controller::Impl::SetPrimaryCursorPosition(CharacterIndex index)
1131 if(nullptr == mEventData)
1133 // Nothing to do if there is no text.
1137 if(mEventData->mPrimaryCursorPosition == index)
1139 // Nothing for same cursor position.
1143 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
1144 mEventData->mPrimaryCursorPosition = std::min(index, length);
1145 ChangeState(EventData::EDITING);
1146 mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1147 mEventData->mUpdateCursorPosition = true;
1148 ScrollTextToMatchCursor();
1152 Uint32Pair Controller::Impl::GetTextSelectionRange() const
1158 range.first = mEventData->mLeftSelectionPosition;
1159 range.second = mEventData->mRightSelectionPosition;
1165 bool Controller::Impl::IsEditable() const
1167 return mEventData && mEventData->mEditingEnabled;
1170 void Controller::Impl::SetEditable(bool editable)
1174 mEventData->mEditingEnabled = editable;
1178 void Controller::Impl::RetrieveSelection(std::string& selectedText, bool deleteAfterRetrieval)
1180 if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
1182 // Nothing to select if handles are in the same place.
1183 selectedText.clear();
1187 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1189 //Get start and end position of selection
1190 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1191 const Length lengthOfSelectedText = (handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition) - startOfSelectedText;
1193 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1194 const Length numberOfCharacters = utf32Characters.Count();
1196 // Validate the start and end selection points
1197 if((startOfSelectedText + lengthOfSelectedText) <= numberOfCharacters)
1199 //Get text as a UTF8 string
1200 Utf32ToUtf8(&utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText);
1202 if(deleteAfterRetrieval) // Only delete text if copied successfully
1204 // Keep a copy of the current input style.
1205 InputStyle currentInputStyle;
1206 currentInputStyle.Copy(mEventData->mInputStyle);
1208 // Set as input style the style of the first deleted character.
1209 mModel->mLogicalModel->RetrieveStyle(startOfSelectedText, mEventData->mInputStyle);
1211 // Compare if the input style has changed.
1212 const bool hasInputStyleChanged = !currentInputStyle.Equal(mEventData->mInputStyle);
1214 if(hasInputStyleChanged)
1216 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask(mEventData->mInputStyle);
1217 // Queue the input style changed signal.
1218 mEventData->mInputStyleChangedQueue.PushBack(styleChangedMask);
1221 mModel->mLogicalModel->UpdateTextStyleRuns(startOfSelectedText, -static_cast<int>(lengthOfSelectedText));
1223 // Mark the paragraphs to be updated.
1224 if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1226 mTextUpdateInfo.mCharacterIndex = 0;
1227 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1228 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1229 mTextUpdateInfo.mClearAll = true;
1233 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1234 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1237 // Delete text between handles
1238 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1239 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1240 utf32Characters.Erase(first, last);
1242 // Will show the cursor at the first character of the selection.
1243 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1247 // Will show the cursor at the last character of the selection.
1248 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1251 mEventData->mDecoratorUpdated = true;
1255 void Controller::Impl::SetSelection(int start, int end)
1257 mEventData->mLeftSelectionPosition = start;
1258 mEventData->mRightSelectionPosition = end;
1259 mEventData->mUpdateCursorPosition = true;
1262 std::pair<int, int> Controller::Impl::GetSelectionIndexes() const
1264 return {mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition};
1267 void Controller::Impl::ShowClipboard()
1271 mClipboard.ShowClipboard();
1275 void Controller::Impl::HideClipboard()
1277 if(mClipboard && mClipboardHideEnabled)
1279 mClipboard.HideClipboard();
1283 void Controller::Impl::SetClipboardHideEnable(bool enable)
1285 mClipboardHideEnabled = enable;
1288 bool Controller::Impl::CopyStringToClipboard(const std::string& source)
1290 //Send string to clipboard
1291 return (mClipboard && mClipboard.SetItem(source));
1294 void Controller::Impl::SendSelectionToClipboard(bool deleteAfterSending)
1296 std::string selectedText;
1297 RetrieveSelection(selectedText, deleteAfterSending);
1298 CopyStringToClipboard(selectedText);
1299 ChangeState(EventData::EDITING);
1302 void Controller::Impl::RequestGetTextFromClipboard()
1306 mClipboard.RequestItem();
1310 void Controller::Impl::RepositionSelectionHandles()
1312 SelectionHandleController::Reposition(*this);
1314 void Controller::Impl::RepositionSelectionHandles(float visualX, float visualY, Controller::NoTextTap::Action action)
1316 SelectionHandleController::Reposition(*this, visualX, visualY, action);
1319 void Controller::Impl::SetPopupButtons()
1322 * Sets the Popup buttons to be shown depending on State.
1324 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1326 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1329 bool isEditable = IsEditable();
1330 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1332 if(EventData::SELECTING == mEventData->mState)
1334 buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::COPY);
1337 buttonsToShow = TextSelectionPopup::Buttons(buttonsToShow | TextSelectionPopup::CUT);
1340 if(!IsClipboardEmpty())
1344 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1346 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1349 if(!mEventData->mAllTextSelected)
1351 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::SELECT_ALL));
1354 else if(EventData::EDITING_WITH_POPUP == mEventData->mState)
1356 if(mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
1358 buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL);
1361 if(!IsClipboardEmpty())
1365 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1367 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1370 else if(EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState)
1372 if(!IsClipboardEmpty())
1376 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1378 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1382 mEventData->mDecorator->SetEnabledPopupButtons(buttonsToShow);
1385 void Controller::Impl::ChangeState(EventData::State newState)
1387 if(NULL == mEventData)
1389 // Nothing to do if there is no text input.
1393 DALI_LOG_INFO(gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState);
1395 if(mEventData->mState != newState)
1397 mEventData->mPreviousState = mEventData->mState;
1398 mEventData->mState = newState;
1400 switch(mEventData->mState)
1402 case EventData::INACTIVE:
1404 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1405 mEventData->mDecorator->StopCursorBlink();
1406 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1407 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1408 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1409 mEventData->mDecorator->SetHighlightActive(false);
1410 mEventData->mDecorator->SetPopupActive(false);
1411 mEventData->mDecoratorUpdated = true;
1414 case EventData::INTERRUPTED:
1416 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1417 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1418 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1419 mEventData->mDecorator->SetHighlightActive(false);
1420 mEventData->mDecorator->SetPopupActive(false);
1421 mEventData->mDecoratorUpdated = true;
1424 case EventData::SELECTING:
1426 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1427 mEventData->mDecorator->StopCursorBlink();
1428 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1429 if(mEventData->mGrabHandleEnabled)
1431 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
1432 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
1434 mEventData->mDecorator->SetHighlightActive(true);
1435 if(mEventData->mGrabHandlePopupEnabled)
1438 mEventData->mDecorator->SetPopupActive(true);
1440 mEventData->mDecoratorUpdated = true;
1443 case EventData::EDITING:
1445 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1446 if(mEventData->mCursorBlinkEnabled)
1448 mEventData->mDecorator->StartCursorBlink();
1450 // Grab handle is not shown until a tap is received whilst EDITING
1451 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1452 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1453 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1454 mEventData->mDecorator->SetHighlightActive(false);
1455 if(mEventData->mGrabHandlePopupEnabled)
1457 mEventData->mDecorator->SetPopupActive(false);
1459 mEventData->mDecoratorUpdated = true;
1462 case EventData::EDITING_WITH_POPUP:
1464 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState);
1466 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1467 if(mEventData->mCursorBlinkEnabled)
1469 mEventData->mDecorator->StartCursorBlink();
1471 if(mEventData->mSelectionEnabled)
1473 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1474 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1475 mEventData->mDecorator->SetHighlightActive(false);
1477 else if(mEventData->mGrabHandleEnabled)
1479 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1481 if(mEventData->mGrabHandlePopupEnabled)
1484 mEventData->mDecorator->SetPopupActive(true);
1486 mEventData->mDecoratorUpdated = true;
1489 case EventData::EDITING_WITH_GRAB_HANDLE:
1491 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState);
1493 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1494 if(mEventData->mCursorBlinkEnabled)
1496 mEventData->mDecorator->StartCursorBlink();
1498 // Grab handle is not shown until a tap is received whilst EDITING
1499 if(mEventData->mGrabHandleEnabled)
1501 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1503 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1504 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1505 mEventData->mDecorator->SetHighlightActive(false);
1506 if(mEventData->mGrabHandlePopupEnabled)
1508 mEventData->mDecorator->SetPopupActive(false);
1510 mEventData->mDecoratorUpdated = true;
1513 case EventData::SELECTION_HANDLE_PANNING:
1515 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1516 mEventData->mDecorator->StopCursorBlink();
1517 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1518 if(mEventData->mGrabHandleEnabled)
1520 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
1521 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
1523 mEventData->mDecorator->SetHighlightActive(true);
1524 if(mEventData->mGrabHandlePopupEnabled)
1526 mEventData->mDecorator->SetPopupActive(false);
1528 mEventData->mDecoratorUpdated = true;
1531 case EventData::GRAB_HANDLE_PANNING:
1533 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState);
1535 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1536 if(mEventData->mCursorBlinkEnabled)
1538 mEventData->mDecorator->StartCursorBlink();
1540 if(mEventData->mGrabHandleEnabled)
1542 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1544 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1545 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1546 mEventData->mDecorator->SetHighlightActive(false);
1547 if(mEventData->mGrabHandlePopupEnabled)
1549 mEventData->mDecorator->SetPopupActive(false);
1551 mEventData->mDecoratorUpdated = true;
1554 case EventData::EDITING_WITH_PASTE_POPUP:
1556 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState);
1558 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1559 if(mEventData->mCursorBlinkEnabled)
1561 mEventData->mDecorator->StartCursorBlink();
1564 if(mEventData->mGrabHandleEnabled)
1566 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1568 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1569 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1570 mEventData->mDecorator->SetHighlightActive(false);
1572 if(mEventData->mGrabHandlePopupEnabled)
1575 mEventData->mDecorator->SetPopupActive(true);
1577 mEventData->mDecoratorUpdated = true;
1580 case EventData::TEXT_PANNING:
1582 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1583 mEventData->mDecorator->StopCursorBlink();
1584 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1585 if(mEventData->mDecorator->IsHandleActive(LEFT_SELECTION_HANDLE) ||
1586 mEventData->mDecorator->IsHandleActive(RIGHT_SELECTION_HANDLE))
1588 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1589 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1590 mEventData->mDecorator->SetHighlightActive(true);
1593 if(mEventData->mGrabHandlePopupEnabled)
1595 mEventData->mDecorator->SetPopupActive(false);
1598 mEventData->mDecoratorUpdated = true;
1605 void Controller::Impl::GetCursorPosition(CharacterIndex logical,
1606 CursorInfo& cursorInfo)
1608 if(!IsShowingRealText())
1610 // Do not want to use the place-holder text to set the cursor position.
1612 // Use the line's height of the font's family set to set the cursor's size.
1613 // If there is no font's family set, use the default font.
1614 // Use the current alignment to place the cursor at the beginning, center or end of the box.
1616 cursorInfo.lineOffset = 0.f;
1617 cursorInfo.lineHeight = GetDefaultFontLineHeight();
1618 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1621 if(mModel->mMatchSystemLanguageDirection)
1623 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1626 switch(mModel->mHorizontalAlignment)
1628 case Text::HorizontalAlignment::BEGIN:
1632 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1636 cursorInfo.primaryPosition.x = 0.f;
1640 case Text::HorizontalAlignment::CENTER:
1642 cursorInfo.primaryPosition.x = floorf(0.5f * mModel->mVisualModel->mControlSize.width);
1645 case Text::HorizontalAlignment::END:
1649 cursorInfo.primaryPosition.x = 0.f;
1653 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1659 // Nothing else to do.
1663 const bool isMultiLine = (Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout());
1664 GetCursorPositionParameters parameters;
1665 parameters.visualModel = mModel->mVisualModel;
1666 parameters.logicalModel = mModel->mLogicalModel;
1667 parameters.metrics = mMetrics;
1668 parameters.logical = logical;
1669 parameters.isMultiline = isMultiLine;
1671 Text::GetCursorPosition(parameters,
1674 // Adds Outline offset.
1675 const float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1676 cursorInfo.primaryPosition.x += outlineWidth;
1677 cursorInfo.primaryPosition.y += outlineWidth;
1678 cursorInfo.secondaryPosition.x += outlineWidth;
1679 cursorInfo.secondaryPosition.y += outlineWidth;
1683 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
1685 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
1686 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
1688 if(0.f > cursorInfo.primaryPosition.x)
1690 cursorInfo.primaryPosition.x = 0.f;
1693 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>(mEventData->mDecorator->GetCursorWidth());
1694 if(cursorInfo.primaryPosition.x > edgeWidth)
1696 cursorInfo.primaryPosition.x = edgeWidth;
1701 CharacterIndex Controller::Impl::CalculateNewCursorIndex(CharacterIndex index) const
1703 if(NULL == mEventData)
1705 // Nothing to do if there is no text input.
1709 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1711 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1712 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1714 GlyphIndex glyphIndex = *(charactersToGlyphBuffer + index);
1715 Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1717 if(numberOfCharacters > 1u)
1719 const Script script = mModel->mLogicalModel->GetScript(index);
1720 if(HasLigatureMustBreak(script))
1722 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
1723 numberOfCharacters = 1u;
1728 while(0u == numberOfCharacters)
1731 numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1735 if(index < mEventData->mPrimaryCursorPosition)
1737 cursorIndex -= numberOfCharacters;
1741 cursorIndex += numberOfCharacters;
1744 // Will update the cursor hook position.
1745 mEventData->mUpdateCursorHookPosition = true;
1750 void Controller::Impl::UpdateCursorPosition(const CursorInfo& cursorInfo)
1752 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this);
1753 if(NULL == mEventData)
1755 // Nothing to do if there is no text input.
1756 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n");
1760 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
1762 mEventData->mDecorator->SetGlyphOffset(PRIMARY_CURSOR, cursorInfo.glyphOffset);
1764 // Sets the cursor position.
1765 mEventData->mDecorator->SetPosition(PRIMARY_CURSOR,
1768 cursorInfo.primaryCursorHeight,
1769 cursorInfo.lineHeight);
1770 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y);
1772 if(mEventData->mUpdateGrabHandlePosition)
1774 // Sets the grab handle position.
1775 mEventData->mDecorator->SetPosition(GRAB_HANDLE,
1777 cursorInfo.lineOffset + mModel->mScrollPosition.y,
1778 cursorInfo.lineHeight);
1781 if(cursorInfo.isSecondaryCursor)
1783 mEventData->mDecorator->SetPosition(SECONDARY_CURSOR,
1784 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
1785 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
1786 cursorInfo.secondaryCursorHeight,
1787 cursorInfo.lineHeight);
1788 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y);
1791 // Set which cursors are active according the state.
1792 if(EventData::IsEditingState(mEventData->mState) || (EventData::GRAB_HANDLE_PANNING == mEventData->mState))
1794 if(cursorInfo.isSecondaryCursor)
1796 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_BOTH);
1800 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1805 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1808 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n");
1811 void Controller::Impl::UpdateSelectionHandle(HandleType handleType,
1812 const CursorInfo& cursorInfo)
1814 SelectionHandleController::Update(*this, handleType, cursorInfo);
1817 void Controller::Impl::ClampHorizontalScroll(const Vector2& layoutSize)
1819 // Clamp between -space & -alignment offset.
1821 if(layoutSize.width > mModel->mVisualModel->mControlSize.width)
1823 const float space = (layoutSize.width - mModel->mVisualModel->mControlSize.width) + mModel->mAlignmentOffset;
1824 mModel->mScrollPosition.x = (mModel->mScrollPosition.x < -space) ? -space : mModel->mScrollPosition.x;
1825 mModel->mScrollPosition.x = (mModel->mScrollPosition.x > -mModel->mAlignmentOffset) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
1827 mEventData->mDecoratorUpdated = true;
1831 mModel->mScrollPosition.x = 0.f;
1835 void Controller::Impl::ClampVerticalScroll(const Vector2& layoutSize)
1837 if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1839 // Nothing to do if the text is single line.
1843 // Clamp between -space & 0.
1844 if(layoutSize.height > mModel->mVisualModel->mControlSize.height)
1846 const float space = (layoutSize.height - mModel->mVisualModel->mControlSize.height);
1847 mModel->mScrollPosition.y = (mModel->mScrollPosition.y < -space) ? -space : mModel->mScrollPosition.y;
1848 mModel->mScrollPosition.y = (mModel->mScrollPosition.y > 0.f) ? 0.f : mModel->mScrollPosition.y;
1850 mEventData->mDecoratorUpdated = true;
1854 mModel->mScrollPosition.y = 0.f;
1858 void Controller::Impl::ScrollToMakePositionVisible(const Vector2& position, float lineHeight)
1860 const float cursorWidth = mEventData->mDecorator ? static_cast<float>(mEventData->mDecorator->GetCursorWidth()) : 0.f;
1862 // position is in actor's coords.
1863 const float positionEndX = position.x + cursorWidth;
1864 const float positionEndY = position.y + lineHeight;
1866 // Transform the position to decorator coords.
1867 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
1868 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
1870 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
1871 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
1873 if(decoratorPositionBeginX < 0.f)
1875 mModel->mScrollPosition.x = -position.x;
1877 else if(decoratorPositionEndX > mModel->mVisualModel->mControlSize.width)
1879 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
1882 if(Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout())
1884 if(decoratorPositionBeginY < 0.f)
1886 mModel->mScrollPosition.y = -position.y;
1888 else if(decoratorPositionEndY > mModel->mVisualModel->mControlSize.height)
1890 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
1895 void Controller::Impl::ScrollTextToMatchCursor(const CursorInfo& cursorInfo)
1897 // Get the current cursor position in decorator coords.
1898 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition(PRIMARY_CURSOR);
1900 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter(mEventData->mPrimaryCursorPosition);
1902 // Calculate the offset to match the cursor position before the character was deleted.
1903 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
1905 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
1906 if(mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() - 1u)
1908 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset(PRIMARY_CURSOR);
1909 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
1912 ClampHorizontalScroll(mModel->mVisualModel->GetLayoutSize());
1913 ClampVerticalScroll(mModel->mVisualModel->GetLayoutSize());
1915 // Makes the new cursor position visible if needed.
1916 ScrollToMakePositionVisible(cursorInfo.primaryPosition, cursorInfo.lineHeight);
1919 void Controller::Impl::ScrollTextToMatchCursor()
1921 CursorInfo cursorInfo;
1922 GetCursorPosition(mEventData->mPrimaryCursorPosition, cursorInfo);
1923 ScrollTextToMatchCursor(cursorInfo);
1926 void Controller::Impl::RequestRelayout()
1928 if(NULL != mControlInterface)
1930 mControlInterface->RequestTextRelayout();
1934 Actor Controller::Impl::CreateBackgroundActor()
1936 // NOTE: Currently we only support background color for one line left-to-right text,
1937 // so the following calculation is based on one line left-to-right text only!
1941 Length numberOfGlyphs = mView.GetNumberOfGlyphs();
1942 if(numberOfGlyphs > 0u)
1944 Vector<GlyphInfo> glyphs;
1945 glyphs.Resize(numberOfGlyphs);
1947 Vector<Vector2> positions;
1948 positions.Resize(numberOfGlyphs);
1950 // Get the line where the glyphs are laid-out.
1951 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
1952 float alignmentOffset = lineRun->alignmentOffset;
1953 numberOfGlyphs = mView.GetGlyphs(glyphs.Begin(),
1959 glyphs.Resize(numberOfGlyphs);
1960 positions.Resize(numberOfGlyphs);
1962 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
1963 const Vector2* const positionsBuffer = positions.Begin();
1965 BackgroundMesh mesh;
1966 mesh.mVertices.Reserve(4u * glyphs.Size());
1967 mesh.mIndices.Reserve(6u * glyphs.Size());
1969 const Vector2 textSize = mView.GetLayoutSize();
1971 const float offsetX = textSize.width * 0.5f;
1972 const float offsetY = textSize.height * 0.5f;
1974 const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
1975 const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
1976 const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
1979 uint32_t numberOfQuads = 0u;
1981 for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i)
1983 const GlyphInfo& glyph = *(glyphsBuffer + i);
1985 // Get the background color of the character.
1986 // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
1987 const ColorIndex backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + i);
1988 const Vector4& backgroundColor = (0u == backgroundColorIndex) ? defaultBackgroundColor : *(backgroundColorsBuffer + backgroundColorIndex - 1u);
1990 // Only create quads for glyphs with a background color
1991 if(backgroundColor != Color::TRANSPARENT)
1993 const Vector2 position = *(positionsBuffer + i);
1995 if(i == 0u && glyphSize == 1u) // Only one glyph in the whole text
1997 quad.x = position.x;
1999 quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
2000 quad.w = textSize.height;
2002 else if(i == 0u) // The first glyph in the whole text
2004 quad.x = position.x;
2006 quad.z = quad.x - glyph.xBearing + glyph.advance;
2007 quad.w = textSize.height;
2009 else if(i == glyphSize - 1u) // The last glyph in the whole text
2011 quad.x = position.x - glyph.xBearing;
2013 quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
2014 quad.w = textSize.height;
2016 else // The glyph in the middle of the text
2018 quad.x = position.x - glyph.xBearing;
2020 quad.z = quad.x + glyph.advance;
2021 quad.w = textSize.height;
2024 BackgroundVertex vertex;
2027 vertex.mPosition.x = quad.x - offsetX;
2028 vertex.mPosition.y = quad.y - offsetY;
2029 vertex.mColor = backgroundColor;
2030 mesh.mVertices.PushBack(vertex);
2033 vertex.mPosition.x = quad.z - offsetX;
2034 vertex.mPosition.y = quad.y - offsetY;
2035 vertex.mColor = backgroundColor;
2036 mesh.mVertices.PushBack(vertex);
2039 vertex.mPosition.x = quad.x - offsetX;
2040 vertex.mPosition.y = quad.w - offsetY;
2041 vertex.mColor = backgroundColor;
2042 mesh.mVertices.PushBack(vertex);
2045 vertex.mPosition.x = quad.z - offsetX;
2046 vertex.mPosition.y = quad.w - offsetY;
2047 vertex.mColor = backgroundColor;
2048 mesh.mVertices.PushBack(vertex);
2050 // Six indices in counter clockwise winding
2051 mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
2052 mesh.mIndices.PushBack(0u + 4 * numberOfQuads);
2053 mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
2054 mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
2055 mesh.mIndices.PushBack(3u + 4 * numberOfQuads);
2056 mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
2062 // Only create the background actor if there are glyphs with background color
2063 if(mesh.mVertices.Count() > 0u)
2065 Property::Map quadVertexFormat;
2066 quadVertexFormat["aPosition"] = Property::VECTOR2;
2067 quadVertexFormat["aColor"] = Property::VECTOR4;
2069 VertexBuffer quadVertices = VertexBuffer::New(quadVertexFormat);
2070 quadVertices.SetData(&mesh.mVertices[0], mesh.mVertices.Size());
2072 Geometry quadGeometry = Geometry::New();
2073 quadGeometry.AddVertexBuffer(quadVertices);
2074 quadGeometry.SetIndexBuffer(&mesh.mIndices[0], mesh.mIndices.Size());
2076 if(!mShaderBackground)
2078 mShaderBackground = Shader::New(SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_VERT, SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_FRAG);
2081 Dali::Renderer renderer = Dali::Renderer::New(quadGeometry, mShaderBackground);
2082 renderer.SetProperty(Dali::Renderer::Property::BLEND_MODE, BlendMode::ON);
2083 renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT);
2085 actor = Actor::New();
2086 actor.SetProperty(Dali::Actor::Property::NAME, "TextBackgroundColorActor");
2087 actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
2088 actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
2089 actor.SetProperty(Actor::Property::SIZE, textSize);
2090 actor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR);
2091 actor.AddRenderer(renderer);
2100 } // namespace Toolkit