[NUI] Update theme system
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / Button.cs
index e8f3bf7..aee49a8 100755 (executable)
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2020 Samsung Electronics Co., Ltd.
+ * 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.
@@ -16,9 +16,9 @@
  */
 using System;
 using System.ComponentModel;
+using System.Diagnostics;
 using Tizen.NUI.BaseComponents;
 using Tizen.NUI.Binding;
-using Tizen.NUI.Components.Extension;
 using Tizen.NUI.Accessibility;
 
 namespace Tizen.NUI.Components
@@ -32,6 +32,17 @@ namespace Tizen.NUI.Components
     }
 
     /// <summary>
+    /// SelectedChangedEventArgs is a class to record item selected arguments which will sent to user.
+    /// </summary>
+    /// <since_tizen> 8 </since_tizen>
+    public class SelectedChangedEventArgs : EventArgs
+    {
+        /// <summary> Selected state </summary>
+        /// <since_tizen> 8 </since_tizen>
+        public bool IsSelected { get; set; }
+    }
+
+    /// <summary>
     /// Button is one kind of common component, a button clearly describes what action will occur when the user selects it.
     /// Button may contain text or an icon.
     /// </summary>
@@ -43,20 +54,16 @@ namespace Tizen.NUI.Components
         public static readonly BindableProperty IconRelativeOrientationProperty = BindableProperty.Create(nameof(IconRelativeOrientation), typeof(IconOrientation?), typeof(Button), null, propertyChanged: (bindable, oldValue, newValue) =>
         {
             var instance = (Button)bindable;
-            if (newValue != null)
+            var newIconOrientation = (IconOrientation?)newValue;
+            if (instance.iconRelativeOrientation != newIconOrientation)
             {
-                if (instance.buttonStyle != null && instance.buttonStyle.IconRelativeOrientation != (IconOrientation?)newValue)
-                {
-                    instance.buttonStyle.IconRelativeOrientation = (IconOrientation?)newValue;
-                    instance.UpdateUIContent();
-                }
+                instance.iconRelativeOrientation = newIconOrientation;
+                instance.LayoutItems();
             }
         },
-        defaultValueCreator: (bindable) =>
-        {
-            var instance = (Button)bindable;
-            return instance.buttonStyle?.IconRelativeOrientation;
-        });
+        defaultValueCreator: (bindable) => ((Button)bindable).iconRelativeOrientation
+        );
+
         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
         [EditorBrowsable(EditorBrowsableState.Never)]
         public static readonly BindableProperty IsEnabledProperty = BindableProperty.Create(nameof(IsEnabled), typeof(bool), typeof(Button), true, propertyChanged: (bindable, oldValue, newValue) =>
@@ -68,12 +75,7 @@ namespace Tizen.NUI.Components
                 if (instance.isEnabled != newEnabled)
                 {
                     instance.isEnabled = newEnabled;
-
-                    if (instance.buttonStyle != null)
-                    {
-                        instance.buttonStyle.IsEnabled = newEnabled;
-                    }
-
+                    instance.Sensitive = newEnabled;
                     instance.UpdateState();
                 }
             }
@@ -91,14 +93,14 @@ namespace Tizen.NUI.Components
                 {
                     instance.isSelected = newSelected;
 
-                    if (instance.buttonStyle != null)
+                    if (instance.isSelectable)
                     {
-                        instance.buttonStyle.IsSelected = newSelected;
+                        instance.UpdateState();
                     }
 
-                    if (instance.isSelectable)
+                    if (instance.IsHighlighted)
                     {
-                        instance.UpdateState();
+                        instance.EmitAccessibilityStatesChangedEvent(AccessibilityStates.Checked, newSelected);
                     }
                 }
             }
@@ -119,12 +121,6 @@ namespace Tizen.NUI.Components
                 if (instance.isSelectable != newSelectable)
                 {
                     instance.isSelectable = newSelectable;
-
-                    if (instance.buttonStyle != null)
-                    {
-                        instance.buttonStyle.IsSelectable = newSelectable;
-                    }
-
                     instance.UpdateState();
                 }
             }
