/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <dali-toolkit/internal/text/text-controller-impl.h>
// EXTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/window-devel.h>
#include <dali/integration-api/debug.h>
+#include <dali/public-api/actors/layer.h>
#include <dali/public-api/rendering/renderer.h>
+#include <cmath>
// INTERNAL INCLUDES
-#include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
-#include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
-#include <dali-toolkit/internal/text/bidirectional-support.h>
#include <dali-toolkit/internal/text/character-set-conversion.h>
-#include <dali-toolkit/internal/text/color-segmentation.h>
#include <dali-toolkit/internal/text/cursor-helper-functions.h>
-#include <dali-toolkit/internal/text/hyphenator.h>
-#include <dali-toolkit/internal/text/multi-language-support.h>
-#include <dali-toolkit/internal/text/segmentation.h>
-#include <dali-toolkit/internal/text/shaper.h>
+#include <dali-toolkit/internal/text/glyph-metrics-helper.h>
#include <dali-toolkit/internal/text/text-control-interface.h>
+#include <dali-toolkit/internal/text/text-controller-impl-data-clearer.h>
#include <dali-toolkit/internal/text/text-controller-impl-event-handler.h>
+#include <dali-toolkit/internal/text/text-controller-impl-model-updater.h>
+#include <dali-toolkit/internal/text/text-controller-placeholder-handler.h>
+#include <dali-toolkit/internal/text/text-controller-relayouter.h>
#include <dali-toolkit/internal/text/text-editable-control-interface.h>
#include <dali-toolkit/internal/text/text-enumerations-impl.h>
#include <dali-toolkit/internal/text/text-run-container.h>
#include <dali-toolkit/internal/text/text-selection-handle-controller.h>
+#include <dali-toolkit/internal/text/underlined-glyph-run.h>
using namespace Dali;
Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
#endif
-struct BackgroundVertex
-{
- Vector2 mPosition; ///< Vertex posiiton
- Vector4 mColor; ///< Vertex color
-};
-
-struct BackgroundMesh
-{
- Vector<BackgroundVertex> mVertices; ///< container of vertices
- Vector<unsigned short> mIndices; ///< container of indices
-};
+constexpr float MAX_FLOAT = std::numeric_limits<float>::max();
-// The relative luminance of a color is defined as (L = 0.2126 * R + 0.7152 * G + 0.0722 * B)
-// based on W3C Recommendations (https://www.w3.org/TR/WCAG20/)
-const float BRIGHTNESS_THRESHOLD = 0.179f;
-const float CONSTANT_R = 0.2126f;
-const float CONSTANT_G = 0.7152f;
-const float CONSTANT_B = 0.0722f;
-const Dali::Vector4 BLACK(0.f, 0.f, 0.f, 1.f);
-const Dali::Vector4 WHITE(1.f, 1.f, 1.f, 1.f);
-const Dali::Vector4 LIGHT_BLUE(0.75f, 0.96f, 1.f, 1.f);
-const Dali::Vector4 BACKGROUND_SUB4(0.58f, 0.87f, 0.96f, 1.f);
-const Dali::Vector4 BACKGROUND_SUB5(0.83f, 0.94f, 0.98f, 1.f);
-const Dali::Vector4 BACKGROUND_SUB6(1.f, 0.5f, 0.5f, 1.f);
-const Dali::Vector4 BACKGROUND_SUB7(1.f, 0.8f, 0.8f, 1.f);
+const std::string EMPTY_STRING("");
} // namespace
-namespace Dali
+namespace Dali::Toolkit::Text
+{
+namespace
{
-namespace Toolkit
+void SetDefaultInputStyle(InputStyle& inputStyle, const FontDefaults* const fontDefaults, const Vector4& textColor)
{
-namespace Text
+ // Sets the default text's color.
+ inputStyle.textColor = textColor;
+ inputStyle.isDefaultColor = true;
+
+ inputStyle.familyName.clear();
+ inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
+ inputStyle.width = TextAbstraction::FontWidth::NORMAL;
+ inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
+ inputStyle.size = 0.f;
+
+ inputStyle.lineSpacing = 0.f;
+
+ inputStyle.underlineProperties.clear();
+ inputStyle.shadowProperties.clear();
+ inputStyle.embossProperties.clear();
+ inputStyle.outlineProperties.clear();
+
+ inputStyle.isFamilyDefined = false;
+ inputStyle.isWeightDefined = false;
+ inputStyle.isWidthDefined = false;
+ inputStyle.isSlantDefined = false;
+ inputStyle.isSizeDefined = false;
+
+ inputStyle.isLineSpacingDefined = false;
+
+ inputStyle.isUnderlineDefined = false;
+ inputStyle.isShadowDefined = false;
+ inputStyle.isEmbossDefined = false;
+ inputStyle.isOutlineDefined = false;
+
+ // Sets the default font's family name, weight, width, slant and size.
+ if(fontDefaults)
+ {
+ if(fontDefaults->familyDefined)
+ {
+ inputStyle.familyName = fontDefaults->mFontDescription.family;
+ inputStyle.isFamilyDefined = true;
+ }
+
+ if(fontDefaults->weightDefined)
+ {
+ inputStyle.weight = fontDefaults->mFontDescription.weight;
+ inputStyle.isWeightDefined = true;
+ }
+
+ if(fontDefaults->widthDefined)
+ {
+ inputStyle.width = fontDefaults->mFontDescription.width;
+ inputStyle.isWidthDefined = true;
+ }
+
+ if(fontDefaults->slantDefined)
+ {
+ inputStyle.slant = fontDefaults->mFontDescription.slant;
+ inputStyle.isSlantDefined = true;
+ }
+
+ if(fontDefaults->sizeDefined)
+ {
+ inputStyle.size = fontDefaults->mDefaultPointSize;
+ inputStyle.isSizeDefined = true;
+ }
+ }
+}
+
+void ChangeTextControllerState(Controller::Impl& impl, EventData::State newState)
{
+ EventData* eventData = impl.mEventData;
+
+ if(nullptr == eventData)
+ {
+ // Nothing to do if there is no text input.
+ return;
+ }
+
+ DecoratorPtr& decorator = eventData->mDecorator;
+ if(!decorator)
+ {
+ // Nothing to do if there is no decorator.
+ return;
+ }
+
+ DALI_LOG_INFO(gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", eventData->mState, newState);
+
+ if(eventData->mState != newState)
+ {
+ eventData->mPreviousState = eventData->mState;
+ eventData->mState = newState;
+
+ switch(eventData->mState)
+ {
+ case EventData::INACTIVE:
+ {
+ decorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
+ decorator->StopCursorBlink();
+ decorator->SetHandleActive(GRAB_HANDLE, false);
+ decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
+ decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
+ decorator->SetHighlightActive(false);
+ decorator->SetPopupActive(false);
+ eventData->mDecoratorUpdated = true;
+ break;
+ }
+
+ case EventData::INTERRUPTED:
+ {
+ decorator->SetHandleActive(GRAB_HANDLE, false);
+ decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
+ decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
+ decorator->SetHighlightActive(false);
+ decorator->SetPopupActive(false);
+ eventData->mDecoratorUpdated = true;
+ break;
+ }
+
+ case EventData::SELECTING:
+ {
+ decorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
+ decorator->StopCursorBlink();
+ decorator->SetHandleActive(GRAB_HANDLE, false);
+ if(eventData->mGrabHandleEnabled)
+ {
+ decorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
+ decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
+ }
+ decorator->SetHighlightActive(true);
+ if(eventData->mGrabHandlePopupEnabled)
+ {
+ impl.SetPopupButtons();
+ decorator->SetPopupActive(true);
+ }
+ eventData->mDecoratorUpdated = true;
+ break;
+ }
+
+ case EventData::EDITING:
+ {
+ decorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
+ if(eventData->mCursorBlinkEnabled)
+ {
+ decorator->StartCursorBlink();
+ }
+ // Grab handle is not shown until a tap is received whilst EDITING
+ decorator->SetHandleActive(GRAB_HANDLE, false);
+ decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
+ decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
+ decorator->SetHighlightActive(false);
+ if(eventData->mGrabHandlePopupEnabled)
+ {
+ decorator->SetPopupActive(false);
+ }
+ eventData->mDecoratorUpdated = true;
+ break;
+ }
+ case EventData::EDITING_WITH_POPUP:
+ {
+ DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState);
+
+ decorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
+ if(eventData->mCursorBlinkEnabled)
+ {
+ decorator->StartCursorBlink();
+ }
+ if(eventData->mSelectionEnabled)
+ {
+ decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
+ decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
+ decorator->SetHighlightActive(false);
+ }
+ else if(eventData->mGrabHandleEnabled)
+ {
+ decorator->SetHandleActive(GRAB_HANDLE, true);
+ }
+ if(eventData->mGrabHandlePopupEnabled)
+ {
+ impl.SetPopupButtons();
+ decorator->SetPopupActive(true);
+ }
+ eventData->mDecoratorUpdated = true;
+ break;
+ }
+ case EventData::EDITING_WITH_GRAB_HANDLE:
+ {
+ DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState);
+
+ decorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
+ if(eventData->mCursorBlinkEnabled)
+ {
+ decorator->StartCursorBlink();
+ }
+ // Grab handle is not shown until a tap is received whilst EDITING
+ if(eventData->mGrabHandleEnabled)
+ {
+ decorator->SetHandleActive(GRAB_HANDLE, true);
+ }
+ decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
+ decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
+ decorator->SetHighlightActive(false);
+ if(eventData->mGrabHandlePopupEnabled)
+ {
+ decorator->SetPopupActive(false);
+ }
+ eventData->mDecoratorUpdated = true;
+ break;
+ }
+
+ case EventData::SELECTION_HANDLE_PANNING:
+ {
+ decorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
+ decorator->StopCursorBlink();
+ decorator->SetHandleActive(GRAB_HANDLE, false);
+ if(eventData->mGrabHandleEnabled)
+ {
+ decorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
+ decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
+ }
+ decorator->SetHighlightActive(true);
+ if(eventData->mGrabHandlePopupEnabled)
+ {
+ decorator->SetPopupActive(false);
+ }
+ eventData->mDecoratorUpdated = true;
+ break;
+ }
+
+ case EventData::GRAB_HANDLE_PANNING:
+ {
+ DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState);
+
+ decorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
+ if(eventData->mCursorBlinkEnabled)
+ {
+ decorator->StartCursorBlink();
+ }
+ if(eventData->mGrabHandleEnabled)
+ {
+ decorator->SetHandleActive(GRAB_HANDLE, true);
+ }
+ decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
+ decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
+ decorator->SetHighlightActive(false);
+ if(eventData->mGrabHandlePopupEnabled)
+ {
+ decorator->SetPopupActive(false);
+ }
+ eventData->mDecoratorUpdated = true;
+ break;
+ }
+
+ case EventData::EDITING_WITH_PASTE_POPUP:
+ {
+ DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState);
+
+ decorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
+ if(eventData->mCursorBlinkEnabled)
+ {
+ decorator->StartCursorBlink();
+ }
+
+ if(eventData->mGrabHandleEnabled)
+ {
+ decorator->SetHandleActive(GRAB_HANDLE, true);
+ }
+ decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
+ decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
+ decorator->SetHighlightActive(false);
+
+ if(eventData->mGrabHandlePopupEnabled)
+ {
+ impl.SetPopupButtons();
+ decorator->SetPopupActive(true);
+ }
+ eventData->mDecoratorUpdated = true;
+ break;
+ }
+
+ case EventData::TEXT_PANNING:
+ {
+ decorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
+ decorator->StopCursorBlink();
+ decorator->SetHandleActive(GRAB_HANDLE, false);
+ if(eventData->mDecorator->IsHandleActive(LEFT_SELECTION_HANDLE) ||
+ decorator->IsHandleActive(RIGHT_SELECTION_HANDLE))
+ {
+ decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
+ decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
+ decorator->SetHighlightActive(true);
+ }
+
+ if(eventData->mGrabHandlePopupEnabled)
+ {
+ decorator->SetPopupActive(false);
+ }
+
+ eventData->mDecoratorUpdated = true;
+ break;
+ }
+ }
+ }
+}
+
+} // unnamed Namespace
+
EventData::EventData(DecoratorPtr decorator, InputMethodContext& inputMethodContext)
: mDecorator(decorator),
mInputMethodContext(inputMethodContext),
- mPlaceholderFont(NULL),
+ mPlaceholderFont(nullptr),
mPlaceholderTextActive(),
mPlaceholderTextInactive(),
mPlaceholderTextColor(0.8f, 0.8f, 0.8f, 0.8f), // This color has been published in the Public API (placeholder-properties.h).
return numberOfWhiteSpaces;
}
+void Controller::Impl::GetText(std::string& text) const
+{
+ if(!IsShowingPlaceholderText())
+ {
+ // Retrieves the text string.
+ GetText(0u, text);
+ }
+ else
+ {
+ DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Controller::GetText %p empty (but showing placeholder)\n", this);
+ }
+}
+
void Controller::Impl::GetText(CharacterIndex index, std::string& text) const
{
// Get the total number of characters.
}
}
+Dali::LayoutDirection::Type Controller::Impl::GetLayoutDirection(Dali::Actor& actor) const
+{
+ if(mModel->mMatchLayoutDirection == DevelText::MatchLayoutDirection::LOCALE ||
+ (mModel->mMatchLayoutDirection == DevelText::MatchLayoutDirection::INHERIT && !mIsLayoutDirectionChanged))
+ {
+ return static_cast<Dali::LayoutDirection::Type>(DevelWindow::Get(actor).GetRootLayer().GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get<int>());
+ }
+ else
+ {
+ return static_cast<Dali::LayoutDirection::Type>(actor.GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get<int>());
+ }
+}
+
+Toolkit::DevelText::TextDirection::Type Controller::Impl::GetTextDirection()
+{
+ if(mUpdateTextDirection)
+ {
+ // Operations that can be done only once until the text changes.
+ const OperationsMask onlyOnceOperations = static_cast<OperationsMask>(CONVERT_TO_UTF32 |
+ GET_SCRIPTS |
+ VALIDATE_FONTS |
+ GET_LINE_BREAKS |
+ BIDI_INFO |
+ SHAPE_TEXT |
+ GET_GLYPH_METRICS);
+
+ // Set the update info to relayout the whole text.
+ mTextUpdateInfo.mParagraphCharacterIndex = 0u;
+ mTextUpdateInfo.mRequestedNumberOfCharacters = mModel->mLogicalModel->mText.Count();
+
+ // Make sure the model is up-to-date before layouting
+ UpdateModel(onlyOnceOperations);
+
+ Vector3 naturalSize;
+ Relayouter::DoRelayout(*this,
+ Size(MAX_FLOAT, MAX_FLOAT),
+ static_cast<OperationsMask>(onlyOnceOperations |
+ LAYOUT | REORDER | UPDATE_DIRECTION),
+ naturalSize.GetVectorXY());
+
+ // Do not do again the only once operations.
+ mOperationsPending = static_cast<OperationsMask>(mOperationsPending & ~onlyOnceOperations);
+
+ // Clear the update info. This info will be set the next time the text is updated.
+ mTextUpdateInfo.Clear();
+
+ // FullRelayoutNeeded should be true because DoRelayout is MAX_FLOAT, MAX_FLOAT.
+ mTextUpdateInfo.mFullRelayoutNeeded = true;
+
+ mUpdateTextDirection = false;
+ }
+
+ return mIsTextDirectionRTL ? Toolkit::DevelText::TextDirection::RIGHT_TO_LEFT : Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT;
+}
+
void Controller::Impl::CalculateTextUpdateIndices(Length& numberOfCharacters)
{
mTextUpdateInfo.mParagraphCharacterIndex = 0u;
mTextUpdateInfo.mStartGlyphIndex = *(mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex);
}
-void Controller::Impl::ClearFullModelData(OperationsMask operations)
+void Controller::Impl::ClearModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
{
- if(NO_OPERATION != (GET_LINE_BREAKS & operations))
- {
- mModel->mLogicalModel->mLineBreakInfo.Clear();
- mModel->mLogicalModel->mParagraphInfo.Clear();
- }
+ ControllerImplDataClearer::ClearModelData(*this, startIndex, endIndex, operations);
+}
- if(NO_OPERATION != (GET_SCRIPTS & operations))
- {
- mModel->mLogicalModel->mScriptRuns.Clear();
- }
+bool Controller::Impl::UpdateModel(OperationsMask operationsRequired)
+{
+ return ControllerImplModelUpdater::Update(*this, operationsRequired);
+}
+
+void Controller::Impl::RetrieveDefaultInputStyle(InputStyle& inputStyle)
+{
+ SetDefaultInputStyle(inputStyle, mFontDefaults, mTextColor);
+}
- if(NO_OPERATION != (VALIDATE_FONTS & operations))
+float Controller::Impl::GetDefaultFontLineHeight()
+{
+ FontId defaultFontId = 0u;
+ if(nullptr == mFontDefaults)
{
- mModel->mLogicalModel->mFontRuns.Clear();
+ TextAbstraction::FontDescription fontDescription;
+ defaultFontId = mFontClient.GetFontId(fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * GetFontSizeScale());
}
-
- if(0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count())
+ else
{
- if(NO_OPERATION != (BIDI_INFO & operations))
- {
- mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
- mModel->mLogicalModel->mCharacterDirections.Clear();
- }
+ defaultFontId = mFontDefaults->GetFontId(mFontClient, mFontDefaults->mDefaultPointSize * GetFontSizeScale());
+ }
- if(NO_OPERATION != (REORDER & operations))
- {
- // Free the allocated memory used to store the conversion table in the bidirectional line info run.
- for(Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
- endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
- it != endIt;
- ++it)
- {
- BidirectionalLineInfoRun& bidiLineInfo = *it;
+ Text::FontMetrics fontMetrics;
+ mMetrics->GetFontMetrics(defaultFontId, fontMetrics);
- free(bidiLineInfo.visualToLogicalMap);
- bidiLineInfo.visualToLogicalMap = NULL;
- }
- mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
- }
- }
+ return (fontMetrics.ascender - fontMetrics.descender);
+}
- if(NO_OPERATION != (SHAPE_TEXT & operations))
+bool Controller::Impl::SetDefaultLineSpacing(float lineSpacing)
+{
+ if(std::fabs(lineSpacing - mLayoutEngine.GetDefaultLineSpacing()) > Math::MACHINE_EPSILON_1000)
{
- mModel->mVisualModel->mGlyphs.Clear();
- mModel->mVisualModel->mGlyphsToCharacters.Clear();
- mModel->mVisualModel->mCharactersToGlyph.Clear();
- mModel->mVisualModel->mCharactersPerGlyph.Clear();
- mModel->mVisualModel->mGlyphsPerCharacter.Clear();
- mModel->mVisualModel->mGlyphPositions.Clear();
- }
+ mLayoutEngine.SetDefaultLineSpacing(lineSpacing);
- if(NO_OPERATION != (LAYOUT & operations))
- {
- mModel->mVisualModel->mLines.Clear();
+ RelayoutAllCharacters();
+ return true;
}
+ return false;
+}
- if(NO_OPERATION != (COLOR & operations))
+bool Controller::Impl::SetDefaultLineSize(float lineSize)
+{
+ if(std::fabs(lineSize - mLayoutEngine.GetDefaultLineSize()) > Math::MACHINE_EPSILON_1000)
{
- mModel->mVisualModel->mColorIndices.Clear();
- mModel->mVisualModel->mBackgroundColorIndices.Clear();
+ mLayoutEngine.SetDefaultLineSize(lineSize);
+
+ RelayoutAllCharacters();
+ return true;
}
+ return false;
}
-void Controller::Impl::ClearCharacterModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
+bool Controller::Impl::SetRelativeLineSize(float relativeLineSize)
{
- const CharacterIndex endIndexPlusOne = endIndex + 1u;
-
- if(NO_OPERATION != (GET_LINE_BREAKS & operations))
+ if(std::fabs(relativeLineSize - GetRelativeLineSize()) > Math::MACHINE_EPSILON_1000)
{
- // Clear the line break info.
- LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
+ mLayoutEngine.SetRelativeLineSize(relativeLineSize);
- mModel->mLogicalModel->mLineBreakInfo.Erase(lineBreakInfoBuffer + startIndex,
- lineBreakInfoBuffer + endIndexPlusOne);
-
- // Clear the paragraphs.
- ClearCharacterRuns(startIndex,
- endIndex,
- mModel->mLogicalModel->mParagraphInfo);
+ RelayoutAllCharacters();
+ return true;
}
+ return false;
+}
- if(NO_OPERATION != (GET_SCRIPTS & operations))
- {
- // Clear the scripts.
- ClearCharacterRuns(startIndex,
- endIndex,
- mModel->mLogicalModel->mScriptRuns);
- }
+float Controller::Impl::GetRelativeLineSize()
+{
+ return mLayoutEngine.GetRelativeLineSize();
+}
- if(NO_OPERATION != (VALIDATE_FONTS & operations))
+string Controller::Impl::GetSelectedText()
+{
+ string text;
+ if(EventData::SELECTING == mEventData->mState)
{
- // Clear the fonts.
- ClearCharacterRuns(startIndex,
- endIndex,
- mModel->mLogicalModel->mFontRuns);
- }
-
- if(0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count())
- {
- if(NO_OPERATION != (BIDI_INFO & operations))
- {
- // Clear the bidirectional paragraph info.
- ClearCharacterRuns(startIndex,
- endIndex,
- mModel->mLogicalModel->mBidirectionalParagraphInfo);
-
- // Clear the character's directions.
- CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
-
- mModel->mLogicalModel->mCharacterDirections.Erase(characterDirectionsBuffer + startIndex,
- characterDirectionsBuffer + endIndexPlusOne);
- }
-
- if(NO_OPERATION != (REORDER & operations))
- {
- uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
- uint32_t endRemoveIndex = startRemoveIndex;
- ClearCharacterRuns(startIndex,
- endIndex,
- mModel->mLogicalModel->mBidirectionalLineInfo,
- startRemoveIndex,
- endRemoveIndex);
-
- BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
-
- // Free the allocated memory used to store the conversion table in the bidirectional line info run.
- for(Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
- endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
- it != endIt;
- ++it)
- {
- BidirectionalLineInfoRun& bidiLineInfo = *it;
-
- free(bidiLineInfo.visualToLogicalMap);
- bidiLineInfo.visualToLogicalMap = NULL;
- }
-
- mModel->mLogicalModel->mBidirectionalLineInfo.Erase(bidirectionalLineInfoBuffer + startRemoveIndex,
- bidirectionalLineInfoBuffer + endRemoveIndex);
- }
- }
-}
-
-void Controller::Impl::ClearGlyphModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
-{
- const CharacterIndex endIndexPlusOne = endIndex + 1u;
- const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
-
- // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
- GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
- Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
-
- const GlyphIndex endGlyphIndexPlusOne = *(charactersToGlyphBuffer + endIndex) + *(glyphsPerCharacterBuffer + endIndex);
- const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
-
- if(NO_OPERATION != (SHAPE_TEXT & operations))
- {
- // Update the character to glyph indices.
- for(Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
- endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
- it != endIt;
- ++it)
- {
- CharacterIndex& index = *it;
- index -= numberOfGlyphsRemoved;
- }
-
- // Clear the character to glyph conversion table.
- mModel->mVisualModel->mCharactersToGlyph.Erase(charactersToGlyphBuffer + startIndex,
- charactersToGlyphBuffer + endIndexPlusOne);
-
- // Clear the glyphs per character table.
- mModel->mVisualModel->mGlyphsPerCharacter.Erase(glyphsPerCharacterBuffer + startIndex,
- glyphsPerCharacterBuffer + endIndexPlusOne);
-
- // Clear the glyphs buffer.
- GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
- mModel->mVisualModel->mGlyphs.Erase(glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
- glyphsBuffer + endGlyphIndexPlusOne);
-
- CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
-
- // Update the glyph to character indices.
- for(Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
- endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
- it != endIt;
- ++it)
- {
- CharacterIndex& index = *it;
- index -= numberOfCharactersRemoved;
- }
-
- // Clear the glyphs to characters buffer.
- mModel->mVisualModel->mGlyphsToCharacters.Erase(glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
- glyphsToCharactersBuffer + endGlyphIndexPlusOne);
-
- // Clear the characters per glyph buffer.
- Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
- mModel->mVisualModel->mCharactersPerGlyph.Erase(charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
- charactersPerGlyphBuffer + endGlyphIndexPlusOne);
-
- // Clear the positions buffer.
- Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
- mModel->mVisualModel->mGlyphPositions.Erase(positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
- positionsBuffer + endGlyphIndexPlusOne);
- }
-
- if(NO_OPERATION != (LAYOUT & operations))
- {
- // Clear the lines.
- uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
- uint32_t endRemoveIndex = startRemoveIndex;
- ClearCharacterRuns(startIndex,
- endIndex,
- mModel->mVisualModel->mLines,
- startRemoveIndex,
- endRemoveIndex);
-
- // Will update the glyph runs.
- startRemoveIndex = mModel->mVisualModel->mLines.Count();
- endRemoveIndex = startRemoveIndex;
- ClearGlyphRuns(mTextUpdateInfo.mStartGlyphIndex,
- endGlyphIndexPlusOne - 1u,
- mModel->mVisualModel->mLines,
- startRemoveIndex,
- endRemoveIndex);
-
- // Set the line index from where to insert the new laid-out lines.
- mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
-
- LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
- mModel->mVisualModel->mLines.Erase(linesBuffer + startRemoveIndex,
- linesBuffer + endRemoveIndex);
- }
-
- if(NO_OPERATION != (COLOR & operations))
- {
- if(0u != mModel->mVisualModel->mColorIndices.Count())
- {
- ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
- mModel->mVisualModel->mColorIndices.Erase(colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
- colorIndexBuffer + endGlyphIndexPlusOne);
- }
-
- if(0u != mModel->mVisualModel->mBackgroundColorIndices.Count())
- {
- ColorIndex* backgroundColorIndexBuffer = mModel->mVisualModel->mBackgroundColorIndices.Begin();
- mModel->mVisualModel->mBackgroundColorIndices.Erase(backgroundColorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
- backgroundColorIndexBuffer + endGlyphIndexPlusOne);
- }
+ RetrieveSelection(text, false);
}
+ return text;
}
-void Controller::Impl::ClearModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
+string Controller::Impl::CopyText()
{
- if(mTextUpdateInfo.mClearAll ||
- ((0u == startIndex) &&
- (mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u)))
- {
- ClearFullModelData(operations);
- }
- else
- {
- // Clear the model data related with characters.
- ClearCharacterModelData(startIndex, endIndex, operations);
+ string text;
+ RetrieveSelection(text, false);
+ SendSelectionToClipboard(false); // Text not modified
- // Clear the model data related with glyphs.
- ClearGlyphModelData(startIndex, endIndex, operations);
- }
+ mEventData->mUpdateCursorPosition = true;
- // The estimated number of lines. Used to avoid reallocations when layouting.
- mTextUpdateInfo.mEstimatedNumberOfLines = std::max(mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count());
+ RequestRelayout(); // Cursor, Handles, Selection Highlight, Popup
- mModel->mVisualModel->ClearCaches();
+ return text;
}
-bool Controller::Impl::UpdateModel(OperationsMask operationsRequired)
+string Controller::Impl::CutText()
{
- DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::UpdateModel\n");
-
- // Calculate the operations to be done.
- const OperationsMask operations = static_cast<OperationsMask>(mOperationsPending & operationsRequired);
-
- if(NO_OPERATION == operations)
- {
- // Nothing to do if no operations are pending and required.
- return false;
- }
-
- Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
- Vector<Character> displayCharacters;
- bool useHiddenText = false;
- if(mHiddenInput && mEventData != nullptr && !mEventData->mIsShowingPlaceholderText)
- {
- mHiddenInput->Substitute(srcCharacters, displayCharacters);
- useHiddenText = true;
- }
-
- Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
- const Length numberOfCharacters = utf32Characters.Count();
-
- // Index to the first character of the first paragraph to be updated.
- CharacterIndex startIndex = 0u;
- // Number of characters of the paragraphs to be removed.
- Length paragraphCharacters = 0u;
-
- CalculateTextUpdateIndices(paragraphCharacters);
-
- // Check whether the indices for updating the text is valid
- if(numberOfCharacters > 0u &&
- (mTextUpdateInfo.mParagraphCharacterIndex > numberOfCharacters ||
- mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters))
- {
- std::string currentText;
- Utf32ToUtf8(mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText);
-
- DALI_LOG_ERROR("Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n");
- DALI_LOG_ERROR("Number of characters: %d, current text is: %s\n", numberOfCharacters, currentText.c_str());
-
- // Dump mTextUpdateInfo
- DALI_LOG_ERROR("Dump mTextUpdateInfo:\n");
- DALI_LOG_ERROR(" mTextUpdateInfo.mCharacterIndex = %u\n", mTextUpdateInfo.mCharacterIndex);
- DALI_LOG_ERROR(" mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", mTextUpdateInfo.mNumberOfCharactersToRemove);
- DALI_LOG_ERROR(" mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", mTextUpdateInfo.mNumberOfCharactersToAdd);
- DALI_LOG_ERROR(" mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", mTextUpdateInfo.mPreviousNumberOfCharacters);
- DALI_LOG_ERROR(" mTextUpdateInfo.mParagraphCharacterIndex = %u\n", mTextUpdateInfo.mParagraphCharacterIndex);
- DALI_LOG_ERROR(" mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", mTextUpdateInfo.mRequestedNumberOfCharacters);
- DALI_LOG_ERROR(" mTextUpdateInfo.mStartGlyphIndex = %u\n", mTextUpdateInfo.mStartGlyphIndex);
- DALI_LOG_ERROR(" mTextUpdateInfo.mStartLineIndex = %u\n", mTextUpdateInfo.mStartLineIndex);
- DALI_LOG_ERROR(" mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", mTextUpdateInfo.mEstimatedNumberOfLines);
- DALI_LOG_ERROR(" mTextUpdateInfo.mClearAll = %d\n", mTextUpdateInfo.mClearAll);
- DALI_LOG_ERROR(" mTextUpdateInfo.mFullRelayoutNeeded = %d\n", mTextUpdateInfo.mFullRelayoutNeeded);
- DALI_LOG_ERROR(" mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", mTextUpdateInfo.mIsLastCharacterNewParagraph);
-
- return false;
- }
-
- startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
-
- if(mTextUpdateInfo.mClearAll ||
- (0u != paragraphCharacters))
- {
- ClearModelData(startIndex, startIndex + ((paragraphCharacters > 0u) ? paragraphCharacters - 1u : 0u), operations);
- }
-
- mTextUpdateInfo.mClearAll = false;
-
- // Whether the model is updated.
- bool updated = false;
-
- Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
- const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
-
- if(NO_OPERATION != (GET_LINE_BREAKS & operations))
- {
- // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
- // calculate the bidirectional info for each 'paragraph'.
- // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
- // is not shaped together).
- lineBreakInfo.Resize(numberOfCharacters, TextAbstraction::LINE_NO_BREAK);
-
- SetLineBreakInfo(utf32Characters,
- startIndex,
- requestedNumberOfCharacters,
- lineBreakInfo);
-
- if(mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
- mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::MIXED))
- {
- CharacterIndex end = startIndex + requestedNumberOfCharacters;
- LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
-
- for(CharacterIndex index = startIndex; index < end; index++)
- {
- CharacterIndex wordEnd = index;
- while((*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_ALLOW_BREAK) && (*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_MUST_BREAK))
- {
- wordEnd++;
- }
-
- if((wordEnd + 1) == end) // add last char
- {
- wordEnd++;
- }
-
- Vector<bool> hyphens = GetWordHyphens(utf32Characters.Begin() + index, wordEnd - index, nullptr);
-
- for(CharacterIndex i = 0; i < (wordEnd - index); i++)
- {
- if(hyphens[i])
- {
- *(lineBreakInfoBuffer + index + i) = TextAbstraction::LINE_HYPHENATION_BREAK;
- }
- }
-
- index = wordEnd;
- }
- }
-
- // Create the paragraph info.
- mModel->mLogicalModel->CreateParagraphInfo(startIndex,
- requestedNumberOfCharacters);
- updated = true;
- }
-
- const bool getScripts = NO_OPERATION != (GET_SCRIPTS & operations);
- const bool validateFonts = NO_OPERATION != (VALIDATE_FONTS & operations);
-
- Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
- Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
-
- if(getScripts || validateFonts)
- {
- // Validates the fonts assigned by the application or assigns default ones.
- // It makes sure all the characters are going to be rendered by the correct font.
- MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
-
- if(getScripts)
- {
- // Retrieves the scripts used in the text.
- multilanguageSupport.SetScripts(utf32Characters,
- startIndex,
- requestedNumberOfCharacters,
- scripts);
- }
-
- if(validateFonts)
- {
- // Validate the fonts set through the mark-up string.
- Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
-
- // Get the default font's description.
- TextAbstraction::FontDescription defaultFontDescription;
- TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale;
-
- //Get the number of points per one unit of point-size
- uint32_t numberOfPointsPerOneUnitOfPointSize = mFontClient.GetNumberOfPointsPerOneUnitOfPointSize();
+ string text;
+ RetrieveSelection(text, false);
- if(IsShowingPlaceholderText() && mEventData && (nullptr != mEventData->mPlaceholderFont))
- {
- // If the placeholder font is set specifically, only placeholder font is changed.
- defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
- if(mEventData->mPlaceholderFont->sizeDefined)
- {
- defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * mFontSizeScale * numberOfPointsPerOneUnitOfPointSize;
- }
- }
- else if(nullptr != mFontDefaults)
- {
- // Set the normal font and the placeholder font.
- defaultFontDescription = mFontDefaults->mFontDescription;
-
- if(mTextFitEnabled)
- {
- defaultPointSize = mFontDefaults->mFitPointSize * numberOfPointsPerOneUnitOfPointSize;
- }
- else
- {
- defaultPointSize = mFontDefaults->mDefaultPointSize * mFontSizeScale * numberOfPointsPerOneUnitOfPointSize;
- }
- }
-
- // Validates the fonts. If there is a character with no assigned font it sets a default one.
- // After this call, fonts are validated.
- multilanguageSupport.ValidateFonts(utf32Characters,
- scripts,
- fontDescriptionRuns,
- defaultFontDescription,
- defaultPointSize,
- startIndex,
- requestedNumberOfCharacters,
- validFonts);
- }
- updated = true;
- }
-
- Vector<Character> mirroredUtf32Characters;
- bool textMirrored = false;
- const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
- if(NO_OPERATION != (BIDI_INFO & operations))
- {
- Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
- bidirectionalInfo.Reserve(numberOfParagraphs);
-
- // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
- SetBidirectionalInfo(utf32Characters,
- scripts,
- lineBreakInfo,
- startIndex,
- requestedNumberOfCharacters,
- bidirectionalInfo,
- mModel->mMatchSystemLanguageDirection,
- mLayoutDirection);
-
- if(0u != bidirectionalInfo.Count())
- {
- // Only set the character directions if there is right to left characters.
- Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
- GetCharactersDirection(bidirectionalInfo,
- numberOfCharacters,
- startIndex,
- requestedNumberOfCharacters,
- directions);
-
- // This paragraph has right to left text. Some characters may need to be mirrored.
- // TODO: consider if the mirrored string can be stored as well.
-
- textMirrored = GetMirroredText(utf32Characters,
- directions,
- bidirectionalInfo,
- startIndex,
- requestedNumberOfCharacters,
- mirroredUtf32Characters);
- }
- else
- {
- // There is no right to left characters. Clear the directions vector.
- mModel->mLogicalModel->mCharacterDirections.Clear();
- }
- updated = true;
- }
-
- Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
- Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
- Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
- Vector<GlyphIndex> newParagraphGlyphs;
- newParagraphGlyphs.Reserve(numberOfParagraphs);
-
- const Length currentNumberOfGlyphs = glyphs.Count();
- if(NO_OPERATION != (SHAPE_TEXT & operations))
+ if(!IsEditable())
{
- const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
- // Shapes the text.
- ShapeText(textToShape,
- lineBreakInfo,
- scripts,
- validFonts,
- startIndex,
- mTextUpdateInfo.mStartGlyphIndex,
- requestedNumberOfCharacters,
- glyphs,
- glyphsToCharactersMap,
- charactersPerGlyph,
- newParagraphGlyphs);
-
- // Create the 'number of glyphs' per character and the glyph to character conversion tables.
- mModel->mVisualModel->CreateGlyphsPerCharacterTable(startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters);
- mModel->mVisualModel->CreateCharacterToGlyphTable(startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters);
-
- updated = true;
+ return EMPTY_STRING;
}
- const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
+ SendSelectionToClipboard(true); // Synchronous call to modify text
+ mOperationsPending = ALL_OPERATIONS;
- if(NO_OPERATION != (GET_GLYPH_METRICS & operations))
+ if((0u != mModel->mLogicalModel->mText.Count()) ||
+ !IsPlaceholderAvailable())
{
- GlyphInfo* glyphsBuffer = glyphs.Begin();
- mMetrics->GetGlyphMetrics(glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs);
-
- // Update the width and advance of all new paragraph characters.
- for(Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it)
- {
- const GlyphIndex index = *it;
- GlyphInfo& glyph = *(glyphsBuffer + index);
-
- glyph.xBearing = 0.f;
- glyph.width = 0.f;
- glyph.advance = 0.f;
- }
- updated = true;
+ QueueModifyEvent(ModifyEvent::TEXT_DELETED);
}
-
- if((nullptr != mEventData) &&
- mEventData->mPreEditFlag &&
- (0u != mModel->mVisualModel->mCharactersToGlyph.Count()))
+ else
{
- Dali::InputMethodContext::PreEditAttributeDataContainer attrs;
- mEventData->mInputMethodContext.GetPreeditStyle(attrs);
- Dali::InputMethodContext::PreeditStyle type = Dali::InputMethodContext::PreeditStyle::NONE;
-
- // Check the type of preedit and run it.
- for(Dali::InputMethodContext::PreEditAttributeDataContainer::Iterator it = attrs.Begin(), endIt = attrs.End(); it != endIt; it++)
- {
- Dali::InputMethodContext::PreeditAttributeData attrData = *it;
- DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::UpdateModel PreeditStyle type : %d start %d end %d \n", attrData.preeditType, attrData.startIndex, attrData.endIndex);
- type = attrData.preeditType;
-
- // Check the number of commit characters for the start position.
- unsigned int numberOfCommit = mEventData->mPrimaryCursorPosition - mEventData->mPreEditLength;
- Length numberOfIndices = attrData.endIndex - attrData.startIndex;
-
- switch(type)
- {
- case Dali::InputMethodContext::PreeditStyle::UNDERLINE:
- {
- // Add the underline for the pre-edit text.
- GlyphRun underlineRun;
- underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
- underlineRun.numberOfGlyphs = numberOfIndices;
- mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
-
- //Mark-up processor case
- if(mModel->mVisualModel->IsMarkupProcessorEnabled())
- {
- CopyUnderlinedFromLogicalToVisualModels(false);
- }
- break;
- }
- case Dali::InputMethodContext::PreeditStyle::REVERSE:
- {
- Vector4 textColor = mModel->mVisualModel->GetTextColor();
- ColorRun backgroundColorRun;
- backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
- backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
- backgroundColorRun.color = textColor;
- mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
-
- Vector4 backgroundColor = mModel->mVisualModel->GetBackgroundColor();
- if(backgroundColor.a == 0) // There is no text background color.
- {
- // Try use the control's background color.
- if(nullptr != mEditableControlInterface)
- {
- mEditableControlInterface->GetControlBackgroundColor(backgroundColor);
- if(backgroundColor.a == 0) // There is no control background color.
- {
- // Determines black or white color according to text color.
- // Based on W3C Recommendations (https://www.w3.org/TR/WCAG20/)
- float L = CONSTANT_R * textColor.r + CONSTANT_G * textColor.g + CONSTANT_B * textColor.b;
- backgroundColor = L > BRIGHTNESS_THRESHOLD ? BLACK : WHITE;
- }
- }
- }
-
- Vector<ColorRun> colorRuns;
- colorRuns.Resize(1u);
- ColorRun& colorRun = *(colorRuns.Begin());
- colorRun.color = backgroundColor;
- colorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
- colorRun.characterRun.numberOfCharacters = numberOfIndices;
- mModel->mLogicalModel->mColorRuns.PushBack(colorRun);
-
- //Mark-up processor case
- if(mModel->mVisualModel->IsMarkupProcessorEnabled())
- {
- CopyUnderlinedFromLogicalToVisualModels(false);
- }
- break;
- }
- case Dali::InputMethodContext::PreeditStyle::HIGHLIGHT:
- {
- ColorRun backgroundColorRun;
- backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
- backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
- backgroundColorRun.color = LIGHT_BLUE;
- mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
-
- //Mark-up processor case
- if(mModel->mVisualModel->IsMarkupProcessorEnabled())
- {
- CopyUnderlinedFromLogicalToVisualModels(false);
- }
- break;
- }
- case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_1:
- {
- // CUSTOM_PLATFORM_STYLE_1 should be drawn with background and underline together.
- ColorRun backgroundColorRun;
- backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
- backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
- backgroundColorRun.color = BACKGROUND_SUB4;
- mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
-
- GlyphRun underlineRun;
- underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
- underlineRun.numberOfGlyphs = numberOfIndices;
- mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
-
- //Mark-up processor case
- if(mModel->mVisualModel->IsMarkupProcessorEnabled())
- {
- CopyUnderlinedFromLogicalToVisualModels(false);
- }
- break;
- }
- case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_2:
- {
- // CUSTOM_PLATFORM_STYLE_2 should be drawn with background and underline together.
- ColorRun backgroundColorRun;
- backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
- backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
- backgroundColorRun.color = BACKGROUND_SUB5;
- mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
-
- GlyphRun underlineRun;
- underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
- underlineRun.numberOfGlyphs = numberOfIndices;
- mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
-
- //Mark-up processor case
- if(mModel->mVisualModel->IsMarkupProcessorEnabled())
- {
- CopyUnderlinedFromLogicalToVisualModels(false);
- }
- break;
- }
- case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_3:
- {
- // CUSTOM_PLATFORM_STYLE_3 should be drawn with background and underline together.
- ColorRun backgroundColorRun;
- backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
- backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
- backgroundColorRun.color = BACKGROUND_SUB6;
- mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
-
- GlyphRun underlineRun;
- underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
- underlineRun.numberOfGlyphs = numberOfIndices;
- mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
-
- //Mark-up processor case
- if(mModel->mVisualModel->IsMarkupProcessorEnabled())
- {
- CopyUnderlinedFromLogicalToVisualModels(false);
- }
- break;
- }
- case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_4:
- {
- // CUSTOM_PLATFORM_STYLE_4 should be drawn with background and underline together.
- ColorRun backgroundColorRun;
- backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
- backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
- backgroundColorRun.color = BACKGROUND_SUB7;
- mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
-
- GlyphRun underlineRun;
- underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
- underlineRun.numberOfGlyphs = numberOfIndices;
- mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
-
- //Mark-up processor case
- if(mModel->mVisualModel->IsMarkupProcessorEnabled())
- {
- CopyUnderlinedFromLogicalToVisualModels(false);
- }
- break;
- }
- case Dali::InputMethodContext::PreeditStyle::NONE:
- default:
- {
- break;
- }
- }
- }
- attrs.Clear();
- updated = true;
- }
-
- if(NO_OPERATION != (COLOR & operations))
- {
- // Set the color runs in glyphs.
- SetColorSegmentationInfo(mModel->mLogicalModel->mColorRuns,
- mModel->mVisualModel->mCharactersToGlyph,
- mModel->mVisualModel->mGlyphsPerCharacter,
- startIndex,
- mTextUpdateInfo.mStartGlyphIndex,
- requestedNumberOfCharacters,
- mModel->mVisualModel->mColors,
- mModel->mVisualModel->mColorIndices);
-
- // Set the background color runs in glyphs.
- SetColorSegmentationInfo(mModel->mLogicalModel->mBackgroundColorRuns,
- mModel->mVisualModel->mCharactersToGlyph,
- mModel->mVisualModel->mGlyphsPerCharacter,
- startIndex,
- mTextUpdateInfo.mStartGlyphIndex,
- requestedNumberOfCharacters,
- mModel->mVisualModel->mBackgroundColors,
- mModel->mVisualModel->mBackgroundColorIndices);
-
- updated = true;
- }
-
- if((NO_OPERATION != (SHAPE_TEXT & operations)) &&
- !((nullptr != mEventData) &&
- mEventData->mPreEditFlag &&
- (0u != mModel->mVisualModel->mCharactersToGlyph.Count())))
- {
- //Mark-up processor case
- if(mModel->mVisualModel->IsMarkupProcessorEnabled())
- {
- CopyUnderlinedFromLogicalToVisualModels(true);
- }
-
- updated = true;
+ PlaceholderHandler::ShowPlaceholderText(*this);
}
- // The estimated number of lines. Used to avoid reallocations when layouting.
- mTextUpdateInfo.mEstimatedNumberOfLines = std::max(mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count());
-
- // Set the previous number of characters for the next time the text is updated.
- mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
-
- return updated;
-}
-
-void Controller::Impl::RetrieveDefaultInputStyle(InputStyle& inputStyle)
-{
- // Sets the default text's color.
- inputStyle.textColor = mTextColor;
- inputStyle.isDefaultColor = true;
-
- inputStyle.familyName.clear();
- inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
- inputStyle.width = TextAbstraction::FontWidth::NORMAL;
- inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
- inputStyle.size = 0.f;
-
- inputStyle.lineSpacing = 0.f;
-
- inputStyle.underlineProperties.clear();
- inputStyle.shadowProperties.clear();
- inputStyle.embossProperties.clear();
- inputStyle.outlineProperties.clear();
-
- inputStyle.isFamilyDefined = false;
- inputStyle.isWeightDefined = false;
- inputStyle.isWidthDefined = false;
- inputStyle.isSlantDefined = false;
- inputStyle.isSizeDefined = false;
-
- inputStyle.isLineSpacingDefined = false;
-
- inputStyle.isUnderlineDefined = false;
- inputStyle.isShadowDefined = false;
- inputStyle.isEmbossDefined = false;
- inputStyle.isOutlineDefined = false;
-
- // Sets the default font's family name, weight, width, slant and size.
- if(mFontDefaults)
- {
- if(mFontDefaults->familyDefined)
- {
- inputStyle.familyName = mFontDefaults->mFontDescription.family;
- inputStyle.isFamilyDefined = true;
- }
-
- if(mFontDefaults->weightDefined)
- {
- inputStyle.weight = mFontDefaults->mFontDescription.weight;
- inputStyle.isWeightDefined = true;
- }
-
- if(mFontDefaults->widthDefined)
- {
- inputStyle.width = mFontDefaults->mFontDescription.width;
- inputStyle.isWidthDefined = true;
- }
-
- if(mFontDefaults->slantDefined)
- {
- inputStyle.slant = mFontDefaults->mFontDescription.slant;
- inputStyle.isSlantDefined = true;
- }
+ mEventData->mUpdateCursorPosition = true;
+ mEventData->mScrollAfterDelete = true;
- if(mFontDefaults->sizeDefined)
- {
- inputStyle.size = mFontDefaults->mDefaultPointSize;
- inputStyle.isSizeDefined = true;
- }
- }
-}
+ RequestRelayout();
-float Controller::Impl::GetDefaultFontLineHeight()
-{
- FontId defaultFontId = 0u;
- if(nullptr == mFontDefaults)
+ if(nullptr != mEditableControlInterface)
{
- TextAbstraction::FontDescription fontDescription;
- defaultFontId = mFontClient.GetFontId(fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale);
+ mEditableControlInterface->TextChanged(true);
}
- else
- {
- defaultFontId = mFontDefaults->GetFontId(mFontClient, mFontDefaults->mDefaultPointSize * mFontSizeScale);
- }
-
- Text::FontMetrics fontMetrics;
- mMetrics->GetFontMetrics(defaultFontId, fontMetrics);
-
- return (fontMetrics.ascender - fontMetrics.descender);
+ return text;
}
void Controller::Impl::SetTextSelectionRange(const uint32_t* pStart, const uint32_t* pEnd)
if(mEventData->mSelectionEnabled && (pStart || pEnd))
{
- uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
+ uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
+ uint32_t oldStart = mEventData->mLeftSelectionPosition;
+ uint32_t oldEnd = mEventData->mRightSelectionPosition;
if(pStart)
{
mEventData->mUpdateLeftSelectionPosition = true;
mEventData->mUpdateRightSelectionPosition = true;
}
+
+ if(mSelectableControlInterface != nullptr)
+ {
+ mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
+ }
}
}
return mEventData->mPrimaryCursorPosition;
}
-bool Controller::Impl::SetPrimaryCursorPosition(CharacterIndex index)
+bool Controller::Impl::SetPrimaryCursorPosition(CharacterIndex index, bool focused)
{
if(nullptr == mEventData)
{
return false;
}
- if(mEventData->mPrimaryCursorPosition == index)
+ if(mEventData->mPrimaryCursorPosition == index && mEventData->mState != EventData::SELECTING)
{
// Nothing for same cursor position.
return false;
}
uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
+ uint32_t oldCursorPos = mEventData->mPrimaryCursorPosition;
mEventData->mPrimaryCursorPosition = std::min(index, length);
- ChangeState(EventData::EDITING);
- mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
- mEventData->mUpdateCursorPosition = true;
- ScrollTextToMatchCursor();
+ // If there is no focus, only the value is updated.
+ if(focused)
+ {
+ bool wasInSelectingState = mEventData->mState == EventData::SELECTING;
+ uint32_t oldStart = mEventData->mLeftSelectionPosition;
+ uint32_t oldEnd = mEventData->mRightSelectionPosition;
+ ChangeState(EventData::EDITING);
+ mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
+ mEventData->mUpdateCursorPosition = true;
+
+ if(mSelectableControlInterface != nullptr && wasInSelectingState)
+ {
+ mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
+ }
+
+ ScrollTextToMatchCursor();
+ }
+
+ if(nullptr != mEditableControlInterface)
+ {
+ mEditableControlInterface->CursorPositionChanged(oldCursorPos, mEventData->mPrimaryCursorPosition);
+ }
+
return true;
}
if(mEventData)
{
mEventData->mEditingEnabled = editable;
+
+ if(mEventData->mDecorator)
+ {
+ mEventData->mDecorator->SetEditable(editable);
+ }
+ }
+}
+
+void Controller::Impl::UpdateAfterFontChange(const std::string& newDefaultFont)
+{
+ DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Controller::UpdateAfterFontChange\n");
+
+ if(!mFontDefaults->familyDefined) // If user defined font then should not update when system font changes
+ {
+ DALI_LOG_INFO(gLogFilter, Debug::Concise, "Controller::UpdateAfterFontChange newDefaultFont(%s)\n", newDefaultFont.c_str());
+ mFontDefaults->mFontDescription.family = newDefaultFont;
+
+ ClearFontData();
+
+ RequestRelayout();
}
}
void Controller::Impl::SetSelection(int start, int end)
{
+ uint32_t oldStart = mEventData->mLeftSelectionPosition;
+ uint32_t oldEnd = mEventData->mRightSelectionPosition;
+
mEventData->mLeftSelectionPosition = start;
mEventData->mRightSelectionPosition = end;
mEventData->mUpdateCursorPosition = true;
+
+ if(mSelectableControlInterface != nullptr)
+ {
+ mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, start, end);
+ }
}
std::pair<int, int> Controller::Impl::GetSelectionIndexes() const
}
}
-void Controller::Impl::RepositionSelectionHandles()
-{
- SelectionHandleController::Reposition(*this);
-}
-void Controller::Impl::RepositionSelectionHandles(float visualX, float visualY, Controller::NoTextTap::Action action)
-{
- SelectionHandleController::Reposition(*this, visualX, visualY, action);
-}
-
-void Controller::Impl::SetPopupButtons()
-{
- /**
- * Sets the Popup buttons to be shown depending on State.
- *
- * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
- *
- * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
- */
-
- bool isEditable = IsEditable();
- TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
-
- if(EventData::SELECTING == mEventData->mState)
- {
- buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::COPY);
- if(isEditable)
- {
- buttonsToShow = TextSelectionPopup::Buttons(buttonsToShow | TextSelectionPopup::CUT);
- }
-
- if(!IsClipboardEmpty())
- {
- if(isEditable)
- {
- buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
- }
- buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
- }
-
- if(!mEventData->mAllTextSelected)
- {
- buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::SELECT_ALL));
- }
- }
- else if(EventData::EDITING_WITH_POPUP == mEventData->mState)
- {
- if(mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
- {
- buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL);
- }
-
- if(!IsClipboardEmpty())
- {
- if(isEditable)
- {
- buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
- }
- buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
- }
- }
- else if(EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState)
- {
- if(!IsClipboardEmpty())
- {
- if(isEditable)
- {
- buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
- }
- buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
- }
- }
-
- mEventData->mDecorator->SetEnabledPopupButtons(buttonsToShow);
-}
-
-void Controller::Impl::ChangeState(EventData::State newState)
-{
- if(nullptr == mEventData)
- {
- // Nothing to do if there is no text input.
- return;
- }
-
- DALI_LOG_INFO(gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState);
-
- if(mEventData->mState != newState)
- {
- mEventData->mPreviousState = mEventData->mState;
- mEventData->mState = newState;
-
- switch(mEventData->mState)
- {
- case EventData::INACTIVE:
- {
- mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
- mEventData->mDecorator->StopCursorBlink();
- mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
- mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
- mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
- mEventData->mDecorator->SetHighlightActive(false);
- mEventData->mDecorator->SetPopupActive(false);
- mEventData->mDecoratorUpdated = true;
- break;
- }
- case EventData::INTERRUPTED:
- {
- mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
- mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
- mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
- mEventData->mDecorator->SetHighlightActive(false);
- mEventData->mDecorator->SetPopupActive(false);
- mEventData->mDecoratorUpdated = true;
- break;
- }
- case EventData::SELECTING:
- {
- mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
- mEventData->mDecorator->StopCursorBlink();
- mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
- if(mEventData->mGrabHandleEnabled)
- {
- mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
- mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
- }
- mEventData->mDecorator->SetHighlightActive(true);
- if(mEventData->mGrabHandlePopupEnabled)
- {
- SetPopupButtons();
- mEventData->mDecorator->SetPopupActive(true);
- }
- mEventData->mDecoratorUpdated = true;
- break;
- }
- case EventData::EDITING:
- {
- mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
- if(mEventData->mCursorBlinkEnabled)
- {
- mEventData->mDecorator->StartCursorBlink();
- }
- // Grab handle is not shown until a tap is received whilst EDITING
- mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
- mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
- mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
- mEventData->mDecorator->SetHighlightActive(false);
- if(mEventData->mGrabHandlePopupEnabled)
- {
- mEventData->mDecorator->SetPopupActive(false);
- }
- mEventData->mDecoratorUpdated = true;
- break;
- }
- case EventData::EDITING_WITH_POPUP:
- {
- DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState);
-
- mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
- if(mEventData->mCursorBlinkEnabled)
- {
- mEventData->mDecorator->StartCursorBlink();
- }
- if(mEventData->mSelectionEnabled)
- {
- mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
- mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
- mEventData->mDecorator->SetHighlightActive(false);
- }
- else if(mEventData->mGrabHandleEnabled)
- {
- mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
- }
- if(mEventData->mGrabHandlePopupEnabled)
- {
- SetPopupButtons();
- mEventData->mDecorator->SetPopupActive(true);
- }
- mEventData->mDecoratorUpdated = true;
- break;
- }
- case EventData::EDITING_WITH_GRAB_HANDLE:
- {
- DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState);
-
- mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
- if(mEventData->mCursorBlinkEnabled)
- {
- mEventData->mDecorator->StartCursorBlink();
- }
- // Grab handle is not shown until a tap is received whilst EDITING
- if(mEventData->mGrabHandleEnabled)
- {
- mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
- }
- mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
- mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
- mEventData->mDecorator->SetHighlightActive(false);
- if(mEventData->mGrabHandlePopupEnabled)
- {
- mEventData->mDecorator->SetPopupActive(false);
- }
- mEventData->mDecoratorUpdated = true;
- break;
- }
- case EventData::SELECTION_HANDLE_PANNING:
- {
- mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
- mEventData->mDecorator->StopCursorBlink();
- mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
- if(mEventData->mGrabHandleEnabled)
- {
- mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
- mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
- }
- mEventData->mDecorator->SetHighlightActive(true);
- if(mEventData->mGrabHandlePopupEnabled)
- {
- mEventData->mDecorator->SetPopupActive(false);
- }
- mEventData->mDecoratorUpdated = true;
- break;
- }
- case EventData::GRAB_HANDLE_PANNING:
- {
- DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState);
-
- mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
- if(mEventData->mCursorBlinkEnabled)
- {
- mEventData->mDecorator->StartCursorBlink();
- }
- if(mEventData->mGrabHandleEnabled)
- {
- mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
- }
- mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
- mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
- mEventData->mDecorator->SetHighlightActive(false);
- if(mEventData->mGrabHandlePopupEnabled)
- {
- mEventData->mDecorator->SetPopupActive(false);
- }
- mEventData->mDecoratorUpdated = true;
- break;
- }
- case EventData::EDITING_WITH_PASTE_POPUP:
- {
- DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState);
-
- mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
- if(mEventData->mCursorBlinkEnabled)
- {
- mEventData->mDecorator->StartCursorBlink();
- }
+void Controller::Impl::RepositionSelectionHandles()
+{
+ SelectionHandleController::Reposition(*this);
+}
+void Controller::Impl::RepositionSelectionHandles(float visualX, float visualY, Controller::NoTextTap::Action action)
+{
+ SelectionHandleController::Reposition(*this, visualX, visualY, action);
+}
- if(mEventData->mGrabHandleEnabled)
- {
- mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
- }
- mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
- mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
- mEventData->mDecorator->SetHighlightActive(false);
+void Controller::Impl::SetPopupButtons()
+{
+ /**
+ * Sets the Popup buttons to be shown depending on State.
+ *
+ * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
+ *
+ * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
+ */
- if(mEventData->mGrabHandlePopupEnabled)
- {
- SetPopupButtons();
- mEventData->mDecorator->SetPopupActive(true);
- }
- mEventData->mDecoratorUpdated = true;
- break;
- }
- case EventData::TEXT_PANNING:
+ bool isEditable = IsEditable();
+ TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
+
+ if(EventData::SELECTING == mEventData->mState)
+ {
+ buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::COPY);
+ if(isEditable)
+ {
+ buttonsToShow = TextSelectionPopup::Buttons(buttonsToShow | TextSelectionPopup::CUT);
+ }
+
+ if(!IsClipboardEmpty())
+ {
+ if(isEditable)
{
- mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
- mEventData->mDecorator->StopCursorBlink();
- mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
- if(mEventData->mDecorator->IsHandleActive(LEFT_SELECTION_HANDLE) ||
- mEventData->mDecorator->IsHandleActive(RIGHT_SELECTION_HANDLE))
- {
- mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
- mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
- mEventData->mDecorator->SetHighlightActive(true);
- }
+ buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
+ }
+ buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
+ }
- if(mEventData->mGrabHandlePopupEnabled)
- {
- mEventData->mDecorator->SetPopupActive(false);
- }
+ if(!mEventData->mAllTextSelected)
+ {
+ buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::SELECT_ALL));
+ }
+ }
+ else if(EventData::EDITING_WITH_POPUP == mEventData->mState)
+ {
+ if(mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
+ {
+ buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL);
+ }
- mEventData->mDecoratorUpdated = true;
- break;
+ if(!IsClipboardEmpty())
+ {
+ if(isEditable)
+ {
+ buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
+ }
+ buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
+ }
+ }
+ else if(EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState)
+ {
+ if(!IsClipboardEmpty())
+ {
+ if(isEditable)
+ {
+ buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
}
+ buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
}
}
+
+ mEventData->mDecorator->SetEnabledPopupButtons(buttonsToShow);
+}
+
+void Controller::Impl::ChangeState(EventData::State newState)
+{
+ ChangeTextControllerState(*this, newState);
}
void Controller::Impl::GetCursorPosition(CharacterIndex logical,
cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
bool isRTL = false;
- if(mModel->mMatchSystemLanguageDirection)
+ if(mModel->mMatchLayoutDirection != DevelText::MatchLayoutDirection::CONTENTS)
{
isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
}
parameters.logical = logical;
parameters.isMultiline = isMultiLine;
+ float defaultFontLineHeight = GetDefaultFontLineHeight();
+
Text::GetCursorPosition(parameters,
+ defaultFontLineHeight,
cursorInfo);
// Adds Outline offset.
}
}
-Actor Controller::Impl::CreateBackgroundActor()
+void Controller::Impl::RelayoutAllCharacters()
{
- // NOTE: Currently we only support background color for left-to-right text.
+ // relayout all characters
+ mTextUpdateInfo.mCharacterIndex = 0;
+ mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
+ mTextUpdateInfo.mNumberOfCharactersToAdd = mModel->mLogicalModel->mText.Count();
+ mOperationsPending = static_cast<OperationsMask>(mOperationsPending | LAYOUT);
- Actor actor;
+ mTextUpdateInfo.mFullRelayoutNeeded = true;
- Length numberOfGlyphs = mView.GetNumberOfGlyphs();
- if(numberOfGlyphs > 0u)
- {
- Vector<GlyphInfo> glyphs;
- glyphs.Resize(numberOfGlyphs);
-
- Vector<Vector2> positions;
- positions.Resize(numberOfGlyphs);
+ // Need to recalculate natural size
+ mRecalculateNaturalSize = true;
- // Get the line where the glyphs are laid-out.
- const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
- float alignmentOffset = lineRun->alignmentOffset;
- numberOfGlyphs = mView.GetGlyphs(glyphs.Begin(),
- positions.Begin(),
- alignmentOffset,
- 0u,
- numberOfGlyphs);
-
- glyphs.Resize(numberOfGlyphs);
- positions.Resize(numberOfGlyphs);
+ //remove selection
+ if((mEventData != nullptr) && (mEventData->mState == EventData::SELECTING))
+ {
+ ChangeState(EventData::EDITING);
+ }
- const GlyphInfo* const glyphsBuffer = glyphs.Begin();
- const Vector2* const positionsBuffer = positions.Begin();
+ RequestRelayout();
+}
- BackgroundMesh mesh;
- mesh.mVertices.Reserve(4u * glyphs.Size());
- mesh.mIndices.Reserve(6u * glyphs.Size());
+bool Controller::Impl::IsInputStyleChangedSignalsQueueEmpty()
+{
+ return (NULL == mEventData) || (0u == mEventData->mInputStyleChangedQueue.Count());
+}
- const Vector2 textSize = mView.GetLayoutSize();
+void Controller::Impl::ProcessInputStyleChangedSignals()
+{
+ if(mEventData)
+ {
+ if(mEditableControlInterface)
+ {
+ // Emit the input style changed signal for each mask
+ std::for_each(mEventData->mInputStyleChangedQueue.begin(),
+ mEventData->mInputStyleChangedQueue.end(),
+ [&](const auto mask) { mEditableControlInterface->InputStyleChanged(mask); });
+ }
- const float offsetX = textSize.width * 0.5f;
- const float offsetY = textSize.height * 0.5f;
+ mEventData->mInputStyleChangedQueue.Clear();
+ }
+}
- const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
- const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
- const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
+void Controller::Impl::ScrollBy(Vector2 scroll)
+{
+ if(mEventData && (fabs(scroll.x) > Math::MACHINE_EPSILON_0 || fabs(scroll.y) > Math::MACHINE_EPSILON_0))
+ {
+ const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
+ const Vector2 currentScroll = mModel->mScrollPosition;
- Vector4 quad;
- uint32_t numberOfQuads = 0u;
- Length yLineOffset = 0;
- Length prevLineIndex = 0;
- LineIndex lineIndex;
- Length numberOfLines;
+ scroll.x = -scroll.x;
+ scroll.y = -scroll.y;
- for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i)
+ if(fabs(scroll.x) > Math::MACHINE_EPSILON_0)
{
- const GlyphInfo& glyph = *(glyphsBuffer + i);
-
- // Get the background color of the character.
- // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
- const bool isMarkupBackground = mView.IsMarkupBackgroundColorSet();
- const ColorIndex backgroundColorIndex = isMarkupBackground ? *(backgroundColorIndicesBuffer + i) : 0u;
- const bool isDefaultBackgroundColor = (0u == backgroundColorIndex);
- const Vector4& backgroundColor = isDefaultBackgroundColor ? defaultBackgroundColor : *(backgroundColorsBuffer + backgroundColorIndex - 1u);
-
- mModel->mVisualModel->GetNumberOfLines(i, 1, lineIndex, numberOfLines);
- Length lineHeight = lineRun[lineIndex].ascender + -(lineRun[lineIndex].descender) + lineRun[lineIndex].lineSpacing;
+ mModel->mScrollPosition.x += scroll.x;
+ ClampHorizontalScroll(layoutSize);
+ }
- if(lineIndex != prevLineIndex)
- {
- yLineOffset += lineHeight;
- }
+ if(fabs(scroll.y) > Math::MACHINE_EPSILON_0)
+ {
+ mModel->mScrollPosition.y += scroll.y;
+ ClampVerticalScroll(layoutSize);
+ }
- // Only create quads for glyphs with a background color
- if(backgroundColor != Color::TRANSPARENT)
- {
- const Vector2 position = *(positionsBuffer + i);
+ if(mModel->mScrollPosition != currentScroll)
+ {
+ mEventData->mDecorator->UpdatePositions(mModel->mScrollPosition - currentScroll);
+ RequestRelayout();
+ }
+ }
+}
- if(i == 0u && glyphSize == 1u) // Only one glyph in the whole text
- {
- quad.x = position.x;
- quad.y = yLineOffset;
- quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
- quad.w = lineHeight;
- }
- else if((lineIndex != prevLineIndex) || (i == 0u)) // The first glyph in the line
- {
- quad.x = position.x;
- quad.y = yLineOffset;
- quad.z = quad.x - glyph.xBearing + glyph.advance;
- quad.w = quad.y + lineHeight;
- }
- else if(i == glyphSize - 1u) // The last glyph in the whole text
- {
- quad.x = position.x - glyph.xBearing;
- quad.y = yLineOffset;
- quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
- quad.w = quad.y + lineHeight;
- }
- else // The glyph in the middle of the text
- {
- quad.x = position.x - glyph.xBearing;
- quad.y = yLineOffset;
- quad.z = quad.x + glyph.advance;
- quad.w = quad.y + lineHeight;
- }
+float Controller::Impl::GetHorizontalScrollPosition()
+{
+ // Scroll values are negative internally so we convert them to positive numbers
+ return mEventData ? -mModel->mScrollPosition.x : 0.0f;
+}
- BackgroundVertex vertex;
-
- // Top left
- vertex.mPosition.x = quad.x - offsetX;
- vertex.mPosition.y = quad.y - offsetY;
- vertex.mColor = backgroundColor;
- mesh.mVertices.PushBack(vertex);
-
- // Top right
- vertex.mPosition.x = quad.z - offsetX;
- vertex.mPosition.y = quad.y - offsetY;
- vertex.mColor = backgroundColor;
- mesh.mVertices.PushBack(vertex);
-
- // Bottom left
- vertex.mPosition.x = quad.x - offsetX;
- vertex.mPosition.y = quad.w - offsetY;
- vertex.mColor = backgroundColor;
- mesh.mVertices.PushBack(vertex);
-
- // Bottom right
- vertex.mPosition.x = quad.z - offsetX;
- vertex.mPosition.y = quad.w - offsetY;
- vertex.mColor = backgroundColor;
- mesh.mVertices.PushBack(vertex);
-
- // Six indices in counter clockwise winding
- mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
- mesh.mIndices.PushBack(0u + 4 * numberOfQuads);
- mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
- mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
- mesh.mIndices.PushBack(3u + 4 * numberOfQuads);
- mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
-
- numberOfQuads++;
- }
+float Controller::Impl::GetVerticalScrollPosition()
+{
+ // Scroll values are negative internally so we convert them to positive numbers
+ return mEventData ? -mModel->mScrollPosition.y : 0.0f;
+}
- if(lineIndex != prevLineIndex)
- {
- prevLineIndex = lineIndex;
- }
- }
+Vector3 Controller::Impl::GetAnchorPosition(Anchor anchor) const
+{
+ //TODO
+ return Vector3(10.f, 10.f, 10.f);
+}
- // Only create the background actor if there are glyphs with background color
- if(mesh.mVertices.Count() > 0u)
- {
- Property::Map quadVertexFormat;
- quadVertexFormat["aPosition"] = Property::VECTOR2;
- quadVertexFormat["aColor"] = Property::VECTOR4;
+Vector2 Controller::Impl::GetAnchorSize(Anchor anchor) const
+{
+ //TODO
+ return Vector2(10.f, 10.f);
+}
- VertexBuffer quadVertices = VertexBuffer::New(quadVertexFormat);
- quadVertices.SetData(&mesh.mVertices[0], mesh.mVertices.Size());
+Toolkit::TextAnchor Controller::Impl::CreateAnchorActor(Anchor anchor)
+{
+ auto actor = Toolkit::TextAnchor::New();
+ actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
+ actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
+ const Vector3 anchorPosition = GetAnchorPosition(anchor);
+ actor.SetProperty(Actor::Property::POSITION, anchorPosition);
+ const Vector2 anchorSize = GetAnchorSize(anchor);
+ actor.SetProperty(Actor::Property::SIZE, anchorSize);
+ std::string anchorText(mModel->mLogicalModel->mText.Begin() + anchor.startIndex, mModel->mLogicalModel->mText.Begin() + anchor.endIndex);
+ actor.SetProperty(Actor::Property::NAME, anchorText);
+ actor.SetProperty(Toolkit::TextAnchor::Property::URI, std::string(anchor.href));
+ actor.SetProperty(Toolkit::TextAnchor::Property::START_CHARACTER_INDEX, static_cast<int>(anchor.startIndex));
+ actor.SetProperty(Toolkit::TextAnchor::Property::END_CHARACTER_INDEX, static_cast<int>(anchor.endIndex));
+ return actor;
+}
- Geometry quadGeometry = Geometry::New();
- quadGeometry.AddVertexBuffer(quadVertices);
- quadGeometry.SetIndexBuffer(&mesh.mIndices[0], mesh.mIndices.Size());
+void Controller::Impl::GetAnchorActors(std::vector<Toolkit::TextAnchor>& anchorActors)
+{
+ /* TODO: Now actors are created/destroyed in every "RenderText" function call. Even when we add just 1 character,
+ we need to create and destroy potentially many actors. Some optimization can be considered here.
+ Maybe a "dirty" flag in mLogicalModel? */
+ anchorActors.clear();
+ for(auto& anchor : mModel->mLogicalModel->mAnchors)
+ {
+ auto actor = CreateAnchorActor(anchor);
+ anchorActors.push_back(actor);
+ }
+}
- if(!mShaderBackground)
- {
- mShaderBackground = Shader::New(SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_VERT, SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_FRAG);
- }
+int32_t Controller::Impl::GetAnchorIndex(size_t characterOffset) const
+{
+ Vector<Anchor>::Iterator it = mModel->mLogicalModel->mAnchors.Begin();
- Dali::Renderer renderer = Dali::Renderer::New(quadGeometry, mShaderBackground);
- renderer.SetProperty(Dali::Renderer::Property::BLEND_MODE, BlendMode::ON);
- renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT);
-
- actor = Actor::New();
- actor.SetProperty(Dali::Actor::Property::NAME, "TextBackgroundColorActor");
- actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
- actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
- actor.SetProperty(Actor::Property::SIZE, textSize);
- actor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR);
- actor.AddRenderer(renderer);
- }
+ while(it != mModel->mLogicalModel->mAnchors.End() && (it->startIndex > characterOffset || it->endIndex <= characterOffset))
+ {
+ it++;
}
- return actor;
+ return it == mModel->mLogicalModel->mAnchors.End() ? -1 : it - mModel->mLogicalModel->mAnchors.Begin();
}
void Controller::Impl::CopyUnderlinedFromLogicalToVisualModels(bool shouldClearPreUnderlineRuns)
{
CharacterIndex characterIndex = it->characterRun.characterIndex;
Length numberOfCharacters = it->characterRun.numberOfCharacters;
- for(Length index = 0u; index < numberOfCharacters; index++)
+
+ if(numberOfCharacters == 0)
+ {
+ continue;
+ }
+
+ // Create one run for all glyphs of all run's characters that has same properties
+ // This enhance performance and reduce the needed memory to store glyphs-runs
+ UnderlinedGlyphRun underlineGlyphRun;
+ underlineGlyphRun.glyphRun.glyphIndex = charactersToGlyph[characterIndex];
+ underlineGlyphRun.glyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex];
+ //Copy properties (attributes)
+ underlineGlyphRun.properties = it->properties;
+
+ for(Length index = 1u; index < numberOfCharacters; index++)
+ {
+ underlineGlyphRun.glyphRun.numberOfGlyphs += glyphsPerCharacter[characterIndex + index];
+ }
+
+ mModel->mVisualModel->mUnderlineRuns.PushBack(underlineGlyphRun);
+ }
+}
+
+void Controller::Impl::CopyStrikethroughFromLogicalToVisualModels()
+{
+ //Strikethrough character runs from markup-processor
+ const Vector<StrikethroughCharacterRun>& strikethroughCharacterRuns = mModel->mLogicalModel->mStrikethroughCharacterRuns;
+ const Vector<GlyphIndex>& charactersToGlyph = mModel->mVisualModel->mCharactersToGlyph;
+ const Vector<Length>& glyphsPerCharacter = mModel->mVisualModel->mGlyphsPerCharacter;
+
+ mModel->mVisualModel->mStrikethroughRuns.Clear();
+
+ for(Vector<StrikethroughCharacterRun>::ConstIterator it = strikethroughCharacterRuns.Begin(), endIt = strikethroughCharacterRuns.End(); it != endIt; ++it)
+ {
+ CharacterIndex characterIndex = it->characterRun.characterIndex;
+ Length numberOfCharacters = it->characterRun.numberOfCharacters;
+
+ if(numberOfCharacters == 0)
+ {
+ continue;
+ }
+
+ StrikethroughGlyphRun strikethroughGlyphRun;
+ strikethroughGlyphRun.color = it->color;
+ strikethroughGlyphRun.isColorSet = it->isColorSet;
+ strikethroughGlyphRun.glyphRun.glyphIndex = charactersToGlyph[characterIndex];
+ strikethroughGlyphRun.glyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex];
+
+ for(Length index = 1u; index < numberOfCharacters; index++)
+ {
+ strikethroughGlyphRun.glyphRun.numberOfGlyphs += glyphsPerCharacter[characterIndex + index];
+ }
+
+ mModel->mVisualModel->mStrikethroughRuns.PushBack(strikethroughGlyphRun);
+ }
+}
+
+void Controller::Impl::SetAutoScrollEnabled(bool enable)
+{
+ if(mLayoutEngine.GetLayout() == Layout::Engine::SINGLE_LINE_BOX)
+ {
+ mOperationsPending = static_cast<OperationsMask>(mOperationsPending |
+ LAYOUT |
+ ALIGN |
+ UPDATE_LAYOUT_SIZE |
+ REORDER);
+
+ if(enable)
+ {
+ DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::SetAutoScrollEnabled for SINGLE_LINE_BOX\n");
+ mOperationsPending = static_cast<OperationsMask>(mOperationsPending | UPDATE_DIRECTION);
+ }
+ else
+ {
+ DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::SetAutoScrollEnabled Disabling autoscroll\n");
+ }
+
+ mIsAutoScrollEnabled = enable;
+ RequestRelayout();
+ }
+ else
+ {
+ DALI_LOG_WARNING("Attempted AutoScrolling on a non SINGLE_LINE_BOX, request ignored\n");
+ mIsAutoScrollEnabled = false;
+ }
+}
+
+void Controller::Impl::SetEnableCursorBlink(bool enable)
+{
+ DALI_ASSERT_DEBUG(NULL != mEventData && "TextInput disabled");
+
+ if(mEventData)
+ {
+ mEventData->mCursorBlinkEnabled = enable;
+
+ if(!enable && mEventData->mDecorator)
+ {
+ mEventData->mDecorator->StopCursorBlink();
+ }
+ }
+}
+
+void Controller::Impl::SetMultiLineEnabled(bool enable)
+{
+ const Layout::Engine::Type layout = enable ? Layout::Engine::MULTI_LINE_BOX : Layout::Engine::SINGLE_LINE_BOX;
+
+ if(layout != mLayoutEngine.GetLayout())
+ {
+ // Set the layout type.
+ mLayoutEngine.SetLayout(layout);
+
+ // Set the flags to redo the layout operations
+ const OperationsMask layoutOperations = static_cast<OperationsMask>(LAYOUT |
+ UPDATE_LAYOUT_SIZE |
+ ALIGN |
+ REORDER);
+
+ mTextUpdateInfo.mFullRelayoutNeeded = true;
+ mOperationsPending = static_cast<OperationsMask>(mOperationsPending | layoutOperations);
+
+ // Need to recalculate natural size
+ mRecalculateNaturalSize = true;
+
+ RequestRelayout();
+ }
+}
+
+void Controller::Impl::SetHorizontalAlignment(Text::HorizontalAlignment::Type alignment)
+{
+ if(alignment != mModel->mHorizontalAlignment)
+ {
+ // Set the alignment.
+ mModel->mHorizontalAlignment = alignment;
+
+ // Set the flag to redo the alignment operation.
+ mOperationsPending = static_cast<OperationsMask>(mOperationsPending | ALIGN);
+
+ if(mEventData)
+ {
+ mEventData->mUpdateAlignment = true;
+
+ // Update the cursor if it's in editing mode
+ if(EventData::IsEditingState(mEventData->mState))
+ {
+ ChangeState(EventData::EDITING);
+ mEventData->mUpdateCursorPosition = true;
+ }
+ }
+
+ RequestRelayout();
+ }
+}
+
+void Controller::Impl::SetVerticalAlignment(VerticalAlignment::Type alignment)
+{
+ if(alignment != mModel->mVerticalAlignment)
+ {
+ // Set the alignment.
+ mModel->mVerticalAlignment = alignment;
+ mOperationsPending = static_cast<OperationsMask>(mOperationsPending | ALIGN);
+ RequestRelayout();
+ }
+}
+
+void Controller::Impl::SetLineWrapMode(Text::LineWrap::Mode lineWrapMode)
+{
+ if(lineWrapMode != mModel->mLineWrapMode)
+ {
+ // Update Text layout for applying wrap mode
+ mOperationsPending = static_cast<OperationsMask>(mOperationsPending |
+ ALIGN |
+ LAYOUT |
+ UPDATE_LAYOUT_SIZE |
+ REORDER);
+
+ if((mModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) || (lineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
+ (mModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED) || (lineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED)) // hyphen is treated as line break
{
- GlyphRun underlineGlyphRun;
- underlineGlyphRun.glyphIndex = charactersToGlyph[characterIndex + index];
- underlineGlyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex + index];
- mModel->mVisualModel->mUnderlineRuns.PushBack(underlineGlyphRun);
+ mOperationsPending = static_cast<OperationsMask>(mOperationsPending | GET_LINE_BREAKS);
}
+
+ // Set the text wrap mode.
+ mModel->mLineWrapMode = lineWrapMode;
+
+ mTextUpdateInfo.mCharacterIndex = 0u;
+ mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
+ mTextUpdateInfo.mNumberOfCharactersToAdd = mModel->mLogicalModel->mText.Count();
+
+ // Request relayout
+ RequestRelayout();
+ }
+}
+
+void Controller::Impl::SetDefaultColor(const Vector4& color)
+{
+ mTextColor = color;
+
+ if(!IsShowingPlaceholderText())
+ {
+ mModel->mVisualModel->SetTextColor(color);
+ mModel->mLogicalModel->mColorRuns.Clear();
+ mOperationsPending = static_cast<OperationsMask>(mOperationsPending | COLOR);
+ RequestRelayout();
}
}
-} // namespace Text
+void Controller::Impl::ClearFontData()
+{
+ if(mFontDefaults)
+ {
+ mFontDefaults->mFontId = 0u; // Remove old font ID
+ }
+
+ // Set flags to update the model.
+ mTextUpdateInfo.mCharacterIndex = 0u;
+ mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
+ mTextUpdateInfo.mNumberOfCharactersToAdd = mModel->mLogicalModel->mText.Count();
+
+ mTextUpdateInfo.mClearAll = true;
+ mTextUpdateInfo.mFullRelayoutNeeded = true;
+ mRecalculateNaturalSize = true;
+
+ mOperationsPending = static_cast<OperationsMask>(mOperationsPending |
+ VALIDATE_FONTS |
+ SHAPE_TEXT |
+ BIDI_INFO |
+ GET_GLYPH_METRICS |
+ LAYOUT |
+ UPDATE_LAYOUT_SIZE |
+ REORDER |
+ ALIGN);
+}
-} // namespace Toolkit
+void Controller::Impl::ClearStyleData()
+{
+ mModel->mLogicalModel->mColorRuns.Clear();
+ mModel->mLogicalModel->ClearFontDescriptionRuns();
+ mModel->mLogicalModel->ClearStrikethroughRuns();
+}
+
+void Controller::Impl::ResetScrollPosition()
+{
+ if(mEventData)
+ {
+ // Reset the scroll position.
+ mModel->mScrollPosition = Vector2::ZERO;
+ mEventData->mScrollAfterUpdatePosition = true;
+ }
+}
-} // namespace Dali
+} // namespace Dali::Toolkit::Text