2 * Copyright(c) 2022 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 styleApplying = false;
40 /// Gets accessibility name.
42 [EditorBrowsable(EditorBrowsableState.Never)]
43 protected override string AccessibilityGetName()
49 /// The ButtonExtension instance that is injected by ButtonStyle.
51 [EditorBrowsable(EditorBrowsableState.Never)]
52 protected ButtonExtension Extension { get; set; }
55 /// Creates Button's text part.
57 /// <return>The created Button's text part.</return>
58 [EditorBrowsable(EditorBrowsableState.Never)]
59 protected virtual TextLabel CreateText()
61 return new TextLabel(new TextLabelStyle())
63 HorizontalAlignment = HorizontalAlignment.Center,
64 VerticalAlignment = VerticalAlignment.Center,
65 AccessibilityHidden = true,
70 /// Creates Button's icon part.
72 /// <return>The created Button's icon part.</return>
73 [EditorBrowsable(EditorBrowsableState.Never)]
74 protected virtual ImageView CreateIcon()
76 return new ImageView()
78 AccessibilityHidden = true,
83 /// Creates Button's overlay image part.
85 /// <return>The created Button's overlay image part.</return>
86 [EditorBrowsable(EditorBrowsableState.Never)]
87 protected virtual ImageView CreateOverlayImage()
91 PositionUsesPivotPoint = true,
92 ParentOrigin = NUI.ParentOrigin.Center,
93 PivotPoint = NUI.PivotPoint.Center,
94 WidthResizePolicy = ResizePolicyType.FillToParent,
95 HeightResizePolicy = ResizePolicyType.FillToParent,
96 AccessibilityHidden = true,
101 /// Called when the Button is Clicked by a user
103 /// <param name="eventArgs">The click information.</param>
104 [EditorBrowsable(EditorBrowsableState.Never)]
105 protected virtual void OnClicked(ClickedEventArgs eventArgs)
110 /// Get Button style.
112 /// <returns>The default button style.</returns>
113 /// <since_tizen> 8 </since_tizen>
114 protected override ViewStyle CreateViewStyle()
116 return new ButtonStyle();
119 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
120 [EditorBrowsable(EditorBrowsableState.Never)]
121 protected override void OnUpdate()
124 Extension?.OnRelayout(this);
128 [EditorBrowsable(EditorBrowsableState.Never)]
129 protected override bool HandleControlStateOnTouch(Touch touch)
131 if (!IsEnabled || null == touch)
136 PointStateType state = touch.GetState(0);
140 case PointStateType.Down:
142 Extension?.SetTouchInfo(touch);
145 case PointStateType.Interrupted:
149 case PointStateType.Up:
160 Extension?.SetTouchInfo(touch);
161 IsSelected = !IsSelected;
165 Extension?.SetTouchInfo(touch);
169 ClickedEventArgs eventArgs = new ClickedEventArgs();
170 OnClickedInternal(eventArgs, touch);
177 return base.HandleControlStateOnTouch(touch);
181 [EditorBrowsable(EditorBrowsableState.Never)]
182 protected override void OnEnabled(bool enabled)
184 base.OnEnabled(enabled);
189 /// Update Button State.
191 /// <since_tizen> 6 </since_tizen>
192 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
193 [EditorBrowsable(EditorBrowsableState.Never)]
194 protected void UpdateState()
196 if (styleApplying) return;
198 ControlState sourceState = ControlState;
199 ControlState targetState;
202 targetState = IsEnabled ? ControlState.Normal : ControlState.Disabled;
204 // Selected, DisabledSelected
205 if (IsSelected) targetState += ControlState.Selected;
207 // Pressed, PressedSelected
208 if (isPressed) targetState += ControlState.Pressed;
210 // Focused, FocusedPressed, FocusedPressedSelected, DisabledFocused, DisabledSelectedFocused
211 if (IsFocused) targetState += ControlState.Focused;
213 if (sourceState != targetState)
215 ControlState = targetState;
218 StateChangedEventArgs e = new StateChangedEventArgs
220 PreviousState = ControlStatesExtension.FromControlStateClass(sourceState),
221 CurrentState = ControlStatesExtension.FromControlStateClass(targetState)
223 stateChangeHandler?.Invoke(this, e);
225 Extension?.OnControlStateChanged(this, new ControlStateChangedEventArgs(sourceState, targetState));
230 /// Dispose Button and all children on it.
232 /// <param name="type">Dispose type.</param>
233 /// <since_tizen> 6 </since_tizen>
234 protected override void Dispose(DisposeTypes type)
241 if (type == DisposeTypes.Explicit)
243 Extension?.OnDispose(this);
245 if (buttonIcon != null)
247 Utility.Dispose(buttonIcon);
249 if (buttonText != null)
251 Utility.Dispose(buttonText);
253 if (overlayImage != null)
255 Utility.Dispose(overlayImage);
263 /// Initializes AT-SPI object.
265 [EditorBrowsable(EditorBrowsableState.Never)]
266 public override void OnInitialize()
270 AccessibilityRole = Role.PushButton;
271 AccessibilityHighlightable = true;
272 EnableControlStatePropagation = true;
274 AccessibilityManager.Instance.SetAccessibilityAttribute(this, AccessibilityManager.AccessibilityAttribute.Trait, "Button");
276 buttonText = CreateText();
277 buttonIcon = CreateIcon();
284 [EditorBrowsable(EditorBrowsableState.Never)]
285 public override void OnRelayout(Vector2 size, RelayoutContainer container)
287 if (size == null) return;
289 if (size.Equals(this.size))
294 this.size = new Vector2(size);
296 UpdateSizeAndSpacing();
300 [EditorBrowsable(EditorBrowsableState.Never)]
301 protected override void OnControlStateChanged(ControlStateChangedEventArgs controlStateChangedInfo)
303 base.OnControlStateChanged(controlStateChangedInfo);
305 var stateEnabled = !controlStateChangedInfo.CurrentState.Contains(ControlState.Disabled);
307 if (IsEnabled != stateEnabled)
309 IsEnabled = stateEnabled;
312 var statePressed = controlStateChangedInfo.CurrentState.Contains(ControlState.Pressed);
314 if (isPressed != statePressed)
316 isPressed = statePressed;
321 var stateSelected = controlStateChangedInfo.CurrentState.Contains(ControlState.Selected);
323 if (IsSelected != stateSelected)
325 IsSelected = stateSelected;
331 /// Put sub items (e.g. buttonText, buttonIcon) to the right place.
333 [EditorBrowsable(EditorBrowsableState.Never)]
334 protected virtual void LayoutItems()
336 if (buttonIcon == null || buttonText == null)
341 buttonIcon.Unparent();
342 buttonText.Unparent();
343 overlayImage?.Unparent();
345 #pragma warning disable CA2000
346 Size2D cellPadding = String.IsNullOrEmpty(buttonText.Text) ? new Size2D(0, 0) : itemSpacing;
347 #pragma warning restore CA2000
349 if (IconRelativeOrientation == IconOrientation.Left)
351 Layout = new LinearLayout()
353 LinearOrientation = LinearLayout.Orientation.Horizontal,
354 HorizontalAlignment = itemHorizontalAlignment,
355 VerticalAlignment = itemVerticalAlignment,
356 CellPadding = cellPadding
362 else if (IconRelativeOrientation == IconOrientation.Right)
364 Layout = new LinearLayout()
366 LinearOrientation = LinearLayout.Orientation.Horizontal,
367 HorizontalAlignment = itemHorizontalAlignment,
368 VerticalAlignment = itemVerticalAlignment,
369 CellPadding = cellPadding
375 else if (IconRelativeOrientation == IconOrientation.Top)
377 Layout = new LinearLayout()
379 LinearOrientation = LinearLayout.Orientation.Vertical,
380 HorizontalAlignment = itemHorizontalAlignment,
381 VerticalAlignment = itemVerticalAlignment,
382 CellPadding = cellPadding
388 else if (IconRelativeOrientation == IconOrientation.Bottom)
390 Layout = new LinearLayout()
392 LinearOrientation = LinearLayout.Orientation.Vertical,
393 HorizontalAlignment = itemHorizontalAlignment,
394 VerticalAlignment = itemVerticalAlignment,
395 CellPadding = cellPadding
402 if (overlayImage != null)
404 overlayImage.ExcludeLayouting = true;
409 private void UpdateSizeAndSpacing()
411 if (size == null || buttonIcon == null || buttonText == null)
416 LinearLayout layout = Layout as LinearLayout;
423 float lengthWithoutText = 0;
424 Size2D cellPadding = null;
425 Extents iconMargin = buttonIcon.Margin ?? new Extents(0);
426 Extents textMargin = buttonText.Margin ?? new Extents(0);
428 if (buttonIcon.Size.Width != 0 && buttonIcon.Size.Height != 0)
430 lengthWithoutText = buttonIcon.Size.Width;
432 if (!String.IsNullOrEmpty(buttonText.Text))
434 cellPadding = itemSpacing;
436 if (iconRelativeOrientation == IconOrientation.Left || iconRelativeOrientation == IconOrientation.Right)
438 lengthWithoutText += (itemSpacing?.Width ?? 0) + iconMargin.Start + iconMargin.End + textMargin.Start + textMargin.End;
442 lengthWithoutText += (itemSpacing?.Height ?? 0) + iconMargin.Top + iconMargin.Bottom + textMargin.Top + textMargin.Bottom;
447 layout.CellPadding = cellPadding ?? new Size2D(0, 0);
449 // If the button has fixed width and the text is not empty, the text should not exceed button boundary.
450 if (WidthSpecification != LayoutParamPolicies.WrapContent && !String.IsNullOrEmpty(buttonText.Text))
452 buttonText.MaximumSize = new Size2D((int)Math.Max(size.Width - lengthWithoutText, Math.Max(buttonText.MinimumSize.Width, 1)), (int)size.Height);
456 private void OnClickedInternal(ClickedEventArgs eventArgs, Touch touch)
458 // If GrabTouchAfterLeave is true, Up will result in Finished rather than Interrupted even if it is out of the button area.
459 // So, it is necessary to check whether it is Up in the button area.
460 if (GrabTouchAfterLeave == true)
462 Vector2 localPosition = touch.GetLocalPosition(0);
463 if ((localPosition != null && Size != null &&
464 0 <= localPosition.X && localPosition.X <= Size.Width &&
465 0 <= localPosition.Y && localPosition.Y <= Size.Height) == false)
470 OnClickedInternal(eventArgs);
473 private void OnClickedInternal(ClickedEventArgs eventArgs)
475 Command?.Execute(CommandParameter);
476 OnClicked(eventArgs);
477 Extension?.OnClicked(this, eventArgs);
479 ClickEventArgs nestedEventArgs = new ClickEventArgs();
480 ClickEvent?.Invoke(this, nestedEventArgs);
481 Clicked?.Invoke(this, eventArgs);