2 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/text-controller-impl.h>
22 #include <dali/integration-api/debug.h>
23 #include <dali/public-api/rendering/renderer.h>
26 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
27 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
28 #include <dali-toolkit/internal/text/bidirectional-support.h>
29 #include <dali-toolkit/internal/text/character-set-conversion.h>
30 #include <dali-toolkit/internal/text/color-segmentation.h>
31 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
32 #include <dali-toolkit/internal/text/hyphenator.h>
33 #include <dali-toolkit/internal/text/multi-language-support.h>
34 #include <dali-toolkit/internal/text/segmentation.h>
35 #include <dali-toolkit/internal/text/shaper.h>
36 #include <dali-toolkit/internal/text/text-control-interface.h>
37 #include <dali-toolkit/internal/text/text-controller-impl-event-handler.h>
38 #include <dali-toolkit/internal/text/text-run-container.h>
39 #include <dali-toolkit/internal/text/text-selection-handle-controller.h>
41 #include <dali-toolkit/internal/text/text-enumerations-impl.h>
47 #if defined(DEBUG_ENABLED)
48 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
51 struct BackgroundVertex
53 Vector2 mPosition; ///< Vertex posiiton
54 Vector4 mColor; ///< Vertex color
59 Vector<BackgroundVertex> mVertices; ///< container of vertices
60 Vector<unsigned short> mIndices; ///< container of indices
63 const Dali::Vector4 LIGHT_BLUE(0.75f, 0.96f, 1.f, 1.f);
64 const Dali::Vector4 BACKGROUND_SUB4(0.58f, 0.87f, 0.96f, 1.f);
65 const Dali::Vector4 BACKGROUND_SUB5(0.83f, 0.94f, 0.98f, 1.f);
66 const Dali::Vector4 BACKGROUND_SUB6(1.f, 0.5f, 0.5f, 1.f);
67 const Dali::Vector4 BACKGROUND_SUB7(1.f, 0.8f, 0.8f, 1.f);
77 EventData::EventData(DecoratorPtr decorator, InputMethodContext& inputMethodContext)
78 : mDecorator(decorator),
79 mInputMethodContext(inputMethodContext),
80 mPlaceholderFont(NULL),
81 mPlaceholderTextActive(),
82 mPlaceholderTextInactive(),
83 mPlaceholderTextColor(0.8f, 0.8f, 0.8f, 0.8f), // This color has been published in the Public API (placeholder-properties.h).
85 mInputStyleChangedQueue(),
86 mPreviousState(INACTIVE),
88 mPrimaryCursorPosition(0u),
89 mLeftSelectionPosition(0u),
90 mRightSelectionPosition(0u),
91 mPreEditStartPosition(0u),
93 mCursorHookPositionX(0.f),
94 mDoubleTapAction(Controller::NoTextTap::NO_ACTION),
95 mLongPressAction(Controller::NoTextTap::SHOW_SELECTION_POPUP),
96 mIsShowingPlaceholderText(false),
98 mDecoratorUpdated(false),
99 mCursorBlinkEnabled(true),
100 mGrabHandleEnabled(true),
101 mGrabHandlePopupEnabled(true),
102 mSelectionEnabled(true),
103 mUpdateCursorHookPosition(false),
104 mUpdateCursorPosition(false),
105 mUpdateGrabHandlePosition(false),
106 mUpdateLeftSelectionPosition(false),
107 mUpdateRightSelectionPosition(false),
108 mIsLeftHandleSelected(false),
109 mIsRightHandleSelected(false),
110 mUpdateHighlightBox(false),
111 mScrollAfterUpdatePosition(false),
112 mScrollAfterDelete(false),
113 mAllTextSelected(false),
114 mUpdateInputStyle(false),
115 mPasswordInput(false),
116 mCheckScrollAmount(false),
117 mIsPlaceholderPixelSize(false),
118 mIsPlaceholderElideEnabled(false),
119 mPlaceholderEllipsisFlag(false),
120 mShiftSelectionFlag(true),
121 mUpdateAlignment(false),
122 mEditingEnabled(true)
126 bool Controller::Impl::ProcessInputEvents()
128 return ControllerImplEventHandler::ProcessInputEvents(*this);
131 void Controller::Impl::NotifyInputMethodContext()
133 if(mEventData && mEventData->mInputMethodContext)
135 CharacterIndex cursorPosition = GetLogicalCursorPosition();
137 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces(0u);
139 // Update the cursor position by removing the initial white spaces.
140 if(cursorPosition < numberOfWhiteSpaces)
146 cursorPosition -= numberOfWhiteSpaces;
149 mEventData->mInputMethodContext.SetCursorPosition(cursorPosition);
150 mEventData->mInputMethodContext.NotifyCursorPosition();
154 void Controller::Impl::NotifyInputMethodContextMultiLineStatus()
156 if(mEventData && mEventData->mInputMethodContext)
158 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
159 mEventData->mInputMethodContext.NotifyTextInputMultiLine(layout == Text::Layout::Engine::MULTI_LINE_BOX);
163 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
165 CharacterIndex cursorPosition = 0u;
169 if((EventData::SELECTING == mEventData->mState) ||
170 (EventData::SELECTION_HANDLE_PANNING == mEventData->mState))
172 cursorPosition = std::min(mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition);
176 cursorPosition = mEventData->mPrimaryCursorPosition;
180 return cursorPosition;
183 Length Controller::Impl::GetNumberOfWhiteSpaces(CharacterIndex index) const
185 Length numberOfWhiteSpaces = 0u;
187 // Get the buffer to the text.
188 Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
190 const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
191 for(; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces)
193 if(!TextAbstraction::IsWhiteSpace(*(utf32CharacterBuffer + index)))
199 return numberOfWhiteSpaces;
202 void Controller::Impl::GetText(CharacterIndex index, std::string& text) const
204 // Get the total number of characters.
205 Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
207 // Retrieve the text.
208 if(0u != numberOfCharacters)
210 Utf32ToUtf8(mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text);
214 void Controller::Impl::CalculateTextUpdateIndices(Length& numberOfCharacters)
216 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
217 mTextUpdateInfo.mStartGlyphIndex = 0u;
218 mTextUpdateInfo.mStartLineIndex = 0u;
219 numberOfCharacters = 0u;
221 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
222 if(0u == numberOfParagraphs)
224 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
225 numberOfCharacters = 0u;
227 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
229 // Nothing else to do if there are no paragraphs.
233 // Find the paragraphs to be updated.
234 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
235 if(mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters)
237 // Text is being added at the end of the current text.
238 if(mTextUpdateInfo.mIsLastCharacterNewParagraph)
240 // Text is being added in a new paragraph after the last character of the text.
241 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
242 numberOfCharacters = 0u;
243 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
245 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
246 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
248 // Nothing else to do;
252 paragraphsToBeUpdated.PushBack(numberOfParagraphs - 1u);
256 Length numberOfCharactersToUpdate = 0u;
257 if(mTextUpdateInfo.mFullRelayoutNeeded)
259 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
263 numberOfCharactersToUpdate = (mTextUpdateInfo.mNumberOfCharactersToRemove > 0u) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
265 mModel->mLogicalModel->FindParagraphs(mTextUpdateInfo.mCharacterIndex,
266 numberOfCharactersToUpdate,
267 paragraphsToBeUpdated);
270 if(0u != paragraphsToBeUpdated.Count())
272 const ParagraphRunIndex firstParagraphIndex = *(paragraphsToBeUpdated.Begin());
273 const ParagraphRun& firstParagraph = *(mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex);
274 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
276 ParagraphRunIndex lastParagraphIndex = *(paragraphsToBeUpdated.End() - 1u);
277 const ParagraphRun& lastParagraph = *(mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex);
279 if((mTextUpdateInfo.mNumberOfCharactersToRemove > 0u) && // Some character are removed.
280 (lastParagraphIndex < numberOfParagraphs - 1u) && // There is a next paragraph.
281 ((lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters) == // The last removed character is the new paragraph character.
282 (mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove)))
284 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
285 const ParagraphRun& lastParagraph = *(mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u);
287 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
291 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
295 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
296 mTextUpdateInfo.mStartGlyphIndex = *(mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex);
299 void Controller::Impl::ClearFullModelData(OperationsMask operations)
301 if(NO_OPERATION != (GET_LINE_BREAKS & operations))
303 mModel->mLogicalModel->mLineBreakInfo.Clear();
304 mModel->mLogicalModel->mParagraphInfo.Clear();
307 if(NO_OPERATION != (GET_SCRIPTS & operations))
309 mModel->mLogicalModel->mScriptRuns.Clear();
312 if(NO_OPERATION != (VALIDATE_FONTS & operations))
314 mModel->mLogicalModel->mFontRuns.Clear();
317 if(0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count())
319 if(NO_OPERATION != (BIDI_INFO & operations))
321 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
322 mModel->mLogicalModel->mCharacterDirections.Clear();
325 if(NO_OPERATION != (REORDER & operations))
327 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
328 for(Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
329 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
333 BidirectionalLineInfoRun& bidiLineInfo = *it;
335 free(bidiLineInfo.visualToLogicalMap);
336 bidiLineInfo.visualToLogicalMap = NULL;
338 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
342 if(NO_OPERATION != (SHAPE_TEXT & operations))
344 mModel->mVisualModel->mGlyphs.Clear();
345 mModel->mVisualModel->mGlyphsToCharacters.Clear();
346 mModel->mVisualModel->mCharactersToGlyph.Clear();
347 mModel->mVisualModel->mCharactersPerGlyph.Clear();
348 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
349 mModel->mVisualModel->mGlyphPositions.Clear();
352 if(NO_OPERATION != (LAYOUT & operations))
354 mModel->mVisualModel->mLines.Clear();
357 if(NO_OPERATION != (COLOR & operations))
359 mModel->mVisualModel->mColorIndices.Clear();
360 mModel->mVisualModel->mBackgroundColorIndices.Clear();
364 void Controller::Impl::ClearCharacterModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
366 const CharacterIndex endIndexPlusOne = endIndex + 1u;
368 if(NO_OPERATION != (GET_LINE_BREAKS & operations))
370 // Clear the line break info.
371 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
373 mModel->mLogicalModel->mLineBreakInfo.Erase(lineBreakInfoBuffer + startIndex,
374 lineBreakInfoBuffer + endIndexPlusOne);
376 // Clear the paragraphs.
377 ClearCharacterRuns(startIndex,
379 mModel->mLogicalModel->mParagraphInfo);
382 if(NO_OPERATION != (GET_SCRIPTS & operations))
384 // Clear the scripts.
385 ClearCharacterRuns(startIndex,
387 mModel->mLogicalModel->mScriptRuns);
390 if(NO_OPERATION != (VALIDATE_FONTS & operations))
393 ClearCharacterRuns(startIndex,
395 mModel->mLogicalModel->mFontRuns);
398 if(0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count())
400 if(NO_OPERATION != (BIDI_INFO & operations))
402 // Clear the bidirectional paragraph info.
403 ClearCharacterRuns(startIndex,
405 mModel->mLogicalModel->mBidirectionalParagraphInfo);
407 // Clear the character's directions.
408 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
410 mModel->mLogicalModel->mCharacterDirections.Erase(characterDirectionsBuffer + startIndex,
411 characterDirectionsBuffer + endIndexPlusOne);
414 if(NO_OPERATION != (REORDER & operations))
416 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
417 uint32_t endRemoveIndex = startRemoveIndex;
418 ClearCharacterRuns(startIndex,
420 mModel->mLogicalModel->mBidirectionalLineInfo,
424 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
426 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
427 for(Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
428 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
432 BidirectionalLineInfoRun& bidiLineInfo = *it;
434 free(bidiLineInfo.visualToLogicalMap);
435 bidiLineInfo.visualToLogicalMap = NULL;
438 mModel->mLogicalModel->mBidirectionalLineInfo.Erase(bidirectionalLineInfoBuffer + startRemoveIndex,
439 bidirectionalLineInfoBuffer + endRemoveIndex);
444 void Controller::Impl::ClearGlyphModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
446 const CharacterIndex endIndexPlusOne = endIndex + 1u;
447 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
449 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
450 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
451 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
453 const GlyphIndex endGlyphIndexPlusOne = *(charactersToGlyphBuffer + endIndex) + *(glyphsPerCharacterBuffer + endIndex);
454 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
456 if(NO_OPERATION != (SHAPE_TEXT & operations))
458 // Update the character to glyph indices.
459 for(Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
460 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
464 CharacterIndex& index = *it;
465 index -= numberOfGlyphsRemoved;
468 // Clear the character to glyph conversion table.
469 mModel->mVisualModel->mCharactersToGlyph.Erase(charactersToGlyphBuffer + startIndex,
470 charactersToGlyphBuffer + endIndexPlusOne);
472 // Clear the glyphs per character table.
473 mModel->mVisualModel->mGlyphsPerCharacter.Erase(glyphsPerCharacterBuffer + startIndex,
474 glyphsPerCharacterBuffer + endIndexPlusOne);
476 // Clear the glyphs buffer.
477 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
478 mModel->mVisualModel->mGlyphs.Erase(glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
479 glyphsBuffer + endGlyphIndexPlusOne);
481 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
483 // Update the glyph to character indices.
484 for(Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
485 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
489 CharacterIndex& index = *it;
490 index -= numberOfCharactersRemoved;
493 // Clear the glyphs to characters buffer.
494 mModel->mVisualModel->mGlyphsToCharacters.Erase(glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
495 glyphsToCharactersBuffer + endGlyphIndexPlusOne);
497 // Clear the characters per glyph buffer.
498 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
499 mModel->mVisualModel->mCharactersPerGlyph.Erase(charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
500 charactersPerGlyphBuffer + endGlyphIndexPlusOne);
502 // Clear the positions buffer.
503 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
504 mModel->mVisualModel->mGlyphPositions.Erase(positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
505 positionsBuffer + endGlyphIndexPlusOne);
508 if(NO_OPERATION != (LAYOUT & operations))
511 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
512 uint32_t endRemoveIndex = startRemoveIndex;
513 ClearCharacterRuns(startIndex,
515 mModel->mVisualModel->mLines,
519 // Will update the glyph runs.
520 startRemoveIndex = mModel->mVisualModel->mLines.Count();
521 endRemoveIndex = startRemoveIndex;
522 ClearGlyphRuns(mTextUpdateInfo.mStartGlyphIndex,
523 endGlyphIndexPlusOne - 1u,
524 mModel->mVisualModel->mLines,
528 // Set the line index from where to insert the new laid-out lines.
529 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
531 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
532 mModel->mVisualModel->mLines.Erase(linesBuffer + startRemoveIndex,
533 linesBuffer + endRemoveIndex);
536 if(NO_OPERATION != (COLOR & operations))
538 if(0u != mModel->mVisualModel->mColorIndices.Count())
540 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
541 mModel->mVisualModel->mColorIndices.Erase(colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
542 colorIndexBuffer + endGlyphIndexPlusOne);
545 if(0u != mModel->mVisualModel->mBackgroundColorIndices.Count())
547 ColorIndex* backgroundColorIndexBuffer = mModel->mVisualModel->mBackgroundColorIndices.Begin();
548 mModel->mVisualModel->mBackgroundColorIndices.Erase(backgroundColorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
549 backgroundColorIndexBuffer + endGlyphIndexPlusOne);
554 void Controller::Impl::ClearModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
556 if(mTextUpdateInfo.mClearAll ||
557 ((0u == startIndex) &&
558 (mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u)))
560 ClearFullModelData(operations);
564 // Clear the model data related with characters.
565 ClearCharacterModelData(startIndex, endIndex, operations);
567 // Clear the model data related with glyphs.
568 ClearGlyphModelData(startIndex, endIndex, operations);
571 // The estimated number of lines. Used to avoid reallocations when layouting.
572 mTextUpdateInfo.mEstimatedNumberOfLines = std::max(mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count());
574 mModel->mVisualModel->ClearCaches();
577 bool Controller::Impl::UpdateModel(OperationsMask operationsRequired)
579 DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::UpdateModel\n");
581 // Calculate the operations to be done.
582 const OperationsMask operations = static_cast<OperationsMask>(mOperationsPending & operationsRequired);
584 if(NO_OPERATION == operations)
586 // Nothing to do if no operations are pending and required.
590 Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
591 Vector<Character> displayCharacters;
592 bool useHiddenText = false;
593 if(mHiddenInput && mEventData != nullptr && !mEventData->mIsShowingPlaceholderText)
595 mHiddenInput->Substitute(srcCharacters, displayCharacters);
596 useHiddenText = true;
599 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
600 const Length numberOfCharacters = utf32Characters.Count();
602 // Index to the first character of the first paragraph to be updated.
603 CharacterIndex startIndex = 0u;
604 // Number of characters of the paragraphs to be removed.
605 Length paragraphCharacters = 0u;
607 CalculateTextUpdateIndices(paragraphCharacters);
609 // Check whether the indices for updating the text is valid
610 if(numberOfCharacters > 0u &&
611 (mTextUpdateInfo.mParagraphCharacterIndex > numberOfCharacters ||
612 mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters))
614 std::string currentText;
615 Utf32ToUtf8(mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText);
617 DALI_LOG_ERROR("Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n");
618 DALI_LOG_ERROR("Number of characters: %d, current text is: %s\n", numberOfCharacters, currentText.c_str());
620 // Dump mTextUpdateInfo
621 DALI_LOG_ERROR("Dump mTextUpdateInfo:\n");
622 DALI_LOG_ERROR(" mTextUpdateInfo.mCharacterIndex = %u\n", mTextUpdateInfo.mCharacterIndex);
623 DALI_LOG_ERROR(" mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", mTextUpdateInfo.mNumberOfCharactersToRemove);
624 DALI_LOG_ERROR(" mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", mTextUpdateInfo.mNumberOfCharactersToAdd);
625 DALI_LOG_ERROR(" mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", mTextUpdateInfo.mPreviousNumberOfCharacters);
626 DALI_LOG_ERROR(" mTextUpdateInfo.mParagraphCharacterIndex = %u\n", mTextUpdateInfo.mParagraphCharacterIndex);
627 DALI_LOG_ERROR(" mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", mTextUpdateInfo.mRequestedNumberOfCharacters);
628 DALI_LOG_ERROR(" mTextUpdateInfo.mStartGlyphIndex = %u\n", mTextUpdateInfo.mStartGlyphIndex);
629 DALI_LOG_ERROR(" mTextUpdateInfo.mStartLineIndex = %u\n", mTextUpdateInfo.mStartLineIndex);
630 DALI_LOG_ERROR(" mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", mTextUpdateInfo.mEstimatedNumberOfLines);
631 DALI_LOG_ERROR(" mTextUpdateInfo.mClearAll = %d\n", mTextUpdateInfo.mClearAll);
632 DALI_LOG_ERROR(" mTextUpdateInfo.mFullRelayoutNeeded = %d\n", mTextUpdateInfo.mFullRelayoutNeeded);
633 DALI_LOG_ERROR(" mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", mTextUpdateInfo.mIsLastCharacterNewParagraph);
638 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
640 if(mTextUpdateInfo.mClearAll ||
641 (0u != paragraphCharacters))
643 ClearModelData(startIndex, startIndex + ((paragraphCharacters > 0u) ? paragraphCharacters - 1u : 0u), operations);
646 mTextUpdateInfo.mClearAll = false;
648 // Whether the model is updated.
649 bool updated = false;
651 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
652 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
654 if(NO_OPERATION != (GET_LINE_BREAKS & operations))
656 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
657 // calculate the bidirectional info for each 'paragraph'.
658 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
659 // is not shaped together).
660 lineBreakInfo.Resize(numberOfCharacters, TextAbstraction::LINE_NO_BREAK);
662 SetLineBreakInfo(utf32Characters,
664 requestedNumberOfCharacters,
667 if(mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
668 mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::MIXED))
670 CharacterIndex end = startIndex + requestedNumberOfCharacters;
671 LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
673 for(CharacterIndex index = startIndex; index < end; index++)
675 CharacterIndex wordEnd = index;
676 while((*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_ALLOW_BREAK) && (*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_MUST_BREAK))
681 if((wordEnd + 1) == end) // add last char
686 Vector<bool> hyphens = GetWordHyphens(utf32Characters.Begin() + index, wordEnd - index, nullptr);
688 for(CharacterIndex i = 0; i < (wordEnd - index); i++)
692 *(lineBreakInfoBuffer + index + i) = TextAbstraction::LINE_HYPHENATION_BREAK;
700 // Create the paragraph info.
701 mModel->mLogicalModel->CreateParagraphInfo(startIndex,
702 requestedNumberOfCharacters);
706 const bool getScripts = NO_OPERATION != (GET_SCRIPTS & operations);
707 const bool validateFonts = NO_OPERATION != (VALIDATE_FONTS & operations);
709 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
710 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
712 if(getScripts || validateFonts)
714 // Validates the fonts assigned by the application or assigns default ones.
715 // It makes sure all the characters are going to be rendered by the correct font.
716 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
720 // Retrieves the scripts used in the text.
721 multilanguageSupport.SetScripts(utf32Characters,
723 requestedNumberOfCharacters,
729 // Validate the fonts set through the mark-up string.
730 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
732 // Get the default font's description.
733 TextAbstraction::FontDescription defaultFontDescription;
734 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale;
736 //Get the number of points per one unit of point-size
737 uint32_t numberOfPointsPerOneUnitOfPointSize = mFontClient.GetNumberOfPointsPerOneUnitOfPointSize();
739 if(IsShowingPlaceholderText() && mEventData && (nullptr != mEventData->mPlaceholderFont))
741 // If the placeholder font is set specifically, only placeholder font is changed.
742 defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
743 if(mEventData->mPlaceholderFont->sizeDefined)
745 defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * mFontSizeScale * numberOfPointsPerOneUnitOfPointSize;
748 else if(nullptr != mFontDefaults)
750 // Set the normal font and the placeholder font.
751 defaultFontDescription = mFontDefaults->mFontDescription;
755 defaultPointSize = mFontDefaults->mFitPointSize * numberOfPointsPerOneUnitOfPointSize;
759 defaultPointSize = mFontDefaults->mDefaultPointSize * mFontSizeScale * numberOfPointsPerOneUnitOfPointSize;
763 // Validates the fonts. If there is a character with no assigned font it sets a default one.
764 // After this call, fonts are validated.
765 multilanguageSupport.ValidateFonts(utf32Characters,
768 defaultFontDescription,
771 requestedNumberOfCharacters,
777 Vector<Character> mirroredUtf32Characters;
778 bool textMirrored = false;
779 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
780 if(NO_OPERATION != (BIDI_INFO & operations))
782 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
783 bidirectionalInfo.Reserve(numberOfParagraphs);
785 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
786 SetBidirectionalInfo(utf32Characters,
790 requestedNumberOfCharacters,
792 mModel->mMatchSystemLanguageDirection,
795 if(0u != bidirectionalInfo.Count())
797 // Only set the character directions if there is right to left characters.
798 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
799 GetCharactersDirection(bidirectionalInfo,
802 requestedNumberOfCharacters,
805 // This paragraph has right to left text. Some characters may need to be mirrored.
806 // TODO: consider if the mirrored string can be stored as well.
808 textMirrored = GetMirroredText(utf32Characters,
812 requestedNumberOfCharacters,
813 mirroredUtf32Characters);
817 // There is no right to left characters. Clear the directions vector.
818 mModel->mLogicalModel->mCharacterDirections.Clear();
823 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
824 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
825 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
826 Vector<GlyphIndex> newParagraphGlyphs;
827 newParagraphGlyphs.Reserve(numberOfParagraphs);
829 const Length currentNumberOfGlyphs = glyphs.Count();
830 if(NO_OPERATION != (SHAPE_TEXT & operations))
832 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
834 ShapeText(textToShape,
839 mTextUpdateInfo.mStartGlyphIndex,
840 requestedNumberOfCharacters,
842 glyphsToCharactersMap,
846 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
847 mModel->mVisualModel->CreateGlyphsPerCharacterTable(startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters);
848 mModel->mVisualModel->CreateCharacterToGlyphTable(startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters);
853 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
855 if(NO_OPERATION != (GET_GLYPH_METRICS & operations))
857 GlyphInfo* glyphsBuffer = glyphs.Begin();
858 mMetrics->GetGlyphMetrics(glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs);
860 // Update the width and advance of all new paragraph characters.
861 for(Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it)
863 const GlyphIndex index = *it;
864 GlyphInfo& glyph = *(glyphsBuffer + index);
866 glyph.xBearing = 0.f;
873 if((nullptr != mEventData) &&
874 mEventData->mPreEditFlag &&
875 (0u != mModel->mVisualModel->mCharactersToGlyph.Count()))
877 Dali::InputMethodContext::PreEditAttributeDataContainer attrs;
878 mEventData->mInputMethodContext.GetPreeditStyle(attrs);
879 Dali::InputMethodContext::PreeditStyle type = Dali::InputMethodContext::PreeditStyle::NONE;
881 // Check the type of preedit and run it.
882 for(Dali::InputMethodContext::PreEditAttributeDataContainer::Iterator it = attrs.Begin(), endIt = attrs.End(); it != endIt; it++)
884 Dali::InputMethodContext::PreeditAttributeData attrData = *it;
885 DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::UpdateModel PreeditStyle type : %d start %d end %d \n", attrData.preeditType, attrData.startIndex, attrData.endIndex);
886 type = attrData.preeditType;
888 // Check the number of commit characters for the start position.
889 unsigned int numberOfCommit = mEventData->mPrimaryCursorPosition - mEventData->mPreEditLength;
890 Length numberOfIndices = attrData.endIndex - attrData.startIndex;
894 case Dali::InputMethodContext::PreeditStyle::UNDERLINE:
896 // Add the underline for the pre-edit text.
897 GlyphRun underlineRun;
898 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
899 underlineRun.numberOfGlyphs = numberOfIndices;
900 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
902 //Mark-up processor case
903 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
905 CopyUnderlinedFromLogicalToVisualModels(false);
909 case Dali::InputMethodContext::PreeditStyle::REVERSE:
911 Vector4 textColor = mModel->mVisualModel->GetTextColor();
912 ColorRun backgroundColorRun;
913 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
914 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
915 backgroundColorRun.color = textColor;
916 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
918 Vector4 backgroundColor = mModel->mVisualModel->GetBackgroundColor();
919 Vector<ColorRun> colorRuns;
920 colorRuns.Resize(1u);
921 ColorRun& colorRun = *(colorRuns.Begin());
922 colorRun.color = backgroundColor;
923 colorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
924 colorRun.characterRun.numberOfCharacters = numberOfIndices;
925 mModel->mLogicalModel->mColorRuns.PushBack(colorRun);
927 //Mark-up processor case
928 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
930 CopyUnderlinedFromLogicalToVisualModels(false);
934 case Dali::InputMethodContext::PreeditStyle::HIGHLIGHT:
936 ColorRun backgroundColorRun;
937 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
938 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
939 backgroundColorRun.color = LIGHT_BLUE;
940 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
942 //Mark-up processor case
943 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
945 CopyUnderlinedFromLogicalToVisualModels(false);
949 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_1:
951 // CUSTOM_PLATFORM_STYLE_1 should be drawn with background and underline together.
952 ColorRun backgroundColorRun;
953 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
954 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
955 backgroundColorRun.color = BACKGROUND_SUB4;
956 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
958 GlyphRun underlineRun;
959 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
960 underlineRun.numberOfGlyphs = numberOfIndices;
961 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
963 //Mark-up processor case
964 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
966 CopyUnderlinedFromLogicalToVisualModels(false);
970 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_2:
972 // CUSTOM_PLATFORM_STYLE_2 should be drawn with background and underline together.
973 ColorRun backgroundColorRun;
974 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
975 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
976 backgroundColorRun.color = BACKGROUND_SUB5;
977 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
979 GlyphRun underlineRun;
980 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
981 underlineRun.numberOfGlyphs = numberOfIndices;
982 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
984 //Mark-up processor case
985 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
987 CopyUnderlinedFromLogicalToVisualModels(false);
991 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_3:
993 // CUSTOM_PLATFORM_STYLE_3 should be drawn with background and underline together.
994 ColorRun backgroundColorRun;
995 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
996 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
997 backgroundColorRun.color = BACKGROUND_SUB6;
998 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
1000 GlyphRun underlineRun;
1001 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1002 underlineRun.numberOfGlyphs = numberOfIndices;
1003 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
1005 //Mark-up processor case
1006 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
1008 CopyUnderlinedFromLogicalToVisualModels(false);
1012 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_4:
1014 // CUSTOM_PLATFORM_STYLE_4 should be drawn with background and underline together.
1015 ColorRun backgroundColorRun;
1016 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1017 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1018 backgroundColorRun.color = BACKGROUND_SUB7;
1019 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
1021 GlyphRun underlineRun;
1022 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1023 underlineRun.numberOfGlyphs = numberOfIndices;
1024 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
1026 //Mark-up processor case
1027 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
1029 CopyUnderlinedFromLogicalToVisualModels(false);
1033 case Dali::InputMethodContext::PreeditStyle::NONE:
1044 if(NO_OPERATION != (COLOR & operations))
1046 // Set the color runs in glyphs.
1047 SetColorSegmentationInfo(mModel->mLogicalModel->mColorRuns,
1048 mModel->mVisualModel->mCharactersToGlyph,
1049 mModel->mVisualModel->mGlyphsPerCharacter,
1051 mTextUpdateInfo.mStartGlyphIndex,
1052 requestedNumberOfCharacters,
1053 mModel->mVisualModel->mColors,
1054 mModel->mVisualModel->mColorIndices);
1056 // Set the background color runs in glyphs.
1057 SetColorSegmentationInfo(mModel->mLogicalModel->mBackgroundColorRuns,
1058 mModel->mVisualModel->mCharactersToGlyph,
1059 mModel->mVisualModel->mGlyphsPerCharacter,
1061 mTextUpdateInfo.mStartGlyphIndex,
1062 requestedNumberOfCharacters,
1063 mModel->mVisualModel->mBackgroundColors,
1064 mModel->mVisualModel->mBackgroundColorIndices);
1069 if((NO_OPERATION != (SHAPE_TEXT & operations)) &&
1070 !((nullptr != mEventData) &&
1071 mEventData->mPreEditFlag &&
1072 (0u != mModel->mVisualModel->mCharactersToGlyph.Count())))
1074 //Mark-up processor case
1075 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
1077 CopyUnderlinedFromLogicalToVisualModels(true);
1083 // The estimated number of lines. Used to avoid reallocations when layouting.
1084 mTextUpdateInfo.mEstimatedNumberOfLines = std::max(mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count());
1086 // Set the previous number of characters for the next time the text is updated.
1087 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1092 void Controller::Impl::RetrieveDefaultInputStyle(InputStyle& inputStyle)
1094 // Sets the default text's color.
1095 inputStyle.textColor = mTextColor;
1096 inputStyle.isDefaultColor = true;
1098 inputStyle.familyName.clear();
1099 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1100 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1101 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1102 inputStyle.size = 0.f;
1104 inputStyle.lineSpacing = 0.f;
1106 inputStyle.underlineProperties.clear();
1107 inputStyle.shadowProperties.clear();
1108 inputStyle.embossProperties.clear();
1109 inputStyle.outlineProperties.clear();
1111 inputStyle.isFamilyDefined = false;
1112 inputStyle.isWeightDefined = false;
1113 inputStyle.isWidthDefined = false;
1114 inputStyle.isSlantDefined = false;
1115 inputStyle.isSizeDefined = false;
1117 inputStyle.isLineSpacingDefined = false;
1119 inputStyle.isUnderlineDefined = false;
1120 inputStyle.isShadowDefined = false;
1121 inputStyle.isEmbossDefined = false;
1122 inputStyle.isOutlineDefined = false;
1124 // Sets the default font's family name, weight, width, slant and size.
1127 if(mFontDefaults->familyDefined)
1129 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1130 inputStyle.isFamilyDefined = true;
1133 if(mFontDefaults->weightDefined)
1135 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1136 inputStyle.isWeightDefined = true;
1139 if(mFontDefaults->widthDefined)
1141 inputStyle.width = mFontDefaults->mFontDescription.width;
1142 inputStyle.isWidthDefined = true;
1145 if(mFontDefaults->slantDefined)
1147 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1148 inputStyle.isSlantDefined = true;
1151 if(mFontDefaults->sizeDefined)
1153 inputStyle.size = mFontDefaults->mDefaultPointSize;
1154 inputStyle.isSizeDefined = true;
1159 float Controller::Impl::GetDefaultFontLineHeight()
1161 FontId defaultFontId = 0u;
1162 if(nullptr == mFontDefaults)
1164 TextAbstraction::FontDescription fontDescription;
1165 defaultFontId = mFontClient.GetFontId(fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale);
1169 defaultFontId = mFontDefaults->GetFontId(mFontClient, mFontDefaults->mDefaultPointSize * mFontSizeScale);
1172 Text::FontMetrics fontMetrics;
1173 mMetrics->GetFontMetrics(defaultFontId, fontMetrics);
1175 return (fontMetrics.ascender - fontMetrics.descender);
1178 void Controller::Impl::SetTextSelectionRange(const uint32_t* pStart, const uint32_t* pEnd)
1180 if(nullptr == mEventData)
1182 // Nothing to do if there is no text.
1186 if(mEventData->mSelectionEnabled && (pStart || pEnd))
1188 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
1192 mEventData->mLeftSelectionPosition = std::min(*pStart, length);
1196 mEventData->mRightSelectionPosition = std::min(*pEnd, length);
1199 if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
1201 ChangeState(EventData::EDITING);
1202 mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition;
1203 mEventData->mUpdateCursorPosition = true;
1207 ChangeState(EventData::SELECTING);
1208 mEventData->mUpdateHighlightBox = true;
1209 mEventData->mUpdateLeftSelectionPosition = true;
1210 mEventData->mUpdateRightSelectionPosition = true;
1215 CharacterIndex Controller::Impl::GetPrimaryCursorPosition() const
1217 if(nullptr == mEventData)
1221 return mEventData->mPrimaryCursorPosition;
1224 bool Controller::Impl::SetPrimaryCursorPosition(CharacterIndex index)
1226 if(nullptr == mEventData)
1228 // Nothing to do if there is no text.
1232 if(mEventData->mPrimaryCursorPosition == index)
1234 // Nothing for same cursor position.
1238 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
1239 mEventData->mPrimaryCursorPosition = std::min(index, length);
1240 ChangeState(EventData::EDITING);
1241 mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1242 mEventData->mUpdateCursorPosition = true;
1243 ScrollTextToMatchCursor();
1247 Uint32Pair Controller::Impl::GetTextSelectionRange() const
1253 range.first = mEventData->mLeftSelectionPosition;
1254 range.second = mEventData->mRightSelectionPosition;
1260 bool Controller::Impl::IsEditable() const
1262 return mEventData && mEventData->mEditingEnabled;
1265 void Controller::Impl::SetEditable(bool editable)
1269 mEventData->mEditingEnabled = editable;
1273 void Controller::Impl::RetrieveSelection(std::string& selectedText, bool deleteAfterRetrieval)
1275 if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
1277 // Nothing to select if handles are in the same place.
1278 selectedText.clear();
1282 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1284 //Get start and end position of selection
1285 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1286 const Length lengthOfSelectedText = (handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition) - startOfSelectedText;
1288 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1289 const Length numberOfCharacters = utf32Characters.Count();
1291 // Validate the start and end selection points
1292 if((startOfSelectedText + lengthOfSelectedText) <= numberOfCharacters)
1294 //Get text as a UTF8 string
1295 Utf32ToUtf8(&utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText);
1297 if(deleteAfterRetrieval) // Only delete text if copied successfully
1299 // Keep a copy of the current input style.
1300 InputStyle currentInputStyle;
1301 currentInputStyle.Copy(mEventData->mInputStyle);
1303 // Set as input style the style of the first deleted character.
1304 mModel->mLogicalModel->RetrieveStyle(startOfSelectedText, mEventData->mInputStyle);
1306 // Compare if the input style has changed.
1307 const bool hasInputStyleChanged = !currentInputStyle.Equal(mEventData->mInputStyle);
1309 if(hasInputStyleChanged)
1311 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask(mEventData->mInputStyle);
1312 // Queue the input style changed signal.
1313 mEventData->mInputStyleChangedQueue.PushBack(styleChangedMask);
1316 mModel->mLogicalModel->UpdateTextStyleRuns(startOfSelectedText, -static_cast<int>(lengthOfSelectedText));
1318 // Mark the paragraphs to be updated.
1319 if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1321 mTextUpdateInfo.mCharacterIndex = 0;
1322 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1323 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1324 mTextUpdateInfo.mClearAll = true;
1328 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1329 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1332 // Delete text between handles
1333 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1334 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1335 utf32Characters.Erase(first, last);
1337 // Will show the cursor at the first character of the selection.
1338 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1342 // Will show the cursor at the last character of the selection.
1343 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1346 mEventData->mDecoratorUpdated = true;
1350 void Controller::Impl::SetSelection(int start, int end)
1352 mEventData->mLeftSelectionPosition = start;
1353 mEventData->mRightSelectionPosition = end;
1354 mEventData->mUpdateCursorPosition = true;
1357 std::pair<int, int> Controller::Impl::GetSelectionIndexes() const
1359 return {mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition};
1362 void Controller::Impl::ShowClipboard()
1366 mClipboard.ShowClipboard();
1370 void Controller::Impl::HideClipboard()
1372 if(mClipboard && mClipboardHideEnabled)
1374 mClipboard.HideClipboard();
1378 void Controller::Impl::SetClipboardHideEnable(bool enable)
1380 mClipboardHideEnabled = enable;
1383 bool Controller::Impl::CopyStringToClipboard(const std::string& source)
1385 //Send string to clipboard
1386 return (mClipboard && mClipboard.SetItem(source));
1389 void Controller::Impl::SendSelectionToClipboard(bool deleteAfterSending)
1391 std::string selectedText;
1392 RetrieveSelection(selectedText, deleteAfterSending);
1393 CopyStringToClipboard(selectedText);
1394 ChangeState(EventData::EDITING);
1397 void Controller::Impl::RequestGetTextFromClipboard()
1401 mClipboard.RequestItem();
1405 void Controller::Impl::RepositionSelectionHandles()
1407 SelectionHandleController::Reposition(*this);
1409 void Controller::Impl::RepositionSelectionHandles(float visualX, float visualY, Controller::NoTextTap::Action action)
1411 SelectionHandleController::Reposition(*this, visualX, visualY, action);
1414 void Controller::Impl::SetPopupButtons()
1417 * Sets the Popup buttons to be shown depending on State.
1419 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1421 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1424 bool isEditable = IsEditable();
1425 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1427 if(EventData::SELECTING == mEventData->mState)
1429 buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::COPY);
1432 buttonsToShow = TextSelectionPopup::Buttons(buttonsToShow | TextSelectionPopup::CUT);
1435 if(!IsClipboardEmpty())
1439 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1441 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1444 if(!mEventData->mAllTextSelected)
1446 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::SELECT_ALL));
1449 else if(EventData::EDITING_WITH_POPUP == mEventData->mState)
1451 if(mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
1453 buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL);
1456 if(!IsClipboardEmpty())
1460 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1462 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1465 else if(EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState)
1467 if(!IsClipboardEmpty())
1471 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1473 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1477 mEventData->mDecorator->SetEnabledPopupButtons(buttonsToShow);
1480 void Controller::Impl::ChangeState(EventData::State newState)
1482 if(nullptr == mEventData)
1484 // Nothing to do if there is no text input.
1488 DALI_LOG_INFO(gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState);
1490 if(mEventData->mState != newState)
1492 mEventData->mPreviousState = mEventData->mState;
1493 mEventData->mState = newState;
1495 switch(mEventData->mState)
1497 case EventData::INACTIVE:
1499 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1500 mEventData->mDecorator->StopCursorBlink();
1501 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1502 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1503 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1504 mEventData->mDecorator->SetHighlightActive(false);
1505 mEventData->mDecorator->SetPopupActive(false);
1506 mEventData->mDecoratorUpdated = true;
1509 case EventData::INTERRUPTED:
1511 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1512 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1513 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1514 mEventData->mDecorator->SetHighlightActive(false);
1515 mEventData->mDecorator->SetPopupActive(false);
1516 mEventData->mDecoratorUpdated = true;
1519 case EventData::SELECTING:
1521 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1522 mEventData->mDecorator->StopCursorBlink();
1523 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1524 if(mEventData->mGrabHandleEnabled)
1526 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
1527 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
1529 mEventData->mDecorator->SetHighlightActive(true);
1530 if(mEventData->mGrabHandlePopupEnabled)
1533 mEventData->mDecorator->SetPopupActive(true);
1535 mEventData->mDecoratorUpdated = true;
1538 case EventData::EDITING:
1540 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1541 if(mEventData->mCursorBlinkEnabled)
1543 mEventData->mDecorator->StartCursorBlink();
1545 // Grab handle is not shown until a tap is received whilst EDITING
1546 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1547 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1548 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1549 mEventData->mDecorator->SetHighlightActive(false);
1550 if(mEventData->mGrabHandlePopupEnabled)
1552 mEventData->mDecorator->SetPopupActive(false);
1554 mEventData->mDecoratorUpdated = true;
1557 case EventData::EDITING_WITH_POPUP:
1559 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState);
1561 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1562 if(mEventData->mCursorBlinkEnabled)
1564 mEventData->mDecorator->StartCursorBlink();
1566 if(mEventData->mSelectionEnabled)
1568 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1569 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1570 mEventData->mDecorator->SetHighlightActive(false);
1572 else if(mEventData->mGrabHandleEnabled)
1574 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1576 if(mEventData->mGrabHandlePopupEnabled)
1579 mEventData->mDecorator->SetPopupActive(true);
1581 mEventData->mDecoratorUpdated = true;
1584 case EventData::EDITING_WITH_GRAB_HANDLE:
1586 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState);
1588 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1589 if(mEventData->mCursorBlinkEnabled)
1591 mEventData->mDecorator->StartCursorBlink();
1593 // Grab handle is not shown until a tap is received whilst EDITING
1594 if(mEventData->mGrabHandleEnabled)
1596 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1598 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1599 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1600 mEventData->mDecorator->SetHighlightActive(false);
1601 if(mEventData->mGrabHandlePopupEnabled)
1603 mEventData->mDecorator->SetPopupActive(false);
1605 mEventData->mDecoratorUpdated = true;
1608 case EventData::SELECTION_HANDLE_PANNING:
1610 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1611 mEventData->mDecorator->StopCursorBlink();
1612 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1613 if(mEventData->mGrabHandleEnabled)
1615 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
1616 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
1618 mEventData->mDecorator->SetHighlightActive(true);
1619 if(mEventData->mGrabHandlePopupEnabled)
1621 mEventData->mDecorator->SetPopupActive(false);
1623 mEventData->mDecoratorUpdated = true;
1626 case EventData::GRAB_HANDLE_PANNING:
1628 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState);
1630 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1631 if(mEventData->mCursorBlinkEnabled)
1633 mEventData->mDecorator->StartCursorBlink();
1635 if(mEventData->mGrabHandleEnabled)
1637 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1639 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1640 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1641 mEventData->mDecorator->SetHighlightActive(false);
1642 if(mEventData->mGrabHandlePopupEnabled)
1644 mEventData->mDecorator->SetPopupActive(false);
1646 mEventData->mDecoratorUpdated = true;
1649 case EventData::EDITING_WITH_PASTE_POPUP:
1651 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState);
1653 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1654 if(mEventData->mCursorBlinkEnabled)
1656 mEventData->mDecorator->StartCursorBlink();
1659 if(mEventData->mGrabHandleEnabled)
1661 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1663 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1664 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1665 mEventData->mDecorator->SetHighlightActive(false);
1667 if(mEventData->mGrabHandlePopupEnabled)
1670 mEventData->mDecorator->SetPopupActive(true);
1672 mEventData->mDecoratorUpdated = true;
1675 case EventData::TEXT_PANNING:
1677 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1678 mEventData->mDecorator->StopCursorBlink();
1679 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1680 if(mEventData->mDecorator->IsHandleActive(LEFT_SELECTION_HANDLE) ||
1681 mEventData->mDecorator->IsHandleActive(RIGHT_SELECTION_HANDLE))
1683 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1684 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1685 mEventData->mDecorator->SetHighlightActive(true);
1688 if(mEventData->mGrabHandlePopupEnabled)
1690 mEventData->mDecorator->SetPopupActive(false);
1693 mEventData->mDecoratorUpdated = true;
1700 void Controller::Impl::GetCursorPosition(CharacterIndex logical,
1701 CursorInfo& cursorInfo)
1703 if(!IsShowingRealText())
1705 // Do not want to use the place-holder text to set the cursor position.
1707 // Use the line's height of the font's family set to set the cursor's size.
1708 // If there is no font's family set, use the default font.
1709 // Use the current alignment to place the cursor at the beginning, center or end of the box.
1711 cursorInfo.lineOffset = 0.f;
1712 cursorInfo.lineHeight = GetDefaultFontLineHeight();
1713 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1716 if(mModel->mMatchSystemLanguageDirection)
1718 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1721 switch(mModel->mHorizontalAlignment)
1723 case Text::HorizontalAlignment::BEGIN:
1727 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1731 cursorInfo.primaryPosition.x = 0.f;
1735 case Text::HorizontalAlignment::CENTER:
1737 cursorInfo.primaryPosition.x = floorf(0.5f * mModel->mVisualModel->mControlSize.width);
1740 case Text::HorizontalAlignment::END:
1744 cursorInfo.primaryPosition.x = 0.f;
1748 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1754 // Nothing else to do.
1758 const bool isMultiLine = (Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout());
1759 GetCursorPositionParameters parameters;
1760 parameters.visualModel = mModel->mVisualModel;
1761 parameters.logicalModel = mModel->mLogicalModel;
1762 parameters.metrics = mMetrics;
1763 parameters.logical = logical;
1764 parameters.isMultiline = isMultiLine;
1766 Text::GetCursorPosition(parameters,
1769 // Adds Outline offset.
1770 const float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1771 cursorInfo.primaryPosition.x += outlineWidth;
1772 cursorInfo.primaryPosition.y += outlineWidth;
1773 cursorInfo.secondaryPosition.x += outlineWidth;
1774 cursorInfo.secondaryPosition.y += outlineWidth;
1778 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
1780 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
1781 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
1783 if(0.f > cursorInfo.primaryPosition.x)
1785 cursorInfo.primaryPosition.x = 0.f;
1788 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>(mEventData->mDecorator->GetCursorWidth());
1789 if(cursorInfo.primaryPosition.x > edgeWidth)
1791 cursorInfo.primaryPosition.x = edgeWidth;
1796 CharacterIndex Controller::Impl::CalculateNewCursorIndex(CharacterIndex index) const
1798 if(nullptr == mEventData)
1800 // Nothing to do if there is no text input.
1804 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1806 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1807 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1809 GlyphIndex glyphIndex = *(charactersToGlyphBuffer + index);
1810 Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1812 if(numberOfCharacters > 1u)
1814 const Script script = mModel->mLogicalModel->GetScript(index);
1815 if(HasLigatureMustBreak(script))
1817 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
1818 numberOfCharacters = 1u;
1823 while(0u == numberOfCharacters)
1826 numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1830 if(index < mEventData->mPrimaryCursorPosition)
1832 cursorIndex -= numberOfCharacters;
1836 cursorIndex += numberOfCharacters;
1839 // Will update the cursor hook position.
1840 mEventData->mUpdateCursorHookPosition = true;
1845 void Controller::Impl::UpdateCursorPosition(const CursorInfo& cursorInfo)
1847 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this);
1848 if(nullptr == mEventData)
1850 // Nothing to do if there is no text input.
1851 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n");
1855 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
1857 mEventData->mDecorator->SetGlyphOffset(PRIMARY_CURSOR, cursorInfo.glyphOffset);
1859 // Sets the cursor position.
1860 mEventData->mDecorator->SetPosition(PRIMARY_CURSOR,
1863 cursorInfo.primaryCursorHeight,
1864 cursorInfo.lineHeight);
1865 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y);
1867 if(mEventData->mUpdateGrabHandlePosition)
1869 // Sets the grab handle position.
1870 mEventData->mDecorator->SetPosition(GRAB_HANDLE,
1872 cursorInfo.lineOffset + mModel->mScrollPosition.y,
1873 cursorInfo.lineHeight);
1876 if(cursorInfo.isSecondaryCursor)
1878 mEventData->mDecorator->SetPosition(SECONDARY_CURSOR,
1879 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
1880 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
1881 cursorInfo.secondaryCursorHeight,
1882 cursorInfo.lineHeight);
1883 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y);
1886 // Set which cursors are active according the state.
1887 if(EventData::IsEditingState(mEventData->mState) || (EventData::GRAB_HANDLE_PANNING == mEventData->mState))
1889 if(cursorInfo.isSecondaryCursor)
1891 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_BOTH);
1895 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1900 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1903 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n");
1906 void Controller::Impl::UpdateSelectionHandle(HandleType handleType,
1907 const CursorInfo& cursorInfo)
1909 SelectionHandleController::Update(*this, handleType, cursorInfo);
1912 void Controller::Impl::ClampHorizontalScroll(const Vector2& layoutSize)
1914 // Clamp between -space & -alignment offset.
1916 if(layoutSize.width > mModel->mVisualModel->mControlSize.width)
1918 const float space = (layoutSize.width - mModel->mVisualModel->mControlSize.width) + mModel->mAlignmentOffset;
1919 mModel->mScrollPosition.x = (mModel->mScrollPosition.x < -space) ? -space : mModel->mScrollPosition.x;
1920 mModel->mScrollPosition.x = (mModel->mScrollPosition.x > -mModel->mAlignmentOffset) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
1922 mEventData->mDecoratorUpdated = true;
1926 mModel->mScrollPosition.x = 0.f;
1930 void Controller::Impl::ClampVerticalScroll(const Vector2& layoutSize)
1932 if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1934 // Nothing to do if the text is single line.
1938 // Clamp between -space & 0.
1939 if(layoutSize.height > mModel->mVisualModel->mControlSize.height)
1941 const float space = (layoutSize.height - mModel->mVisualModel->mControlSize.height);
1942 mModel->mScrollPosition.y = (mModel->mScrollPosition.y < -space) ? -space : mModel->mScrollPosition.y;
1943 mModel->mScrollPosition.y = (mModel->mScrollPosition.y > 0.f) ? 0.f : mModel->mScrollPosition.y;
1945 mEventData->mDecoratorUpdated = true;
1949 mModel->mScrollPosition.y = 0.f;
1953 void Controller::Impl::ScrollToMakePositionVisible(const Vector2& position, float lineHeight)
1955 const float cursorWidth = mEventData->mDecorator ? static_cast<float>(mEventData->mDecorator->GetCursorWidth()) : 0.f;
1957 // position is in actor's coords.
1958 const float positionEndX = position.x + cursorWidth;
1959 const float positionEndY = position.y + lineHeight;
1961 // Transform the position to decorator coords.
1962 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
1963 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
1965 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
1966 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
1968 if(decoratorPositionBeginX < 0.f)
1970 mModel->mScrollPosition.x = -position.x;
1972 else if(decoratorPositionEndX > mModel->mVisualModel->mControlSize.width)
1974 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
1977 if(Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout())
1979 if(decoratorPositionBeginY < 0.f)
1981 mModel->mScrollPosition.y = -position.y;
1983 else if(decoratorPositionEndY > mModel->mVisualModel->mControlSize.height)
1985 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
1990 void Controller::Impl::ScrollTextToMatchCursor(const CursorInfo& cursorInfo)
1992 // Get the current cursor position in decorator coords.
1993 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition(PRIMARY_CURSOR);
1995 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter(mEventData->mPrimaryCursorPosition);
1997 // Calculate the offset to match the cursor position before the character was deleted.
1998 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2000 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
2001 if(mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() - 1u)
2003 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset(PRIMARY_CURSOR);
2004 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
2007 ClampHorizontalScroll(mModel->mVisualModel->GetLayoutSize());
2008 ClampVerticalScroll(mModel->mVisualModel->GetLayoutSize());
2010 // Makes the new cursor position visible if needed.
2011 ScrollToMakePositionVisible(cursorInfo.primaryPosition, cursorInfo.lineHeight);
2014 void Controller::Impl::ScrollTextToMatchCursor()
2016 CursorInfo cursorInfo;
2017 GetCursorPosition(mEventData->mPrimaryCursorPosition, cursorInfo);
2018 ScrollTextToMatchCursor(cursorInfo);
2021 void Controller::Impl::RequestRelayout()
2023 if(nullptr != mControlInterface)
2025 mControlInterface->RequestTextRelayout();
2029 Actor Controller::Impl::CreateBackgroundActor()
2031 // NOTE: Currently we only support background color for left-to-right text.
2035 Length numberOfGlyphs = mView.GetNumberOfGlyphs();
2036 if(numberOfGlyphs > 0u)
2038 Vector<GlyphInfo> glyphs;
2039 glyphs.Resize(numberOfGlyphs);
2041 Vector<Vector2> positions;
2042 positions.Resize(numberOfGlyphs);
2044 // Get the line where the glyphs are laid-out.
2045 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
2046 float alignmentOffset = lineRun->alignmentOffset;
2047 numberOfGlyphs = mView.GetGlyphs(glyphs.Begin(),
2053 glyphs.Resize(numberOfGlyphs);
2054 positions.Resize(numberOfGlyphs);
2056 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
2057 const Vector2* const positionsBuffer = positions.Begin();
2059 BackgroundMesh mesh;
2060 mesh.mVertices.Reserve(4u * glyphs.Size());
2061 mesh.mIndices.Reserve(6u * glyphs.Size());
2063 const Vector2 textSize = mView.GetLayoutSize();
2065 const float offsetX = textSize.width * 0.5f;
2066 const float offsetY = textSize.height * 0.5f;
2068 const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
2069 const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
2070 const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
2073 uint32_t numberOfQuads = 0u;
2074 Length yLineOffset = 0;
2075 Length prevLineIndex = 0;
2076 LineIndex lineIndex;
2077 Length numberOfLines;
2079 for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i)
2081 const GlyphInfo& glyph = *(glyphsBuffer + i);
2083 // Get the background color of the character.
2084 // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
2085 const ColorIndex backgroundColorIndex = (nullptr == backgroundColorsBuffer) ? 0u : *(backgroundColorIndicesBuffer + i);
2086 const Vector4& backgroundColor = (0u == backgroundColorIndex) ? defaultBackgroundColor : *(backgroundColorsBuffer + backgroundColorIndex - 1u);
2088 mModel->mVisualModel->GetNumberOfLines(i, 1, lineIndex, numberOfLines);
2089 Length lineHeight = lineRun[lineIndex].ascender + -(lineRun[lineIndex].descender) + lineRun[lineIndex].lineSpacing;
2091 if(lineIndex != prevLineIndex)
2093 yLineOffset += lineHeight;
2096 // Only create quads for glyphs with a background color
2097 if(backgroundColor != Color::TRANSPARENT)
2099 const Vector2 position = *(positionsBuffer + i);
2101 if(i == 0u && glyphSize == 1u) // Only one glyph in the whole text
2103 quad.x = position.x;
2104 quad.y = yLineOffset;
2105 quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
2106 quad.w = lineHeight;
2108 else if((lineIndex != prevLineIndex) || (i == 0u)) // The first glyph in the line
2110 quad.x = position.x;
2111 quad.y = yLineOffset;
2112 quad.z = quad.x - glyph.xBearing + glyph.advance;
2113 quad.w = quad.y + lineHeight;
2115 else if(i == glyphSize - 1u) // The last glyph in the whole text
2117 quad.x = position.x - glyph.xBearing;
2118 quad.y = yLineOffset;
2119 quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
2120 quad.w = quad.y + lineHeight;
2122 else // The glyph in the middle of the text
2124 quad.x = position.x - glyph.xBearing;
2125 quad.y = yLineOffset;
2126 quad.z = quad.x + glyph.advance;
2127 quad.w = quad.y + lineHeight;
2130 BackgroundVertex vertex;
2133 vertex.mPosition.x = quad.x - offsetX;
2134 vertex.mPosition.y = quad.y - offsetY;
2135 vertex.mColor = backgroundColor;
2136 mesh.mVertices.PushBack(vertex);
2139 vertex.mPosition.x = quad.z - offsetX;
2140 vertex.mPosition.y = quad.y - offsetY;
2141 vertex.mColor = backgroundColor;
2142 mesh.mVertices.PushBack(vertex);
2145 vertex.mPosition.x = quad.x - offsetX;
2146 vertex.mPosition.y = quad.w - offsetY;
2147 vertex.mColor = backgroundColor;
2148 mesh.mVertices.PushBack(vertex);
2151 vertex.mPosition.x = quad.z - offsetX;
2152 vertex.mPosition.y = quad.w - offsetY;
2153 vertex.mColor = backgroundColor;
2154 mesh.mVertices.PushBack(vertex);
2156 // Six indices in counter clockwise winding
2157 mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
2158 mesh.mIndices.PushBack(0u + 4 * numberOfQuads);
2159 mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
2160 mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
2161 mesh.mIndices.PushBack(3u + 4 * numberOfQuads);
2162 mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
2167 if(lineIndex != prevLineIndex)
2169 prevLineIndex = lineIndex;
2173 // Only create the background actor if there are glyphs with background color
2174 if(mesh.mVertices.Count() > 0u)
2176 Property::Map quadVertexFormat;
2177 quadVertexFormat["aPosition"] = Property::VECTOR2;
2178 quadVertexFormat["aColor"] = Property::VECTOR4;
2180 VertexBuffer quadVertices = VertexBuffer::New(quadVertexFormat);
2181 quadVertices.SetData(&mesh.mVertices[0], mesh.mVertices.Size());
2183 Geometry quadGeometry = Geometry::New();
2184 quadGeometry.AddVertexBuffer(quadVertices);
2185 quadGeometry.SetIndexBuffer(&mesh.mIndices[0], mesh.mIndices.Size());
2187 if(!mShaderBackground)
2189 mShaderBackground = Shader::New(SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_VERT, SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_FRAG);
2192 Dali::Renderer renderer = Dali::Renderer::New(quadGeometry, mShaderBackground);
2193 renderer.SetProperty(Dali::Renderer::Property::BLEND_MODE, BlendMode::ON);
2194 renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT);
2196 actor = Actor::New();
2197 actor.SetProperty(Dali::Actor::Property::NAME, "TextBackgroundColorActor");
2198 actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
2199 actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
2200 actor.SetProperty(Actor::Property::SIZE, textSize);
2201 actor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR);
2202 actor.AddRenderer(renderer);
2209 void Controller::Impl::CopyUnderlinedFromLogicalToVisualModels(bool shouldClearPreUnderlineRuns)
2211 //Underlined character runs for markup-processor
2212 const Vector<UnderlinedCharacterRun>& underlinedCharacterRuns = mModel->mLogicalModel->mUnderlinedCharacterRuns;
2213 const Vector<GlyphIndex>& charactersToGlyph = mModel->mVisualModel->mCharactersToGlyph;
2214 const Vector<Length>& glyphsPerCharacter = mModel->mVisualModel->mGlyphsPerCharacter;
2216 if(shouldClearPreUnderlineRuns)
2218 mModel->mVisualModel->mUnderlineRuns.Clear();
2221 for(Vector<UnderlinedCharacterRun>::ConstIterator it = underlinedCharacterRuns.Begin(), endIt = underlinedCharacterRuns.End(); it != endIt; ++it)
2223 CharacterIndex characterIndex = it->characterRun.characterIndex;
2224 Length numberOfCharacters = it->characterRun.numberOfCharacters;
2225 for(Length index = 0u; index < numberOfCharacters; index++)
2227 GlyphRun underlineGlyphRun;
2228 underlineGlyphRun.glyphIndex = charactersToGlyph[characterIndex + index];
2229 underlineGlyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex + index];
2230 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineGlyphRun);
2237 } // namespace Toolkit