X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=blobdiff_plain;f=dali-toolkit%2Finternal%2Ftext%2Ftext-controller-impl.cpp;h=47877c487090e25e61bd10a3b0e29bcc35f56d85;hp=8a1dd762f60c308de911f13cb7ea704af01c67d9;hb=750fadba87bb959c9af32e89e3f1bc7af6cb6dd2;hpb=3ce558ca09f928cb18cacfa80f52eb0c591181b9 diff --git a/dali-toolkit/internal/text/text-controller-impl.cpp b/dali-toolkit/internal/text/text-controller-impl.cpp index 8a1dd76..47877c4 100644 --- a/dali-toolkit/internal/text/text-controller-impl.cpp +++ b/dali-toolkit/internal/text/text-controller-impl.cpp @@ -1,5 +1,5 @@ /* - * 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. @@ -19,24 +19,27 @@ #include // EXTERNAL INCLUDES -#include +#include #include +#include #include -#include +#include // INTERNAL INCLUDES -#include -#include #include #include +#include #include #include #include #include +#include +#include #include #include #include #include +#include using namespace Dali; @@ -46,26 +49,16 @@ namespace Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS"); #endif -struct BackgroundVertex -{ - Vector2 mPosition; ///< Vertex posiiton - Vector4 mColor; ///< Vertex color -}; +constexpr float MAX_FLOAT = std::numeric_limits::max(); -struct BackgroundMesh -{ - Vector mVertices; ///< container of vertices - Vector mIndices; ///< container of indices -}; +const std::string EMPTY_STRING(""); } // namespace namespace Dali::Toolkit::Text { - namespace { - void SetDefaultInputStyle(InputStyle& inputStyle, const FontDefaults* const fontDefaults, const Vector4& textColor) { // Sets the default text's color. @@ -350,7 +343,7 @@ void ChangeTextControllerState(Controller::Impl& impl, EventData::State newState decorator->StopCursorBlink(); decorator->SetHandleActive(GRAB_HANDLE, false); if(eventData->mDecorator->IsHandleActive(LEFT_SELECTION_HANDLE) || - decorator->IsHandleActive(RIGHT_SELECTION_HANDLE)) + decorator->IsHandleActive(RIGHT_SELECTION_HANDLE)) { decorator->SetHandleActive(LEFT_SELECTION_HANDLE, false); decorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); @@ -369,6 +362,27 @@ void ChangeTextControllerState(Controller::Impl& impl, EventData::State newState } } +void UpdateCursorPositionForAlignment(Controller::Impl& impl, bool needFullAlignment) +{ + EventData* eventData = impl.mEventData; + + // Set the flag to redo the alignment operation + impl.mOperationsPending = static_cast(impl.mOperationsPending | Controller::OperationsMask::ALIGN); + + if(eventData) + { + // Note: mUpdateAlignment is currently only needed for horizontal alignment + eventData->mUpdateAlignment = needFullAlignment; + + // Update the cursor if it's in editing mode + if(EventData::IsEditingState(eventData->mState)) + { + impl.ChangeState(EventData::EDITING); + eventData->mUpdateCursorPosition = true; + } + } +} + } // unnamed Namespace EventData::EventData(DecoratorPtr decorator, InputMethodContext& inputMethodContext) @@ -496,6 +510,19 @@ Length Controller::Impl::GetNumberOfWhiteSpaces(CharacterIndex index) const 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. @@ -513,7 +540,8 @@ Dali::LayoutDirection::Type Controller::Impl::GetLayoutDirection(Dali::Actor& ac if(mModel->mMatchLayoutDirection == DevelText::MatchLayoutDirection::LOCALE || (mModel->mMatchLayoutDirection == DevelText::MatchLayoutDirection::INHERIT && !mIsLayoutDirectionChanged)) { - return static_cast(DevelWindow::Get(actor).GetRootLayer().GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get()); + Window window = DevelWindow::Get(actor); + return static_cast(window ? window.GetRootLayer().GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get() : LayoutDirection::LEFT_TO_RIGHT); } else { @@ -521,6 +549,48 @@ Dali::LayoutDirection::Type Controller::Impl::GetLayoutDirection(Dali::Actor& ac } } +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(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(onlyOnceOperations | + LAYOUT | REORDER | UPDATE_DIRECTION), + naturalSize.GetVectorXY()); + + // Do not do again the only once operations. + mOperationsPending = static_cast(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; @@ -627,11 +697,11 @@ float Controller::Impl::GetDefaultFontLineHeight() if(nullptr == mFontDefaults) { TextAbstraction::FontDescription fontDescription; - defaultFontId = mFontClient.GetFontId(fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale); + defaultFontId = mFontClient.GetFontId(fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * GetFontSizeScale()); } else { - defaultFontId = mFontDefaults->GetFontId(mFontClient, mFontDefaults->mDefaultPointSize * mFontSizeScale); + defaultFontId = mFontDefaults->GetFontId(mFontClient, mFontDefaults->mDefaultPointSize * GetFontSizeScale()); } Text::FontMetrics fontMetrics; @@ -645,9 +715,8 @@ bool Controller::Impl::SetDefaultLineSpacing(float lineSpacing) if(std::fabs(lineSpacing - mLayoutEngine.GetDefaultLineSpacing()) > Math::MACHINE_EPSILON_1000) { mLayoutEngine.SetDefaultLineSpacing(lineSpacing); - mRecalculateNaturalSize = true; - RelayoutForNewLineSize(); + RelayoutAllCharacters(); return true; } return false; @@ -658,14 +727,88 @@ bool Controller::Impl::SetDefaultLineSize(float lineSize) if(std::fabs(lineSize - mLayoutEngine.GetDefaultLineSize()) > Math::MACHINE_EPSILON_1000) { mLayoutEngine.SetDefaultLineSize(lineSize); - mRecalculateNaturalSize = true; - RelayoutForNewLineSize(); + RelayoutAllCharacters(); return true; } return false; } +bool Controller::Impl::SetRelativeLineSize(float relativeLineSize) +{ + if(std::fabs(relativeLineSize - GetRelativeLineSize()) > Math::MACHINE_EPSILON_1000) + { + mLayoutEngine.SetRelativeLineSize(relativeLineSize); + + RelayoutAllCharacters(); + return true; + } + return false; +} + +float Controller::Impl::GetRelativeLineSize() +{ + return mLayoutEngine.GetRelativeLineSize(); +} + +string Controller::Impl::GetSelectedText() +{ + string text; + if(EventData::SELECTING == mEventData->mState) + { + RetrieveSelection(text, false); + } + return text; +} + +string Controller::Impl::CopyText() +{ + string text; + RetrieveSelection(text, false); + SendSelectionToClipboard(false); // Text not modified + + mEventData->mUpdateCursorPosition = true; + + RequestRelayout(); // Cursor, Handles, Selection Highlight, Popup + + return text; +} + +string Controller::Impl::CutText() +{ + string text; + RetrieveSelection(text, false); + + if(!IsEditable()) + { + return EMPTY_STRING; + } + + SendSelectionToClipboard(true); // Synchronous call to modify text + mOperationsPending = ALL_OPERATIONS; + + if((0u != mModel->mLogicalModel->mText.Count()) || + !IsPlaceholderAvailable()) + { + QueueModifyEvent(ModifyEvent::TEXT_DELETED); + } + else + { + PlaceholderHandler::ShowPlaceholderText(*this); + } + + mEventData->mUpdateCursorPosition = true; + mEventData->mScrollAfterDelete = true; + + RequestRelayout(); + + if(nullptr != mEditableControlInterface) + { + mEditableControlInterface->TextChanged(true); + } + return text; +} + void Controller::Impl::SetTextSelectionRange(const uint32_t* pStart, const uint32_t* pEnd) { if(nullptr == mEventData) @@ -788,7 +931,8 @@ void Controller::Impl::SetEditable(bool editable) if(mEventData->mDecorator) { - mEventData->mDecorator->SetEditable(editable); + bool decoratorEditable = editable && mIsUserInteractionEnabled; + mEventData->mDecorator->SetEditable(decoratorEditable); } } } @@ -1094,7 +1238,10 @@ void Controller::Impl::GetCursorPosition(CharacterIndex logical, parameters.logical = logical; parameters.isMultiline = isMultiLine; + float defaultFontLineHeight = GetDefaultFontLineHeight(); + Text::GetCursorPosition(parameters, + defaultFontLineHeight, cursorInfo); // Adds Outline offset. @@ -1315,6 +1462,10 @@ void Controller::Impl::ScrollToMakePositionVisible(const Vector2& position, floa { mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY; } + else if(mModel->mLogicalModel->mText.Count() == 0u) + { + Relayouter::CalculateVerticalOffset(*this, mModel->mVisualModel->mControlSize); + } } } @@ -1357,189 +1508,7 @@ void Controller::Impl::RequestRelayout() } } -Actor Controller::Impl::CreateBackgroundActor() -{ - // NOTE: Currently we only support background color for left-to-right text. - - Actor actor; - - Length numberOfGlyphs = mView.GetNumberOfGlyphs(); - if(numberOfGlyphs > 0u) - { - Vector glyphs; - glyphs.Resize(numberOfGlyphs); - - Vector positions; - positions.Resize(numberOfGlyphs); - - // 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); - - const GlyphInfo* const glyphsBuffer = glyphs.Begin(); - const Vector2* const positionsBuffer = positions.Begin(); - - BackgroundMesh mesh; - mesh.mVertices.Reserve(4u * glyphs.Size()); - mesh.mIndices.Reserve(6u * glyphs.Size()); - - const Vector2 textSize = mView.GetLayoutSize(); - - const float offsetX = alignmentOffset + textSize.width * 0.5f; - const float offsetY = textSize.height * 0.5f; - - const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors(); - const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices(); - const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT; - - Vector4 quad; - uint32_t numberOfQuads = 0u; - Length yLineOffset = 0; - Length prevLineIndex = 0; - LineIndex lineIndex; - Length numberOfLines; - - for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i) - { - 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; - - if(lineIndex != prevLineIndex) - { - yLineOffset += lineHeight; - } - - // Only create quads for glyphs with a background color - if(backgroundColor != Color::TRANSPARENT) - { - const Vector2 position = *(positionsBuffer + i); - - 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; - } - - 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++; - } - - if(lineIndex != prevLineIndex) - { - prevLineIndex = lineIndex; - } - } - - // 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; - - VertexBuffer quadVertices = VertexBuffer::New(quadVertexFormat); - quadVertices.SetData(&mesh.mVertices[0], mesh.mVertices.Size()); - - Geometry quadGeometry = Geometry::New(); - quadGeometry.AddVertexBuffer(quadVertices); - quadGeometry.SetIndexBuffer(&mesh.mIndices[0], mesh.mIndices.Size()); - - if(!mShaderBackground) - { - mShaderBackground = Shader::New(SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_VERT, SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_FRAG); - } - - 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); - } - } - - return actor; -} - -void Controller::Impl::RelayoutForNewLineSize() +void Controller::Impl::RelayoutAllCharacters() { // relayout all characters mTextUpdateInfo.mCharacterIndex = 0; @@ -1547,8 +1516,13 @@ void Controller::Impl::RelayoutForNewLineSize() mTextUpdateInfo.mNumberOfCharactersToAdd = mModel->mLogicalModel->mText.Count(); mOperationsPending = static_cast(mOperationsPending | LAYOUT); + mTextUpdateInfo.mFullRelayoutNeeded = true; + + // Need to recalculate natural size + mRecalculateNaturalSize = true; + //remove selection - if(mEventData && mEventData->mState == EventData::SELECTING) + if((mEventData != nullptr) && (mEventData->mState == EventData::SELECTING)) { ChangeState(EventData::EDITING); } @@ -1570,7 +1544,7 @@ void Controller::Impl::ProcessInputStyleChangedSignals() // 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 auto mask) { mEditableControlInterface->InputStyleChanged(mask); }); } mEventData->mInputStyleChangedQueue.Clear(); @@ -1619,6 +1593,60 @@ float Controller::Impl::GetVerticalScrollPosition() return mEventData ? -mModel->mScrollPosition.y : 0.0f; } +Vector3 Controller::Impl::GetAnchorPosition(Anchor anchor) const +{ + //TODO + return Vector3(10.f, 10.f, 10.f); +} + +Vector2 Controller::Impl::GetAnchorSize(Anchor anchor) const +{ + //TODO + return Vector2(10.f, 10.f); +} + +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(anchor.startIndex)); + actor.SetProperty(Toolkit::TextAnchor::Property::END_CHARACTER_INDEX, static_cast(anchor.endIndex)); + return actor; +} + +void Controller::Impl::GetAnchorActors(std::vector& 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); + } +} + +int32_t Controller::Impl::GetAnchorIndex(size_t characterOffset) const +{ + Vector::Iterator it = mModel->mLogicalModel->mAnchors.Begin(); + + while(it != mModel->mLogicalModel->mAnchors.End() && (it->startIndex > characterOffset || it->endIndex <= characterOffset)) + { + it++; + } + + return it == mModel->mLogicalModel->mAnchors.End() ? -1 : it - mModel->mLogicalModel->mAnchors.Begin(); +} + void Controller::Impl::CopyUnderlinedFromLogicalToVisualModels(bool shouldClearPreUnderlineRuns) { //Underlined character runs for markup-processor @@ -1635,13 +1663,92 @@ void Controller::Impl::CopyUnderlinedFromLogicalToVisualModels(bool shouldClearP { 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& strikethroughCharacterRuns = mModel->mLogicalModel->mStrikethroughCharacterRuns; + const Vector& charactersToGlyph = mModel->mVisualModel->mCharactersToGlyph; + const Vector& glyphsPerCharacter = mModel->mVisualModel->mGlyphsPerCharacter; + + mModel->mVisualModel->mStrikethroughRuns.Clear(); + + for(Vector::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.properties = it->properties; + 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::CopyCharacterSpacingFromLogicalToVisualModels() +{ + //CharacterSpacing character runs from markup-processor + const Vector& characterSpacingCharacterRuns = mModel->mLogicalModel->mCharacterSpacingCharacterRuns; + const Vector& charactersToGlyph = mModel->mVisualModel->mCharactersToGlyph; + const Vector& glyphsPerCharacter = mModel->mVisualModel->mGlyphsPerCharacter; + + mModel->mVisualModel->mCharacterSpacingRuns.Clear(); + + for(Vector::ConstIterator it = characterSpacingCharacterRuns.Begin(), endIt = characterSpacingCharacterRuns.End(); it != endIt; ++it) + { + const CharacterIndex& characterIndex = it->characterRun.characterIndex; + const Length& numberOfCharacters = it->characterRun.numberOfCharacters; + + if(numberOfCharacters == 0) { - GlyphRun underlineGlyphRun; - underlineGlyphRun.glyphIndex = charactersToGlyph[characterIndex + index]; - underlineGlyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex + index]; - mModel->mVisualModel->mUnderlineRuns.PushBack(underlineGlyphRun); + continue; } + + CharacterSpacingGlyphRun characterSpacingGlyphRun; + characterSpacingGlyphRun.value = it->value; + characterSpacingGlyphRun.glyphRun.glyphIndex = charactersToGlyph[characterIndex]; + characterSpacingGlyphRun.glyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex]; + + for(Length index = 1u; index < numberOfCharacters; index++) + { + characterSpacingGlyphRun.glyphRun.numberOfGlyphs += glyphsPerCharacter[characterIndex + index]; + } + + mModel->mVisualModel->mCharacterSpacingRuns.PushBack(characterSpacingGlyphRun); } } @@ -1721,22 +1828,7 @@ void Controller::Impl::SetHorizontalAlignment(Text::HorizontalAlignment::Type al { // Set the alignment. mModel->mHorizontalAlignment = alignment; - - // Set the flag to redo the alignment operation. - mOperationsPending = static_cast(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; - } - } - + UpdateCursorPositionForAlignment(*this, true); RequestRelayout(); } } @@ -1747,7 +1839,7 @@ void Controller::Impl::SetVerticalAlignment(VerticalAlignment::Type alignment) { // Set the alignment. mModel->mVerticalAlignment = alignment; - mOperationsPending = static_cast(mOperationsPending | ALIGN); + UpdateCursorPositionForAlignment(*this, false); RequestRelayout(); } } @@ -1794,6 +1886,17 @@ void Controller::Impl::SetDefaultColor(const Vector4& color) } } +void Controller::Impl::SetUserInteractionEnabled(bool enabled) +{ + mIsUserInteractionEnabled = enabled; + + if(mEventData && mEventData->mDecorator) + { + bool editable = mEventData->mEditingEnabled && enabled; + mEventData->mDecorator->SetEditable(editable); + } +} + void Controller::Impl::ClearFontData() { if(mFontDefaults) @@ -1825,9 +1928,9 @@ void Controller::Impl::ClearStyleData() { mModel->mLogicalModel->mColorRuns.Clear(); mModel->mLogicalModel->ClearFontDescriptionRuns(); + mModel->mLogicalModel->ClearStrikethroughRuns(); } - void Controller::Impl::ResetScrollPosition() { if(mEventData)