Add InputFilter to TextField, TextEditor
authorBowon Ryu <bowon.ryu@samsung.com>
Fri, 21 May 2021 08:58:13 +0000 (17:58 +0900)
committerdongsug-song <35130733+dongsug-song@users.noreply.github.com>
Tue, 10 Aug 2021 09:08:13 +0000 (18:08 +0900)
Text input filter based on regular expressions.

Add a InputFilter struct to pass data of DALi InputFilter PropertyMap.
The InputFilter struct can be used as an argument to SetInputFilter and GetInputFilter methods.

// example
InputFilter inputFilter;
inputFilter.Accepted = new Regex(@"[\d]");
inputFilter.Rejected = new Regex("[0-3]");

field.SetInputFilter(inputFilter);

field.InputFiltered += (s, e) =>
{
    if (e.Type == InputFilterType.Accept)
    {
        // If input is filtered by InputFilter of Accept type.
    }
    else if (e.Type == InputFilterType.Reject)
    {
        // If input is filtered by InputFilter of Reject type.
    }
};

Signed-off-by: Bowon Ryu <bowon.ryu@samsung.com>
src/Tizen.NUI/src/internal/Interop/Interop.TextEditor.cs
src/Tizen.NUI/src/internal/Interop/Interop.TextField.cs
src/Tizen.NUI/src/public/BaseComponents/TextEditor.cs
src/Tizen.NUI/src/public/BaseComponents/TextEditorEvent.cs
src/Tizen.NUI/src/public/BaseComponents/TextEvent.cs
src/Tizen.NUI/src/public/BaseComponents/TextField.cs
src/Tizen.NUI/src/public/BaseComponents/TextFieldEvent.cs
src/Tizen.NUI/src/public/Common/NUIConstants.cs

index 1512563..bbf8954 100755 (executable)
@@ -307,6 +307,12 @@ namespace Tizen.NUI
 
             [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_TextEditor_Property_ELLIPSIS_POSITION_get")]
             public static extern int EllipsisPositionGet();
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_TextEditor_Property_INPUT_FILTER_get")]
+            public static extern int InputFilterGet();
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_TextEditor_InputFilteredSignal")]
+            public static extern global::System.IntPtr InputFilteredSignal(global::System.Runtime.InteropServices.HandleRef textEditorRef);
         }
     }
 }
index 3be4719..a68a46f 100755 (executable)
@@ -279,6 +279,12 @@ namespace Tizen.NUI
 
             [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_TextField_Property_GRAB_HANDLE_COLOR_get")]
             public static extern int GrabHandleColorGet();
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_TextField_Property_INPUT_FILTER_get")]
+            public static extern int InputFilterGet();
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_TextField_InputFilteredSignal")]
+            public static extern global::System.IntPtr InputFilteredSignal(global::System.Runtime.InteropServices.HandleRef textFieldRef);
         }
     }
 }
index 2d6e115..8373f91 100755 (executable)
 
 extern alias TizenSystemSettings;
 using TizenSystemSettings.Tizen.System;
+using System.Text.RegularExpressions;
 
 using System;
 using System.Globalization;
 using System.ComponentModel;
+using Tizen.NUI.Text;
 
 namespace Tizen.NUI.BaseComponents
 {
@@ -1222,6 +1224,61 @@ namespace Tizen.NUI.BaseComponents
         }
 
         /// <summary>
