Merge "[ATSPI] Implementation of Hypertext and Hyperlink in text controls" into devel...
authorSeoyeon Kim <seoyeon2.kim@samsung.com>
Tue, 23 Nov 2021 08:59:25 +0000 (08:59 +0000)
committerGerrit Code Review <gerrit@review>
Tue, 23 Nov 2021 08:59:25 +0000 (08:59 +0000)
22 files changed:
automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Controls.cpp
dali-toolkit/devel-api/controls/accessible-impl.h
dali-toolkit/devel-api/controls/text-controls/text-anchor-devel.cpp [new file with mode: 0644]
dali-toolkit/devel-api/controls/text-controls/text-anchor-devel.h [new file with mode: 0644]
dali-toolkit/devel-api/file.list
dali-toolkit/internal/controls/text-controls/common-text-utils.cpp
dali-toolkit/internal/controls/text-controls/common-text-utils.h
dali-toolkit/internal/controls/text-controls/text-anchor-impl.cpp [new file with mode: 0644]
dali-toolkit/internal/controls/text-controls/text-anchor-impl.h [new file with mode: 0644]
dali-toolkit/internal/controls/text-controls/text-editor-impl.cpp
dali-toolkit/internal/controls/text-controls/text-editor-impl.h
dali-toolkit/internal/controls/text-controls/text-editor-property-handler.cpp
dali-toolkit/internal/controls/text-controls/text-field-impl.cpp
dali-toolkit/internal/controls/text-controls/text-field-impl.h
dali-toolkit/internal/controls/text-controls/text-field-property-handler.cpp
dali-toolkit/internal/controls/text-controls/text-label-impl.cpp
dali-toolkit/internal/controls/text-controls/text-label-impl.h
dali-toolkit/internal/file.list
dali-toolkit/internal/text/text-controller-impl.cpp
dali-toolkit/internal/text/text-controller-impl.h
dali-toolkit/internal/text/text-controller.cpp
dali-toolkit/internal/text/text-controller.h

index 9243a0b..a30078a 100644 (file)
@@ -516,6 +516,39 @@ int UtcDaliAccessibilityBloomViewConstructor(void)
 }
 
 #include <dali-toolkit/internal/controls/text-controls/text-field-impl.h>
+#include <dali-toolkit/internal/controls/text-controls/text-anchor-impl.h>
+int UtcDaliAccessibilityTextAnchor(void)
+{
+  ToolkitTestApplication application;
+
+  auto textanchor = TextAnchor::New();
+  DALI_TEST_CHECK( textanchor );
+
+  auto textlabel = TextLabel::New();
+  DALI_TEST_CHECK( textlabel );
+
+  Dali::Accessibility::TestEnableSC( true );
+
+  textlabel.Add(textanchor);
+  auto accessible = Dali::Accessibility::Accessible::Get( textanchor );
+  DALI_TEST_CHECK( accessible );
+  auto hyperlink = dynamic_cast< Dali::Accessibility::Hyperlink* >( accessible );
+  DALI_TEST_CHECK( hyperlink );
+  textanchor.SetProperty( Toolkit::TextAnchor::Property::URI, "https://www.tizen.org" );
+  DALI_TEST_EQUALS( hyperlink->IsValid(), true, TEST_LOCATION );
+  auto action = dynamic_cast<Dali::Accessibility::Action*>( accessible );
+  // activation of valid hyperlink
+  DALI_TEST_CHECK( action->DoAction( "accessibilityActivated" ) );
+  // making hyperlink invalid
+  textanchor.SetProperty( Toolkit::TextAnchor::Property::URI, "" );
+  DALI_TEST_EQUALS( hyperlink->IsValid(), false, TEST_LOCATION );
+  DALI_TEST_CHECK( !action->DoAction( "accessibilityActivated" ) );
+
+  Dali::Accessibility::TestEnableSC( false );
+
+  END_TEST;
+}
+
 int UtcDaliAccessibilityTextField(void)
 {
   ToolkitTestApplication application;
@@ -566,6 +599,51 @@ int UtcDaliAccessibilityTextField(void)
   DALI_TEST_EQUALS(editabletext->DeleteText(1, 5), true, TEST_LOCATION);
   DALI_TEST_EQUALS(text->GetText(0, 2), "af", TEST_LOCATION);
 
+  auto hypertext = dynamic_cast< Dali::Accessibility::Hypertext* >( accessible );
+  DALI_TEST_CHECK( hypertext );
+  // text without the anchors markup and ENABLE_MARKUP property set (by default) to false
+  DALI_TEST_EQUALS( hypertext->GetLinkCount(), 0, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( -1 ), -1, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( 0 ), -1, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( 5 ), -1, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLink( -1 ) == nullptr, true, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLink( 0 ) == nullptr, true, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLink( 5 ) == nullptr, true, TEST_LOCATION );
+  // text with the anchors markup and ENABLE_MARKUP property set (by default) to false
+  textfield.SetProperty( Toolkit::TextField::Property::TEXT, "12345<a href = 'https://www.tizen.org'>anchor1</a>12345<a href = 'https://www.tizen.org' >veryveryveryveryveryveryveryverylonganchor2</a>12345<a href = 'https://www.tizen.org'>anchor3</a>12345" );
+  DALI_TEST_EQUALS( hypertext->GetLinkCount(), 0, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( -1 ), -1, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( 0 ), -1, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( 5 ), -1, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLink( -1 ) == nullptr, true, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLink( 0 ) == nullptr, true, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLink( 5 ) == nullptr, true, TEST_LOCATION );
+  // text with the anchors markup and ENABLE_MARKUP property set to true
+  textfield.SetProperty( Toolkit::TextField::Property::ENABLE_MARKUP, true);
+  DALI_TEST_EQUALS( hypertext->GetLinkCount(), 3, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( -1 ), -1, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( 0 ), -1, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( 5 ), 0, TEST_LOCATION ); //1st anchor index
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( 17 ), 1, TEST_LOCATION ); //2nd anchor index
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( 66 ), 2, TEST_LOCATION ); //3rd anchor index
+  DALI_TEST_EQUALS( hypertext->GetLink( -1 ) == nullptr, true, TEST_LOCATION );
+  auto hyperlink = hypertext->GetLink( 0 );
+  DALI_TEST_CHECK ( hyperlink );
+  DALI_TEST_EQUALS( hyperlink->GetStartIndex(), 5, TEST_LOCATION );
+  DALI_TEST_EQUALS( hyperlink->GetEndIndex(), 12, TEST_LOCATION );
+  DALI_TEST_EQUALS( hyperlink->GetAnchorCount(), 1, TEST_LOCATION );
+  DALI_TEST_EQUALS( hyperlink->GetAnchorUri( 0 ), "https://www.tizen.org", TEST_LOCATION );
+  auto anchorAccessible = hyperlink->GetAnchorAccessible( 0 );
+  DALI_TEST_EQUALS( hyperlink == anchorAccessible, true, TEST_LOCATION );
+  hyperlink = hypertext->GetLink( 1 );
+  DALI_TEST_CHECK ( hyperlink );
+  DALI_TEST_EQUALS( hyperlink->GetStartIndex(), 17, TEST_LOCATION );
+  DALI_TEST_EQUALS( hyperlink->GetEndIndex(), 60, TEST_LOCATION );
+  hyperlink = hypertext->GetLink( 2 );
+  DALI_TEST_CHECK ( hyperlink );
+  DALI_TEST_EQUALS( hyperlink->GetStartIndex(), 65, TEST_LOCATION );
+  DALI_TEST_EQUALS( hyperlink->GetEndIndex(), 72, TEST_LOCATION );
+
   Dali::Accessibility::TestEnableSC( false );
 
   END_TEST;
@@ -622,6 +700,51 @@ int UtcDaliAccessibilityTextEditor(void)
   DALI_TEST_EQUALS(editabletext->DeleteText(1, 5), true, TEST_LOCATION);
   DALI_TEST_EQUALS(text->GetText(0, 2), "af", TEST_LOCATION);
 
