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