+        /// Set InputFilter to TextEditor. <br />
+        /// </summary>
+        /// <param name="inputFilter">The InputFilter</param>
+        /// <remarks>
+        /// <see cref="Tizen.NUI.Text.InputFilter"/> filters input based on regular expressions. <br />
+        /// Users can set the Accepted or Rejected regular expression set, or both. <br />
+        /// If both are used, Rejected has higher priority. <br />
+        /// InputFiltered signal is emitted when the input is filtered by InputFilter <br />
+        /// See <see cref="InputFiltered"/>, <see cref="InputFilterType"/> and <see cref="InputFilteredEventArgs"/> for a detailed description. <br />
+        /// </remarks>
+        /// <example>
+        /// The following example demonstrates how to use the SetInputFilter method.
+        /// <code>
+        /// Tizen.NUI.Text.InputFilter inputFilter;
+        /// inputFilter.Accepted = new Regex(@"[\d]"); // accept whole digits
+        /// inputFilter.Rejected = new Regex("[0-3]"); // reject 0, 1, 2, 3
+        /// editor.SetInputFilter(inputFilter); // acceptable inputs are 4, 5, 6, 7, 8, 9
+        /// </code>
+        /// </example>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void SetInputFilter(InputFilter inputFilter)
+        {
+            var map = new PropertyMap();
+            var accepted = inputFilter.Accepted != null ? new PropertyValue(inputFilter.Accepted.ToString()) : new PropertyValue("");
+            var rejected = inputFilter.Rejected != null ? new PropertyValue(inputFilter.Rejected.ToString()) : new PropertyValue("");
+            map.Add(0, accepted);
+            map.Add(1, rejected);
+            SetProperty(TextEditor.Property.InputFilter, new PropertyValue(map));
+        }
+
+        /// <summary>
+        /// Get InputFilter from TextEditor. <br />
+        /// </summary>
+        /// <returns>The InputFilter</returns>
+        /// <remarks>
+        /// <see cref="Tizen.NUI.Text.InputFilter"/>
+        /// </remarks>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public InputFilter GetInputFilter()
+        {
+            var map = new PropertyMap();
+            GetProperty(TextEditor.Property.InputFilter).Get(map);
+            string accepted = "";
+            string rejected = "";
+            map.Find(0)?.Get(out accepted);
+            map.Find(1)?.Get(out rejected);
+
+            var inputFilter = new InputFilter();
+            inputFilter.Accepted = new Regex(accepted);
+            inputFilter.Rejected = new Regex(rejected);
+
+            return inputFilter;
+        }
+
+        /// <summary>
         /// The Placeholder property.
         /// The placeholder map contains the following keys :<br />
         /// <list type="table">
@@ -1830,6 +1887,7 @@ namespace Tizen.NUI.BaseComponents
             internal static readonly int InputMethodSettings = Interop.TextEditor.InputMethodSettingsGet();
             internal static readonly int ELLIPSIS = Interop.TextEditor.EllipsisGet();
             internal static readonly int EllipsisPosition = Interop.TextEditor.EllipsisPositionGet();
+            internal static readonly int InputFilter = Interop.TextEditor.InputFilterGet();
         }
 
         internal class InputStyle
index a322026..d073412 100755 (executable)
@@ -39,6 +39,9 @@ namespace Tizen.NUI.BaseComponents
         private EventHandler<AnchorClickedEventArgs> textEditorAnchorClickedEventHandler;
         private AnchorClickedCallbackDelegate textEditorAnchorClickedCallbackDelegate;
 
+        private EventHandler<InputFilteredEventArgs> textEditorInputFilteredEventHandler;
+        private InputFilteredCallbackDelegate textEditorInputFilteredCallbackDelegate;
+
         [UnmanagedFunctionPointer(CallingConvention.StdCall)]
         private delegate void TextChangedCallbackDelegate(IntPtr textEditor);
 
@@ -51,6 +54,9 @@ namespace Tizen.NUI.BaseComponents
         [UnmanagedFunctionPointer(CallingConvention.StdCall)]
         private delegate void AnchorClickedCallbackDelegate(IntPtr textEditor, IntPtr href, uint hrefLength);
 
+        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+        private delegate void InputFilteredCallbackDelegate(IntPtr textEditor, InputFilterType type);
+
         /// <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 />
@@ -154,6 +160,50 @@ namespace Tizen.NUI.BaseComponents
             }
         }
 