+  auto hypertext = dynamic_cast< Dali::Accessibility::Hypertext* >( accessible );
+  DALI_TEST_CHECK( hypertext );
+  // text without the anchors markup and ENABLE_MARKUP property set (by default) to false
+  DALI_TEST_EQUALS( hypertext->GetLinkCount(), 0, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( -1 ), -1, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( 0 ), -1, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( 5 ), -1, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLink( -1 ) == nullptr, true, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLink( 0 ) == nullptr, true, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLink( 5 ) == nullptr, true, TEST_LOCATION );
+  // text with the anchors markup and ENABLE_MARKUP property set (by default) to false
+  texteditor.SetProperty( Toolkit::TextEditor::Property::TEXT, "12345<a href = 'https://www.tizen.org'>anchor1</a>12345<a href = 'https://www.tizen.org' >veryveryveryveryveryveryveryverylonganchor2</a>12345<a href = 'https://www.tizen.org'>anchor3</a>12345" );
+  DALI_TEST_EQUALS( hypertext->GetLinkCount(), 0, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( -1 ), -1, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( 0 ), -1, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( 5 ), -1, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLink( -1 ) == nullptr, true, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLink( 0 ) == nullptr, true, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLink( 5 ) == nullptr, true, TEST_LOCATION );
+  // text with the anchors markup and ENABLE_MARKUP property set to true
+  texteditor.SetProperty( Toolkit::TextEditor::Property::ENABLE_MARKUP, true);
+  DALI_TEST_EQUALS( hypertext->GetLinkCount(), 3, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( -1 ), -1, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( 0 ), -1, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( 5 ), 0, TEST_LOCATION ); //1st anchor index
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( 17 ), 1, TEST_LOCATION ); //2nd anchor index
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( 66 ), 2, TEST_LOCATION ); //3rd anchor index
+  DALI_TEST_EQUALS( hypertext->GetLink( -1 ) == nullptr, true, TEST_LOCATION );
+  auto hyperlink = hypertext->GetLink( 0 );
+  DALI_TEST_CHECK ( hyperlink );
+  DALI_TEST_EQUALS( hyperlink->GetStartIndex(), 5, TEST_LOCATION );
+  DALI_TEST_EQUALS( hyperlink->GetEndIndex(), 12, TEST_LOCATION );
+  DALI_TEST_EQUALS( hyperlink->GetAnchorCount(), 1, TEST_LOCATION );
+  DALI_TEST_EQUALS( hyperlink->GetAnchorUri( 0 ), "https://www.tizen.org", TEST_LOCATION );
+  auto anchorAccessible = hyperlink->GetAnchorAccessible( 0 );
+  DALI_TEST_EQUALS( hyperlink == anchorAccessible, true, TEST_LOCATION );
+  hyperlink = hypertext->GetLink( 1 );
+  DALI_TEST_CHECK ( hyperlink );
+  DALI_TEST_EQUALS( hyperlink->GetStartIndex(), 17, TEST_LOCATION );
+  DALI_TEST_EQUALS( hyperlink->GetEndIndex(), 60, TEST_LOCATION );
+  hyperlink = hypertext->GetLink( 2 );
+  DALI_TEST_CHECK ( hyperlink );
+  DALI_TEST_EQUALS( hyperlink->GetStartIndex(), 65, TEST_LOCATION );
+  DALI_TEST_EQUALS( hyperlink->GetEndIndex(), 72, TEST_LOCATION );
+
   Dali::Accessibility::TestEnableSC( false );
 
   END_TEST;
@@ -660,6 +783,51 @@ int UtcDaliAccessibilityTextLabel(void)
   DALI_TEST_EQUALS( text->SetRangeOfSelection( 1, 0, 1 ), false, TEST_LOCATION );
   DALI_TEST_EQUALS( text->RemoveSelection( 1 ), false, TEST_LOCATION );
 
+  auto hypertext = dynamic_cast< Dali::Accessibility::Hypertext* >( accessible );
+  DALI_TEST_CHECK( hypertext );
+  // text without the anchors markup and ENABLE_MARKUP property set (by default) to false
+  DALI_TEST_EQUALS( hypertext->GetLinkCount(), 0, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( -1 ), -1, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( 0 ), -1, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( 5 ), -1, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLink( -1 ) == nullptr, true, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLink( 0 ) == nullptr, true, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLink( 5 ) == nullptr, true, TEST_LOCATION );
+  // text with the anchors markup and ENABLE_MARKUP property set (by default) to false
+  textlabel.SetProperty( Toolkit::TextLabel::Property::TEXT, "12345<a href = 'https://www.tizen.org'>anchor1</a>12345<a href = 'https://www.tizen.org' >veryveryveryveryveryveryveryverylonganchor2</a>12345<a href = 'https://www.tizen.org'>anchor3</a>12345" );
+  DALI_TEST_EQUALS( hypertext->GetLinkCount(), 0, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( -1 ), -1, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( 0 ), -1, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( 5 ), -1, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLink( -1 ) == nullptr, true, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLink( 0 ) == nullptr, true, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLink( 5 ) == nullptr, true, TEST_LOCATION );
+  // text with the anchors markup and ENABLE_MARKUP property set to true
+  textlabel.SetProperty( Toolkit::TextLabel::Property::ENABLE_MARKUP, true);
+  DALI_TEST_EQUALS( hypertext->GetLinkCount(), 3, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( -1 ), -1, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( 0 ), -1, TEST_LOCATION );
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( 5 ), 0, TEST_LOCATION ); //1st anchor index
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( 17 ), 1, TEST_LOCATION ); //2nd anchor index
+  DALI_TEST_EQUALS( hypertext->GetLinkIndex( 66 ), 2, TEST_LOCATION ); //3rd anchor index
+  DALI_TEST_EQUALS( hypertext->GetLink( -1 ) == nullptr, true, TEST_LOCATION );
+  auto hyperlink = hypertext->GetLink( 0 );
+  DALI_TEST_CHECK ( hyperlink );
+  DALI_TEST_EQUALS( hyperlink->GetStartIndex(), 5, TEST_LOCATION );
+  DALI_TEST_EQUALS( hyperlink->GetEndIndex(), 12, TEST_LOCATION );
+  DALI_TEST_EQUALS( hyperlink->GetAnchorCount(), 1, TEST_LOCATION );
+  DALI_TEST_EQUALS( hyperlink->GetAnchorUri( 0 ), "https://www.tizen.org", TEST_LOCATION );
+  auto anchorAccessible = hyperlink->GetAnchorAccessible( 0 );
+  DALI_TEST_EQUALS( hyperlink == anchorAccessible, true, TEST_LOCATION );
+  hyperlink = hypertext->GetLink( 1 );
+  DALI_TEST_CHECK ( hyperlink );
+  DALI_TEST_EQUALS( hyperlink->GetStartIndex(), 17, TEST_LOCATION );
+  DALI_TEST_EQUALS( hyperlink->GetEndIndex(), 60, TEST_LOCATION );
+  hyperlink = hypertext->GetLink( 2 );
+  DALI_TEST_CHECK ( hyperlink );
+  DALI_TEST_EQUALS( hyperlink->GetStartIndex(), 65, TEST_LOCATION );
+  DALI_TEST_EQUALS( hyperlink->GetEndIndex(), 72, TEST_LOCATION );
+
   Dali::Accessibility::TestEnableSC( false );
 
   END_TEST;
index 8bd0714..ea80eef 100644 (file)
@@ -53,7 +53,7 @@ protected:
   bool mIsModal = false;
   bool mIsRoot = false;
 