@@ -136,37 +132,58 @@ namespace Tizen.NUI.Components
         public static readonly BindableProperty IconPaddingProperty = BindableProperty.Create(nameof(IconPadding), typeof(Extents), typeof(Button), null, propertyChanged: (bindable, oldValue, newValue) =>
         {
             var instance = (Button)bindable;
-            if (null != newValue && null != instance.buttonStyle?.IconPadding)
+            if (instance.buttonIcon == null)
             {
-                instance.buttonStyle.IconPadding.CopyFrom((Extents)newValue);
-                instance.UpdateUIContent();
+                return;
             }
+            instance.buttonIcon.Padding = (Extents)newValue;
         },
-        defaultValueCreator: (bindable) =>
-        {
-            var instance = (Button)bindable;
-            return instance.buttonStyle?.IconPadding;
-        });
+        defaultValueCreator: (bindable) => ((Button)bindable).buttonIcon?.Padding);
+
         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
         [EditorBrowsable(EditorBrowsableState.Never)]
         public static readonly BindableProperty TextPaddingProperty = BindableProperty.Create(nameof(TextPadding), typeof(Extents), typeof(Button), null, propertyChanged: (bindable, oldValue, newValue) =>
         {
             var instance = (Button)bindable;
-            if (null != newValue && null != instance.buttonStyle?.TextPadding)
+            if (instance.buttonText == null)
             {
-                instance.buttonStyle.TextPadding.CopyFrom((Extents)newValue);
-                instance.UpdateUIContent();
+                return;
             }
+            instance.buttonText.Padding = (Extents)newValue;
         },
-        defaultValueCreator: (bindable) =>
+        defaultValueCreator: (bindable) => ((Button)bindable).buttonText?.Padding);
+
+        /// <summary> The bindable property of ItemAlignment. </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        internal static readonly BindableProperty ItemAlignmentProperty = BindableProperty.Create(nameof(ItemAlignment), typeof(LinearLayout.Alignment), typeof(Button), LinearLayout.Alignment.Center, propertyChanged: (bindable, oldValue, newValue) =>
         {
             var instance = (Button)bindable;
-            return instance.buttonStyle?.TextPadding;
-        });
+            var newAlignment = (LinearLayout.Alignment)newValue;
 
+            if (instance.itemAlignment != newAlignment)
+            {
+                instance.itemAlignment = newAlignment;
+                instance.LayoutItems();
+            }
+        },
+        defaultValueCreator: (bindable) => ((Button)bindable).itemAlignment);
+
+        /// <summary> The bindable property of ItemSpacing. </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        internal static readonly BindableProperty ItemSpacingProperty = BindableProperty.Create(nameof(ItemSpacing), typeof(Size2D), typeof(Button), null, propertyChanged: (bindable, oldValue, newValue) =>
+        {
+            var instance = (Button)bindable;
+            instance.itemSpacing = (Size2D)newValue;
+            instance.UpdateSizeAndSpacing();
+        },
+        defaultValueCreator: (bindable) => ((Button)bindable).itemSpacing);
+
+        private IconOrientation? iconRelativeOrientation = IconOrientation.Left;
         private bool isSelected = false;
         private bool isSelectable = false;
         private bool isEnabled = true;
+        private Size2D itemSpacing;
+        private LinearLayout.Alignment itemAlignment = LinearLayout.Alignment.Center;
 
         static Button() { }
 
@@ -176,7 +193,6 @@ namespace Tizen.NUI.Components
         /// <since_tizen> 6 </since_tizen>
         public Button() : base()
         {
-            Initialize();
         }
 
         /// <summary>
@@ -186,7 +202,6 @@ namespace Tizen.NUI.Components
         /// <since_tizen> 8 </since_tizen>
         public Button(string style) : base(style)
         {
-            Initialize();
         }
 
         /// <summary>
@@ -196,7 +211,18 @@ namespace Tizen.NUI.Components
         /// <since_tizen> 8 </since_tizen>
         public Button(ButtonStyle buttonStyle) : base(buttonStyle)
         {
-            Initialize();
+        }
+
+        /// <summary>
+        /// Calculates current states for the button<br />
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected override AccessibilityStates AccessibilityCalculateStates()
+        {
+            var states = base.AccessibilityCalculateStates();
+            FlagSetter(ref states, AccessibilityStates.Checked, this.IsSelected);
+            FlagSetter(ref states, AccessibilityStates.Enabled, this.IsEnabled);
+            return states;
         }
 
         /// <summary>
