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