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