-  Dali::Actor Self()
+  Dali::Actor Self() const
   {
     auto handle = mSelf.GetHandle();
 
diff --git a/dali-toolkit/devel-api/controls/text-controls/text-anchor-devel.cpp b/dali-toolkit/devel-api/controls/text-controls/text-anchor-devel.cpp
new file mode 100644 (file)
index 0000000..9b72f8a
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2021 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/devel-api/controls/text-controls/text-anchor-devel.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/controls/text-controls/text-anchor-impl.h>
+
+using namespace Dali;
+
+namespace Dali
+{
+namespace Toolkit
+{
+TextAnchor TextAnchor::New()
+{
+  return Internal::TextAnchor::New();
+}
+
+TextAnchor::TextAnchor()
+{
+}
+
+TextAnchor::TextAnchor(const TextAnchor& handle)
+: Control(handle)
+{
+}
+
+TextAnchor& TextAnchor::operator=(const TextAnchor& handle)
+{
+  if(&handle != this)
+  {
+    Control::operator=(handle);
+  }
+  return *this;
+}
+
+TextAnchor::~TextAnchor()
+{
+}
+
+TextAnchor TextAnchor::DownCast(BaseHandle handle)
+{
+  return Control::DownCast<TextAnchor, Internal::TextAnchor>(handle);
+}
+
+TextAnchor::TextAnchor(Internal::TextAnchor& implementation)
+: Control(implementation)
+{
+}
+
+TextAnchor::TextAnchor(Dali::Internal::CustomActor* internal)
+: Control(internal)
+{
+  VerifyCustomActorPointer<Internal::TextAnchor>(internal);
+}
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/devel-api/controls/text-controls/text-anchor-devel.h b/dali-toolkit/devel-api/controls/text-controls/text-anchor-devel.h
new file mode 100644 (file)
index 0000000..83fbc76
--- /dev/null
@@ -0,0 +1,140 @@
+#ifndef DALI_TOOLKIT_TEXT_ANCHOR_DEVEL_H
+#define DALI_TOOLKIT_TEXT_ANCHOR_DEVEL_H
+
+/*
+ * Copyright (c) 2021 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/public-api/controls/control.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal DALI_INTERNAL
+{
+class TextAnchor;
+}
+
+/**
+ * @brief A control which renders anchor (hyperlink) in hypertext.
+ */
+class DALI_TOOLKIT_API TextAnchor : public Control
+{
+public:
+  /**
+   * @brief The start and end property ranges for this control.
+   */
+  enum PropertyRange
+  {
+    PROPERTY_START_INDEX = Control::CONTROL_PROPERTY_END_INDEX + 1,
+    PROPERTY_END_INDEX   = PROPERTY_START_INDEX + 1000 ///< Reserve property indices
+  };
+
+  /**
+   * @brief An enumeration of properties belonging to the TextAnchor class.
+   */
+  struct Property
+  {
+    enum
+    {
+      /**
+       * @brief The index of a character in text at which an anchor starts.
+       * @details Name "startCharacterIndex", type INTEGER.
+       */
+      START_CHARACTER_INDEX = PROPERTY_START_INDEX,
+
+      /**
+       * @brief The index of a character in text that stands one position after the anchor's last character.
+       * @details Name "endCharacterIndex", type INTEGER.
+       */
+      END_CHARACTER_INDEX,
+
+      /**
+       * @brief The URI associated with an anchor.
+       * @details Name "uri", type STRING.
+       */
+      URI
+    };
+  };
+
+  /**
+   * @brief Creates the TextAnchor control.
+   * @return A handle to the TextAnchor control.
+   */
+  static TextAnchor New();
+
+  /**
+   * @brief Creates an empty handle.
+   */
+  TextAnchor();
+
+  /**
+   * @brief Copy constructor.
+   *
+   * @param[in] handle The handle to copy from.
+   */
+  TextAnchor(const TextAnchor& handle);
+
+  /**
+   * @brief Assignment operator.
+   *
+   * @param[in] handle The handle to copy from.
+   * @return A reference to this.
+   */
+  TextAnchor& operator=(const TextAnchor& handle);
+
+  /**
+   * @brief Destructor
+   *
+   * This is non-virtual since derived Handle types must not contain data or virtual methods.
+   */
+  ~TextAnchor();
+
+  /**
+   * @brief Downcast a handle to TextAnchor.
+   *
+   * If the BaseHandle points is a TextAnchor the downcast returns a valid handle.
+   * If not the returned handle is left empty.
+   *
+   * @param[in] handle Handle to an object
+   * @return handle to a TextAnchor or an empty handle
+   */
+  static TextAnchor DownCast(BaseHandle handle);
+
+public: // Not intended for application developers
+  /**
+   * @brief Creates a handle using the Toolkit::Internal implementation.
+   *
+   * @param[in] implementation The Control implementation.
+   */
+  DALI_INTERNAL TextAnchor(Internal::TextAnchor& implementation);
+
+  /**
+   * @brief Allows the creation of this Control from an Internal::CustomActor pointer.
+   *
+   * @param[in]  internal  A pointer to the internal CustomActor.
+   */
+  explicit DALI_INTERNAL TextAnchor(Dali::Internal::CustomActor* internal);
+
+}; // Class TextAnchor
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_TEXT_ANCHOR_DEVEL_H
index c88b7a0..3a40bc9 100755 (executable)
@@ -32,6 +32,7 @@ SET( devel_api_src_files
   ${devel_api_src_dir}/controls/shadow-view/shadow-view.cpp
   ${devel_api_src_dir}/controls/super-blur-view/super-blur-view.cpp
   ${devel_api_src_dir}/controls/table-view/table-view.cpp
+  ${devel_api_src_dir}/controls/text-controls/text-anchor-devel.cpp
   ${devel_api_src_dir}/controls/text-controls/text-editor-devel.cpp
   ${devel_api_src_dir}/controls/text-controls/text-field-devel.cpp
   ${devel_api_src_dir}/controls/text-controls/text-label-devel.cpp
@@ -217,6 +218,7 @@ SET( devel_api_super_blur_view_header_files
 )
 
 SET( devel_api_text_controls_header_files
+  ${devel_api_src_dir}/controls/text-controls/text-anchor-devel.h
   ${devel_api_src_dir}/controls/text-controls/text-editor-devel.h
   ${devel_api_src_dir}/controls/text-controls/text-field-devel.h
   ${devel_api_src_dir}/controls/text-controls/text-label-devel.h
index 7033e6d..696b44a 100644 (file)
 #include <dali/public-api/actors/layer.h>
 
 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
+#include <dali-toolkit/devel-api/controls/control-devel.h>
 #include <dali-toolkit/internal/controls/text-controls/common-text-utils.h>
 #include <dali-toolkit/internal/text/text-view.h>
 
 namespace Dali::Toolkit::Internal
 {
+void CommonTextUtils::SynchronizeTextAnchorsInParent(
+  Actor                             parent,
+  Text::ControllerPtr               controller,
+  std::vector<Toolkit::TextAnchor>& anchorActors)
+{
+  for(auto& anchorActor : anchorActors)
+  {
+    parent.Remove(anchorActor);
+  }
+  if(Dali::Accessibility::IsUp())
+  {
+    controller->GetAnchorActors(anchorActors);
+    for(auto& anchorActor : anchorActors)
+    {
+      parent.Add(anchorActor);
+    }
+  }
+}
+
 void CommonTextUtils::RenderText(
-  Actor                            textActor,
-  Text::RendererPtr                renderer,
-  Text::ControllerPtr              controller,
-  Text::DecoratorPtr               decorator,
-  float&                           alignmentOffset,
-  Actor&                           renderableActor,
-  Actor&                           backgroundActor,
-  Toolkit::Control&                stencil,
-  std::vector<Actor>&              clippingDecorationActors,
-  Text::Controller::UpdateTextType updateTextType)
+  Actor                             textActor,
+  Text::RendererPtr                 renderer,
+  Text::ControllerPtr               controller,
+  Text::DecoratorPtr                decorator,
+  float&                            alignmentOffset,
+  Actor&                            renderableActor,
+  Actor&                            backgroundActor,
+  Toolkit::Control&                 stencil,
+  std::vector<Actor>&               clippingDecorationActors,
+  std::vector<Toolkit::TextAnchor>& anchorActors,
+  Text::Controller::UpdateTextType  updateTextType)
 {
   Actor newRenderableActor;
 
@@ -126,6 +147,7 @@ void CommonTextUtils::RenderText(
         backgroundActor.LowerToBottom();
       }
     }
+    SynchronizeTextAnchorsInParent(textActor, controller, anchorActors);
   }
 }
 
index f9069a2..212f162 100644 (file)
@@ -16,7 +16,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
+#include <dali-toolkit/devel-api/controls/text-controls/text-anchor-devel.h>
 #include <dali-toolkit/internal/text/decorator/text-decorator.h>
 #include <dali-toolkit/internal/text/rendering/text-renderer.h>
 #include <dali-toolkit/internal/text/text-controller.h>
@@ -42,6 +42,7 @@ public:
    * @param[in,out] backgroundActor Actor for rendering background
    * @param[in,out] stencil Clipping actor
    * @param[in,out] clippingDecorationActors Clipping decoration actors
+   * @param[in,out] anchorActors Anchor actors
    * @param[in] updateTextType How the text has been updated
    */
   static void RenderText(
@@ -54,7 +55,19 @@ public:
     Actor&                           backgroundActor,
     Toolkit::Control&                stencil,
     std::vector<Actor>&              clippingDecorationActors,
+    std::vector<Toolkit::TextAnchor>& anchorActors,
     Text::Controller::UpdateTextType updateTextType);
+
+  /**
+   * Common method to synchronize TextAnchor actors with Anchor objects in text's logical model.
+   * @param[in] parent The actor that is a parent of anchor actors
+   * @param[in] controller pointer to the text controller
+   * @param[in,out] anchorActors Anchor actors
+   */
+  static void SynchronizeTextAnchorsInParent(
+    Actor                             parent,
+    Text::ControllerPtr               controller,
+    std::vector<Toolkit::TextAnchor>& anchorActors);
 };
 
 } // namespace Dali::Toolkit::Internal
diff --git a/dali-toolkit/internal/controls/text-controls/text-anchor-impl.cpp b/dali-toolkit/internal/controls/text-controls/text-anchor-impl.cpp
new file mode 100644 (file)
index 0000000..2fde91c
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2021 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/controls/text-controls/text-anchor-impl.h>
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/actors/actor-devel.h>
+#include <dali/devel-api/object/property-helper-devel.h>
+#include <dali/integration-api/debug.h>
+#include <dali/public-api/common/dali-common.h>
+#include <dali/public-api/object/type-registry-helper.h>
+
+// INTERNAL INCLUDES
+
+// DEVEL INCLUDES
+#include <dali-toolkit/devel-api/controls/control-devel.h>
+
+using namespace Dali::Toolkit::Text;
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+namespace
+{
+#if defined(DEBUG_ENABLED)
+Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
+#endif
+
+// Type registration
+BaseHandle Create()
+{
+  return Toolkit::TextAnchor::New();
+}
+
+// clang-format off
+// Setup properties, signals and actions using the type-registry.
+DALI_TYPE_REGISTRATION_BEGIN(Toolkit::TextAnchor, Toolkit::Control, Create);
+
+DALI_PROPERTY_REGISTRATION(Toolkit, TextAnchor, "startCharacterIndex", INTEGER, START_CHARACTER_INDEX)
+DALI_PROPERTY_REGISTRATION(Toolkit, TextAnchor, "endCharacterIndex",   INTEGER, END_CHARACTER_INDEX  )
+DALI_PROPERTY_REGISTRATION(Toolkit, TextAnchor, "uri",                 STRING,  URI        )
+
+DALI_TYPE_REGISTRATION_END()
+// clang-format on
+
+} // namespace
+
+Toolkit::TextAnchor TextAnchor::New()
+{
+  // Create the implementation, temporarily owned by this handle on stack
+  IntrusivePtr<TextAnchor> impl = new TextAnchor();
+
+  // Pass ownership to CustomActor handle
+  Toolkit::TextAnchor handle(*impl);
+
+  // Second-phase init of the implementation
+  // This can only be done after the CustomActor connection has been made...
+  impl->Initialize();
+
+  return handle;
+}
+
+Property::Value TextAnchor::GetProperty(BaseObject* object, Property::Index index)
+{
+  Property::Value value;
+
+  Toolkit::TextAnchor anchor = Toolkit::TextAnchor::DownCast(Dali::BaseHandle(object));
+
+  if(anchor)
+  {
+    TextAnchor& impl(GetImpl(anchor));
+
+    switch(index)
+    {
+      case Toolkit::TextAnchor::Property::START_CHARACTER_INDEX:
+      {
+        value = impl.mStartCharacterIndex;
+        break;
+      }
+      case Toolkit::TextAnchor::Property::END_CHARACTER_INDEX:
+      {
+        value = impl.mEndCharacterIndex;
+        break;
+      }
+      case Toolkit::TextAnchor::Property::URI:
+      {
+        value = impl.mUri;
+        break;
+      }
+    }
+  }
+
+  return value;
+}
+
+void TextAnchor::SetProperty(BaseObject* object, Property::Index index, const Property::Value& value)
+{
+  Toolkit::TextAnchor anchor = Toolkit::TextAnchor::DownCast(Dali::BaseHandle(object));
+
+  if(anchor)
+  {
+    TextAnchor& impl(GetImpl(anchor));
+    switch(index)
+    {
+      case Toolkit::TextAnchor::Property::START_CHARACTER_INDEX:
+      {
+        value.Get(impl.mStartCharacterIndex);
+        break;
+      }
+
+      case Toolkit::TextAnchor::Property::END_CHARACTER_INDEX:
+      {
+        value.Get(impl.mEndCharacterIndex);
+        break;
+      }
+
+      case Toolkit::TextAnchor::Property::URI:
+      {
+        value.Get(impl.mUri);
+        break;
+      }
+    }
+  }
+}
+
+void TextAnchor::OnInitialize()
+{
+  Actor self = Self();
+
+  // Enable highlightability
+  self.SetProperty(Toolkit::DevelControl::Property::ACCESSIBILITY_HIGHLIGHTABLE, true);
+
+  DevelControl::SetAccessibilityConstructor(self, [](Dali::Actor actor) {
+    return std::unique_ptr<Dali::Accessibility::Accessible>(
+      new AccessibleImpl(actor, Dali::Accessibility::Role::LINK));
+  });
+}
+
+TextAnchor::TextAnchor()
+: Control(ControlBehaviour(CONTROL_BEHAVIOUR_DEFAULT))
+{
+}
+
+TextAnchor::~TextAnchor()
+{
+}
+
+int32_t TextAnchor::AccessibleImpl::GetEndIndex() const
+{
+  auto self = Toolkit::TextAnchor::DownCast(Self());
+  return self.GetProperty(Toolkit::TextAnchor::Property::END_CHARACTER_INDEX).Get<int>();
+}
+
+int32_t TextAnchor::AccessibleImpl::GetStartIndex() const
+{
+  auto self = Toolkit::TextAnchor::DownCast(Self());
+  return self.GetProperty(Toolkit::TextAnchor::Property::START_CHARACTER_INDEX).Get<int>();
+}
+
+int32_t TextAnchor::AccessibleImpl::GetAnchorCount() const
+{
+  return 1;
+}
+
+Dali::Accessibility::Accessible* TextAnchor::AccessibleImpl::GetAnchorAccessible(int32_t anchorIndex) const
+{
+  return Control::Impl::GetAccessibilityObject(Self());
+}
+
+std::string TextAnchor::AccessibleImpl::GetAnchorUri(int32_t anchorIndex) const
+{
+  auto self = Toolkit::TextAnchor::DownCast(Self());
+  return self.GetProperty(Toolkit::TextAnchor::Property::URI).Get<std::string>();
+}
+
+bool TextAnchor::AccessibleImpl::IsValid() const
+{
+  return !GetAnchorUri(0).empty();
+}
+
+bool TextAnchor::OnAccessibilityActivated()
+{
+  auto uri = Self().GetProperty(Toolkit::TextAnchor::Property::URI).Get<std::string>();
+  if(!uri.empty())
+  {
+    Dali::Actor                                  current                             = Self();
+    Dali::Toolkit::Text::AnchorControlInterface* parentImplementationAnchorInterface = nullptr;
+    while(!current.GetProperty<bool>(Actor::Property::IS_ROOT) && !parentImplementationAnchorInterface)
+    {
+      Dali::Actor            parentAsActor        = current.GetParent();
+      Dali::CustomActor      parentAsCustomActor  = Dali::CustomActor::DownCast(parentAsActor);
+      Dali::CustomActorImpl& parentImplementation = parentAsCustomActor.GetImplementation();
+      parentImplementationAnchorInterface         = dynamic_cast<Dali::Toolkit::Text::AnchorControlInterface*>(&parentImplementation);
+      current                                     = parentAsActor;
+    }
+    if(parentImplementationAnchorInterface)
+    {
+      parentImplementationAnchorInterface->AnchorClicked(uri);
+      return true;
+    }
+    else
+    {
+      DALI_LOG_ERROR("TextAnchor::OnAccessibilityActivate cannot find ancestor actor implementing Dali::Toolkit::Text::AnchorControlInterface.\n");
+    }
+  }
+  return false;
+}
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/controls/text-controls/text-anchor-impl.h b/dali-toolkit/internal/controls/text-controls/text-anchor-impl.h
new file mode 100644 (file)
index 0000000..b590862
--- /dev/null
@@ -0,0 +1,167 @@
+#ifndef DALI_TOOLKIT_INTERNAL_TEXT_ANCHOR_H
+#define DALI_TOOLKIT_INTERNAL_TEXT_ANCHOR_H
+
+/*
+ * Copyright (c) 2021 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali-toolkit/public-api/controls/control-impl.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/devel-api/controls/text-controls/text-anchor-devel.h>
+#include <dali-toolkit/internal/controls/control/control-data-impl.h>
+#include <dali-toolkit/internal/text/text-anchor-control-interface.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+/**
+ * @brief A control which renders anchor (hyperlink) in hypertext.
+ */
+class TextAnchor : public Control
+{
+public:
+  /**
+   * @copydoc Dali::Toollkit::TextAnchor::New()
+   */
+  static Toolkit::TextAnchor New();
+
+  // Properties
+
+  /**
+   * @brief Called when a property of an object of this type is set.
+   *
+   * @param[in] object The object whose property is set.
+   * @param[in] index The property index.
+   * @param[in] value The new property value.
+   */
+  static void SetProperty(BaseObject* object, Property::Index index, const Property::Value& value);
+
+  /**
+   * @brief Called to retrieve a property of an object of this type.
+   *
+   * @param[in] object The object whose property is to be retrieved.
+   * @param[in] index The property index.
+   * @return The current value of the property.
+   */
+  static Property::Value GetProperty(BaseObject* object, Property::Index index);
+
+private: // From Control
+  /**
+   * @copydoc Control::OnInitialize()
+   */
+  void OnInitialize() override;
+
+  /**
+   * @copydoc Control::OnPropertySet()
+   */
+  // void OnPropertySet(Property::Index index, const Property::Value& propertyValue) override;
+
+  /**
+   * @copydoc Control::OnAccessibilityActivated()
+   */
+  bool OnAccessibilityActivated() override;
+
+private: // Implementation
+  /**
+   * Construct a new TextAnchor.
+   */
+  TextAnchor();
+
+  /**
+   * A reference counted object may only be deleted by calling Unreference()
+   */
+  virtual ~TextAnchor();
+
+private:
+  // Undefined copy constructor and assignment operators
+  TextAnchor(const TextAnchor&);
+  TextAnchor& operator=(const TextAnchor& rhs);
+
+  //Data
+  int         mStartCharacterIndex;
+  int         mEndCharacterIndex;
+  std::string mUri;
+
+protected:
+  /**
+   * @brief This structure is to connect TextAnchor with Accessible functions.
+   */
+  struct AccessibleImpl : public DevelControl::AccessibleImpl,
+                          public virtual Dali::Accessibility::Hyperlink
+  {
+    using DevelControl::AccessibleImpl::AccessibleImpl;
+    /**
+     * @copydoc Dali::Accessibility::Hyperlink::GetEndIndex()
+     */
+    int32_t GetEndIndex() const override;
+
+    /**
+     * @copydoc Dali::Accessibility::Hyperlink::GetStartIndex()
+     */
+    int32_t GetStartIndex() const override;
+
+    /**
+     * @copydoc Dali::Accessibility::Hyperlink::GetAnchorCount()
+     */
+    int32_t GetAnchorCount() const override;
+
+    /**
+     * @copydoc Dali::Accessibility::Hyperlink::GetAnchorAccessible()
+     */
+    Accessible* GetAnchorAccessible(int32_t anchorIndex) const override;
+
+    /**
+     * @copydoc Dali::Accessibility::Hyperlink::GetAnchorUri()
+     */
+    std::string GetAnchorUri(int32_t anchorIndex) const override;
+
+    /**
+     * @copydoc Dali::Accessibility::Hyperlink::IsValid()
+     */
+    bool IsValid() const override;
+  };
+};
+
+inline Toolkit::Internal::TextAnchor& GetImpl(Toolkit::TextAnchor& textAnchor)
+{
+  DALI_ASSERT_ALWAYS(textAnchor);
+
+  Dali::RefObject& handle = textAnchor.GetImplementation();
+
+  return static_cast<Toolkit::Internal::TextAnchor&>(handle);
+}
+
+inline const Toolkit::Internal::TextAnchor& GetImpl(const Toolkit::TextAnchor& textAnchor)
+{
+  DALI_ASSERT_ALWAYS(textAnchor);
+
+  const Dali::RefObject& handle = textAnchor.GetImplementation();
+
+  return static_cast<const Toolkit::Internal::TextAnchor&>(handle);
+}
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_INTERNAL_TEXT_ANCHOR_H
index 0259471..44c1176 100644 (file)
@@ -478,6 +478,11 @@ Toolkit::TextEditor::ScrollStateChangedSignalType& TextEditor::ScrollStateChange
   return mScrollStateChangedSignal;
 }
 
