[NUI] Fix TV TCT fails
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / Button.Internal.cs
1 using System;
2 using System.ComponentModel;
3 using Tizen.NUI.BaseComponents;
4 using Tizen.NUI.Components.Extension;
5 using Tizen.NUI.Accessibility; // To use AccessibilityManager
6
7 namespace Tizen.NUI.Components
8 {
9     public partial class Button
10     {
11         private ImageView overlayImage;
12         private TextLabel buttonText;
13         private ImageView buttonIcon;
14
15         private EventHandler<StateChangedEventArgs> stateChangeHandler;
16
17         private bool isPressed = false;
18         private bool styleApplied = false;
19
20         /// <summary>
21         /// The ButtonExtension instance that is injected by ButtonStyle.
22         /// </summary>
23         [EditorBrowsable(EditorBrowsableState.Never)]
24         protected ButtonExtension Extension { get; set; }
25
26         /// <summary>
27         /// Creates Button's text part.
28         /// </summary>
29         /// <return>The created Button's text part.</return>
30         [EditorBrowsable(EditorBrowsableState.Never)]
31         protected virtual TextLabel CreateText()
32         {
33             return new TextLabel
34             {
35                 PositionUsesPivotPoint = true,
36                 ParentOrigin = NUI.ParentOrigin.Center,
37                 PivotPoint = NUI.PivotPoint.Center,
38                 WidthResizePolicy = ResizePolicyType.FillToParent,
39                 HeightResizePolicy = ResizePolicyType.FillToParent,
40                 HorizontalAlignment = HorizontalAlignment.Center,
41                 VerticalAlignment = VerticalAlignment.Center
42             };
43         }
44
45         /// <summary>
46         /// Creates Button's icon part.
47         /// </summary>
48         /// <return>The created Button's icon part.</return>
49         [EditorBrowsable(EditorBrowsableState.Never)]
50         protected virtual ImageView CreateIcon()
51         {
52             return new ImageView
53             {
54                 PositionUsesPivotPoint = true,
55                 ParentOrigin = NUI.ParentOrigin.Center,
56                 PivotPoint = NUI.PivotPoint.Center
57             };
58         }
59
60         /// <summary>
61         /// Creates Button's overlay image part.
62         /// </summary>
63         /// <return>The created Button's overlay image part.</return>
64         [EditorBrowsable(EditorBrowsableState.Never)]
65         protected virtual ImageView CreateOverlayImage()
66         {
67             return new ImageView
68             {
69                 PositionUsesPivotPoint = true,
70                 ParentOrigin = NUI.ParentOrigin.Center,
71                 PivotPoint = NUI.PivotPoint.Center,
72                 WidthResizePolicy = ResizePolicyType.FillToParent,
73                 HeightResizePolicy = ResizePolicyType.FillToParent
74             };
75         }
76
77         /// <summary>
78         /// Called when the Button is Clicked by a user
79         /// </summary>
80         /// <param name="eventArgs">The click information.</param>
81         [EditorBrowsable(EditorBrowsableState.Never)]
82         protected virtual void OnClicked(ClickedEventArgs eventArgs)
83         {
84         }
85
86         /// <summary>
87         /// Get Button style.
88         /// </summary>
89         /// <returns>The default button style.</returns>
90         /// <since_tizen> 8 </since_tizen>
91         protected override ViewStyle CreateViewStyle()
92         {
93             return new ButtonStyle();
94         }
95
96         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
97         [EditorBrowsable(EditorBrowsableState.Never)]
98         protected override void OnUpdate()
99         {
100             base.OnUpdate();
101             UpdateUIContent();
102
103             Extension?.OnRelayout(this);
104         }
105
106         /// <inheritdoc/>
107         [EditorBrowsable(EditorBrowsableState.Never)]
108         protected override bool HandleControlStateOnTouch(Touch touch)
109         {
110             if (!IsEnabled || null == touch)
111             {
112                 return false;
113             }
114
115             PointStateType state = touch.GetState(0);
116
117             switch (state)
118             {
119                 case PointStateType.Down:
120                     isPressed = true;
121                     Extension?.SetTouchInfo(touch);
122                     UpdateState();
123                     return true;
124                 case PointStateType.Interrupted:
125                     isPressed = false;
126                     UpdateState();
127                     return true;
128                 case PointStateType.Up:
129                     {
130                         bool clicked = isPressed && IsEnabled;
131
132                         isPressed = false;
133
134                         if (IsSelectable)
135                         {
136                             Extension?.SetTouchInfo(touch);
137                             IsSelected = !IsSelected;
138                         }
139                         else
140                         {
141                             Extension?.SetTouchInfo(touch);
142                             UpdateState();
143                         }
144
145                         if (clicked)
146                         {
147                             ClickedEventArgs eventArgs = new ClickedEventArgs();
148                             OnClickedInternal(eventArgs);
149                         }
150
151                         return true;
152                     }
153                 default:
154                     break;
155             }
156             return base.HandleControlStateOnTouch(touch);
157         }
158
159         /// <summary>
160         /// Update Button State.
161         /// </summary>
162         /// <since_tizen> 6 </since_tizen>
163         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
164         [EditorBrowsable(EditorBrowsableState.Never)]
165         protected void UpdateState()
166         {
167             if (!styleApplied) return;
168
169             ControlState sourceState = ControlState;
170             ControlState targetState;
171
172             // Normal, Disabled
173             targetState = IsEnabled ? ControlState.Normal : ControlState.Disabled;
174
175             // Selected, DisabledSelected
176             if (IsSelected) targetState += ControlState.Selected;
177
178             // Pressed, PressedSelected
179             if (isPressed) targetState += ControlState.Pressed;
180
181             // Focused, FocusedPressed, FocusedPressedSelected, DisabledFocused, DisabledSelectedFocused
182             if (IsFocused) targetState += ControlState.Focused;
183
184             if (sourceState != targetState)
185             {
186                 ControlState = targetState;
187                 OnUpdate();
188
189                 StateChangedEventArgs e = new StateChangedEventArgs
190                 {
191                     PreviousState = ControlStatesExtension.FromControlStateClass(sourceState),
192                     CurrentState = ControlStatesExtension.FromControlStateClass(targetState)
193                 };
194                 stateChangeHandler?.Invoke(this, e);
195
196                 Extension?.OnControlStateChanged(this, new ControlStateChangedEventArgs(sourceState, targetState));
197             }
198         }
199
200         /// <summary>
201         /// Measure text, it can be override.
202         /// </summary>
203         /// <since_tizen> 6 </since_tizen>
204         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
205         [EditorBrowsable(EditorBrowsableState.Never)]
206         protected virtual void MeasureText()
207         {
208             if (buttonIcon == null || buttonText == null)
209             {
210                 return;
211             }
212             buttonText.WidthResizePolicy = ResizePolicyType.Fixed;
213             buttonText.HeightResizePolicy = ResizePolicyType.Fixed;
214
215             var textPadding = TextPadding;
216             int textPaddingStart = textPadding.Start;
217             int textPaddingEnd = textPadding.End;
218             int textPaddingTop = textPadding.Top;
219             int textPaddingBottom = textPadding.Bottom;
220
221             var iconPadding = IconPadding;
222             int iconPaddingStart = iconPadding.Start;
223             int iconPaddingEnd = iconPadding.End;
224             int iconPaddingTop = iconPadding.Top;
225             int iconPaddingBottom = iconPadding.Bottom;
226
227             if (IconRelativeOrientation == IconOrientation.Top || IconRelativeOrientation == IconOrientation.Bottom)
228             {
229                 buttonText.SizeWidth = SizeWidth - textPaddingStart - textPaddingEnd;
230                 buttonText.SizeHeight = SizeHeight - textPaddingTop - textPaddingBottom - iconPaddingTop - iconPaddingBottom - buttonIcon.SizeHeight;
231             }
232             else
233             {
234                 buttonText.SizeWidth = SizeWidth - textPaddingStart - textPaddingEnd - iconPaddingStart - iconPaddingEnd - buttonIcon.SizeWidth;
235                 buttonText.SizeHeight = SizeHeight - textPaddingTop - textPaddingBottom;
236             }
237         }
238
239         /// <summary>
240         /// Layout child, it can be override.
241         /// </summary>
242         /// <since_tizen> 6 </since_tizen>
243         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
244         [EditorBrowsable(EditorBrowsableState.Never)]
245         protected virtual void LayoutChild()
246         {
247             if (buttonIcon == null || buttonText == null)
248             {
249                 return;
250             }
251
252             var textPadding = TextPadding;
253             int textPaddingStart = textPadding.Start;
254             int textPaddingEnd = textPadding.End;
255             int textPaddingTop = textPadding.Top;
256             int textPaddingBottom = textPadding.Bottom;
257
258             var iconPadding = IconPadding;
259             int iconPaddingStart = iconPadding.Start;
260             int iconPaddingEnd = iconPadding.End;
261             int iconPaddingTop = iconPadding.Top;
262             int iconPaddingBottom = iconPadding.Bottom;
263
264             switch (IconRelativeOrientation)
265             {
266                 case IconOrientation.Top:
267                     buttonIcon.PositionUsesPivotPoint = true;
268                     buttonIcon.ParentOrigin = NUI.ParentOrigin.TopCenter;
269                     buttonIcon.PivotPoint = NUI.PivotPoint.TopCenter;
270                     buttonIcon.Position2D = new Position2D(0, iconPaddingTop);
271
272                     buttonText.PositionUsesPivotPoint = true;
273                     buttonText.ParentOrigin = NUI.ParentOrigin.BottomCenter;
274                     buttonText.PivotPoint = NUI.PivotPoint.BottomCenter;
275                     buttonText.Position2D = new Position2D(0, -textPaddingBottom);
276                     break;
277                 case IconOrientation.Bottom:
278                     buttonIcon.PositionUsesPivotPoint = true;
279                     buttonIcon.ParentOrigin = NUI.ParentOrigin.BottomCenter;
280                     buttonIcon.PivotPoint = NUI.PivotPoint.BottomCenter;
281                     buttonIcon.Position2D = new Position2D(0, -iconPaddingBottom);
282
283                     buttonText.PositionUsesPivotPoint = true;
284                     buttonText.ParentOrigin = NUI.ParentOrigin.TopCenter;
285                     buttonText.PivotPoint = NUI.PivotPoint.TopCenter;
286                     buttonText.Position2D = new Position2D(0, textPaddingTop);
287                     break;
288                 case IconOrientation.Left:
289                     if (LayoutDirection == ViewLayoutDirectionType.LTR)
290                     {
291                         buttonIcon.PositionUsesPivotPoint = true;
292                         buttonIcon.ParentOrigin = NUI.ParentOrigin.CenterLeft;
293                         buttonIcon.PivotPoint = NUI.PivotPoint.CenterLeft;
294                         buttonIcon.Position2D = new Position2D(iconPaddingStart, 0);
295
296                         buttonText.PositionUsesPivotPoint = true;
297                         buttonText.ParentOrigin = NUI.ParentOrigin.CenterRight;
298                         buttonText.PivotPoint = NUI.PivotPoint.CenterRight;
299                         buttonText.Position2D = new Position2D(-textPaddingEnd, 0);
300                     }
301                     else
302                     {
303                         buttonIcon.PositionUsesPivotPoint = true;
304                         buttonIcon.ParentOrigin = NUI.ParentOrigin.CenterRight;
305                         buttonIcon.PivotPoint = NUI.PivotPoint.CenterRight;
306                         buttonIcon.Position2D = new Position2D(-iconPaddingStart, 0);
307
308                         buttonText.PositionUsesPivotPoint = true;
309                         buttonText.ParentOrigin = NUI.ParentOrigin.CenterLeft;
310                         buttonText.PivotPoint = NUI.PivotPoint.CenterLeft;
311                         buttonText.Position2D = new Position2D(textPaddingEnd, 0);
312                     }
313
314                     break;
315                 case IconOrientation.Right:
316                     if (LayoutDirection == ViewLayoutDirectionType.RTL)
317                     {
318                         buttonIcon.PositionUsesPivotPoint = true;
319                         buttonIcon.ParentOrigin = NUI.ParentOrigin.CenterLeft;
320                         buttonIcon.PivotPoint = NUI.PivotPoint.CenterLeft;
321                         buttonIcon.Position2D = new Position2D(iconPaddingEnd, 0);
322
323                         buttonText.PositionUsesPivotPoint = true;
324                         buttonText.ParentOrigin = NUI.ParentOrigin.CenterRight;
325                         buttonText.PivotPoint = NUI.PivotPoint.CenterRight;
326                         buttonText.Position2D = new Position2D(-textPaddingStart, 0);
327                     }
328                     else
329                     {
330                         buttonIcon.PositionUsesPivotPoint = true;
331                         buttonIcon.ParentOrigin = NUI.ParentOrigin.CenterRight;
332                         buttonIcon.PivotPoint = NUI.PivotPoint.CenterRight;
333                         buttonIcon.Position2D = new Position2D(-iconPaddingEnd, 0);
334
335                         buttonText.PositionUsesPivotPoint = true;
336                         buttonText.ParentOrigin = NUI.ParentOrigin.CenterLeft;
337                         buttonText.PivotPoint = NUI.PivotPoint.CenterLeft;
338                         buttonText.Position2D = new Position2D(textPaddingStart, 0);
339                     }
340                     break;
341                 default:
342                     break;
343             }
344             if (string.IsNullOrEmpty(buttonText.Text))
345             {
346                 buttonIcon.ParentOrigin = NUI.ParentOrigin.Center;
347                 buttonIcon.PivotPoint = NUI.PivotPoint.Center;
348             }
349         }
350
351         /// <summary>
352         /// Dispose Button and all children on it.
353         /// </summary>
354         /// <param name="type">Dispose type.</param>
355         /// <since_tizen> 6 </since_tizen>
356         protected override void Dispose(DisposeTypes type)
357         {
358             if (disposed)
359             {
360                 return;
361             }
362
363             if (type == DisposeTypes.Explicit)
364             {
365 #if (PROFILE_MOBILE)
366                 AccessibilityManager.Instance.DeleteAccessibilityAttribute(this);
367 #endif
368                 Extension?.OnDispose(this);
369
370                 if (buttonIcon != null)
371                 {
372                     Utility.Dispose(buttonIcon);
373                 }
374                 if (buttonText != null)
375                 {
376                     Utility.Dispose(buttonText);
377                 }
378                 if (overlayImage != null)
379                 {
380                     Utility.Dispose(overlayImage);
381                 }
382             }
383
384             base.Dispose(type);
385         }
386
387         /// <inheritdoc/>
388         [EditorBrowsable(EditorBrowsableState.Never)]
389         protected override void OnControlStateChanged(ControlStateChangedEventArgs controlStateChangedInfo)
390         {
391             base.OnControlStateChanged(controlStateChangedInfo);
392
393             var stateEnabled = !controlStateChangedInfo.CurrentState.Contains(ControlState.Disabled);
394
395             if (IsEnabled != stateEnabled)
396             {
397                 IsEnabled = stateEnabled;
398             }
399
400             var statePressed = controlStateChangedInfo.CurrentState.Contains(ControlState.Pressed);
401
402             if (isPressed != statePressed)
403             {
404                 isPressed = statePressed;
405             }
406         }
407
408         /// <summary>
409         /// It is hijack by using protected, style copy problem when class inherited from Button.
410         /// </summary>
411         /// <since_tizen> 6 </since_tizen>
412         private void Initialize()
413         {
414             EnableControlStatePropagation = true;
415             UpdateState();
416             LayoutDirectionChanged += OnLayoutDirectionChanged;
417 #if (PROFILE_MOBILE)
418             AccessibilityManager.Instance.SetAccessibilityAttribute(this, AccessibilityManager.AccessibilityAttribute.Trait, "Button");
419             Feedback = true;
420 #endif
421         }
422
423         private void UpdateUIContent()
424         {
425             MeasureText();
426             LayoutChild();
427
428             Sensitive = IsEnabled;
429         }
430
431         private void OnLayoutDirectionChanged(object sender, LayoutDirectionChangedEventArgs e)
432         {
433             MeasureText();
434             LayoutChild();
435         }
436
437         private void OnClickedInternal(ClickedEventArgs eventArgs)
438         {
439             Command?.Execute(CommandParameter);
440             OnClicked(eventArgs);
441             Extension?.OnClicked(this, eventArgs);
442
443             ClickEventArgs nestedEventArgs = new ClickEventArgs();
444             ClickEvent?.Invoke(this, nestedEventArgs);
445             Clicked?.Invoke(this, eventArgs);
446         }
447
448         private void OnIconRelayout(object sender, EventArgs e)
449         {
450             MeasureText();
451             LayoutChild();
452         }
453
454         internal override bool OnAccessibilityActivated()
455         {
456             if (!IsEnabled)
457             {
458                 return false;
459             }
460
461             // Touch Down
462             isPressed = true;
463             UpdateState();
464
465             // Touch Up
466             bool clicked = isPressed && IsEnabled;
467             isPressed = false;
468
469             if (IsSelectable)
470             {
471                 IsSelected = !IsSelected;
472             }
473             else
474             {
475                 UpdateState();
476             }
477
478             if (clicked)
479             {
480                 ClickedEventArgs eventArgs = new ClickedEventArgs();
481                 OnClickedInternal(eventArgs);
482             }
483             return true;
484         }
485
486     }
487 }