@@ -263,23 +289,7 @@ namespace Tizen.NUI.Components
         /// <since_tizen> 8 </since_tizen>
         public ImageView Icon
         {
-            get
-            {
-                if (null == buttonIcon)
-                {
-                    buttonIcon = CreateIcon();
-                    if (null != Extension)
-                    {
-                        buttonIcon = Extension.OnCreateIcon(this, buttonIcon);
-                    }
-                    if (null != buttonIcon)
-                    {
-                        Add(buttonIcon);
-                        buttonIcon.Relayout += OnIconRelayout;
-                    }
-                }
-                return buttonIcon;
-            }
+            get => buttonIcon;
             internal set
             {
                 buttonIcon = value;
@@ -303,6 +313,7 @@ namespace Tizen.NUI.Components
                     }
                     if (null != overlayImage)
                     {
+                        overlayImage.ExcludeLayouting = true;
                         Add(overlayImage);
                     }
                 }
@@ -320,22 +331,7 @@ namespace Tizen.NUI.Components
         /// <since_tizen> 8 </since_tizen>
         public TextLabel TextLabel
         {
-            get
-            {
-                if (null == buttonText)
-                {
-                    buttonText = CreateText();
-                    if (null != Extension)
-                    {
-                        buttonText = Extension.OnCreateText(this, buttonText);
-                    }
-                    if (null != buttonText)
-                    {
-                        Add(buttonText);
-                    }
-                }
-                return buttonText;
-            }
+            get => buttonText;
             internal set
             {
                 buttonText = value;
@@ -344,25 +340,13 @@ namespace Tizen.NUI.Components
         }
 
         /// <summary>
-        /// Return a copied Style instance of Button
+        /// The last applied style object copy.
         /// </summary>
         /// <remarks>
-        /// It returns copied Style instance and changing it does not effect to the Button.
-        /// Style setting is possible by using constructor or the function of ApplyStyle(ViewStyle viewStyle)
+        /// Modifying contents in style may cause unexpected behaviour.
         /// </remarks>
         /// <since_tizen> 8 </since_tizen>
-        public new ButtonStyle Style
-        {
-            get
-            {
-                var result = (ButtonStyle)ViewStyle.Clone();
-                result.CopyPropertiesFromView(this);
-                result.Text.CopyPropertiesFromView(TextLabel);
-                result.Icon.CopyPropertiesFromView(Icon);
-                result.Overlay.CopyPropertiesFromView(OverlayImage);
-                return result;
-            }
-        }
+        public ButtonStyle Style => (ButtonStyle)(ViewStyle as ButtonStyle)?.Clone();
 
         /// <summary>
         /// The text of Button.
@@ -377,6 +361,11 @@ namespace Tizen.NUI.Components
             set
             {
                 TextLabel.Text = value;
+
+                if (IsHighlighted && String.IsNullOrEmpty(AccessibilityName) && GetAccessibilityNameSignal().Empty())
+                {
+                    EmitAccessibilityEvent(AccessibilityPropertyChangeEvent.Name);
+                }
             }
         }
 
@@ -500,7 +489,7 @@ namespace Tizen.NUI.Components
         /// <since_tizen> 6 </since_tizen>
         public StringSelector TextSelector
         {
-            get => buttonText == null ? null : new StringSelector((Selector<string>)buttonText.GetValue(TextLabel.TextSelectorProperty));
+            get => buttonText == null ? null : new StringSelector(buttonText.TextSelector);
             set
             {
                 if (value == null || buttonText == null)
@@ -509,20 +498,20 @@ namespace Tizen.NUI.Components
                 }
                 else
                 {
-                    buttonText.SetValue(TextLabel.TextSelectorProperty, value);
+                    buttonText.TextSelector = value;
                 }
             }
         }
 
         /// <summary>
