[NUI] CheckBox, RadioButton and Switch can place text without setting text size ...
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / Button.Internal.cs
1 /*
2  * Copyright(c) 2021 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 using System;
19 using System.ComponentModel;
20 using Tizen.NUI.BaseComponents;
21 using Tizen.NUI.Components.Extension;
22 using Tizen.NUI.Accessibility; // To use AccessibilityManager
23
24 namespace Tizen.NUI.Components
25 {
26     public partial class Button
27     {
28         private ImageView overlayImage;
29         private TextLabel buttonText;
30         private ImageView buttonIcon;
31
32         private EventHandler<StateChangedEventArgs> stateChangeHandler;
33
34         private bool isPressed = false;
35         private bool styleApplied = false;
36
37         /// <summary>
38         /// Get accessibility name.
39         /// </summary>
40         [EditorBrowsable(EditorBrowsableState.Never)]
41         protected override string AccessibilityGetName()
42         {
43             return Text;
44         }
45
46         /// <summary>
47         /// Prevents from showing child widgets in AT-SPI tree.
48         /// </summary>
49         [EditorBrowsable(EditorBrowsableState.Never)]
50         protected override bool AccessibilityShouldReportZeroChildren()
51         {
52             return true;
53         }
54
55         /// <summary>
56         /// The ButtonExtension instance that is injected by ButtonStyle.
57         /// </summary>
58         [EditorBrowsable(EditorBrowsableState.Never)]
59         protected ButtonExtension Extension { get; set; }
60
61         /// <summary>
62         /// Creates Button's text part.
63         /// </summary>
64         /// <return>The created Button's text part.</return>
65         [EditorBrowsable(EditorBrowsableState.Never)]
66         protected virtual TextLabel CreateText()
67         {
68             return new TextLabel
69             {
70                 HorizontalAlignment = HorizontalAlignment.Center,
71                 VerticalAlignment = VerticalAlignment.Center,
72                 AccessibilityHighlightable = false
73             };
74         }
75
76         /// <summary>
77         /// Creates Button's icon part.
78         /// </summary>
79         /// <return>The created Button's icon part.</return>
80         [EditorBrowsable(EditorBrowsableState.Never)]
81         protected virtual ImageView CreateIcon()
82         {
83             return new ImageView
84             {
85                 AccessibilityHighlightable = false
86             };
87         }
88
89         /// <summary>
90         /// Creates Button's overlay image part.
91         /// </summary>
92         /// <return>The created Button's overlay image part.</return>
93         [EditorBrowsable(EditorBrowsableState.Never)]
94         protected virtual ImageView CreateOverlayImage()
95         {
96             return new ImageView
97             {
98                 PositionUsesPivotPoint = true,
99                 ParentOrigin = NUI.ParentOrigin.Center,
100                 PivotPoint = NUI.PivotPoint.Center,
101                 WidthResizePolicy = ResizePolicyType.FillToParent,
102                 HeightResizePolicy = ResizePolicyType.FillToParent,
103                 AccessibilityHighlightable = false
104             };
105         }
106
107         /// <summary>
108         /// Called when the Button is Clicked by a user
109         /// </summary>
110         /// <param name="eventArgs">The click information.</param>
111         [EditorBrowsable(EditorBrowsableState.Never)]
112         protected virtual void OnClicked(ClickedEventArgs eventArgs)
113         {
114         }
115
116         /// <summary>
117         /// Get Button style.
118         /// </summary>
119         /// <returns>The default button style.</returns>
120         /// <since_tizen> 8 </since_tizen>
121         protected override ViewStyle CreateViewStyle()
122         {
123             return new ButtonStyle();
124         }
125
126         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
127         [EditorBrowsable(EditorBrowsableState.Never)]
128         protected override void OnUpdate()
129         {
130             base.OnUpdate();
131             Extension?.OnRelayout(this);
132         }
133
134         /// <inheritdoc/>
135         [EditorBrowsable(EditorBrowsableState.Never)]
136         protected override bool HandleControlStateOnTouch(Touch touch)
137         {
138             if (!IsEnabled || null == touch)
139             {
140                 return false;
141             }
142
143             PointStateType state = touch.GetState(0);
144
145             switch (state)
146             {
147                 case PointStateType.Down:
148                     isPressed = true;
149                     Extension?.SetTouchInfo(touch);
150                     UpdateState();
151                     return true;
152                 case PointStateType.Interrupted:
153                     isPressed = false;
154                     UpdateState();
155                     return true;
156                 case PointStateType.Up:
157                     {
158                         bool clicked = isPressed && IsEnabled;
159
160                         isPressed = false;
161
162                         if (IsSelectable)
163                         {
164                             Extension?.SetTouchInfo(touch);
165                             IsSelected = !IsSelected;
166                         }
167                         else
168                         {
169                             Extension?.SetTouchInfo(touch);
170                             UpdateState();
171                         }
172
173                         if (clicked)
174                         {
175                             ClickedEventArgs eventArgs = new ClickedEventArgs();
176                             OnClickedInternal(eventArgs);
177                         }
178
179                         return true;
180                     }
181                 default:
182                     break;
183             }
184             return base.HandleControlStateOnTouch(touch);
185         }
186
187         /// <summary>
188         /// Update Button State.
189         /// </summary>
190         /// <since_tizen> 6 </since_tizen>
191         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
192         [EditorBrowsable(EditorBrowsableState.Never)]
193         protected void UpdateState()
194         {
195             if (!styleApplied) return;
196
197             ControlState sourceState = ControlState;
198             ControlState targetState;
199
200             // Normal, Disabled
201             targetState = IsEnabled ? ControlState.Normal : ControlState.Disabled;
202
203             // Selected, DisabledSelected
204             if (IsSelected) targetState += ControlState.Selected;
205
206             // Pressed, PressedSelected
207             if (isPressed) targetState += ControlState.Pressed;
208
209             // Focused, FocusedPressed, FocusedPressedSelected, DisabledFocused, DisabledSelectedFocused
210             if (IsFocused) targetState += ControlState.Focused;
211
212             if (sourceState != targetState)
213             {
214                 ControlState = targetState;
215                 OnUpdate();
216
217                 StateChangedEventArgs e = new StateChangedEventArgs
218                 {
219                     PreviousState = ControlStatesExtension.FromControlStateClass(sourceState),
220                     CurrentState = ControlStatesExtension.FromControlStateClass(targetState)
221                 };
222                 stateChangeHandler?.Invoke(this, e);
223
224                 Extension?.OnControlStateChanged(this, new ControlStateChangedEventArgs(sourceState, targetState));
225             }
226         }
227
228         /// <summary>
229         /// Dispose Button and all children on it.
230         /// </summary>
231         /// <param name="type">Dispose type.</param>
232         /// <since_tizen> 6 </since_tizen>
233         protected override void Dispose(DisposeTypes type)
234         {
235             if (disposed)
236             {
237                 return;
238             }
239
240             if (type == DisposeTypes.Explicit)
241             {
242                 Extension?.OnDispose(this);
243
244                 if (buttonIcon != null)
245                 {
246                     Utility.Dispose(buttonIcon);
247                 }
248                 if (buttonText != null)
249                 {
250                     Utility.Dispose(buttonText);
251                 }
252                 if (overlayImage != null)
253                 {
254                     Utility.Dispose(overlayImage);
255                 }
256             }
257
258             base.Dispose(type);
259         }
260
261         /// <summary>
262         /// Initializes AT-SPI object.
263         /// </summary>
264         [EditorBrowsable(EditorBrowsableState.Never)]
265         public override void OnInitialize()
266         {
267             base.OnInitialize();
268             SetAccessibilityConstructor(Role.PushButton);
269
270             AccessibilityHighlightable = true;
271             EnableControlStatePropagation = true;
272
273             AccessibilityManager.Instance.SetAccessibilityAttribute(this, AccessibilityManager.AccessibilityAttribute.Trait, "Button");
274
275             buttonText = CreateText();
276             buttonIcon = CreateIcon();
277             LayoutItems();
278
279 #if PROFILE_MOBILE
280             Feedback = true;
281 #endif
282         }
283
284         /// <inheritdoc/>
285         [EditorBrowsable(EditorBrowsableState.Never)]
286         protected override void OnControlStateChanged(ControlStateChangedEventArgs controlStateChangedInfo)
287         {
288             base.OnControlStateChanged(controlStateChangedInfo);
289
290             var stateEnabled = !controlStateChangedInfo.CurrentState.Contains(ControlState.Disabled);
291
292             if (IsEnabled != stateEnabled)
293             {
294                 IsEnabled = stateEnabled;
295             }
296
297             var statePressed = controlStateChangedInfo.CurrentState.Contains(ControlState.Pressed);
298
299             if (isPressed != statePressed)
300             {
301                 isPressed = statePressed;
302             }
303         }
304
305         /// <summary>
306         /// Put sub items (e.g. buttonText, buttonIcon) to the right place.
307         /// </summary>
308         [EditorBrowsable(EditorBrowsableState.Never)]
309         protected virtual void LayoutItems()
310         {
311             if (buttonIcon == null || buttonText == null)
312             {
313                 return;
314             }
315
316             buttonIcon.Unparent();
317             buttonText.Unparent();
318             overlayImage?.Unparent();
319
320             if (IconRelativeOrientation == IconOrientation.Left)
321             {
322                 Layout = new LinearLayout()
323                 {
324                     LinearOrientation = LinearLayout.Orientation.Horizontal,
325                     LinearAlignment = LinearLayout.Alignment.Center,
326                 };
327
328                 Add(buttonIcon);
329                 Add(buttonText);
330             }
331             else if (IconRelativeOrientation == IconOrientation.Right)
332             {
333                 Layout = new LinearLayout()
334                 {
335                     LinearOrientation = LinearLayout.Orientation.Horizontal,
336                     LinearAlignment = LinearLayout.Alignment.Center,
337                 };
338
339                 Add(buttonText);
340                 Add(buttonIcon);
341             }
342             else if (IconRelativeOrientation == IconOrientation.Top)
343             {
344                 Layout = new LinearLayout()
345                 {
346                     LinearOrientation = LinearLayout.Orientation.Vertical,
347                     LinearAlignment = LinearLayout.Alignment.Center,
348                 };
349
350                 Add(buttonIcon);
351                 Add(buttonText);
352             }
353             else if (IconRelativeOrientation == IconOrientation.Bottom)
354             {
355                 Layout = new LinearLayout()
356                 {
357                     LinearOrientation = LinearLayout.Orientation.Vertical,
358                     LinearAlignment = LinearLayout.Alignment.Center,
359                 };
360
361                 Add(buttonText);
362                 Add(buttonIcon);
363             }
364
365             if (overlayImage != null)
366             {
367                 overlayImage.ExcludeLayouting = true;
368                 Add(overlayImage);
369             }
370         }
371
372         private void OnClickedInternal(ClickedEventArgs eventArgs)
373         {
374             Command?.Execute(CommandParameter);
375             OnClicked(eventArgs);
376             Extension?.OnClicked(this, eventArgs);
377
378             ClickEventArgs nestedEventArgs = new ClickEventArgs();
379             ClickEvent?.Invoke(this, nestedEventArgs);
380             Clicked?.Invoke(this, eventArgs);
381         }
382
383         internal override bool OnAccessibilityActivated()
384         {
385             using (var key = new Key())
386             {
387                 key.State = Key.StateType.Down;
388                 key.KeyPressedName = "Return";
389
390                 // Touch Down
391                 OnKey(key);
392
393                 // Touch Up
394                 key.State = Key.StateType.Up;
395                 OnKey(key);
396             }
397
398             return true;
399         }
400     }
401 }