[NUI] Support markup anchor to TextLabel, TextField, TextEditor (#2813)
authorBowon Ryu <bowon.ryu@samsung.com>
Wed, 7 Apr 2021 10:26:26 +0000 (19:26 +0900)
committerhuiyueun <35286162+huiyueun@users.noreply.github.com>
Tue, 20 Apr 2021 06:13:00 +0000 (15:13 +0900)
* [NUI] Support markup anchor to TextLabel, TextField, TextEditor

User can set href on anchors using the anchor tag <a> in markup.
and also can register AnchorClicked callback in Text components.
the AnchorClicked signal is emitted when the anchor is clicked.

example:
label.Text = "<a href='https://www.tizen.org'>Tizen</a>";
label.EnableMarkup = true;
label.AnchorClicked += (sender, e) =>
{
  Tizen.Log.Info("NUI", e.Href + "\n");
};

Signed-off-by: Bowon Ryu <bowon.ryu@samsung.com>
[NUI] Remove unnecessary codes.

Signed-off-by: Bowon Ryu <bowon.ryu@samsung.com>
* [NUI] Change the name of signal to AnchorClicked.

To prevent confusion with TouchSignal(),
the name of signal was changed to AnchorClicked.

Signed-off-by: Bowon Ryu <bowon.ryu@samsung.com>
src/Tizen.NUI/src/internal/Common/TextLabelSignal.cs [new file with mode: 0644]
src/Tizen.NUI/src/internal/Interop/Interop.TextEditor.cs
src/Tizen.NUI/src/internal/Interop/Interop.TextField.cs
src/Tizen.NUI/src/internal/Interop/Interop.TextLabel.cs
src/Tizen.NUI/src/public/BaseComponents/TextEditorEvent.cs
src/Tizen.NUI/src/public/BaseComponents/TextEvent.cs [new file with mode: 0644]
src/Tizen.NUI/src/public/BaseComponents/TextFieldEvent.cs
src/Tizen.NUI/src/public/BaseComponents/TextLabelEvent.cs [new file with mode: 0644]

diff --git a/src/Tizen.NUI/src/internal/Common/TextLabelSignal.cs b/src/Tizen.NUI/src/internal/Common/TextLabelSignal.cs
new file mode 100644 (file)
index 0000000..7e60e92
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ *
+ */
+
+using Tizen.NUI.BaseComponents;
+
+namespace Tizen.NUI
+{
+    internal class TextLabelSignal : Disposable
+    {
+
+        internal TextLabelSignal(global::System.IntPtr cPtr, bool cMemoryOwn) : base(cPtr, cMemoryOwn)
+        {
+        }
+
+        protected override void ReleaseSwigCPtr(System.Runtime.InteropServices.HandleRef swigCPtr)
+        {
+            Interop.TextLabel.DeleteTextLabelSignal(swigCPtr);
+        }
+
+        public bool Empty()
+        {
+            bool ret = Interop.TextLabel.TextLabelSignalEmpty(SwigCPtr);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            return ret;
+        }
+
+        public uint GetConnectionCount()
+        {
+            uint ret = Interop.TextLabel.TextLabelSignalGetConnectionCount(SwigCPtr);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            return ret;
+        }
+
+        public void Connect(System.Delegate func)
+        {
+            System.IntPtr ip = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate<System.Delegate>(func);
+            {
+                Interop.TextLabel.TextLabelSignalConnect(SwigCPtr, new System.Runtime.InteropServices.HandleRef(this, ip));
+                if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            }
+        }
+
+        public void Disconnect(System.Delegate func)
+        {
+            System.IntPtr ip = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate<System.Delegate>(func);
+            {
+                Interop.TextLabel.TextLabelSignalDisconnect(SwigCPtr, new System.Runtime.InteropServices.HandleRef(this, ip));
+                if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            }
+        }
+
+        public void Emit(TextLabel arg)
+        {
+            Interop.TextLabel.TextLabelSignalEmit(SwigCPtr, TextLabel.getCPtr(arg));
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+        }
+
+        public TextLabelSignal() : this(Interop.TextLabel.NewTextLabelSignal(), true)
+        {
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+        }
+    }
+}
index d0a0a56..2834282 100755 (executable)
@@ -180,6 +180,9 @@ namespace Tizen.NUI
             [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_TextEditor_MaxLengthReachedSignal")]
             public static extern global::System.IntPtr MaxLengthReachedSignal(global::System.Runtime.InteropServices.HandleRef jarg1);
 
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_TextEditor_AnchorClickedSignal")]
+            public static extern global::System.IntPtr AnchorClickedSignal(global::System.Runtime.InteropServices.HandleRef jarg1);
+
             [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_TextEditorSignal_Empty")]
             public static extern bool TextEditorSignalEmpty(global::System.Runtime.InteropServices.HandleRef jarg1);
 
index 4e7706f..698f835 100755 (executable)
@@ -195,6 +195,9 @@ namespace Tizen.NUI
             [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_TextField_InputStyleChangedSignal")]
             public static extern global::System.IntPtr InputStyleChangedSignal(global::System.Runtime.InteropServices.HandleRef jarg1);
 
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_TextField_AnchorClickedSignal")]
+            public static extern global::System.IntPtr AnchorClickedSignal(global::System.Runtime.InteropServices.HandleRef jarg1);
+
             [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_TextFieldSignal_Empty")]
             public static extern bool TextFieldSignalEmpty(global::System.Runtime.InteropServices.HandleRef jarg1);
 
index fc10f77..2990664 100755 (executable)
@@ -140,6 +140,31 @@ namespace Tizen.NUI
 
             [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_TextLabel_Property_FONT_SIZE_SCALE_get")]
             public static extern int FontSizeScaleGet();
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_TextLabel_AnchorClickedSignal")]
+            public static extern global::System.IntPtr AnchorClickedSignal(global::System.Runtime.InteropServices.HandleRef jarg1);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_TextLabelSignal_Empty")]
+            public static extern bool TextLabelSignalEmpty(global::System.Runtime.InteropServices.HandleRef jarg1);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_TextLabelSignal_GetConnectionCount")]
+            public static extern uint TextLabelSignalGetConnectionCount(global::System.Runtime.InteropServices.HandleRef jarg1);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_TextLabelSignal_Connect")]
+            public static extern void TextLabelSignalConnect(global::System.Runtime.InteropServices.HandleRef jarg1, global::System.Runtime.InteropServices.HandleRef jarg2);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_TextLabelSignal_Disconnect")]
+            public static extern void TextLabelSignalDisconnect(global::System.Runtime.InteropServices.HandleRef jarg1, global::System.Runtime.InteropServices.HandleRef jarg2);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_TextLabelSignal_Emit")]
+            public static extern void TextLabelSignalEmit(global::System.Runtime.InteropServices.HandleRef jarg1, global::System.Runtime.InteropServices.HandleRef jarg2);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_new_TextLabelSignal")]
+            public static extern global::System.IntPtr NewTextLabelSignal();
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_delete_TextLabelSignal")]
+            public static extern void DeleteTextLabelSignal(global::System.Runtime.InteropServices.HandleRef jarg1);
+
         }
     }
 }
index f430521..061b966 100755 (executable)
@@ -36,6 +36,9 @@ namespace Tizen.NUI.BaseComponents
         private EventHandler<MaxLengthReachedEventArgs> textEditorMaxLengthReachedEventHandler;
         private MaxLengthReachedCallbackDelegate textEditorMaxLengthReachedCallbackDelegate;
 
+        private EventHandler<AnchorClickedEventArgs> textEditorAnchorClickedEventHandler;
+        private AnchorClickedCallbackDelegate textEditorAnchorClickedCallbackDelegate;
+
         [UnmanagedFunctionPointer(CallingConvention.StdCall)]
         private delegate void TextChangedCallbackDelegate(IntPtr textEditor);
 
@@ -45,6 +48,9 @@ namespace Tizen.NUI.BaseComponents
         [UnmanagedFunctionPointer(CallingConvention.StdCall)]
         private delegate void MaxLengthReachedCallbackDelegate(IntPtr textEditor);
 
+        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+        private delegate void AnchorClickedCallbackDelegate(IntPtr textEditor, IntPtr href, uint hrefLength);
+
         /// <summary>
         /// An event for the TextChanged signal which can be used to subscribe or unsubscribe the event handler
         /// provided by the user. The TextChanged signal is emitted when the text changes.<br />
@@ -123,6 +129,32 @@ namespace Tizen.NUI.BaseComponents
             }
         }
 
+        /// <summary>
+        /// The AnchorClicked signal is emitted when the anchor is clicked.
+        /// </summary>
+        /// This will be public opened in tizen_6.5 after ACR done. Before ACR, need to be hidden as inhouse API.
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public event EventHandler<AnchorClickedEventArgs> AnchorClicked
+        {
+            add
+            {
+                if (textEditorAnchorClickedEventHandler == null)
+                {
+                    textEditorAnchorClickedCallbackDelegate = (OnAnchorClicked);
+                    AnchorClickedSignal().Connect(textEditorAnchorClickedCallbackDelegate);
+                }
+                textEditorAnchorClickedEventHandler += value;
+            }
+            remove
+            {
+                textEditorAnchorClickedEventHandler -= value;
+                if (textEditorAnchorClickedEventHandler == null && AnchorClickedSignal().Empty() == false)
+                {
+                    AnchorClickedSignal().Disconnect(textEditorAnchorClickedCallbackDelegate);
+                }
+            }
+        }
+
         internal TextEditorSignal TextChangedSignal()
         {
             TextEditorSignal ret = new TextEditorSignal(Interop.TextEditor.TextChangedSignal(SwigCPtr), false);
@@ -144,6 +176,13 @@ namespace Tizen.NUI.BaseComponents
             return ret;
         }
 
+        internal TextEditorSignal AnchorClickedSignal()
+        {
+            TextEditorSignal ret = new TextEditorSignal(Interop.TextEditor.AnchorClickedSignal(SwigCPtr), false);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            return ret;
+        }
+
         private void OnTextChanged(IntPtr textEditor)
         {
             if (textEditorTextChangedEventHandler != null)
@@ -187,6 +226,18 @@ namespace Tizen.NUI.BaseComponents
             }
         }
 
