/*
- * 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/devel-api/text/rendering-backend.h>
#include <dali-toolkit/internal/controls/text-controls/common-text-utils.h>
#include <dali-toolkit/internal/controls/text-controls/text-field-property-handler.h>
+#include <dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.h>
#include <dali-toolkit/internal/styling/style-manager-impl.h>
#include <dali-toolkit/internal/text/rendering/text-backend.h>
#include <dali-toolkit/internal/text/text-effects-style.h>
namespace // unnamed namespace
{
const unsigned int DEFAULT_RENDERING_BACKEND = Dali::Toolkit::DevelText::DEFAULT_RENDERING_BACKEND;
+const char* KEY_RETURN_NAME = "Return";
} // unnamed namespace
namespace
DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit, TextField, "strikethrough", MAP, STRIKETHROUGH )
DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit, TextField, "inputStrikethrough", MAP, INPUT_STRIKETHROUGH )
DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit, TextField, "characterSpacing", FLOAT, CHARACTER_SPACING )
+DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit, TextField, "selectionPopupStyle", MAP, SELECTION_POPUP_STYLE )
+DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit, TextField, "removeFrontInset", BOOLEAN, REMOVE_FRONT_INSET )
+DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit, TextField, "removeBackInset", BOOLEAN, REMOVE_BACK_INSET )
DALI_SIGNAL_REGISTRATION(Toolkit, TextField, "textChanged", SIGNAL_TEXT_CHANGED )
DALI_SIGNAL_REGISTRATION(Toolkit, TextField, "maxLengthReached", SIGNAL_MAX_LENGTH_REACHED )
} // namespace
-Toolkit::TextField TextField::New()
+Toolkit::TextField TextField::New(ControlBehaviour additionalBehaviour)
{
// Create the implementation, temporarily owned by this handle on stack
- IntrusivePtr<TextField> impl = new TextField();
+ IntrusivePtr<TextField> impl = new TextField(additionalBehaviour);
// Pass ownership to CustomActor handle
Toolkit::TextField handle(*impl);
}
}
+void TextField::OnPropertySet(Property::Index index, const Property::Value& propertyValue)
+{
+ DALI_LOG_INFO(gTextFieldLogFilter, Debug::Verbose, "TextField::OnPropertySet index[%d]\n", index);
+
+ switch(index)
+ {
+ case DevelActor::Property::USER_INTERACTION_ENABLED:
+ {
+ const bool enabled = propertyValue.Get<bool>();
+ mController->SetUserInteractionEnabled(enabled);
+ if(mStencil)
+ {
+ float opacity = enabled ? 1.0f : mController->GetDisabledColorOpacity();
+ mStencil.SetProperty(Actor::Property::OPACITY, opacity);
+ }
+ break;
+ }
+ default:
+ {
+ Control::OnPropertySet(index, propertyValue); // up call to control for non-handled properties
+ break;
+ }
+ }
+}
+
void TextField::OnRelayout(const Vector2& size, RelayoutContainer& container)
{
DALI_LOG_INFO(gTextFieldLogFilter, Debug::Verbose, "TextField OnRelayout\n");
}
if(mCursorLayer)
{
- mCursorLayer.SetProperty(Actor::Property::POSITION, Vector2(padding.start, padding.top));
+ if(!mStencil)
+ {
+ // If there is a stencil, the cursor layer is added to the stencil in RenderText.
+ // Do not calculate the position because the stencil has already been resized excluding the padding size.
+ mCursorLayer.SetProperty(Actor::Property::POSITION, Vector2(padding.start, padding.top));
+ }
ResizeActor(mCursorLayer, contentSize);
}
if(mDecorator &&
(Text::Controller::NONE_UPDATED != (Text::Controller::DECORATOR_UPDATED & updateTextType)))
{
- mDecorator->Relayout(contentSize);
+ mDecorator->Relayout(contentSize, container);
}
if(!mRenderer)
// The text-field adds an idle callback to the adaptor to emit the signals after the size negotiation.
if(!mController->IsInputStyleChangedSignalsQueueEmpty())
{
- if(Adaptor::IsAvailable())
- {
- Adaptor& adaptor = Adaptor::Get();
-
- if(NULL == mIdleCallback)
- {
- // @note: The callback manager takes the ownership of the callback object.
- mIdleCallback = MakeCallback(this, &TextField::OnIdleSignal);
- adaptor.AddIdle(mIdleCallback, false);
- }
- }
+ mController->RequestProcessInputStyleChangedSignals();
}
}
void TextField::RenderText(Text::Controller::UpdateTextType updateTextType)
{
- CommonTextUtils::RenderText(Self(), mRenderer, mController, mDecorator, mAlignmentOffset, mRenderableActor, mBackgroundActor, mStencil, mClippingDecorationActors, mAnchorActors, updateTextType);
+ CommonTextUtils::RenderText(Self(), mRenderer, mController, mDecorator, mAlignmentOffset, mRenderableActor, mBackgroundActor, mCursorLayer, mStencil, mClippingDecorationActors, mAnchorActors, updateTextType);
}
void TextField::OnKeyInputFocusGained()
// When window gain lost focus, the inputMethodContext is deactivated. Thus when window gain focus again, the inputMethodContext must be activated.
mInputMethodContext.SetRestoreAfterFocusLost(true);
}
- ClipboardEventNotifier notifier(ClipboardEventNotifier::Get());
- if(notifier)
+ if(IsEditable() && mController->IsUserInteractionEnabled())
{
- notifier.ContentSelectedSignal().Connect(this, &TextField::OnClipboardTextSelected);
+ mController->KeyboardFocusGainEvent(); // Called in the case of no virtual keyboard to trigger this event
}
- mController->KeyboardFocusGainEvent(); // Called in the case of no virtual keyboard to trigger this event
-
EmitKeyInputFocusSignal(true); // Calls back into the Control hence done last.
}
mInputMethodContext.EventReceivedSignal().Disconnect(this, &TextField::OnInputMethodContextEvent);
}
- ClipboardEventNotifier notifier(ClipboardEventNotifier::Get());
-
- if(notifier)
- {
- notifier.ContentSelectedSignal().Disconnect(this, &TextField::OnClipboardTextSelected);
- }
mController->KeyboardFocusLostEvent();
void TextField::OnTap(const TapGesture& gesture)
{
DALI_LOG_INFO(gTextFieldLogFilter, Debug::Verbose, "TextField::OnTap %p\n", mController.Get());
- if(mInputMethodContext && IsEditable())
- {
- mInputMethodContext.Activate();
- }
+
// Deliver the tap before the focus event to controller; this allows us to detect when focus is gained due to tap-gestures
Extents padding;
padding = Self().GetProperty<Extents>(Toolkit::Control::Property::PADDING);
mController->TapEvent(gesture.GetNumberOfTaps(), localPoint.x - padding.start, localPoint.y - padding.top);
mController->AnchorEvent(localPoint.x - padding.start, localPoint.y - padding.top);
+ Dali::Toolkit::KeyboardFocusManager keyboardFocusManager = Dali::Toolkit::KeyboardFocusManager::Get();
+ if(keyboardFocusManager)
+ {
+ keyboardFocusManager.SetCurrentFocusActor(Self());
+ }
SetKeyInputFocus();
}
void TextField::OnPan(const PanGesture& gesture)
{
mController->PanEvent(gesture.GetState(), gesture.GetDisplacement());
+ if(gesture.GetState() == GestureState::STARTED && !mController->IsScrollable(gesture.GetDisplacement()))
+ {
+ Dali::DevelActor::SetNeedGesturePropagation(Self(), true);
+ }
}
void TextField::OnLongPress(const LongPressGesture& gesture)
// Make sure ClearKeyInputFocus when only key is up
if(event.GetState() == KeyEvent::UP)
{
+ Dali::Toolkit::KeyboardFocusManager keyboardFocusManager = Dali::Toolkit::KeyboardFocusManager::Get();
+ if(keyboardFocusManager)
+ {
+ keyboardFocusManager.ClearFocus();
+ }
ClearKeyInputFocus();
}
return true;
}
- else if(Dali::DevelKey::DALI_KEY_RETURN == event.GetKeyCode())
+ else if(Dali::DevelKey::DALI_KEY_RETURN == event.GetKeyCode() && KEY_RETURN_NAME == event.GetKeyName())
{
// Do nothing when enter is comming.
return false;
void TextField::TextInserted(unsigned int position, unsigned int length, const std::string& content)
{
- GetAccessibleObject()->EmitTextInserted(position, length, content);
+ auto accessible = GetAccessibleObject();
+ if(DALI_LIKELY(accessible) && accessible->IsHighlighted())
+ {
+ accessible->EmitTextInserted(position, length, content);
+ }
}
void TextField::TextDeleted(unsigned int position, unsigned int length, const std::string& content)
{
- GetAccessibleObject()->EmitTextDeleted(position, length, content);
+ auto accessible = GetAccessibleObject();
+ if(DALI_LIKELY(accessible) && accessible->IsHighlighted())
+ {
+ accessible->EmitTextDeleted(position, length, content);
+ }
}
void TextField::CursorPositionChanged(unsigned int oldPosition, unsigned int newPosition)
{
- GetAccessibleObject()->EmitTextCursorMoved(newPosition);
+ auto accessible = GetAccessibleObject();
+ if(DALI_LIKELY(accessible) && accessible->IsHighlighted())
+ {
+ accessible->EmitTextCursorMoved(newPosition);
+ }
if((oldPosition != newPosition) && !mCursorPositionChanged)
{
mStencil = Control::New();
mStencil.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
mStencil.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
+ mStencil.SetProperty(Toolkit::DevelControl::Property::ACCESSIBILITY_HIDDEN, true);
// Creates a background visual. Even if the color is transparent it updates the stencil.
mStencil.SetProperty(Toolkit::Control::Property::BACKGROUND,
}
}
-void TextField::OnClipboardTextSelected(ClipboardEventNotifier& clipboard)
-{
- mController->PasteClipboardItemEvent();
-}
-
void TextField::KeyboardStatusChanged(bool keyboardShown)
{
DALI_LOG_INFO(gTextFieldLogFilter, Debug::Verbose, "TextField::KeyboardStatusChanged %p keyboardShown %d\n", mController.Get(), keyboardShown);
mController->ChangedLayoutDirection();
}
-void TextField::OnIdleSignal()
-{
- // Emits the change of input style signals.
- mController->ProcessInputStyleChangedSignals();
-
- // Set the pointer to null as the callback manager deletes the callback after execute it.
- mIdleCallback = NULL;
-}
-
-TextField::TextField()
-: Control(ControlBehaviour(CONTROL_BEHAVIOUR_DEFAULT)),
- mIdleCallback(NULL),
+TextField::TextField(ControlBehaviour additionalBehaviour)
+: Control(ControlBehaviour(CONTROL_BEHAVIOUR_DEFAULT | additionalBehaviour)),
mAlignmentOffset(0.f),
mRenderingBackend(DEFAULT_RENDERING_BACKEND),
mExceedPolicy(Dali::Toolkit::TextField::EXCEED_POLICY_CLIP),
TextField::~TextField()
{
UnparentAndReset(mStencil);
-
- if((NULL != mIdleCallback) && Adaptor::IsAvailable())
- {
- Adaptor::Get().RemoveIdle(mIdleCallback);
- }
}
Vector<Vector2> TextField::GetTextSize(const uint32_t startIndex, const uint32_t endIndex) const
return mController->GetTextPosition(startIndex, endIndex);
}
+Rect<float> TextField::GetLineBoundingRectangle(const uint32_t lineIndex) const
+{
+ return mController->GetLineBoundingRectangle(lineIndex);
+}
+
+Rect<float> TextField::GetCharacterBoundingRectangle(const uint32_t charIndex) const
+{
+ return mController->GetCharacterBoundingRectangle(charIndex);
+}
+
+int TextField::GetCharacterIndexAtPosition(float visualX, float visualY) const
+{
+ return mController->GetCharacterIndexAtPosition(visualX, visualY);
+}
+
+Rect<> TextField::GetTextBoundingRectangle(uint32_t startIndex, uint32_t endIndex) const
+{
+ return mController->GetTextBoundingRectangle(startIndex, endIndex);
+}
+
+void TextField::SetSpannedText(const Text::Spanned& spannedText)
+{
+ mController->SetSpannedText(spannedText);
+}
+
+void TextField::SetRemoveFrontInset(bool remove)
+{
+ mController->SetRemoveFrontInset(remove);
+}
+
+bool TextField::IsRemoveFrontInset() const
+{
+ return mController->IsRemoveFrontInset();
+}
+
+void TextField::SetRemoveBackInset(bool remove)
+{
+ mController->SetRemoveBackInset(remove);
+}
+
+bool TextField::IsRemoveBackInset() const
+{
+ return mController->IsRemoveBackInset();
+}
+
std::string TextField::TextFieldAccessible::GetName() const
{
if(IsHiddenInput())