+void TextEditor::OnAccessibilityStatusChanged()
+{
+  CommonTextUtils::SynchronizeTextAnchorsInParent(Self(), mController, mAnchorActors);
+}
+
 void TextEditor::OnInitialize()
 {
   Actor self = Self();
@@ -568,6 +573,9 @@ void TextEditor::OnInitialize()
     return std::unique_ptr<Dali::Accessibility::Accessible>(
       new AccessibleImpl(actor, Dali::Accessibility::Role::ENTRY));
   });
+
+  Accessibility::Bridge::EnabledSignal().Connect(this, &TextEditor::OnAccessibilityStatusChanged);
+  Accessibility::Bridge::DisabledSignal().Connect(this, &TextEditor::OnAccessibilityStatusChanged);
 }
 
 void TextEditor::OnStyleChange(Toolkit::StyleManager styleManager, StyleChange::Type change)
@@ -724,7 +732,7 @@ void TextEditor::OnRelayout(const Vector2& size, RelayoutContainer& container)
 
 void TextEditor::RenderText(Text::Controller::UpdateTextType updateTextType)
 {
-  CommonTextUtils::RenderText(Self(), mRenderer, mController, mDecorator, mAlignmentOffset, mRenderableActor, mBackgroundActor, mStencil, mClippingDecorationActors, updateTextType);
+  CommonTextUtils::RenderText(Self(), mRenderer, mController, mDecorator, mAlignmentOffset, mRenderableActor, mBackgroundActor, mStencil, mClippingDecorationActors, mAnchorActors, updateTextType);
   if(mRenderableActor)
   {
     ApplyScrollPosition();
@@ -1540,6 +1548,30 @@ bool TextEditor::AccessibleImpl::SetTextContents(std::string newContents)
   return true;
 }
 
+int32_t TextEditor::AccessibleImpl::GetLinkCount() const
+{
+  auto self = Toolkit::TextEditor::DownCast(Self());
+  return Dali::Toolkit::GetImpl(self).mAnchorActors.size();
+}
+
+Accessibility::Hyperlink* TextEditor::AccessibleImpl::GetLink(int32_t linkIndex) const
+{
+  if(linkIndex < 0 || linkIndex >= GetLinkCount())
+  {
+    return nullptr;
+  }
+  auto self        = Toolkit::TextEditor::DownCast(Self());
+  auto anchorActor = Dali::Toolkit::GetImpl(self).mAnchorActors[linkIndex];
+  return dynamic_cast<Accessibility::Hyperlink*>(Dali::Accessibility::Accessible::Get(anchorActor));
+}
+
+int32_t TextEditor::AccessibleImpl::GetLinkIndex(int32_t characterOffset) const
+{
+  auto self       = Toolkit::TextEditor::DownCast(Self());
+  auto controller = Dali::Toolkit::GetImpl(self).GetTextController();
+  return controller->GetAnchorIndex(static_cast<size_t>(characterOffset));
+}
+
 } // namespace Internal
 
 } // namespace Toolkit