+        private void OnAnchorClicked(IntPtr textEditor, IntPtr href, uint hrefLength)
+        {
+            // Note: hrefLength is useful for get the length of a const char* (href) in dali-toolkit.
+            // But NUI can get the length of string (href), so hrefLength is not necessary in NUI.
+            AnchorClickedEventArgs e = new AnchorClickedEventArgs();
+
+            // Populate all members of "e" (AnchorClickedEventArgs) with real data
+            e.Href = Marshal.PtrToStringAnsi(href);
+            //here we send all data to user event handlers
+            textEditorAnchorClickedEventHandler?.Invoke(this, e);
+        }
+
         /// <summary>
         /// Event arguments that passed via the TextChanged signal.
         /// </summary>
diff --git a/src/Tizen.NUI/src/public/BaseComponents/TextEvent.cs b/src/Tizen.NUI/src/public/BaseComponents/TextEvent.cs
new file mode 100644 (file)
index 0000000..a21e843
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ *
+ */
+
+using System;
+using System.ComponentModel;
+
+namespace Tizen.NUI.BaseComponents
+{
+    /// <summary>
+    /// AnchorClickedEventArgs is a class to record anchor click event arguments which will sent to user.
+    /// </summary>
+    /// This will be public opened in tizen_6.5 after ACR done. Before ACR, need to be hidden as inhouse API.
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class AnchorClickedEventArgs : EventArgs
+    {
+        /// <summary>
+        /// Anchor href.
+        /// </summary>
+        /// This will be public opened in tizen_6.5 after ACR done. Before ACR, need to be hidden as inhouse API.
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public string Href { get; set; }
+    }
+}
\ No newline at end of file
index fea47ab..413f40c 100755 (executable)
@@ -16,6 +16,7 @@
  */
 
 using System;