+        /// <summary>
+        /// The InputFiltered signal is emitted when the input is filtered by InputFilter. <br />
+        /// </summary>
+        /// <remarks>
+        /// See <see cref="InputFilterType"/> and <see cref="InputFilteredEventArgs"/> for a detailed description. <br />
+        /// </remarks>
+        /// <example>
+        /// The following example demonstrates how to use the InputFiltered event.
+        /// <code>
+        /// editor.InputFiltered += (s, e) =>
+        /// {
+        ///     if (e.Type == InputFilterType.Accept)
+        ///     {
+        ///         // If input is filtered by InputFilter of Accept type.
+        ///     }
+        ///     else if (e.Type == InputFilterType.Reject)
+        ///     {
+        ///         // If input is filtered by InputFilter of Reject type.
+        ///     }
+        /// };
+        /// </code>
+        /// </example>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public event EventHandler<InputFilteredEventArgs> InputFiltered
+        {
+            add
+            {
+                if (textEditorInputFilteredEventHandler == null)
+                {
+                    textEditorInputFilteredCallbackDelegate = (OnInputFiltered);
+                    InputFilteredSignal().Connect(textEditorInputFilteredCallbackDelegate);
+                }
+                textEditorInputFilteredEventHandler += value;
+            }
+            remove
+            {
+                textEditorInputFilteredEventHandler -= value;
+                if (textEditorInputFilteredEventHandler == null && InputFilteredSignal().Empty() == false)
+                {
+                    InputFilteredSignal().Disconnect(textEditorInputFilteredCallbackDelegate);
+                }
+            }
+        }
+
         internal TextEditorSignal TextChangedSignal()
         {
             TextEditorSignal ret = new TextEditorSignal(Interop.TextEditor.TextChangedSignal(SwigCPtr), false);
@@ -182,6 +232,13 @@ namespace Tizen.NUI.BaseComponents
             return ret;
         }
 
+        internal TextEditorSignal InputFilteredSignal()
+        {
+            TextEditorSignal ret = new TextEditorSignal(Interop.TextEditor.InputFilteredSignal(SwigCPtr), false);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            return ret;
+        }
+
         private void OnTextChanged(IntPtr textEditor)
         {
             if (textEditorTextChangedEventHandler != null)
@@ -237,6 +294,16 @@ namespace Tizen.NUI.BaseComponents
             textEditorAnchorClickedEventHandler?.Invoke(this, e);
         }
 
+        private void OnInputFiltered(IntPtr textEditor, InputFilterType type)
+        {
+            InputFilteredEventArgs e = new InputFilteredEventArgs();
+
+            // Populate all members of "e" (InputFilteredEventArgs) with real data
+            e.Type = type;
+            //here we send all data to user event handlers
+            textEditorInputFilteredEventHandler?.Invoke(this, e);
+        }
+
         /// <summary>
         /// Event arguments that passed via the TextChanged signal.
         /// </summary>
index d93fa4b..7aa5e99 100644 (file)
@@ -32,4 +32,19 @@ namespace Tizen.NUI.BaseComponents
         /// <since_tizen> 9 </since_tizen>
         public string Href { get; set; }
     }
+
+    /// <summary>
+    /// InputFilteredEventArgs is a class to record input filter event arguments which will be sent to user.
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class InputFilteredEventArgs : EventArgs
+    {
+        /// <summary>
+        /// The type of InputFilter that filtered the input is stored. <br />
+        /// If the input is filtered by the Accepted Regex of the InputFilter, the Accept type is stored. <br />
+        /// If the input is filtered by the Rejected Regex of the InputFilter, the Reject type is stored. <br />
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public InputFilterType Type { get; set; }
+    }
 }
\ No newline at end of file
index 499e43e..3140224 100755 (executable)
  */
 extern alias TizenSystemSettings;
 using TizenSystemSettings.Tizen.System;
+using System.Text.RegularExpressions;
 
 using System;
 using System.Globalization;
 using System.ComponentModel;
 using Tizen.NUI.Binding;
+using Tizen.NUI.Text;
 
 namespace Tizen.NUI.BaseComponents
 {
@@ -1368,6 +1370,61 @@ namespace Tizen.NUI.BaseComponents
         }
 
         /// <summary>
