2 * Copyright(c) 2021 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 using System.ComponentModel;
20 using System.Diagnostics;
21 using Tizen.NUI.BaseComponents;
22 using Tizen.NUI.Components.Extension;
23 using Tizen.NUI.Accessibility; // To use AccessibilityManager
25 namespace Tizen.NUI.Components
27 public partial class Button
29 private ImageView overlayImage;
30 private TextLabel buttonText;
31 private ImageView buttonIcon;
34 private EventHandler<StateChangedEventArgs> stateChangeHandler;
36 private bool isPressed = false;
37 private bool styleApplied = false;
40 /// Get accessibility name.
42 [EditorBrowsable(EditorBrowsableState.Never)]
43 protected override string AccessibilityGetName()
49 /// Prevents from showing child widgets in AT-SPI tree.
51 [EditorBrowsable(EditorBrowsableState.Never)]
52 protected override bool AccessibilityShouldReportZeroChildren()
58 /// The ButtonExtension instance that is injected by ButtonStyle.
60 [EditorBrowsable(EditorBrowsableState.Never)]
61 protected ButtonExtension Extension { get; set; }
64 /// Creates Button's text part.
66 /// <return>The created Button's text part.</return>
67 [EditorBrowsable(EditorBrowsableState.Never)]
68 protected virtual TextLabel CreateText()
70 return new TextLabel(new TextLabelStyle())
72 HorizontalAlignment = HorizontalAlignment.Center,
73 VerticalAlignment = VerticalAlignment.Center,
74 AccessibilityHighlightable = false,
79 /// Creates Button's icon part.
81 /// <return>The created Button's icon part.</return>
82 [EditorBrowsable(EditorBrowsableState.Never)]
83 protected virtual ImageView CreateIcon()
85 return new ImageView()
87 AccessibilityHighlightable = false
92 /// Creates Button's overlay image part.
94 /// <return>The created Button's overlay image part.</return>
95 [EditorBrowsable(EditorBrowsableState.Never)]
96 protected virtual ImageView CreateOverlayImage()
100 PositionUsesPivotPoint = true,
101 ParentOrigin = NUI.ParentOrigin.Center,
102 PivotPoint = NUI.PivotPoint.Center,
103 WidthResizePolicy = ResizePolicyType.FillToParent,
104 HeightResizePolicy = ResizePolicyType.FillToParent,
105 AccessibilityHighlightable = false
110 /// Called when the Button is Clicked by a user
112 /// <param name="eventArgs">The click information.</param>
113 [EditorBrowsable(EditorBrowsableState.Never)]
114 protected virtual void OnClicked(ClickedEventArgs eventArgs)
119 /// Get Button style.
121 /// <returns>The default button style.</returns>
122 /// <since_tizen> 8 </since_tizen>
123 protected override ViewStyle CreateViewStyle()
125 return new ButtonStyle();
128 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
129 [EditorBrowsable(EditorBrowsableState.Never)]
130 protected override void OnUpdate()
133 Extension?.OnRelayout(this);
137 [EditorBrowsable(EditorBrowsableState.Never)]
138 protected override bool HandleControlStateOnTouch(Touch touch)
140 if (!IsEnabled || null == touch)
145 PointStateType state = touch.GetState(0);
149 case PointStateType.Down:
151 Extension?.SetTouchInfo(touch);
154 case PointStateType.Interrupted:
158 case PointStateType.Up:
169 Extension?.SetTouchInfo(touch);
170 IsSelected = !IsSelected;
174 Extension?.SetTouchInfo(touch);
178 ClickedEventArgs eventArgs = new ClickedEventArgs();
179 OnClickedInternal(eventArgs);
186 return base.HandleControlStateOnTouch(touch);
191 [EditorBrowsable(EditorBrowsableState.Never)]
192 protected override void OnEnabled(bool enabled)
194 base.OnEnabled(enabled);
200 /// Update Button State.
202 /// <since_tizen> 6 </since_tizen>
203 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
204 [EditorBrowsable(EditorBrowsableState.Never)]
205 protected void UpdateState()
207 if (!styleApplied) return;
209 ControlState sourceState = ControlState;
210 ControlState targetState;
213 targetState = IsEnabled ? ControlState.Normal : ControlState.Disabled;
215 // Selected, DisabledSelected
216 if (IsSelected) targetState += ControlState.Selected;
218 // Pressed, PressedSelected
219 if (isPressed) targetState += ControlState.Pressed;
221 // Focused, FocusedPressed, FocusedPressedSelected, DisabledFocused, DisabledSelectedFocused
222 if (IsFocused) targetState += ControlState.Focused;
224 if (sourceState != targetState)
226 ControlState = targetState;
229 StateChangedEventArgs e = new StateChangedEventArgs
231 PreviousState = ControlStatesExtension.FromControlStateClass(sourceState),
232 CurrentState = ControlStatesExtension.FromControlStateClass(targetState)
234 stateChangeHandler?.Invoke(this, e);
236 Extension?.OnControlStateChanged(this, new ControlStateChangedEventArgs(sourceState, targetState));
241 /// Dispose Button and all children on it.
243 /// <param name="type">Dispose type.</param>
244 /// <since_tizen> 6 </since_tizen>
245 protected override void Dispose(DisposeTypes type)
252 if (type == DisposeTypes.Explicit)
254 Extension?.OnDispose(this);
256 if (buttonIcon != null)
258 Utility.Dispose(buttonIcon);
260 if (buttonText != null)
262 Utility.Dispose(buttonText);
264 if (overlayImage != null)
266 Utility.Dispose(overlayImage);
274 /// Initializes AT-SPI object.
276 [EditorBrowsable(EditorBrowsableState.Never)]
277 public override void OnInitialize()
280 SetAccessibilityConstructor(Role.PushButton);
282 AccessibilityHighlightable = true;
283 EnableControlStatePropagation = true;
285 AccessibilityManager.Instance.SetAccessibilityAttribute(this, AccessibilityManager.AccessibilityAttribute.Trait, "Button");
287 buttonText = CreateText();
288 buttonIcon = CreateIcon();
297 [EditorBrowsable(EditorBrowsableState.Never)]
298 public override void OnRelayout(Vector2 size, RelayoutContainer container)
300 if (size == null) return;
302 if (size.Equals(this.size))
307 this.size = new Vector2(size);
309 UpdateSizeAndSpacing();
313 [EditorBrowsable(EditorBrowsableState.Never)]
314 protected override void OnControlStateChanged(ControlStateChangedEventArgs controlStateChangedInfo)
316 base.OnControlStateChanged(controlStateChangedInfo);
318 var stateEnabled = !controlStateChangedInfo.CurrentState.Contains(ControlState.Disabled);
320 if (IsEnabled != stateEnabled)
322 IsEnabled = stateEnabled;
325 var statePressed = controlStateChangedInfo.CurrentState.Contains(ControlState.Pressed);
327 if (isPressed != statePressed)
329 isPressed = statePressed;
334 /// Put sub items (e.g. buttonText, buttonIcon) to the right place.
336 [EditorBrowsable(EditorBrowsableState.Never)]
337 protected virtual void LayoutItems()
339 if (buttonIcon == null || buttonText == null)
344 buttonIcon.Unparent();
345 buttonText.Unparent();
346 overlayImage?.Unparent();
348 #pragma warning disable CA2000
349 Size2D cellPadding = String.IsNullOrEmpty(buttonText.Text) ? new Size2D(0, 0) : itemSpacing;
350 #pragma warning restore CA2000
352 if (IconRelativeOrientation == IconOrientation.Left)
354 Layout = new LinearLayout()
356 LinearOrientation = LinearLayout.Orientation.Horizontal,
357 LinearAlignment = itemAlignment,
358 CellPadding = cellPadding
364 else if (IconRelativeOrientation == IconOrientation.Right)
366 Layout = new LinearLayout()
368 LinearOrientation = LinearLayout.Orientation.Horizontal,
369 LinearAlignment = itemAlignment,
370 CellPadding = cellPadding
376 else if (IconRelativeOrientation == IconOrientation.Top)
378 Layout = new LinearLayout()
380 LinearOrientation = LinearLayout.Orientation.Vertical,
381 LinearAlignment = itemAlignment,
382 CellPadding = cellPadding
388 else if (IconRelativeOrientation == IconOrientation.Bottom)
390 Layout = new LinearLayout()
392 LinearOrientation = LinearLayout.Orientation.Vertical,
393 LinearAlignment = itemAlignment,
394 CellPadding = cellPadding
401 if (overlayImage != null)
403 overlayImage.ExcludeLayouting = true;
408 private void UpdateSizeAndSpacing()
410 if (size == null || buttonIcon == null || buttonText == null)
415 LinearLayout layout = Layout as LinearLayout;
422 float lengthWithoutText = 0;
423 Size2D cellPadding = null;
424 Extents iconMargin = buttonIcon.Margin ?? new Extents(0);
425 Extents textMargin = buttonText.Margin ?? new Extents(0);
427 if (buttonIcon.Size.Width != 0 && buttonIcon.Size.Height != 0)
429 lengthWithoutText = buttonIcon.Size.Width;
431 if (!String.IsNullOrEmpty(buttonText.Text))
433 cellPadding = itemSpacing;
435 if (iconRelativeOrientation == IconOrientation.Left || iconRelativeOrientation == IconOrientation.Right)
437 lengthWithoutText += (itemSpacing?.Width ?? 0) + iconMargin.Start + iconMargin.End + textMargin.Start + textMargin.End;
441 lengthWithoutText += (itemSpacing?.Height ?? 0) + iconMargin.Top + iconMargin.Bottom + textMargin.Top + textMargin.Bottom;
446 layout.CellPadding = cellPadding ?? new Size2D(0, 0);
448 // If the button has fixed width and the text is not empty, the text should not exceed button boundary.
449 if (WidthSpecification != LayoutParamPolicies.WrapContent && !String.IsNullOrEmpty(buttonText.Text))
451 buttonText.MaximumSize = new Size2D((int)Math.Max(size.Width - lengthWithoutText, Math.Max(buttonText.MinimumSize.Width, 1)), (int)size.Height);
455 private void OnClickedInternal(ClickedEventArgs eventArgs)
457 Command?.Execute(CommandParameter);
458 OnClicked(eventArgs);
459 Extension?.OnClicked(this, eventArgs);
461 ClickEventArgs nestedEventArgs = new ClickEventArgs();
462 ClickEvent?.Invoke(this, nestedEventArgs);
463 Clicked?.Invoke(this, eventArgs);