-        /// Translateable text string selector in Button.
+        /// Translatable text string selector in Button.
         /// Getter returns copied selector value if exist, null otherwise.
         /// </summary>
         /// <exception cref="NullReferenceException">Thrown when setting null value.</exception>
         /// <since_tizen> 6 </since_tizen>
         public StringSelector TranslatableTextSelector
         {
-            get => buttonText == null ? null : new StringSelector((Selector<string>)buttonText.GetValue(TextLabel.TranslatableTextSelectorProperty));
+            get => buttonText == null ? null : new StringSelector(buttonText.TranslatableTextSelector);
             set
             {
                 if (value == null || buttonText == null)
@@ -531,7 +520,7 @@ namespace Tizen.NUI.Components
                 }
                 else
                 {
-                    buttonText.SetValue(TextLabel.TranslatableTextSelectorProperty, value);
+                    buttonText.TranslatableTextSelector = value;
                 }
             }
         }
@@ -544,7 +533,7 @@ namespace Tizen.NUI.Components
         /// <since_tizen> 6 </since_tizen>
         public ColorSelector TextColorSelector
         {
-            get => buttonText == null ? null : new ColorSelector((Selector<Color>)buttonText.GetValue(TextLabel.TextColorSelectorProperty));
+            get => buttonText == null ? null : new ColorSelector(buttonText.TextColorSelector);
             set
             {
                 if (value == null || buttonText == null)
@@ -553,7 +542,7 @@ namespace Tizen.NUI.Components
                 }
                 else
                 {
-                    buttonText.SetValue(TextLabel.TextColorSelectorProperty, value);
+                    buttonText.TextColorSelector = value;
                 }
             }
         }
@@ -566,7 +555,7 @@ namespace Tizen.NUI.Components
         /// <since_tizen> 6 </since_tizen>
         public FloatSelector PointSizeSelector
         {
-            get => buttonText == null ? null : new FloatSelector((Selector<float?>)buttonText.GetValue(TextLabel.PointSizeSelectorProperty));
+            get => buttonText == null ? null : new FloatSelector(buttonText.PointSizeSelector);
             set
             {
                 if (value == null || buttonText == null)
@@ -575,7 +564,7 @@ namespace Tizen.NUI.Components
                 }
                 else
                 {
-                    buttonText.SetValue(TextLabel.PointSizeSelectorProperty, value);
+                    buttonText.PointSizeSelector = value;
                 }
             }
         }
@@ -588,7 +577,7 @@ namespace Tizen.NUI.Components
         /// <since_tizen> 6 </since_tizen>
         public StringSelector IconURLSelector
         {
-            get => buttonIcon == null ? null : new StringSelector((Selector<string>)buttonIcon.GetValue(ImageView.ResourceUrlSelectorProperty));
+            get => buttonIcon == null ? null : new StringSelector(buttonIcon.ResourceUrlSelector);
             set
             {
                 if (value == null || buttonIcon == null)
@@ -597,7 +586,7 @@ namespace Tizen.NUI.Components
                 }
                 else
                 {
-                    buttonIcon.SetValue(ImageView.ResourceUrlSelectorProperty, value);
+                    buttonIcon.ResourceUrlSelector = value;
                 }
             }
         }