+        /// Set InputFilter to TextField. <br />
+        /// </summary>
+        /// <param name="inputFilter">The InputFilter</param>
+        /// <remarks>
+        /// <see cref="Tizen.NUI.Text.InputFilter"/> filters input based on regular expressions. <br />
+        /// Users can set the Accepted or Rejected regular expression set, or both. <br />
+        /// If both are used, Rejected has higher priority. <br />
+        /// InputFiltered signal is emitted when the input is filtered by InputFilter <br />
+        /// See <see cref="InputFiltered"/>, <see cref="InputFilterType"/> and <see cref="InputFilteredEventArgs"/> for a detailed description. <br />
+        /// </remarks>
+        /// <example>
+        /// The following example demonstrates how to use the SetInputFilter method.
+        /// <code>
+        /// Tizen.NUI.Text.InputFilter inputFilter;
+        /// inputFilter.Accepted = new Regex(@"[\d]"); // accept whole digits
+        /// inputFilter.Rejected = new Regex("[0-3]"); // reject 0, 1, 2, 3
+        /// field.SetInputFilter(inputFilter); // acceptable inputs are 4, 5, 6, 7, 8, 9
+        /// </code>
+        /// </example>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void SetInputFilter(InputFilter inputFilter)
+        {
+            var map = new PropertyMap();
+            var accepted = inputFilter.Accepted != null ? new PropertyValue(inputFilter.Accepted.ToString()) : new PropertyValue("");
+            var rejected = inputFilter.Rejected != null ? new PropertyValue(inputFilter.Rejected.ToString()) : new PropertyValue("");
+            map.Add(0, accepted);
+            map.Add(1, rejected);
+            SetProperty(TextField.Property.InputFilter, new PropertyValue(map));
+        }
+
+        /// <summary>
+        /// Get InputFilter from TextField. <br />
+        /// </summary>
+        /// <returns>The InputFilter</returns>
+        /// <remarks>
+        /// <see cref="Tizen.NUI.Text.InputFilter"/>
+        /// </remarks>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public InputFilter GetInputFilter()
+        {
+            var map = new PropertyMap();
+            GetProperty(TextField.Property.InputFilter).Get(map);
+            string accepted = "";
+            string rejected = "";
+            map.Find(0)?.Get(out accepted);
+            map.Find(1)?.Get(out rejected);
+
+            var inputFilter = new InputFilter();
+            inputFilter.Accepted = new Regex(accepted);
+            inputFilter.Rejected = new Regex(rejected);
+
+            return inputFilter;
+        }
+
+        /// <summary>
         /// The Placeholder property.
         /// The placeholder map contains the following keys :<br />
         /// <list type="table">
@@ -1853,6 +1910,7 @@ namespace Tizen.NUI.BaseComponents
             internal static readonly int FontSizeScale = Interop.TextField.FontSizeScaleGet();
             internal static readonly int GrabHandleColor = Interop.TextField.GrabHandleColorGet();
             internal static readonly int EllipsisPosition = Interop.TextField.EllipsisPositionGet();
+            internal static readonly int InputFilter = Interop.TextField.InputFilterGet();
         }
 
         internal class InputStyle
index 58dc43d..add2d50 100755 (executable)
@@ -33,6 +33,8 @@ namespace Tizen.NUI.BaseComponents
         private MaxLengthReachedCallbackDelegate textFieldMaxLengthReachedCallbackDelegate;
         private EventHandler<AnchorClickedEventArgs> textFieldAnchorClickedEventHandler;
         private AnchorClickedCallbackDelegate textFieldAnchorClickedCallbackDelegate;
+        private EventHandler<InputFilteredEventArgs> textFieldInputFilteredEventHandler;
+        private InputFilteredCallbackDelegate textFieldInputFilteredCallbackDelegate;
 
         [UnmanagedFunctionPointer(CallingConvention.StdCall)]
         private delegate void TextChangedCallbackDelegate(IntPtr textField);
@@ -43,6 +45,9 @@ namespace Tizen.NUI.BaseComponents
         [UnmanagedFunctionPointer(CallingConvention.StdCall)]
         private delegate void AnchorClickedCallbackDelegate(IntPtr textField, IntPtr href, uint hrefLength);
 
+        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+        private delegate void InputFilteredCallbackDelegate(IntPtr textField, InputFilterType type);
+
         /// <summary>
         /// The TextChanged event.
         /// </summary>
@@ -118,6 +123,50 @@ namespace Tizen.NUI.BaseComponents
             }
         }
 