+using System.ComponentModel;
 using System.Runtime.InteropServices;
 
 namespace Tizen.NUI.BaseComponents
@@ -30,6 +31,8 @@ namespace Tizen.NUI.BaseComponents
         private TextChangedCallbackDelegate textFieldTextChangedCallbackDelegate;
         private EventHandler<MaxLengthReachedEventArgs> textFieldMaxLengthReachedEventHandler;
         private MaxLengthReachedCallbackDelegate textFieldMaxLengthReachedCallbackDelegate;
+        private EventHandler<AnchorClickedEventArgs> textFieldAnchorClickedEventHandler;
+        private AnchorClickedCallbackDelegate textFieldAnchorClickedCallbackDelegate;
 
         [UnmanagedFunctionPointer(CallingConvention.StdCall)]
         private delegate void TextChangedCallbackDelegate(IntPtr textField);
@@ -37,6 +40,9 @@ namespace Tizen.NUI.BaseComponents
         [UnmanagedFunctionPointer(CallingConvention.StdCall)]
         private delegate void MaxLengthReachedCallbackDelegate(IntPtr textField);
 
+        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+        private delegate void AnchorClickedCallbackDelegate(IntPtr textField, IntPtr href, uint hrefLength);
+
         /// <summary>
         /// The TextChanged event.
         /// </summary>
@@ -87,6 +93,32 @@ namespace Tizen.NUI.BaseComponents
             }
         }
 
