/*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 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/controller/text-controller-impl.h>
// EXTERNAL INCLUDES
-#include <dali/devel-api/adaptor-framework/window-devel.h>
+#include <dali/integration-api/adaptor-framework/scene-holder.h>
#include <dali/integration-api/debug.h>
#include <dali/public-api/actors/layer.h>
#include <dali/public-api/rendering/renderer.h>
// INTERNAL INCLUDES
#include <dali-toolkit/internal/text/character-set-conversion.h>
-#include <dali-toolkit/internal/text/cursor-helper-functions.h>
-#include <dali-toolkit/internal/text/glyph-metrics-helper.h>
-#include <dali-toolkit/internal/text/text-control-interface.h>
#include <dali-toolkit/internal/text/controller/text-controller-impl-data-clearer.h>
#include <dali-toolkit/internal/text/controller/text-controller-impl-event-handler.h>
#include <dali-toolkit/internal/text/controller/text-controller-impl-model-updater.h>
#include <dali-toolkit/internal/text/controller/text-controller-placeholder-handler.h>
#include <dali-toolkit/internal/text/controller/text-controller-relayouter.h>
+#include <dali-toolkit/internal/text/cursor-helper-functions.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-editable-control-interface.h>
#include <dali-toolkit/internal/text/text-enumerations-impl.h>
#include <dali-toolkit/internal/text/text-run-container.h>
constexpr float MAX_FLOAT = std::numeric_limits<float>::max();
-const std::string EMPTY_STRING("");
+const char* EMPTY_STRING = "";
+const char* MIME_TYPE_TEXT_PLAIN = "text/plain;charset=utf-8";
+const char* MIME_TYPE_HTML = "application/xhtml+xml";
} // namespace
return ControllerImplEventHandler::ProcessInputEvents(*this);
}
-void Controller::Impl::NotifyInputMethodContext()
+void Controller::Impl::SetAnchorColor(const Vector4& color)
{
- if(mEventData && mEventData->mInputMethodContext)
+ mAnchorColor = color;
+ UpdateAnchorColor();
+}
+
+const Vector4& Controller::Impl::GetAnchorColor() const
+{
+ return mAnchorColor;
+}
+
+void Controller::Impl::SetAnchorClickedColor(const Vector4& color)
+{
+ mAnchorClickedColor = color;
+ UpdateAnchorColor();
+}
+
+const Vector4& Controller::Impl::GetAnchorClickedColor() const
+{
+ return mAnchorClickedColor;
+}
+
+void Controller::Impl::UpdateAnchorColor()
+{
+ if(!mAnchorControlInterface ||
+ !mMarkupProcessorEnabled ||
+ !mModel->mLogicalModel->mAnchors.Count() ||
+ !IsShowingRealText())
{
- CharacterIndex cursorPosition = GetLogicalCursorPosition();
+ return;
+ }
- const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces(0u);
+ bool updateNeeded = false;
- // Update the cursor position by removing the initial white spaces.
- if(cursorPosition < numberOfWhiteSpaces)
+ // The anchor color & clicked color needs to be updated with the property's color.
+ for(auto& anchor : mModel->mLogicalModel->mAnchors)
+ {
+ if(!anchor.isMarkupColorSet && !anchor.isClicked)
{
- cursorPosition = 0u;
+ if(mModel->mLogicalModel->mColorRuns.Count() > anchor.colorRunIndex)
+ {
+ ColorRun& colorRun = *(mModel->mLogicalModel->mColorRuns.Begin() + anchor.colorRunIndex);
+ colorRun.color = mAnchorColor;
+ updateNeeded = true;
+ }
+ if(mModel->mLogicalModel->mUnderlinedCharacterRuns.Count() > anchor.underlinedCharacterRunIndex)
+ {
+ UnderlinedCharacterRun& underlineRun = *(mModel->mLogicalModel->mUnderlinedCharacterRuns.Begin() + anchor.underlinedCharacterRunIndex);
+ underlineRun.properties.color = mAnchorColor;
+ updateNeeded = true;
+ }
}
- else
+ else if(!anchor.isMarkupClickedColorSet && anchor.isClicked)
{
- cursorPosition -= numberOfWhiteSpaces;
+ if(mModel->mLogicalModel->mColorRuns.Count() > anchor.colorRunIndex)
+ {
+ ColorRun& colorRun = *(mModel->mLogicalModel->mColorRuns.Begin() + anchor.colorRunIndex);
+ colorRun.color = mAnchorClickedColor;
+ updateNeeded = true;
+ }
+ if(mModel->mLogicalModel->mUnderlinedCharacterRuns.Count() > anchor.underlinedCharacterRunIndex)
+ {
+ UnderlinedCharacterRun& underlineRun = *(mModel->mLogicalModel->mUnderlinedCharacterRuns.Begin() + anchor.underlinedCharacterRunIndex);
+ underlineRun.properties.color = mAnchorClickedColor;
+ updateNeeded = true;
+ }
}
+ }
+
+ if(updateNeeded)
+ {
+ ClearFontData();
+ mOperationsPending = static_cast<OperationsMask>(mOperationsPending | COLOR);
+ RequestRelayout();
+ }
+}
+void Controller::Impl::NotifyInputMethodContext()
+{
+ if(mEventData && mEventData->mInputMethodContext)
+ {
+ CharacterIndex cursorPosition = GetLogicalCursorPosition();
mEventData->mInputMethodContext.SetCursorPosition(cursorPosition);
mEventData->mInputMethodContext.NotifyCursorPosition();
}
}
}
+Length Controller::Impl::GetNumberOfCharacters() const
+{
+ if(!IsShowingPlaceholderText())
+ {
+ return mModel->GetNumberOfCharacters();
+ }
+ else
+ {
+ DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Controller::GetNumberOfCharacters %p empty (but showing placeholder)\n", this);
+ return 0u;
+ }
+}
+
void Controller::Impl::GetText(CharacterIndex index, std::string& text) const
{
// Get the total number of characters.
if(mModel->mMatchLayoutDirection == DevelText::MatchLayoutDirection::LOCALE ||
(mModel->mMatchLayoutDirection == DevelText::MatchLayoutDirection::INHERIT && !mIsLayoutDirectionChanged))
{
- Window window = DevelWindow::Get(actor);
- return static_cast<Dali::LayoutDirection::Type>(window ? window.GetRootLayer().GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get<int>() : LayoutDirection::LEFT_TO_RIGHT);
+ Integration::SceneHolder sceneHolder = Integration::SceneHolder::Get(actor);
+ return static_cast<Dali::LayoutDirection::Type>(sceneHolder ? sceneHolder.GetRootLayer().GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get<int>() : LayoutDirection::LEFT_TO_RIGHT);
}
else
{
mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
- mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
+ mTextUpdateInfo.mStartLineIndex = (mModel->mVisualModel->mLines.Count() > 0u) ? mModel->mVisualModel->mLines.Count() - 1u : 0u;
// Nothing else to do;
return;
{
bool decoratorEditable = editable && mIsUserInteractionEnabled;
mEventData->mDecorator->SetEditable(decoratorEditable);
+ mEventData->mDecoratorUpdated = true;
+ RequestRelayout();
}
}
}
void Controller::Impl::ShowClipboard()
{
- if(mClipboard)
+ if(EnsureClipboardCreated())
{
mClipboard.ShowClipboard();
}
void Controller::Impl::HideClipboard()
{
- if(mClipboard && mClipboardHideEnabled)
+ if(EnsureClipboardCreated() && mClipboardHideEnabled)
{
mClipboard.HideClipboard();
}
bool Controller::Impl::CopyStringToClipboard(const std::string& source)
{
- //Send string to clipboard
- return (mClipboard && mClipboard.SetItem(source));
+ if(EnsureClipboardCreated())
+ {
+ Dali::Clipboard::ClipData data(MIME_TYPE_TEXT_PLAIN, source.c_str());
+ return mClipboard.SetData(data); // Send clipboard data to clipboard.
+ }
+
+ return false;
+}
+
+bool Controller::Impl::IsClipboardEmpty()
+{
+ bool result(Clipboard::IsAvailable() && EnsureClipboardCreated() && (mClipboard.HasType(MIME_TYPE_TEXT_PLAIN) || mClipboard.HasType(MIME_TYPE_HTML)));
+ return !result;
}
void Controller::Impl::SendSelectionToClipboard(bool deleteAfterSending)
ChangeState(EventData::EDITING);
}
-void Controller::Impl::RequestGetTextFromClipboard()
-{
- if(mClipboard)
- {
- mClipboard.RequestItem();
- }
-}
-
void Controller::Impl::RepositionSelectionHandles()
{
SelectionHandleController::Reposition(*this);
if(index < mEventData->mPrimaryCursorPosition)
{
- cursorIndex -= numberOfCharacters;
+ cursorIndex = cursorIndex < numberOfCharacters ? 0u : cursorIndex - numberOfCharacters;
}
else
{
- cursorIndex += numberOfCharacters;
+ Length textLength = mModel->mVisualModel->mCharactersToGlyph.Count();
+ cursorIndex = cursorIndex + numberOfCharacters > textLength ? textLength : cursorIndex + numberOfCharacters;
}
// Will update the cursor hook position.
{
const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled();
const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled();
- if(isHorizontalScrollEnabled ||isVerticalScrollEnabled)
+ if(isHorizontalScrollEnabled || isVerticalScrollEnabled)
{
- const Vector2& targetSize = mModel->mVisualModel->mControlSize;
- const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
+ const Vector2& targetSize = mModel->mVisualModel->mControlSize;
+ const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
const Vector2& scrollPosition = mModel->mScrollPosition;
if(isHorizontalScrollEnabled)
{
const float displacementX = displacement.x;
- const float positionX = scrollPosition.x + displacementX;
+ const float positionX = scrollPosition.x + displacementX;
if(layoutSize.width > targetSize.width && -positionX > 0.f && -positionX < layoutSize.width - targetSize.width)
{
isScrollable = true;
if(isVerticalScrollEnabled)
{
const float displacementY = displacement.y;
- const float positionY = scrollPosition.y + displacementY;
+ const float positionY = scrollPosition.y + displacementY;
if(layoutSize.height > targetSize.height && -positionY > 0 && -positionY < layoutSize.height - targetSize.height)
{
isScrollable = true;
{
bool editable = mEventData->mEditingEnabled && enabled;
mEventData->mDecorator->SetEditable(editable);
+ mEventData->mDecoratorUpdated = true;
+ RequestRelayout();
}
}
mModel->mLogicalModel->mColorRuns.Clear();
mModel->mLogicalModel->ClearFontDescriptionRuns();
mModel->mLogicalModel->ClearStrikethroughRuns();
+ mModel->mLogicalModel->ClearUnderlineRuns();
}
void Controller::Impl::ResetScrollPosition()