+        /// <summary>
+        /// The InputFiltered signal is emitted when the input is filtered by InputFilter. <br />
+        /// </summary>
+        /// <remarks>
+        /// See <see cref="InputFilterType"/> and <see cref="InputFilteredEventArgs"/> for a detailed description. <br />
+        /// </remarks>
+        /// <example>
+        /// The following example demonstrates how to use the InputFiltered event.
+        /// <code>
+        /// field.InputFiltered += (s, e) =>
+        /// {
+        ///     if (e.Type == InputFilterType.Accept)
+        ///     {
+        ///         // If input is filtered by InputFilter of Accept type.
+        ///     }
+        ///     else if (e.Type == InputFilterType.Reject)
+        ///     {
+        ///         // If input is filtered by InputFilter of Reject type.
+        ///     }
+        /// };
+        /// </code>
+        /// </example>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public event EventHandler<InputFilteredEventArgs> InputFiltered
+        {
+            add
+            {
+                if (textFieldInputFilteredEventHandler == null)
+                {
+                    textFieldInputFilteredCallbackDelegate = (OnInputFiltered);
+                    InputFilteredSignal().Connect(textFieldInputFilteredCallbackDelegate);
+                }
+                textFieldInputFilteredEventHandler += value;
+            }
+            remove
+            {
+                textFieldInputFilteredEventHandler -= value;
+                if (textFieldInputFilteredEventHandler == null && InputFilteredSignal().Empty() == false)
+                {
+                    InputFilteredSignal().Disconnect(textFieldInputFilteredCallbackDelegate);
+                }
+            }
+        }
+
         internal TextFieldSignal TextChangedSignal()
         {
             TextFieldSignal ret = new TextFieldSignal(Interop.TextField.TextChangedSignal(SwigCPtr), false);
@@ -139,6 +188,13 @@ namespace Tizen.NUI.BaseComponents
             return ret;
         }
 
+        internal TextFieldSignal InputFilteredSignal()
+        {
+            TextFieldSignal ret = new TextFieldSignal(Interop.TextField.InputFilteredSignal(SwigCPtr), false);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            return ret;
+        }
+
         private void OnTextChanged(IntPtr textField)
         {
             if (textFieldTextChangedEventHandler != null)
@@ -177,6 +233,16 @@ namespace Tizen.NUI.BaseComponents
             textFieldAnchorClickedEventHandler?.Invoke(this, e);
         }
 
+        private void OnInputFiltered(IntPtr textField, InputFilterType type)
+        {
+            InputFilteredEventArgs e = new InputFilteredEventArgs();
+
+            // Populate all members of "e" (InputFilteredEventArgs) with real data
+            e.Type = type;
+            //here we send all data to user event handlers
+            textFieldInputFilteredEventHandler?.Invoke(this, e);
+        }
+
         /// <summary>
         /// The TextChanged event arguments.
         /// </summary>
index f0260f7..98989b4 100755 (executable)
@@ -15,6 +15,7 @@
 using System;
 using System.ComponentModel;
 using System.Diagnostics.CodeAnalysis;
+using System.Text.RegularExpressions;
 
 namespace Tizen.NUI
 {
@@ -1984,4 +1985,55 @@ namespace Tizen.NUI
       [EditorBrowsable(EditorBrowsableState.Never)]
       OnDemand = 1
     }
+
+    /// <summary>
+    /// Enumeration for the type of InputFilter. <br />
+    /// </summary>
+    /// <remarks>
+    /// The type of InputFilter that is stored in the <see cref="Tizen.NUI.BaseComponents.InputFilteredEventArgs"/> when the input is filtered. <br />
+    /// </remarks>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public enum InputFilterType
+    {
+        /// <summary>
+        /// The type of InputFilter is Accept.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        Accept,
+
+        /// <summary>
+        /// The type of InputFilter is Reject.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        Reject
+    }
+
+    namespace Text
+    {
+        /// <summary>
+        /// A struct to pass data of InputFilter PropertyMap. <br />
+        /// </summary>
+        /// <remarks>
+        /// InputFilter filters input based on regular expressions. <br />
+        /// Users can set the Accepted or Rejected regular expression set, or both. <br />
+        /// If both are used, Rejected has higher priority. <br />
+        /// The InputFilter struct is used as an argument to SetInputFilter and GetInputFilter methods. <br />
+        /// See <see cref="Tizen.NUI.BaseComponents.TextField.SetInputFilter"/>, <see cref="Tizen.NUI.BaseComponents.TextField.GetInputFilter"/>, <see cref="Tizen.NUI.BaseComponents.TextEditor.SetInputFilter"/> and <see cref="Tizen.NUI.BaseComponents.TextEditor.GetInputFilter"/>. <br />
+        /// </remarks>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public struct InputFilter
+        {
+            /// <summary>
+            /// A regular expression in the set of characters to be accepted by the inputFilter.
+            /// </summary>
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            public Regex Accepted { get; set; }
+
+            /// <summary>
+            /// A regular expression in the set of characters to be rejected by the inputFilter.
+            /// </summary>
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            public Regex Rejected { get; set; }
+        }
+    }
 }