[NUI] Refactoring Theme and StyleManager (#1981)
[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         /// <param name="touchInfo">The touch information in case the state has changed by touching.</param>
163         /// <since_tizen> 6 </since_tizen>
164         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
165         [EditorBrowsable(EditorBrowsableState.Never)]
166         protected void UpdateState()
167         {
168             if (!styleApplied) return;
169
170             ControlState sourceState = ControlState;
171             ControlState targetState;
172
173             // Normal, Disabled
174             targetState = IsEnabled ? ControlState.Normal : ControlState.Disabled;
175
176             // Selected, DisabledSelected
177             if (IsSelected) targetState += ControlState.Selected;
178
179             // Pressed, PressedSelected
180             if (isPressed) targetState += ControlState.Pressed;
181
182             // Focused, FocusedPressed, FocusedPressedSelected, DisabledFocused, DisabledSelectedFocused
183             if (IsFocused) targetState += ControlState.Focused;
184
185             if (sourceState != targetState)
186             {
187                 ControlState = targetState;
188                 OnUpdate();
189
190                 StateChangedEventArgs e = new StateChangedEventArgs
191                 {
192                     PreviousState = ControlStatesExtension.FromControlStateClass(sourceState),
193                     CurrentState = ControlStatesExtension.FromControlStateClass(targetState)
194                 };
195                 stateChangeHandler?.Invoke(this, e);
196
197                 Extension?.OnControlStateChanged(this, new ControlStateChangedEventArgs(sourceState, targetState));
198             }
199         }
200
201         /// <summary>
202         /// Measure text, it can be override.
203         /// </summary>
204         /// <since_tizen> 6 </since_tizen>
205         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
206         [EditorBrowsable(EditorBrowsableState.Never)]
207         protected virtual void MeasureText()
208         {
209             if (buttonIcon == null || buttonText == null)
210             {
211                 return;
212             }
213             buttonText.WidthResizePolicy = ResizePolicyType.Fixed;
214             buttonText.HeightResizePolicy = ResizePolicyType.Fixed;
215             int textPaddingStart = buttonStyle.TextPadding.Start;
216             int textPaddingEnd = buttonStyle.TextPadding.End;
217             int textPaddingTop = buttonStyle.TextPadding.Top;
218             int textPaddingBottom = buttonStyle.TextPadding.Bottom;
219
220             int iconPaddingStart = buttonStyle.IconPadding.Start;
221             int iconPaddingEnd = buttonStyle.IconPadding.End;
222             int iconPaddingTop = buttonStyle.IconPadding.Top;
223             int iconPaddingBottom = buttonStyle.IconPadding.Bottom;
224
225             if (IconRelativeOrientation == IconOrientation.Top || IconRelativeOrientation == IconOrientation.Bottom)
226             {
227                 buttonText.SizeWidth = SizeWidth - textPaddingStart - textPaddingEnd;
228                 buttonText.SizeHeight = SizeHeight - textPaddingTop - textPaddingBottom - iconPaddingTop - iconPaddingBottom - buttonIcon.SizeHeight;
229             }
230             else
231             {
232                 buttonText.SizeWidth = SizeWidth - textPaddingStart - textPaddingEnd - iconPaddingStart - iconPaddingEnd - buttonIcon.SizeWidth;
233                 buttonText.SizeHeight = SizeHeight - textPaddingTop - textPaddingBottom;
234             }
235         }
236
237         /// <summary>
238         /// Layout child, it can be override.
239         /// </summary>
240         /// <since_tizen> 6 </since_tizen>
241         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
242         [EditorBrowsable(EditorBrowsableState.Never)]
243         protected virtual void LayoutChild()
244         {
245             if (buttonIcon == null || buttonText == null)
246             {
247                 return;
248             }
249
250             int textPaddingStart = buttonStyle.TextPadding.Start;
251             int textPaddingEnd = buttonStyle.TextPadding.End;
252             int textPaddingTop = buttonStyle.TextPadding.Top;
253             int textPaddingBottom = buttonStyle.TextPadding.Bottom;
254
255             int iconPaddingStart = buttonStyle.IconPadding.Start;
256             int iconPaddingEnd = buttonStyle.IconPadding.End;
257             int iconPaddingTop = buttonStyle.IconPadding.Top;
258             int iconPaddingBottom = buttonStyle.IconPadding.Bottom;
259
260             switch (IconRelativeOrientation)
261             {
262                 case IconOrientation.Top:
263                     buttonIcon.PositionUsesPivotPoint = true;
264                     buttonIcon.ParentOrigin = NUI.ParentOrigin.TopCenter;
265                     buttonIcon.PivotPoint = NUI.PivotPoint.TopCenter;
266                     buttonIcon.Position2D = new Position2D(0, iconPaddingTop);
267
268                     buttonText.PositionUsesPivotPoint = true;
269                     buttonText.ParentOrigin = NUI.ParentOrigin.BottomCenter;
270                     buttonText.PivotPoint = NUI.PivotPoint.BottomCenter;
271                     buttonText.Position2D = new Position2D(0, -textPaddingBottom);
272                     break;
273                 case IconOrientation.Bottom:
274                     buttonIcon.PositionUsesPivotPoint = true;
275                     buttonIcon.ParentOrigin = NUI.ParentOrigin.BottomCenter;
276                     buttonIcon.PivotPoint = NUI.PivotPoint.BottomCenter;
277                     buttonIcon.Position2D = new Position2D(0, -iconPaddingBottom);
278
279                     buttonText.PositionUsesPivotPoint = true;
280                     buttonText.ParentOrigin = NUI.ParentOrigin.TopCenter;
281                     buttonText.PivotPoint = NUI.PivotPoint.TopCenter;
282                     buttonText.Position2D = new Position2D(0, textPaddingTop);
283                     break;
284                 case IconOrientation.Left:
285                     if (LayoutDirection == ViewLayoutDirectionType.LTR)
286                     {
287                         buttonIcon.PositionUsesPivotPoint = true;
288                         buttonIcon.ParentOrigin = NUI.ParentOrigin.CenterLeft;
289                         buttonIcon.PivotPoint = NUI.PivotPoint.CenterLeft;
290                         buttonIcon.Position2D = new Position2D(iconPaddingStart, 0);
291
292                         buttonText.PositionUsesPivotPoint = true;
293                         buttonText.ParentOrigin = NUI.ParentOrigin.CenterRight;
294                         buttonText.PivotPoint = NUI.PivotPoint.CenterRight;
295                         buttonText.Position2D = new Position2D(-textPaddingEnd, 0);
296                     }
297                     else
298                     {
299                         buttonIcon.PositionUsesPivotPoint = true;
300                         buttonIcon.ParentOrigin = NUI.ParentOrigin.CenterRight;
301                         buttonIcon.PivotPoint = NUI.PivotPoint.CenterRight;
302                         buttonIcon.Position2D = new Position2D(-iconPaddingStart, 0);
303
304                         buttonText.PositionUsesPivotPoint = true;
305                         buttonText.ParentOrigin = NUI.ParentOrigin.CenterLeft;
306                         buttonText.PivotPoint = NUI.PivotPoint.CenterLeft;
307                         buttonText.Position2D = new Position2D(textPaddingEnd, 0);
308                     }
309
310                     break;
311                 case IconOrientation.Right:
312                     if (LayoutDirection == ViewLayoutDirectionType.RTL)
313                     {
314                         buttonIcon.PositionUsesPivotPoint = true;
315                         buttonIcon.ParentOrigin = NUI.ParentOrigin.CenterLeft;
316                         buttonIcon.PivotPoint = NUI.PivotPoint.CenterLeft;
317                         buttonIcon.Position2D = new Position2D(iconPaddingEnd, 0);
318
319                         buttonText.PositionUsesPivotPoint = true;
320                         buttonText.ParentOrigin = NUI.ParentOrigin.CenterRight;
321                         buttonText.PivotPoint = NUI.PivotPoint.CenterRight;
322                         buttonText.Position2D = new Position2D(-textPaddingStart, 0);
323                     }
324                     else
325                     {
326                         buttonIcon.PositionUsesPivotPoint = true;
327                         buttonIcon.ParentOrigin = NUI.ParentOrigin.CenterRight;
328                         buttonIcon.PivotPoint = NUI.PivotPoint.CenterRight;
329                         buttonIcon.Position2D = new Position2D(-iconPaddingEnd, 0);
330
331                         buttonText.PositionUsesPivotPoint = true;
332                         buttonText.ParentOrigin = NUI.ParentOrigin.CenterLeft;
333                         buttonText.PivotPoint = NUI.PivotPoint.CenterLeft;
334                         buttonText.Position2D = new Position2D(textPaddingStart, 0);
335                     }
336                     break;
337                 default:
338                     break;
339             }
340             if (string.IsNullOrEmpty(buttonText.Text))
341             {
342                 buttonIcon.ParentOrigin = NUI.ParentOrigin.Center;
343                 buttonIcon.PivotPoint = NUI.PivotPoint.Center;
344             }
345         }
346
347         /// <summary>
348         /// Dispose Button and all children on it.
349         /// </summary>
350         /// <param name="type">Dispose type.</param>
351         /// <since_tizen> 6 </since_tizen>
352         protected override void Dispose(DisposeTypes type)
353         {
354             if (disposed)
355             {
356                 return;
357             }
358
359             if (type == DisposeTypes.Explicit)
360             {
361                 Extension?.OnDispose(this);
362
363                 if (buttonIcon != null)
364                 {
365                     Utility.Dispose(buttonIcon);
366                 }
367                 if (buttonText != null)
368                 {
369                     Utility.Dispose(buttonText);
370                 }
371                 if (overlayImage != null)
372                 {
373                     Utility.Dispose(overlayImage);
374                 }
375             }
376
377             base.Dispose(type);
378         }
379
380         /// <inheritdoc/>
381         [EditorBrowsable(EditorBrowsableState.Never)]
382         protected override void OnControlStateChanged(ControlStateChangedEventArgs controlStateChangedInfo)
383         {
384             base.OnControlStateChanged(controlStateChangedInfo);
385
386             var stateEnabled = !controlStateChangedInfo.CurrentState.Contains(ControlState.Disabled);
387
388             if (IsEnabled != stateEnabled)
389             {
390                 IsEnabled = stateEnabled;
391             }
392
393             var statePressed = controlStateChangedInfo.CurrentState.Contains(ControlState.Pressed);
394
395             if (isPressed != statePressed)
396             {
397                 isPressed = statePressed;
398             }
399         }
400
401         /// <summary>
402         /// It is hijack by using protected, style copy problem when class inherited from Button.
403         /// </summary>
404         /// <since_tizen> 6 </since_tizen>
405         private void Initialize()
406         {
407             EnableControlStatePropagation = true;
408             UpdateState();
409             LayoutDirectionChanged += OnLayoutDirectionChanged;
410
411             AccessibilityManager.Instance.SetAccessibilityAttribute(this, AccessibilityManager.AccessibilityAttribute.Trait, "Button");
412         }
413
414         private void UpdateUIContent()
415         {
416             MeasureText();
417             LayoutChild();
418
419             Sensitive = IsEnabled;
420         }
421
422         private void OnLayoutDirectionChanged(object sender, LayoutDirectionChangedEventArgs e)
423         {
424             MeasureText();
425             LayoutChild();
426         }
427
428         private void OnClickedInternal(ClickedEventArgs eventArgs)
429         {
430             Command?.Execute(CommandParameter);
431             OnClicked(eventArgs);
432             Extension?.OnClicked(this, eventArgs);
433
434             ClickEventArgs nestedEventArgs = new ClickEventArgs();
435             ClickEvent?.Invoke(this, nestedEventArgs);
436             Clicked?.Invoke(this, eventArgs);
437         }
438
439         private void OnIconRelayout(object sender, EventArgs e)
440         {
441             MeasureText();
442             LayoutChild();
443         }
444
445         internal override bool OnAccessibilityActivated()
446         {
447             if (!IsEnabled)
448             {
449                 return false;
450             }
451
452             // Touch Down
453             isPressed = true;
454             UpdateState();
455
456             // Touch Up
457             bool clicked = isPressed && IsEnabled;
458             isPressed = false;
459
460             if (IsSelectable)
461             {
462                 IsSelected = !IsSelected;
463             }
464             else
465             {
466                 UpdateState();
467             }
468
469             if (clicked)
470             {
471                 ClickedEventArgs eventArgs = new ClickedEventArgs();
472                 OnClickedInternal(eventArgs);
473             }
474             return true;
475         }
476
477     }
478 }