@@ -642,7 +631,7 @@ namespace Tizen.NUI.Components
         {
             get
             {
-                return (IconOrientation?)GetValue(IconRelativeOrientationProperty);
+                return (IconOrientation?)GetValue(IconRelativeOrientationProperty) ?? IconOrientation.Left;
             }
             set
             {
@@ -651,26 +640,46 @@ namespace Tizen.NUI.Components
         }
 
         /// <summary>
-        /// Icon padding in Button, work only when show icon and text.
+        /// Icon padding in Button. It is shortcut of Icon.Padding.
         /// </summary>
         /// <since_tizen> 6 </since_tizen>
         public Extents IconPadding
         {
-            get => (Extents)GetValue(IconPaddingProperty);
+            get => (Extents)GetValue(IconPaddingProperty) ?? new Extents();
             set => SetValue(IconPaddingProperty, value);
         }
 
         /// <summary>
-        /// Text padding in Button, work only when show icon and text.
+        /// Text padding in Button. It is shortcut of TextLabel.Padding.
         /// </summary>
         /// <since_tizen> 6 </since_tizen>
         public Extents TextPadding
         {
-            get => (Extents)GetValue(TextPaddingProperty);
+            get => (Extents)GetValue(TextPaddingProperty) ?? new Extents();
             set => SetValue(TextPaddingProperty, value);
         }
 
-        private ButtonStyle buttonStyle => ViewStyle as ButtonStyle;
+        /// <summary>
+        /// The item (text or icon or both) alignment.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public LinearLayout.Alignment ItemAlignment
+        {
+            get => (LinearLayout.Alignment)GetValue(ItemAlignmentProperty);
+            set => SetValue(ItemAlignmentProperty, value);
+        }
+
+        /// <summary>
+        /// The space between icon and text.
+        /// The value is applied when there exist icon and text both.
+        /// The width value is used when the items are arranged horizontally. Otherwise, the height value is used.
+        /// </summary>
+        /// <since_tizen> 9 </since_tizen>
+        public Size2D ItemSpacing
+        {
+            get => (Size2D)GetValue(ItemSpacingProperty);
+            set => SetValue(ItemSpacingProperty, value);
+        }
 
         /// <summary>
         /// Called after a key event is received by the view that has had its focus set.
@@ -747,7 +756,10 @@ namespace Tizen.NUI.Components
         /// <param name="touch">The touch event.</param>
         /// <returns>True if the event should be consumed.</returns>
         /// <since_tizen> 8 </since_tizen>
+        [Obsolete("Deprecated in API8; Will be removed in API10. Please use OnClicked instead.")]
+#pragma warning disable CS0809 // Obsolete member overrides non-obsolete member, It will be removed in API10
         public override bool OnTouch(Touch touch)
+#pragma warning restore CS0809 // Obsolete member overrides non-obsolete member, It will be removed in API10
         {
             return base.OnTouch(touch);
         }
@@ -759,30 +771,46 @@ namespace Tizen.NUI.Components
         /// <since_tizen> 8 </since_tizen>
         public override void ApplyStyle(ViewStyle viewStyle)
         {
+            Debug.Assert(buttonIcon != null && buttonText != null);
+
             styleApplied = false;
 
             base.ApplyStyle(viewStyle);
 
-            if (null != buttonStyle)
+            if (viewStyle is ButtonStyle buttonStyle)
             {
                 Extension = buttonStyle.CreateExtension();
+
                 if (buttonStyle.Overlay != null)
                 {
                     OverlayImage?.ApplyStyle(buttonStyle.Overlay);
                 }
 
+                if (Extension != null)
+                {
+                    buttonIcon.Unparent();
+                    buttonIcon = Extension.OnCreateIcon(this, buttonIcon);
+
+                    buttonText.Unparent();
+                    buttonText = Extension.OnCreateText(this, buttonText);
+
+                    LayoutItems();
+                }
+
                 if (buttonStyle.Text != null)
                 {
-                    TextLabel?.ApplyStyle(buttonStyle.Text);
+                    buttonText.ThemeChangeSensitive = false;
+                    buttonText.ApplyStyle(buttonStyle.Text);
                 }
 
                 if (buttonStyle.Icon != null)
                 {
-                    Icon?.ApplyStyle(buttonStyle.Icon);
+                    buttonIcon.ApplyStyle(buttonStyle.Icon);
                 }
             }
 
             styleApplied = true;
+            UpdateState();
         }
 
         /// <summary>
@@ -790,6 +818,7 @@ namespace Tizen.NUI.Components
         /// </summary>
         /// <since_tizen> 6 </since_tizen>
         [Obsolete("Deprecated in API8; Will be removed in API10. Please use ClickedEventArgs instead.")]
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
         public class ClickEventArgs : EventArgs
         {
         }
@@ -799,14 +828,19 @@ namespace Tizen.NUI.Components
         /// </summary>
         /// <since_tizen> 6 </since_tizen>
         [Obsolete("Deprecated in API8; Will be removed in API10. Please use View.ControlStateChangedEventArgs")]
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
         public class StateChangedEventArgs : EventArgs
         {
             /// <summary> previous state of Button </summary>
             /// <since_tizen> 6 </since_tizen>
+            /// It will be removed in API10
+            [global::System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:Do not declare visible instance fields")]
             [Obsolete("Deprecated in API8; Will be removed in API10")]
             public ControlStates PreviousState;
             /// <summary> current state of Button </summary>
             /// <since_tizen> 6 </since_tizen>
+            /// It will be removed in API10
+            [global::System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:Do not declare visible instance fields")]
             [Obsolete("Deprecated in API8; Will be removed in API10")]
             public ControlStates CurrentState;
         }