[NUI] Fix TCT fail issues by adding null check (#1558)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / Button.cs
1 /*
2  * Copyright(c) 2020 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 using System;
18 using System.ComponentModel;
19 using Tizen.NUI.BaseComponents;
20 using Tizen.NUI.Binding;
21 using Tizen.NUI.Components.Extension;
22
23 namespace Tizen.NUI.Components
24 {
25     /// <summary>
26     /// Button is one kind of common component, a button clearly describes what action will occur when the user selects it.
27     /// Button may contain text or an icon.
28     /// </summary>
29     /// <since_tizen> 6 </since_tizen>
30     public class Button : Control
31     {
32         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
33         [EditorBrowsable(EditorBrowsableState.Never)]
34         public static readonly BindableProperty IconRelativeOrientationProperty = BindableProperty.Create(nameof(IconRelativeOrientation), typeof(IconOrientation?), typeof(Button), null, propertyChanged: (bindable, oldValue, newValue) =>
35         {
36             var instance = (Button)bindable;
37             if (newValue != null)
38             {
39                 instance.privateIconRelativeOrientation = (IconOrientation?)newValue;
40             }
41         },
42         defaultValueCreator: (bindable) =>
43         {
44             var instance = (Button)bindable;
45             return instance.privateIconRelativeOrientation;
46         });
47         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
48         [EditorBrowsable(EditorBrowsableState.Never)]
49         public static readonly BindableProperty IsEnabledProperty = BindableProperty.Create(nameof(IsEnabled), typeof(bool), typeof(Button), true, propertyChanged: (bindable, oldValue, newValue) =>
50         {
51             var instance = (Button)bindable;
52             if (newValue != null)
53             {
54                 instance.privateIsEnabled = (bool)newValue;
55             }
56         },
57         defaultValueCreator: (bindable) =>
58         {
59             var instance = (Button)bindable;
60             return instance.privateIsEnabled;
61         });
62         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
63         [EditorBrowsable(EditorBrowsableState.Never)]
64         public static readonly BindableProperty IsSelectedProperty = BindableProperty.Create(nameof(IsSelected), typeof(bool), typeof(Button), true, propertyChanged: (bindable, oldValue, newValue) =>
65         {
66             var instance = (Button)bindable;
67             if (newValue != null)
68             {
69                 instance.privateIsSelected = (bool)newValue;
70             }
71         },
72         defaultValueCreator: (bindable) =>
73         {
74             var instance = (Button)bindable;
75             return instance.privateIsSelected;
76         });
77         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
78         [EditorBrowsable(EditorBrowsableState.Never)]
79         public static readonly BindableProperty IsSelectableProperty = BindableProperty.Create(nameof(IsSelectable), typeof(bool), typeof(Button), true, propertyChanged: (bindable, oldValue, newValue) =>
80         {
81             var instance = (Button)bindable;
82             if (newValue != null)
83             {
84                 instance.privateIsSelectable = (bool)newValue;
85             }
86         },
87         defaultValueCreator: (bindable) =>
88         {
89             var instance = (Button)bindable;
90             return instance.privateIsSelectable;
91         });
92         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
93         [EditorBrowsable(EditorBrowsableState.Never)]
94         public static readonly BindableProperty IconPaddingProperty = BindableProperty.Create(nameof(IconPadding), typeof(Extents), typeof(Button), null, propertyChanged: (bindable, oldValue, newValue) =>
95         {
96             var instance = (Button)bindable;
97             if (null != newValue && null != instance.Style?.IconPadding)
98             {
99                 instance.Style.IconPadding.CopyFrom((Extents)newValue);
100                 instance.UpdateUIContent();
101             }
102         },
103         defaultValueCreator: (bindable) =>
104         {
105             var instance = (Button)bindable;
106             return instance.Style?.IconPadding;
107         });
108         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
109         [EditorBrowsable(EditorBrowsableState.Never)]
110         public static readonly BindableProperty TextPaddingProperty = BindableProperty.Create(nameof(TextPadding), typeof(Extents), typeof(Button), null, propertyChanged: (bindable, oldValue, newValue) =>
111         {
112             var instance = (Button)bindable;
113             if (null != newValue && null != instance.Style?.TextPadding)
114             {
115                 instance.Style.TextPadding.CopyFrom((Extents)newValue);
116                 instance.UpdateUIContent();
117             }
118         },
119         defaultValueCreator: (bindable) =>
120         {
121             var instance = (Button)bindable;
122             return instance.Style?.TextPadding;
123         });
124
125         private EventHandler<StateChangedEventArgs> stateChangeHander;
126
127         private bool isSelected = false;
128         private bool isEnabled = true;
129         private bool isPressed = false;
130
131         /// <summary>
132         /// Button's overlay image part.
133         /// </summary>
134         [EditorBrowsable(EditorBrowsableState.Never)]
135         protected ImageView ButtonOverlayImage { get; set; }
136
137         /// <summary>
138         /// Button's text part.
139         /// </summary>
140         [EditorBrowsable(EditorBrowsableState.Never)]
141         protected TextLabel ButtonText { get; set; }
142
143         /// <summary>
144         /// Button's icon part.
145         /// </summary>
146         [EditorBrowsable(EditorBrowsableState.Never)]
147         protected ImageView ButtonIcon { get; set; }
148
149         /// <summary>
150         /// The last touch information triggering selected state change.
151         /// </summary>
152         [EditorBrowsable(EditorBrowsableState.Never)]
153         protected Touch SelectionChangedByTouch { get; set; }
154
155         /// <summary>
156         /// The ButtonExtension instance that is injected by ButtonStyle.
157         /// </summary>
158         [EditorBrowsable(EditorBrowsableState.Never)]
159         protected ButtonExtension Extension { get; set; }
160
161         /// <summary>
162         /// Creates Button's text part.
163         /// </summary>
164         /// <return>The created Button's text part.</return>
165         [EditorBrowsable(EditorBrowsableState.Never)]
166         protected virtual TextLabel CreateText()
167         {
168             return new TextLabel();
169         }
170
171         /// <summary>
172         /// Creates Button's icon part.
173         /// </summary>
174         /// <return>The created Button's icon part.</return>
175         [EditorBrowsable(EditorBrowsableState.Never)]
176         protected virtual ImageView CreateIcon()
177         {
178             return new ImageView();
179         }
180
181         /// <summary>
182         /// Creates Button's overlay image part.
183         /// </summary>
184         /// <return>The created Button's overlay image part.</return>
185         [EditorBrowsable(EditorBrowsableState.Never)]
186         protected virtual ImageView CreateOverlayImage()
187         {
188             return new ImageView();
189         }
190
191         /// <summary>
192         /// Called when the Button is Clicked by a user
193         /// </summary>
194         /// <param name="eventArgs">The click information.</param>
195         [EditorBrowsable(EditorBrowsableState.Never)]
196         protected virtual void OnClick(ClickEventArgs eventArgs)
197         {
198         }
199
200         static Button() { }
201
202         /// <summary>
203         /// Creates a new instance of a Button.
204         /// </summary>
205         /// <since_tizen> 6 </since_tizen>
206         public Button() : base()
207         {
208             Initialize();
209         }
210
211         /// <summary>
212         /// Creates a new instance of a Button with style.
213         /// </summary>
214         /// <param name="style">Create Button by special style defined in UX.</param>
215         /// <since_tizen> 8 </since_tizen>
216         public Button(string style) : base(style)
217         {
218             Initialize();
219         }
220
221         /// <summary>
222         /// Creates a new instance of a Button with style.
223         /// </summary>
224         /// <param name="buttonStyle">Create Button by style customized by user.</param>
225         /// <since_tizen> 8 </since_tizen>
226         public Button(ButtonStyle buttonStyle) : base(buttonStyle)
227         {
228             Initialize();
229         }
230
231         /// <summary>
232         /// An event for the button clicked signal which can be used to subscribe or unsubscribe the event handler provided by the user.<br />
233         /// </summary>
234         /// <since_tizen> 6 </since_tizen>
235         public event EventHandler<ClickEventArgs> ClickEvent;
236         /// <summary>
237         /// An event for the button state changed signal which can be used to subscribe or unsubscribe the event handler provided by the user.<br />
238         /// </summary>
239         /// <since_tizen> 6 </since_tizen>
240         public event EventHandler<StateChangedEventArgs> StateChangedEvent
241         {
242             add
243             {
244                 stateChangeHander += value;
245             }
246             remove
247             {
248                 stateChangeHander -= value;
249             }
250         }
251         /// <summary>
252         /// Icon orientation.
253         /// </summary>
254         /// <since_tizen> 6 </since_tizen>
255         public enum IconOrientation
256         {
257             /// <summary>
258             /// Top.
259             /// </summary>
260             /// <since_tizen> 6 </since_tizen>
261             Top,
262             /// <summary>
263             /// Bottom.
264             /// </summary>
265             /// <since_tizen> 6 </since_tizen>
266             Bottom,
267             /// <summary>
268             /// Left.
269             /// </summary>
270             /// <since_tizen> 6 </since_tizen>
271             Left,
272             /// <summary>
273             /// Right.
274             /// </summary>
275             /// <since_tizen> 6 </since_tizen>
276             Right,
277         }
278
279         /// <summary>
280         /// Style of the button.
281         /// </summary>
282         /// <since_tizen> 8 </since_tizen>
283         public new ButtonStyle Style => ViewStyle as ButtonStyle;
284
285         /// <summary>
286         /// The text of Button.
287         /// </summary>
288         /// <since_tizen> 6 </since_tizen>
289         public string Text
290         {
291             get
292             {
293                 return Style?.Text?.Text?.GetValue(ControlState);
294             }
295             set
296             {
297                 if (null != Style?.Text)
298                 {
299                     Style.Text.Text = value;
300                 }
301             }
302         }
303
304         /// <summary>
305         /// Flag to decide Button can be selected or not.
306         /// </summary>
307         /// <since_tizen> 6 </since_tizen>
308         public bool IsSelectable
309         {
310             get
311             {
312                 return (bool)GetValue(IsSelectableProperty);
313             }
314             set
315             {
316                 SetValue(IsSelectableProperty, value);
317             }
318         }
319
320         private bool privateIsSelectable
321         {
322             get
323             {
324                 return Style?.IsSelectable ?? false;
325             }
326             set
327             {
328                 Style.IsSelectable = value;
329             }
330         }
331
332         /// <summary>
333         /// Translate text string in Button.
334         /// </summary>
335         /// <since_tizen> 6 </since_tizen>
336         public string TranslatableText
337         {
338             get
339             {
340                 return Style?.Text?.TranslatableText?.All;
341             }
342             set
343             {
344                 if (null != Style?.Text)
345                 {
346                     Style.Text.TranslatableText = value;
347                 }
348             }
349         }
350
351         /// <summary>
352         /// Text point size in Button.
353         /// </summary>
354         /// <since_tizen> 6 </since_tizen>
355         public float PointSize
356         {
357             get
358             {
359                 return Style?.Text?.PointSize?.All ?? 0;
360             }
361             set
362             {
363                 if (null != Style?.Text)
364                 {
365                     Style.Text.PointSize = value;
366                 }
367             }
368         }
369
370         /// <summary>
371         /// Text font family in Button.
372         /// </summary>
373         /// <since_tizen> 6 </since_tizen>
374         public string FontFamily
375         {
376             get
377             {
378                 return Style?.Text?.FontFamily.All;
379             }
380             set
381             {
382                 if (null != Style?.Text)
383                 {
384                     Style.Text.FontFamily = value;
385                 }
386             }
387         }
388         /// <summary>
389         /// Text color in Button.
390         /// </summary>
391         /// <since_tizen> 6 </since_tizen>
392         public Color TextColor
393         {
394             get
395             {
396                 return Style?.Text?.TextColor?.All;
397             }
398             set
399             {
400                 if (null != Style?.Text)
401                 {
402                     Style.Text.TextColor = value;
403                 }
404             }
405         }
406         /// <summary>
407         /// Text horizontal alignment in Button.
408         /// </summary>
409         /// <since_tizen> 6 </since_tizen>
410         public HorizontalAlignment TextAlignment
411         {
412             get
413             {
414                 return Style?.Text?.HorizontalAlignment ?? HorizontalAlignment.Center;
415             }
416             set
417             {
418                 if (null != Style?.Text)
419                 {
420                     Style.Text.HorizontalAlignment = value;
421                 }
422             }
423         }
424         /// <summary>
425         /// Icon image's resource url in Button.
426         /// </summary>
427         /// <since_tizen> 6 </since_tizen>
428         public string IconURL
429         {
430             get
431             {
432                 return Style?.Icon?.ResourceUrl?.All;
433             }
434             set
435             {
436                 if (null != Style?.Icon)
437                 {
438                     Style.Icon.ResourceUrl = value;
439                 }
440             }
441         }
442
443         private StringSelector textSelector = new StringSelector();
444         /// <summary>
445         /// Text string selector in Button.
446         /// </summary>
447         /// <since_tizen> 6 </since_tizen>
448         public StringSelector TextSelector
449         {
450             get
451             {
452                 return textSelector;
453             }
454             set
455             {
456                 if (value == null || textSelector == null)
457                 {
458                     Tizen.Log.Fatal("NUI", "[Exception] Button.TextSelector is null");
459                     throw new NullReferenceException("Button.TextSelector is null");
460                 }
461                 else
462                 {
463                     textSelector.Clone(value);
464                 }
465             }
466         }
467
468         private StringSelector translatableTextSelector = new StringSelector();
469         /// <summary>
470         /// Translateable text string selector in Button.
471         /// </summary>
472         /// <since_tizen> 6 </since_tizen>
473         public StringSelector TranslatableTextSelector
474         {
475             get
476             {
477                 return translatableTextSelector;
478             }
479             set
480             {
481                 if (value == null || translatableTextSelector == null)
482                 {
483                     Tizen.Log.Fatal("NUI", "[Exception] Button.TranslatableTextSelector is null");
484                     throw new NullReferenceException("Button.TranslatableTextSelector is null");
485                 }
486                 else
487                 {
488                     translatableTextSelector.Clone(value);
489                 }
490             }
491         }
492
493         private ColorSelector textColorSelector = new ColorSelector();
494         /// <summary>
495         /// Text color selector in Button.
496         /// </summary>
497         /// <since_tizen> 6 </since_tizen>
498         public ColorSelector TextColorSelector
499         {
500             get
501             {
502                 return textColorSelector;
503             }
504             set
505             {
506                 if (value == null || textColorSelector == null)
507                 {
508                     Tizen.Log.Fatal("NUI", "[Exception] Button.textColorSelector is null");
509                     throw new NullReferenceException("Button.textColorSelector is null");
510                 }
511                 else
512                 {
513                     textColorSelector.Clone(value);
514                 }
515             }
516         }
517
518         private FloatSelector pointSizeSelector = new FloatSelector();
519         /// <summary>
520         /// Text font size selector in Button.
521         /// </summary>
522         /// <since_tizen> 6 </since_tizen>
523         public FloatSelector PointSizeSelector
524         {
525             get
526             {
527                 return pointSizeSelector;
528             }
529             set
530             {
531                 if (value == null || pointSizeSelector == null)
532                 {
533                     Tizen.Log.Fatal("NUI", "[Exception] Button.pointSizeSelector is null");
534                     throw new NullReferenceException("Button.pointSizeSelector is null");
535                 }
536                 else
537                 {
538                     pointSizeSelector.Clone(value);
539                 }
540             }
541         }
542
543         private StringSelector iconURLSelector = new StringSelector();
544         /// <summary>
545         /// Icon image's resource url selector in Button.
546         /// </summary>
547         /// <since_tizen> 6 </since_tizen>
548         public StringSelector IconURLSelector
549         {
550             get
551             {
552                 return iconURLSelector;
553             }
554             set
555             {
556                 if (value == null || iconURLSelector == null)
557                 {
558                     Tizen.Log.Fatal("NUI", "[Exception] Button.iconURLSelector is null");
559                     throw new NullReferenceException("Button.iconURLSelector is null");
560                 }
561                 else
562                 {
563                     iconURLSelector.Clone(value);
564                 }
565             }
566         }
567
568         /// <summary>
569         /// Flag to decide selected state in Button.
570         /// </summary>
571         /// <since_tizen> 6 </since_tizen>
572         public bool IsSelected
573         {
574             get
575             {
576                 return (bool)GetValue(IsSelectedProperty);
577             }
578             set
579             {
580                 SetValue(IsSelectedProperty, value);
581             }
582         }
583         private bool privateIsSelected
584         {
585             get
586             {
587                 return isSelected;
588             }
589             set
590             {
591                 isSelected = value;
592
593                 UpdateState(SelectionChangedByTouch);
594
595                 SelectionChangedByTouch = null;
596             }
597         }
598
599         /// <summary>
600         /// Flag to decide enable or disable in Button.
601         /// </summary>
602         /// <since_tizen> 6 </since_tizen>
603         public bool IsEnabled
604         {
605             get
606             {
607                 return (bool)GetValue(IsEnabledProperty);
608             }
609             set
610             {
611                 SetValue(IsEnabledProperty, value);
612             }
613         }
614         private bool privateIsEnabled
615         {
616             get
617             {
618                 return isEnabled;
619             }
620             set
621             {
622                 isEnabled = value;
623                 UpdateState();
624             }
625         }
626
627         /// <summary>
628         /// Icon relative orientation in Button, work only when show icon and text.
629         /// </summary>
630         /// <since_tizen> 6 </since_tizen>
631         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
632         [EditorBrowsable(EditorBrowsableState.Never)]
633         public IconOrientation? IconRelativeOrientation
634         {
635             get
636             {
637                 return (IconOrientation?)GetValue(IconRelativeOrientationProperty);
638             }
639             set
640             {
641                 SetValue(IconRelativeOrientationProperty, value);
642             }
643         }
644         private IconOrientation? privateIconRelativeOrientation
645         {
646             get
647             {
648                 return Style?.IconRelativeOrientation;
649             }
650             set
651             {
652                 if (Style != null && Style.IconRelativeOrientation != value)
653                 {
654                     Style.IconRelativeOrientation = value;
655                     UpdateUIContent();
656                 }
657             }
658         }
659
660         /// <summary>
661         /// Icon padding in Button, work only when show icon and text.
662         /// </summary>
663         /// <since_tizen> 6 </since_tizen>
664         public Extents IconPadding
665         {
666             get => (Extents)GetValue(IconPaddingProperty);
667             set => SetValue(IconPaddingProperty, value);
668         }
669
670         /// <summary>
671         /// Text padding in Button, work only when show icon and text.
672         /// </summary>
673         /// <since_tizen> 6 </since_tizen>
674         public Extents TextPadding
675         {
676             get => (Extents)GetValue(TextPaddingProperty);
677             set => SetValue(TextPaddingProperty, value);
678         }
679
680         /// <summary>
681         /// Dispose Button and all children on it.
682         /// </summary>
683         /// <param name="type">Dispose type.</param>
684         /// <since_tizen> 6 </since_tizen>
685         protected override void Dispose(DisposeTypes type)
686         {
687             if (disposed)
688             {
689                 return;
690             }
691
692             if (type == DisposeTypes.Explicit)
693             {
694                 Extension?.OnDispose(this);
695
696                 if (ButtonIcon != null)
697                 {
698                     Utility.Dispose(ButtonIcon);
699                 }
700                 if (ButtonText != null)
701                 {
702                     Utility.Dispose(ButtonText);
703                 }
704                 if (ButtonOverlayImage != null)
705                 {
706                     Utility.Dispose(ButtonOverlayImage);
707                 }
708             }
709
710             base.Dispose(type);
711         }
712         /// <summary>
713         /// Called after a key event is received by the view that has had its focus set.
714         /// </summary>
715         /// <param name="key">The key event.</param>
716         /// <returns>True if the key event should be consumed.</returns>
717         /// <since_tizen> 6 </since_tizen>
718         public override bool OnKey(Key key)
719         {
720             if (null == key) return false;
721             if (key.State == Key.StateType.Down)
722             {
723                 if (key.KeyPressedName == "Return")
724                 {
725                     isPressed = true;
726                     UpdateState();
727                 }
728             }
729             else if (key.State == Key.StateType.Up)
730             {
731                 if (key.KeyPressedName == "Return")
732                 {
733                     bool clicked = isPressed && isEnabled;
734
735                     isPressed = false;
736
737                     if (Style.IsSelectable != null && Style.IsSelectable == true)
738                     {
739                         IsSelected = !IsSelected;
740                     }
741                     else
742                     {
743                         UpdateState();
744                     }
745
746                     if (clicked)
747                     {
748                         ClickEventArgs eventArgs = new ClickEventArgs();
749                         OnClickInternal(eventArgs);
750                     }
751                 }
752             }
753             return base.OnKey(key);
754         }
755
756         /// <summary>
757         /// Called when the control gain key input focus. Should be overridden by derived classes if they need to customize what happens when the focus is gained.
758         /// </summary>
759         /// <since_tizen> 6 </since_tizen>
760         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
761         [EditorBrowsable(EditorBrowsableState.Never)]
762         public override void OnFocusGained()
763         {
764             base.OnFocusGained();
765             UpdateState();
766         }
767
768         /// <summary>
769         /// Called when the control loses key input focus. Should be overridden by derived classes if they need to customize what happens when the focus is lost.
770         /// </summary>
771         /// <since_tizen> 6 </since_tizen>
772         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
773         [EditorBrowsable(EditorBrowsableState.Never)]
774         public override void OnFocusLost()
775         {
776             base.OnFocusLost();
777             UpdateState();
778         }
779
780         /// <summary>
781         /// Called after a touch event is received by the owning view.<br />
782         /// CustomViewBehaviour.REQUIRES_TOUCH_EVENTS must be enabled during construction. See CustomView(ViewWrapperImpl.CustomViewBehaviour behaviour).<br />
783         /// </summary>
784         /// <param name="touch">The touch event.</param>
785         /// <returns>True if the event should be consumed.</returns>
786         /// <since_tizen> 6 </since_tizen>
787         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
788         [EditorBrowsable(EditorBrowsableState.Never)]
789         public override bool OnTouch(Touch touch)
790         {
791             if (null == touch) return false;
792             PointStateType state = touch.GetState(0);
793
794             switch (state)
795             {
796                 case PointStateType.Down:
797                     isPressed = true;
798                     UpdateState(touch);
799                     return true;
800                 case PointStateType.Interrupted:
801                     isPressed = false;
802                     UpdateState();
803                     return true;
804                 case PointStateType.Up:
805                     {
806                         bool clicked = isPressed && isEnabled;
807
808                         isPressed = false;
809
810                         if (Style.IsSelectable != null && Style.IsSelectable == true)
811                         {
812                             SelectionChangedByTouch = touch;
813                             IsSelected = !IsSelected;
814                         }
815                         else
816                         {
817                             UpdateState(touch);
818                         }
819
820                         if (clicked)
821                         {
822                             ClickEventArgs eventArgs = new ClickEventArgs();
823                             OnClickInternal(eventArgs);
824                         }
825
826                         return true;
827                     }
828                 default:
829                     break;
830             }
831             return base.OnTouch(touch);
832         }
833
834         /// <summary>
835         /// Apply style to button.
836         /// </summary>
837         /// <param name="viewStyle">The style to apply.</param>
838         /// <since_tizen> 8 </since_tizen>
839         public override void ApplyStyle(ViewStyle viewStyle)
840         {
841             base.ApplyStyle(viewStyle);
842
843             ButtonStyle buttonStyle = viewStyle as ButtonStyle;
844
845             if (null != buttonStyle)
846             {
847                 ButtonOverlayImage?.ApplyStyle(buttonStyle.Overlay);
848                 ButtonText?.ApplyStyle(buttonStyle.Text);
849                 ButtonIcon?.ApplyStyle(buttonStyle.Icon);
850             }
851         }
852
853         /// <summary>
854         /// Get Button style.
855         /// </summary>
856         /// <returns>The default button style.</returns>
857         /// <since_tizen> 8 </since_tizen>
858         protected override ViewStyle GetViewStyle()
859         {
860             return new ButtonStyle();
861         }
862
863         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
864         [EditorBrowsable(EditorBrowsableState.Never)]
865         protected override void OnUpdate()
866         {
867             base.OnUpdate();
868             UpdateUIContent();
869
870             Extension?.OnRelayout(this);
871         }
872
873         /// <summary>
874         /// Update Button State.
875         /// </summary>
876         /// <param name="touchInfo">The touch information in case the state has changed by touching.</param>
877         /// <since_tizen> 6 </since_tizen>
878         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
879         [EditorBrowsable(EditorBrowsableState.Never)]
880         protected void UpdateState(Touch touchInfo = null)
881         {
882             ControlStates sourceState = ControlState;
883             ControlStates targetState;
884
885             if (isEnabled)
886             {
887                 // Normal
888                 targetState = ControlStates.Normal;
889
890                 // Selected
891                 targetState |= (IsSelected ? ControlStates.Selected : 0);
892
893                 // Pressed, PressedSelected, Focused, SelectedFocused
894                 targetState |= (isPressed ? ControlStates.Pressed : (IsFocused ? ControlStates.Focused : 0));
895             }
896             else
897             {
898                 // Disabled
899                 targetState = ControlStates.Disabled;
900
901                 // DisabledSelected, DisabledFocused
902                 targetState |= (IsSelected ? ControlStates.Selected : (IsFocused ? ControlStates.Focused : 0));
903             }
904
905             if (SetControlState(targetState, touchInfo))
906             {
907                 OnUpdate();
908
909                 StateChangedEventArgs e = new StateChangedEventArgs
910                 {
911                     PreviousState = sourceState,
912                     CurrentState = targetState
913                 };
914                 stateChangeHander?.Invoke(this, e);
915
916                 Extension?.OnControlStateChanged(this, sourceState, touchInfo);
917             }
918         }
919
920         /// <summary>
921         /// It is hijack by using protected, style copy problem when class inherited from Button.
922         /// </summary>
923         /// <since_tizen> 6 </since_tizen>
924         private void Initialize()
925         {
926             var style = (ButtonStyle)Style;
927
928             Extension = style.CreateExtension();
929
930             CreateComponents();
931
932             if (ButtonOverlayImage != null)
933             {
934                 Add(ButtonOverlayImage);
935                 ButtonOverlayImage.ApplyStyle(style.Overlay);
936             }
937
938             if (ButtonIcon != null)
939             {
940                 Add(ButtonIcon);
941                 ButtonIcon.ApplyStyle(style.Icon);
942                 ButtonIcon.Relayout += OnIconRelayout;
943             }
944
945             if (null != ButtonText)
946             {
947                 Add(ButtonText);
948                 ButtonText.ApplyStyle(style.Text);
949             }
950
951             UpdateState();
952
953             LayoutDirectionChanged += OnLayoutDirectionChanged;
954         }
955
956         private void CreateComponents()
957         {
958             ButtonOverlayImage = CreateOverlayImage();
959             ButtonIcon = CreateIcon();
960             ButtonText = CreateText();
961
962             if (Extension == null)
963             {
964                 return;
965             }
966
967             // Update component with extension
968             ButtonOverlayImage = Extension.OnCreateOverlayImage(this, ButtonOverlayImage);
969             ButtonIcon = Extension.OnCreateIcon(this, ButtonIcon);
970             ButtonText = Extension.OnCreateText(this, ButtonText);
971         }
972
973         /// <summary>
974         /// Measure text, it can be override.
975         /// </summary>
976         /// <since_tizen> 6 </since_tizen>
977         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
978         [EditorBrowsable(EditorBrowsableState.Never)]
979         protected virtual void MeasureText()
980         {
981             if (Style.IconRelativeOrientation == null || ButtonIcon == null || ButtonText == null)
982             {
983                 return;
984             }
985             ButtonText.WidthResizePolicy = ResizePolicyType.Fixed;
986             ButtonText.HeightResizePolicy = ResizePolicyType.Fixed;
987             int textPaddingStart = Style.TextPadding.Start;
988             int textPaddingEnd = Style.TextPadding.End;
989             int textPaddingTop = Style.TextPadding.Top;
990             int textPaddingBottom = Style.TextPadding.Bottom;
991
992             int iconPaddingStart = Style.IconPadding.Start;
993             int iconPaddingEnd = Style.IconPadding.End;
994             int iconPaddingTop = Style.IconPadding.Top;
995             int iconPaddingBottom = Style.IconPadding.Bottom;
996
997             if (IconRelativeOrientation == IconOrientation.Top || IconRelativeOrientation == IconOrientation.Bottom)
998             {
999                 ButtonText.SizeWidth = SizeWidth - textPaddingStart - textPaddingEnd;
1000                 ButtonText.SizeHeight = SizeHeight - textPaddingTop - textPaddingBottom - iconPaddingTop - iconPaddingBottom - ButtonIcon.SizeHeight;
1001             }
1002             else
1003             {
1004                 ButtonText.SizeWidth = SizeWidth - textPaddingStart - textPaddingEnd - iconPaddingStart - iconPaddingEnd - ButtonIcon.SizeWidth;
1005                 ButtonText.SizeHeight = SizeHeight - textPaddingTop - textPaddingBottom;
1006             }
1007         }
1008         /// <summary>
1009         /// Layout child, it can be override.
1010         /// </summary>
1011         /// <since_tizen> 6 </since_tizen>
1012         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
1013         [EditorBrowsable(EditorBrowsableState.Never)]
1014         protected virtual void LayoutChild()
1015         {
1016             if (Style.IconRelativeOrientation == null || ButtonIcon == null || ButtonText == null)
1017             {
1018                 return;
1019             }
1020
1021             var buttonIcon = ButtonIcon;
1022             var buttonText = ButtonText;
1023
1024             int textPaddingStart = Style.TextPadding.Start;
1025             int textPaddingEnd = Style.TextPadding.End;
1026             int textPaddingTop = Style.TextPadding.Top;
1027             int textPaddingBottom = Style.TextPadding.Bottom;
1028
1029             int iconPaddingStart = Style.IconPadding.Start;
1030             int iconPaddingEnd = Style.IconPadding.End;
1031             int iconPaddingTop = Style.IconPadding.Top;
1032             int iconPaddingBottom = Style.IconPadding.Bottom;
1033
1034             switch (IconRelativeOrientation)
1035             {
1036                 case IconOrientation.Top:
1037                     buttonIcon.PositionUsesPivotPoint = true;
1038                     buttonIcon.ParentOrigin = NUI.ParentOrigin.TopCenter;
1039                     buttonIcon.PivotPoint = NUI.PivotPoint.TopCenter;
1040                     buttonIcon.Position2D = new Position2D(0, iconPaddingTop);
1041
1042                     buttonText.PositionUsesPivotPoint = true;
1043                     buttonText.ParentOrigin = NUI.ParentOrigin.BottomCenter;
1044                     buttonText.PivotPoint = NUI.PivotPoint.BottomCenter;
1045                     buttonText.Position2D = new Position2D(0, -textPaddingBottom);
1046                     break;
1047                 case IconOrientation.Bottom:
1048                     buttonIcon.PositionUsesPivotPoint = true;
1049                     buttonIcon.ParentOrigin = NUI.ParentOrigin.BottomCenter;
1050                     buttonIcon.PivotPoint = NUI.PivotPoint.BottomCenter;
1051                     buttonIcon.Position2D = new Position2D(0, -iconPaddingBottom);
1052
1053                     buttonText.PositionUsesPivotPoint = true;
1054                     buttonText.ParentOrigin = NUI.ParentOrigin.TopCenter;
1055                     buttonText.PivotPoint = NUI.PivotPoint.TopCenter;
1056                     buttonText.Position2D = new Position2D(0, textPaddingTop);
1057                     break;
1058                 case IconOrientation.Left:
1059                     if (LayoutDirection == ViewLayoutDirectionType.LTR)
1060                     {
1061                         buttonIcon.PositionUsesPivotPoint = true;
1062                         buttonIcon.ParentOrigin = NUI.ParentOrigin.CenterLeft;
1063                         buttonIcon.PivotPoint = NUI.PivotPoint.CenterLeft;
1064                         buttonIcon.Position2D = new Position2D(iconPaddingStart, 0);
1065
1066                         buttonText.PositionUsesPivotPoint = true;
1067                         buttonText.ParentOrigin = NUI.ParentOrigin.CenterRight;
1068                         buttonText.PivotPoint = NUI.PivotPoint.CenterRight;
1069                         buttonText.Position2D = new Position2D(-textPaddingEnd, 0);
1070                     }
1071                     else
1072                     {
1073                         buttonIcon.PositionUsesPivotPoint = true;
1074                         buttonIcon.ParentOrigin = NUI.ParentOrigin.CenterRight;
1075                         buttonIcon.PivotPoint = NUI.PivotPoint.CenterRight;
1076                         buttonIcon.Position2D = new Position2D(-iconPaddingStart, 0);
1077
1078                         buttonText.PositionUsesPivotPoint = true;
1079                         buttonText.ParentOrigin = NUI.ParentOrigin.CenterLeft;
1080                         buttonText.PivotPoint = NUI.PivotPoint.CenterLeft;
1081                         buttonText.Position2D = new Position2D(textPaddingEnd, 0);
1082                     }
1083
1084                     break;
1085                 case IconOrientation.Right:
1086                     if (LayoutDirection == ViewLayoutDirectionType.RTL)
1087                     {
1088                         buttonIcon.PositionUsesPivotPoint = true;
1089                         buttonIcon.ParentOrigin = NUI.ParentOrigin.CenterLeft;
1090                         buttonIcon.PivotPoint = NUI.PivotPoint.CenterLeft;
1091                         buttonIcon.Position2D = new Position2D(iconPaddingEnd, 0);
1092
1093                         buttonText.PositionUsesPivotPoint = true;
1094                         buttonText.ParentOrigin = NUI.ParentOrigin.CenterRight;
1095                         buttonText.PivotPoint = NUI.PivotPoint.CenterRight;
1096                         buttonText.Position2D = new Position2D(-textPaddingStart, 0);
1097                     }
1098                     else
1099                     {
1100                         buttonIcon.PositionUsesPivotPoint = true;
1101                         buttonIcon.ParentOrigin = NUI.ParentOrigin.CenterRight;
1102                         buttonIcon.PivotPoint = NUI.PivotPoint.CenterRight;
1103                         buttonIcon.Position2D = new Position2D(-iconPaddingEnd, 0);
1104
1105                         buttonText.PositionUsesPivotPoint = true;
1106                         buttonText.ParentOrigin = NUI.ParentOrigin.CenterLeft;
1107                         buttonText.PivotPoint = NUI.PivotPoint.CenterLeft;
1108                         buttonText.Position2D = new Position2D(textPaddingStart, 0);
1109                     }
1110                     break;
1111                 default:
1112                     break;
1113             }
1114             if (string.IsNullOrEmpty(buttonText.Text))
1115             {
1116                 buttonIcon.ParentOrigin = NUI.ParentOrigin.Center;
1117                 buttonIcon.PivotPoint = NUI.PivotPoint.Center;
1118             }
1119         }
1120         /// <summary>
1121         /// Theme change callback when theme is changed, this callback will be trigger.
1122         /// </summary>
1123         /// <param name="sender">The sender</param>
1124         /// <param name="e">The event data</param>
1125         /// <since_tizen> 8 </since_tizen>
1126         protected override void OnThemeChangedEvent(object sender, StyleManager.ThemeChangeEventArgs e)
1127         {
1128             ButtonStyle buttonStyle = StyleManager.Instance.GetViewStyle(style) as ButtonStyle;
1129             if (buttonStyle != null)
1130             {
1131                 Style.CopyFrom(buttonStyle);
1132                 UpdateUIContent();
1133             }
1134         }
1135
1136         private void UpdateUIContent()
1137         {
1138             MeasureText();
1139             LayoutChild();
1140
1141             Sensitive = isEnabled;
1142         }
1143
1144         private void OnLayoutDirectionChanged(object sender, LayoutDirectionChangedEventArgs e)
1145         {
1146             MeasureText();
1147             LayoutChild();
1148         }
1149
1150         private void OnClickInternal(ClickEventArgs eventArgs)
1151         {
1152             OnClick(eventArgs);
1153             Extension?.OnClick(this, eventArgs);
1154             ClickEvent?.Invoke(this, eventArgs);
1155         }
1156
1157         private void OnIconRelayout(object sender, EventArgs e)
1158         {
1159             MeasureText();
1160             LayoutChild();
1161         }
1162
1163         /// <summary>
1164         /// ClickEventArgs is a class to record button click event arguments which will sent to user.
1165         /// </summary>
1166         /// <since_tizen> 6 </since_tizen>
1167         public class ClickEventArgs : EventArgs
1168         {
1169         }
1170         /// <summary>
1171         /// StateChangeEventArgs is a class to record button state change event arguments which will sent to user.
1172         /// </summary>
1173         /// <since_tizen> 6 </since_tizen>
1174         public class StateChangedEventArgs : EventArgs
1175         {
1176             /// <summary> previous state of Button </summary>
1177             /// <since_tizen> 6 </since_tizen>
1178             public ControlStates PreviousState;
1179             /// <summary> current state of Button </summary>
1180             /// <since_tizen> 6 </since_tizen>
1181             public ControlStates CurrentState;
1182         }
1183
1184         /// <summary>
1185         /// Get current text part to the attached ButtonExtension.
1186         /// </summary>
1187         /// <remarks>
1188         /// It returns null if the passed extension is invaild.
1189         /// </remarks>
1190         /// <param name="extension">The extension instance that is currently attached to this Button.</param>
1191         /// <return>The button's text part.</return>
1192         [EditorBrowsable(EditorBrowsableState.Never)]
1193         public TextLabel GetCurrentText(ButtonExtension extension)
1194         {
1195             return (extension == Extension) ? ButtonText : null;
1196         }
1197
1198         /// <summary>
1199         /// Get current icon part to the attached ButtonExtension.
1200         /// </summary>
1201         /// <remarks>
1202         /// It returns null if the passed extension is invaild.
1203         /// </remarks>
1204         /// <param name="extension">The extension instance that is currently attached to this Button.</param>
1205         /// <return>The button's icon part.</return>
1206         [EditorBrowsable(EditorBrowsableState.Never)]
1207         public ImageView GetCurrentIcon(ButtonExtension extension)
1208         {
1209             return (extension == Extension) ? ButtonIcon : null;
1210         }
1211
1212         /// <summary>
1213         /// Get current overlay image part to the attached ButtonExtension.
1214         /// </summary>
1215         /// <remarks>
1216         /// It returns null if the passed extension is invaild.
1217         /// </remarks>
1218         /// <param name="extension">The extension instance that is currently attached to this Button.</param>
1219         /// <return>The button's overlay image part.</return>
1220         [EditorBrowsable(EditorBrowsableState.Never)]
1221         public ImageView GetCurrentOverlayImage(ButtonExtension extension)
1222         {
1223             return (extension == Extension) ? ButtonOverlayImage : null;
1224         }
1225     }
1226 }