index 8c5d7be..e4b5fa5 100644 (file)
@@ -473,6 +473,9 @@ private: // Implementation
   // Connection needed to re-render text, when a text editor returns to the scene.
   void OnSceneConnect(Dali::Actor actor);
 
+  // Needed to synchronize TextAnchor actors with Anchor objects in text's logical model
+  void OnAccessibilityStatusChanged();
+
 private: // Data
   // Signals
   Toolkit::TextEditor::TextChangedSignalType                mTextChangedSignal;
@@ -485,17 +488,18 @@ private: // Data
   Toolkit::DevelTextEditor::SelectionChangedSignalType      mSelectionChangedSignal;
   Toolkit::DevelTextEditor::SelectionClearedSignalType      mSelectionClearedSignal;
 
-  InputMethodContext            mInputMethodContext;
-  Text::ControllerPtr           mController;
-  Text::RendererPtr             mRenderer;
-  Text::DecoratorPtr            mDecorator;
-  Text::TextVerticalScrollerPtr mTextVerticalScroller;
-  Toolkit::Control              mStencil;
-  Toolkit::ScrollBar            mScrollBar;
-  Dali::Animation               mAnimation; ///< Scroll indicator Show/Hide Animation.
-  Dali::TimePeriod              mAnimationPeriod;
-  std::vector<Actor>            mClippingDecorationActors; ///< Decoration actors which need clipping.
-  Dali::InputMethodOptions      mInputMethodOptions;
+  InputMethodContext               mInputMethodContext;
+  Text::ControllerPtr              mController;
+  Text::RendererPtr                mRenderer;
+  Text::DecoratorPtr               mDecorator;
+  Text::TextVerticalScrollerPtr    mTextVerticalScroller;
+  Toolkit::Control                 mStencil;
+  Toolkit::ScrollBar               mScrollBar;
+  Dali::Animation                  mAnimation; ///< Scroll indicator Show/Hide Animation.
+  Dali::TimePeriod                 mAnimationPeriod;
+  std::vector<Actor>               mClippingDecorationActors; ///< Decoration actors which need clipping.
+  std::vector<Toolkit::TextAnchor> mAnchorActors;
+  Dali::InputMethodOptions         mInputMethodOptions;
 
   Actor         mRenderableActor;
   Actor         mActiveLayer;