+        /// <summary>
+        /// The AnchorClicked signal is emitted when the anchor is clicked.
+        /// </summary>
+        /// This will be public opened in tizen_6.5 after ACR done. Before ACR, need to be hidden as inhouse API.
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public event EventHandler<AnchorClickedEventArgs> AnchorClicked
+        {
+            add
+            {
+                if (textFieldAnchorClickedEventHandler == null)
+                {
+                    textFieldAnchorClickedCallbackDelegate = (OnAnchorClicked);
+                    AnchorClickedSignal().Connect(textFieldAnchorClickedCallbackDelegate);
+                }
+                textFieldAnchorClickedEventHandler += value;
+            }
+            remove
+            {
+                textFieldAnchorClickedEventHandler -= value;
+                if (textFieldAnchorClickedEventHandler == null && AnchorClickedSignal().Empty() == false)
+                {
+                    AnchorClickedSignal().Disconnect(textFieldAnchorClickedCallbackDelegate);
+                }
+            }
+        }
+
         internal TextFieldSignal TextChangedSignal()
         {
             TextFieldSignal ret = new TextFieldSignal(Interop.TextField.TextChangedSignal(SwigCPtr), false);
@@ -101,6 +133,13 @@ namespace Tizen.NUI.BaseComponents
             return ret;
         }
 
+        internal TextFieldSignal AnchorClickedSignal()
+        {
+            TextFieldSignal ret = new TextFieldSignal(Interop.TextField.AnchorClickedSignal(SwigCPtr), false);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            return ret;
+        }
+
         private void OnTextChanged(IntPtr textField)
         {
             if (textFieldTextChangedEventHandler != null)
@@ -127,6 +166,18 @@ namespace Tizen.NUI.BaseComponents
             }
         }
 
+        private void OnAnchorClicked(IntPtr textField, IntPtr href, uint hrefLength)
+        {
+            // Note: hrefLength is useful for get the length of a const char* (href) in dali-toolkit.
+            // But NUI can get the length of string (href), so hrefLength is not necessary in NUI.
+            AnchorClickedEventArgs e = new AnchorClickedEventArgs();
+
+            // Populate all members of "e" (AnchorClickedEventArgs) with real data
+            e.Href = Marshal.PtrToStringAnsi(href);
+            //here we send all data to user event handlers
+            textFieldAnchorClickedEventHandler?.Invoke(this, e);
+        }
+
         /// <summary>
         /// The TextChanged event arguments.
         /// </summary>
diff --git a/src/Tizen.NUI/src/public/BaseComponents/TextLabelEvent.cs b/src/Tizen.NUI/src/public/BaseComponents/TextLabelEvent.cs
new file mode 100644 (file)
index 0000000..4890eb0
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ *
+ */
+
+using System;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+
+namespace Tizen.NUI.BaseComponents
+{
+    /// <summary>
+    /// A control which renders a short text string.<br />
+    /// Text labels are lightweight, non-editable, and do not respond to the user input.<br />
+    /// </summary>
+    /// <since_tizen> 3 </since_tizen>
+    public partial class TextLabel
+    {
+        private EventHandler<AnchorClickedEventArgs> textLabelAnchorClickedEventHandler;
+        private AnchorClickedCallbackDelegate textLabelAnchorClickedCallbackDelegate;
+
+        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+        private delegate void AnchorClickedCallbackDelegate(IntPtr textLabel, IntPtr href, uint hrefLength);
+
+        /// <summary>
+        /// The AnchorClicked signal is emitted when the anchor is clicked.
+        /// </summary>
+        /// This will be public opened in tizen_6.5 after ACR done. Before ACR, need to be hidden as inhouse API.
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public event EventHandler<AnchorClickedEventArgs> AnchorClicked
+        {
+            add
+            {
+                if (textLabelAnchorClickedEventHandler == null)
+                {
+                    textLabelAnchorClickedCallbackDelegate = (OnAnchorClicked);
+                    AnchorClickedSignal().Connect(textLabelAnchorClickedCallbackDelegate);
+                }
+                textLabelAnchorClickedEventHandler += value;
+            }
+            remove
+            {
+                textLabelAnchorClickedEventHandler -= value;
+                if (textLabelAnchorClickedEventHandler == null && AnchorClickedSignal().Empty() == false)
+                {
+                    AnchorClickedSignal().Disconnect(textLabelAnchorClickedCallbackDelegate);
+                }
+            }
+        }
+
+        internal TextLabelSignal AnchorClickedSignal()
+        {
+            TextLabelSignal ret = new TextLabelSignal(Interop.TextLabel.AnchorClickedSignal(SwigCPtr), false);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            return ret;
+        }
+
+        private void OnAnchorClicked(IntPtr textLabel, IntPtr href, uint hrefLength)
+        {
+            // Note: hrefLength is useful for get the length of a const char* (href) in dali-toolkit.
+            // But NUI can get the length of string (href), so hrefLength is not necessary in NUI.
+            AnchorClickedEventArgs e = new AnchorClickedEventArgs();
+
+            // Populate all members of "e" (AnchorClickedEventArgs) with real data
+            e.Href = Marshal.PtrToStringAnsi(href);
+            //here we send all data to user event handlers
+            textLabelAnchorClickedEventHandler?.Invoke(this, e);
+        }
+    }
+}