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