@@ -529,7 +533,8 @@ private: // Data
    */
   struct AccessibleImpl : public DevelControl::AccessibleImpl,
                           public virtual Dali::Accessibility::Text,
-                          public virtual Dali::Accessibility::EditableText
+                          public virtual Dali::Accessibility::EditableText,
+                          public virtual Dali::Accessibility::Hypertext
   {
     using DevelControl::AccessibleImpl::AccessibleImpl;
 
@@ -607,6 +612,21 @@ private: // Data
      * @copydoc Dali::Accessibility::EditableText::DeleteText()
      */
     bool DeleteText(size_t startPosition, size_t endPosition) override;
+
+    /**
+     * @copydoc Dali::Accessibility::Hypertext::GetLink()
+     */
+    Accessibility::Hyperlink* GetLink(int32_t linkIndex) const override;
+
+    /**
+     * @copydoc Dali::Accessibility::Hypertext::GetLinkIndex()
+     */
+    int32_t GetLinkIndex(int32_t characterOffset) const override;
+
+    /**
+     * @copydoc Dali::Accessibility::Hypertext::GetLinkCount()
+     */
+    int32_t GetLinkCount() const override;
   };
 };
 
index 62dfbda..f35c169 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 #include <dali-toolkit/internal/controls/text-controls/text-editor-property-handler.h>
+#include <dali-toolkit/internal/controls/text-controls/common-text-utils.h>
 
 #include <dali-toolkit/devel-api/focus-manager/keyinput-focus-manager.h>
 
@@ -311,6 +312,7 @@ void TextEditor::PropertyHandler::SetProperty(Toolkit::TextEditor textEditor, Pr
       DALI_LOG_INFO(gTextEditorLogFilter, Debug::General, "TextEditor %p ENABLE_MARKUP %d\n", impl.mController.Get(), enableMarkup);
 
       impl.mController->SetMarkupProcessorEnabled(enableMarkup);
+      CommonTextUtils::SynchronizeTextAnchorsInParent(textEditor, impl.mController, impl.mAnchorActors);
       break;
     }
     case Toolkit::TextEditor::Property::INPUT_COLOR:
index b48faa0..2f4ef60 100644 (file)
@@ -463,6 +463,11 @@ DevelTextField::SelectionClearedSignalType& TextField::SelectionClearedSignal()
   return mSelectionClearedSignal;
 }
 
+void TextField::OnAccessibilityStatusChanged()
+{
+  CommonTextUtils::SynchronizeTextAnchorsInParent(Self(), mController, mAnchorActors);
+}
+
 void TextField::OnInitialize()
 {
   Actor self = Self();
@@ -541,6 +546,9 @@ void TextField::OnInitialize()
     return std::unique_ptr<Dali::Accessibility::Accessible>(
       new AccessibleImpl(actor, Dali::Accessibility::Role::ENTRY));
   });
+
+  Accessibility::Bridge::EnabledSignal().Connect(this, &TextField::OnAccessibilityStatusChanged);
+  Accessibility::Bridge::DisabledSignal().Connect(this, &TextField::OnAccessibilityStatusChanged);
 }
 
 void TextField::OnStyleChange(Toolkit::StyleManager styleManager, StyleChange::Type change)
@@ -702,7 +710,7 @@ Text::ControllerPtr TextField::GetTextController()
 
 void TextField::RenderText(Text::Controller::UpdateTextType updateTextType)
 {
-  CommonTextUtils::RenderText(Self(), mRenderer, mController, mDecorator, mAlignmentOffset, mRenderableActor, mBackgroundActor, mStencil, mClippingDecorationActors, updateTextType);
+  CommonTextUtils::RenderText(Self(), mRenderer, mController, mDecorator, mAlignmentOffset, mRenderableActor, mBackgroundActor, mStencil, mClippingDecorationActors, mAnchorActors, updateTextType);
 }
 
 void TextField::OnKeyInputFocusGained()
@@ -1414,6 +1422,30 @@ bool TextField::AccessibleImpl::SetTextContents(std::string newContents)
   return true;
 }
 
+int32_t TextField::AccessibleImpl::GetLinkCount() const
+{
+  auto self = Toolkit::TextField::DownCast(Self());
+  return Dali::Toolkit::GetImpl(self).mAnchorActors.size();
+}
+
+Accessibility::Hyperlink* TextField::AccessibleImpl::GetLink(int32_t linkIndex) const
+{
+  if(linkIndex < 0 || linkIndex >= GetLinkCount())
+  {
+    return nullptr;
+  }
+  auto self        = Toolkit::TextField::DownCast(Self());
+  auto anchorActor = Dali::Toolkit::GetImpl(self).mAnchorActors[linkIndex];
+  return dynamic_cast<Accessibility::Hyperlink*>(Dali::Accessibility::Accessible::Get(anchorActor));
+}
+
+int32_t TextField::AccessibleImpl::GetLinkIndex(int32_t characterOffset) const
+{
+  auto self       = Toolkit::TextField::DownCast(Self());
+  auto controller = Dali::Toolkit::GetImpl(self).GetTextController();
+  return controller->GetAnchorIndex(static_cast<size_t>(characterOffset));
+}
+
 } // namespace Internal
 
 } // namespace Toolkit
index b65e953..7e18a82 100644 (file)
@@ -429,6 +429,9 @@ private: // Implementation
   // Connection needed to re-render text, when a Text Field returns to the scene.
   void OnSceneConnect(Dali::Actor actor);
 
+  // Needed to synchronize TextAnchor actors with Anchor objects in text's logical model
+  void OnAccessibilityStatusChanged();
+
 private: // Data
   // Signals
   Toolkit::TextField::TextChangedSignalType                mTextChangedSignal;
@@ -440,13 +443,14 @@ private: // Data
   Toolkit::DevelTextField::SelectionChangedSignalType      mSelectionChangedSignal;
   Toolkit::DevelTextField::SelectionClearedSignalType      mSelectionClearedSignal;
 
-  InputMethodContext       mInputMethodContext;
-  Text::ControllerPtr      mController;
-  Text::RendererPtr        mRenderer;
-  Text::DecoratorPtr       mDecorator;
-  Toolkit::Control         mStencil;                  ///< For EXCEED_POLICY_CLIP
-  std::vector<Actor>       mClippingDecorationActors; ///< Decoration actors which need clipping.
-  Dali::InputMethodOptions mInputMethodOptions;
+  InputMethodContext               mInputMethodContext;
+  Text::ControllerPtr              mController;
+  Text::RendererPtr                mRenderer;
+  Text::DecoratorPtr               mDecorator;
+  Toolkit::Control                 mStencil;                  ///< For EXCEED_POLICY_CLIP
+  std::vector<Actor>               mClippingDecorationActors; ///< Decoration actors which need clipping.
+  std::vector<Toolkit::TextAnchor> mAnchorActors;
+  Dali::InputMethodOptions         mInputMethodOptions;
 
   Actor         mRenderableActor;
   Actor         mActiveLayer;
@@ -477,7 +481,8 @@ protected:
    */
   struct AccessibleImpl : public DevelControl::AccessibleImpl,
                           public virtual Dali::Accessibility::Text,
-                          public virtual Dali::Accessibility::EditableText
+                          public virtual Dali::Accessibility::EditableText,
+                          public virtual Dali::Accessibility::Hypertext
   {
     using DevelControl::AccessibleImpl::AccessibleImpl;
 
@@ -555,6 +560,21 @@ protected:
      * @copydoc Dali::Accessibility::EditableText::DeleteText()
      */
     bool DeleteText(size_t startPosition, size_t endPosition) override;
+
+    /**
+     * @copydoc Dali::Accessibility::Hypertext::GetLink()
+     */
+    Accessibility::Hyperlink* GetLink(int32_t linkIndex) const override;
+
+    /**
+     * @copydoc Dali::Accessibility::Hypertext::GetLinkIndex()
+     */
+    int32_t GetLinkIndex(int32_t characterOffset) const override;
+
+    /**
+     * @copydoc Dali::Accessibility::Hypertext::GetLinkCount()
+     */
+    int32_t GetLinkCount() const override;
   };
 };
 
index db08d3a..dc253f0 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 #include <dali-toolkit/internal/controls/text-controls/text-field-property-handler.h>
+#include <dali-toolkit/internal/controls/text-controls/common-text-utils.h>
 
 #include <dali-toolkit/devel-api/focus-manager/keyinput-focus-manager.h>
 #include <dali-toolkit/devel-api/text/rendering-backend.h>
@@ -408,6 +409,7 @@ void TextField::PropertyHandler::SetProperty(Toolkit::TextField textField, Prope
       DALI_LOG_INFO(gTextFieldLogFilter, Debug::General, "TextField %p ENABLE_MARKUP %d\n", impl.mController.Get(), enableMarkup);
 
       impl.mController->SetMarkupProcessorEnabled(enableMarkup);
+      CommonTextUtils::SynchronizeTextAnchorsInParent(textField, impl.mController, impl.mAnchorActors);
       break;
     }
     case Toolkit::TextField::Property::INPUT_FONT_FAMILY:
index a98ef9b..22990d9 100644 (file)
@@ -37,6 +37,7 @@
 #include <dali-toolkit/internal/text/text-effects-style.h>
 #include <dali-toolkit/internal/text/text-font-style.h>
 #include <dali-toolkit/internal/text/text-view.h>
+#include <dali-toolkit/internal/controls/text-controls/common-text-utils.h>
 #include <dali-toolkit/public-api/text/text-enumerations.h>
 
 #include <dali-toolkit/devel-api/controls/control-devel.h>
@@ -850,6 +851,9 @@ void TextLabel::OnInitialize()
     return std::unique_ptr<Dali::Accessibility::Accessible>(
       new AccessibleImpl(actor, Dali::Accessibility::Role::LABEL));
   });
+
+  Accessibility::Bridge::EnabledSignal().Connect(this, &TextLabel::OnAccessibilityStatusChanged);
+  Accessibility::Bridge::DisabledSignal().Connect(this, &TextLabel::OnAccessibilityStatusChanged);
 }
 
 void TextLabel::OnStyleChange(Toolkit::StyleManager styleManager, StyleChange::Type change)
@@ -937,6 +941,12 @@ void TextLabel::OnPropertySet(Property::Index index, const Property::Value& prop
       }
       break;
     }
+    case Toolkit::TextLabel::Property::TEXT:
+    case Toolkit::TextLabel::Property::ENABLE_MARKUP:
+    {
+      CommonTextUtils::SynchronizeTextAnchorsInParent(Self(), mController, mAnchorActors);
+      break;
+    }
     default:
     {
       Control::OnPropertySet(index, propertyValue); // up call to control for non-handled properties
@@ -1110,6 +1120,11 @@ void TextLabel::EmitTextFitChangedSignal()
   mTextFitChangedSignal.Emit(handle);
 }
 
+void TextLabel::OnAccessibilityStatusChanged()
+{
+  CommonTextUtils::SynchronizeTextAnchorsInParent(Self(), mController, mAnchorActors);
+}
+
 TextLabel::TextLabel()
 : Control(ControlBehaviour(CONTROL_BEHAVIOUR_DEFAULT)),
   mRenderingBackend(DEFAULT_RENDERING_BACKEND),
@@ -1170,8 +1185,8 @@ bool TextLabel::AccessibleImpl::SetCursorOffset(size_t offset)
 
 Dali::Accessibility::Range TextLabel::AccessibleImpl::GetTextAtOffset(size_t offset, Dali::Accessibility::TextBoundary boundary)
 {
-  auto self = Toolkit::TextLabel::DownCast(Self());
-  auto text = self.GetProperty(Toolkit::TextLabel::Property::TEXT).Get<std::string>();
+  auto self     = Toolkit::TextLabel::DownCast(Self());
+  auto text     = self.GetProperty(Toolkit::TextLabel::Property::TEXT).Get<std::string>();
   auto textSize = text.size();
 
   auto range = Dali::Accessibility::Range{};
@@ -1192,7 +1207,7 @@ Dali::Accessibility::Range TextLabel::AccessibleImpl::GetTextAtOffset(size_t off
     case Dali::Accessibility::TextBoundary::LINE:
     {
       auto textString = text.c_str();
-      auto breaks = std::vector<char>(textSize, 0);
+      auto breaks     = std::vector<char>(textSize, 0);
 
       if(boundary == Dali::Accessibility::TextBoundary::WORD)
       {
@@ -1203,7 +1218,7 @@ Dali::Accessibility::Range TextLabel::AccessibleImpl::GetTextAtOffset(size_t off
         Accessibility::Accessible::FindLineSeparationsUtf8(reinterpret_cast<const utf8_t*>(textString), textSize, "", breaks.data());
       }
 
-      auto index = 0u;
+      auto index   = 0u;
       auto counter = 0u;
       while(index < textSize && counter <= offset)
       {
@@ -1267,8 +1282,8 @@ Dali::Accessibility::Range TextLabel::AccessibleImpl::GetRangeOfSelection(size_t
     return {};
   }
 
-  auto self  = Toolkit::TextLabel::DownCast(Self());
-  auto controller = Dali::Toolkit::GetImpl(self).GetTextController();
+  auto        self       = Toolkit::TextLabel::DownCast(Self());
+  auto        controller = Dali::Toolkit::GetImpl(self).GetTextController();
   std::string value{};
   controller->RetrieveSelection(value);
   auto indices = controller->GetSelectionIndexes();
@@ -1302,6 +1317,30 @@ bool TextLabel::AccessibleImpl::SetRangeOfSelection(size_t selectionIndex, size_
   return true;
 }
 
+int32_t TextLabel::AccessibleImpl::GetLinkCount() const
+{
+  auto self = Toolkit::TextLabel::DownCast(Self());
+  return Dali::Toolkit::GetImpl(self).mAnchorActors.size();
+}
+
+Accessibility::Hyperlink* TextLabel::AccessibleImpl::GetLink(int32_t linkIndex) const
+{
+  if(linkIndex < 0 || linkIndex >= GetLinkCount())
+  {
+    return nullptr;
+  }
+  auto self        = Toolkit::TextLabel::DownCast(Self());
+  auto anchorActor = Dali::Toolkit::GetImpl(self).mAnchorActors[linkIndex];
+  return dynamic_cast<Accessibility::Hyperlink*>(Dali::Accessibility::Accessible::Get(anchorActor));
+}
+
+int32_t TextLabel::AccessibleImpl::GetLinkIndex(int32_t characterOffset) const
+{
+  auto self       = Toolkit::TextLabel::DownCast(Self());
+  auto controller = Dali::Toolkit::GetImpl(self).GetTextController();
+  return controller->GetAnchorIndex(static_cast<size_t>(characterOffset));
+}
+
 } // namespace Internal
 
 } // namespace Toolkit
index d345d21..5c9a6b1 100644 (file)
@@ -198,6 +198,7 @@ private:
    * @brief Emits TextFitChanged signal.
    */
   void EmitTextFitChangedSignal();
+  void OnAccessibilityStatusChanged();
 
 private: // Data
   Text::ControllerPtr   mController;
@@ -205,6 +206,8 @@ private: // Data
 
   Toolkit::Visual::Base mVisual;
 
+  std::vector<Toolkit::TextAnchor> mAnchorActors;
+
   // Signals
   Toolkit::DevelTextLabel::AnchorClickedSignalType mAnchorClickedSignal;
   Toolkit::DevelTextLabel::TextFitChangedSignalType mTextFitChangedSignal;
@@ -217,7 +220,8 @@ protected:
    * @brief This structure is to connect TextLabel with Accessible functions.
    */
   struct AccessibleImpl : public DevelControl::AccessibleImpl,
-                          public virtual Dali::Accessibility::Text
+                          public virtual Dali::Accessibility::Text,
+                          public virtual Dali::Accessibility::Hypertext
   {
     using DevelControl::AccessibleImpl::AccessibleImpl;
 
@@ -270,6 +274,21 @@ protected:
      * @copydoc Dali::Accessibility::Text::GetNamePropertyIndex()
      */
     Property::Index GetNamePropertyIndex() override;
+
+    /**
+     * @copydoc Dali::Accessibility::Hypertext::GetLink()
+     */
+    Accessibility::Hyperlink* GetLink(int32_t linkIndex) const override;
+
+    /**
+     * @copydoc Dali::Accessibility::Hypertext::GetLinkIndex()
+     */
+    int32_t GetLinkIndex(int32_t characterOffset) const override;
+
+    /**
+     * @copydoc Dali::Accessibility::Hypertext::GetLinkCount()
+     */
+    int32_t GetLinkCount() const override;
   };
 };
 
index 855df54..b9c7d23 100644 (file)
@@ -102,6 +102,7 @@ SET( toolkit_src_files
    ${toolkit_src_dir}/controls/super-blur-view/super-blur-view-impl.cpp
    ${toolkit_src_dir}/controls/table-view/table-view-impl.cpp
    ${toolkit_src_dir}/controls/text-controls/common-text-utils.cpp
+   ${toolkit_src_dir}/controls/text-controls/text-anchor-impl.cpp
    ${toolkit_src_dir}/controls/text-controls/text-editor-impl.cpp
    ${toolkit_src_dir}/controls/text-controls/text-editor-property-handler.cpp
    ${toolkit_src_dir}/controls/text-controls/text-field-impl.cpp
index 8a1dd76..fcd3bed 100644 (file)
@@ -1619,6 +1619,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<int>(anchor.startIndex));
+  actor.SetProperty(Toolkit::TextAnchor::Property::END_CHARACTER_INDEX, static_cast<int>(anchor.endIndex));
+  return actor;
+}
+
+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);
+  }
+}
+
+int32_t Controller::Impl::GetAnchorIndex(size_t characterOffset) const
+{
+  Vector<Anchor>::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
index 814f3a8..e49971a 100644 (file)
@@ -841,6 +841,49 @@ struct Controller::Impl
    */
   void ResetScrollPosition();
 
+  /**
+   * @brief Resets a provided vector with actors that marks the position of anchors in markup enabled text
+   *
+   * @param[out] anchorActors the vector of actor (empty collection if no anchors available).
+   */
+  void GetAnchorActors(std::vector<Toolkit::TextAnchor>& anchorActors);
+
+  /**
+   * @brief Return an index of first anchor in the anchor vector whose boundaries includes given character offset
+   *
+   * @param[in] characterOffset A position in text coords.
+   *
+   * @return the 0-based index in anchor vector (-1 if an anchor not found)
+   */
+  int32_t GetAnchorIndex(size_t characterOffset) const;
+
+  /**
+   * @brief Return the geometrical position of an anchor relative to the parent origin point.
+   *
+   * @param[in] anchor An anchor.
+   *
+   * @return The x, y, z coordinates of an anchor.
+   */
+  Vector3 GetAnchorPosition(Anchor anchor) const;
+
+  /**
+   * @brief Return the size of an anchor expresed as a vector containing anchor's width and height.
+   *
+   * @param[in] anchor An anchor.
+   *
+   * @return The width and height of an anchor.
+   */
+  Vector2 GetAnchorSize(Anchor anchor) const;
+
+  /**
+   * @brief Return the actor representing an anchor.
+   *
+   * @param[in] anchor An anchor.
+   *
+   * @return The actor representing an anchor.
+   */
+  Toolkit::TextAnchor CreateAnchorActor(Anchor anchor);
+
 public:
   /**
    * @brief Gets implementation from the controller handle.
index 4ec306b..88eb9ef 100644 (file)
@@ -1644,6 +1644,16 @@ Actor Controller::CreateBackgroundActor()
   return mImpl->CreateBackgroundActor();
 }
 
+void Controller::GetAnchorActors(std::vector<Toolkit::TextAnchor>& anchorActors)
+{
+  mImpl->GetAnchorActors(anchorActors);
+}
+
+int Controller::GetAnchorIndex(size_t characterOffset)
+{
+  return mImpl->GetAnchorIndex(characterOffset);
+}
+
 Controller::Controller(ControlInterface*           controlInterface,
                        EditableControlInterface*   editableControlInterface,
                        SelectableControlInterface* selectableControlInterface,
index a5bc8b4..1752e3a 100644 (file)
@@ -25,6 +25,7 @@
 // INTERNAL INCLUDES
 #include <dali-toolkit/devel-api/controls/text-controls/text-label-devel.h>
 #include <dali-toolkit/devel-api/controls/text-controls/text-selection-popup-callback-interface.h>
+#include <dali-toolkit/devel-api/controls/text-controls/text-anchor-devel.h>
 #include <dali-toolkit/devel-api/text/text-enumerations-devel.h>
 #include <dali-toolkit/internal/text/decorator/text-decorator.h>
 #include <dali-toolkit/internal/text/hidden-text.h>
@@ -1757,6 +1758,22 @@ public: // Text-input Event Queuing.
    */
   CharacterIndex GetCursorPosition();
 
+  /**
+   * @brief Resets a provided vector with actors that marks the position of anchors in markup enabled text
+   *
+   * @param[out] anchorActors the vector of actor (empty collection if no anchors available).
+   */
+  void GetAnchorActors(std::vector<Toolkit::TextAnchor>& anchorActors);
+
+  /**
+   * @brief Return an index of first anchor in the anchor vector whose boundaries includes given character offset
+   *
+   * @param[in] characterOffset A position in text coords.
+   *
+   * @return the index in anchor vector (-1 if an anchor not found)
+   */
+  int GetAnchorIndex(size_t characterOffset);
+
 protected: // Inherit from Text::Decorator::ControllerInterface.
   /**
    * @copydoc Dali::Toolkit::Text::Decorator::ControllerInterface::GetTargetSize()