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 styleApplied = 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 (!styleApplied) 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 /// Put sub items (e.g. buttonText, buttonIcon) to the right place.
323 [EditorBrowsable(EditorBrowsableState.Never)]
324 protected virtual void LayoutItems()
326 if (buttonIcon == null || buttonText == null)
331 buttonIcon.Unparent();
332 buttonText.Unparent();
333 overlayImage?.Unparent();
335 #pragma warning disable CA2000
336 Size2D cellPadding = String.IsNullOrEmpty(buttonText.Text) ? new Size2D(0, 0) : itemSpacing;
337 #pragma warning restore CA2000
339 if (IconRelativeOrientation == IconOrientation.Left)
341 Layout = new LinearLayout()
343 LinearOrientation = LinearLayout.Orientation.Horizontal,
344 HorizontalAlignment = itemHorizontalAlignment,
345 VerticalAlignment = itemVerticalAlignment,
346 CellPadding = cellPadding
352 else if (IconRelativeOrientation == IconOrientation.Right)
354 Layout = new LinearLayout()
356 LinearOrientation = LinearLayout.Orientation.Horizontal,
357 HorizontalAlignment = itemHorizontalAlignment,
358 VerticalAlignment = itemVerticalAlignment,
359 CellPadding = cellPadding
365 else if (IconRelativeOrientation == IconOrientation.Top)
367 Layout = new LinearLayout()
369 LinearOrientation = LinearLayout.Orientation.Vertical,
370 HorizontalAlignment = itemHorizontalAlignment,
371 VerticalAlignment = itemVerticalAlignment,
372 CellPadding = cellPadding
378 else if (IconRelativeOrientation == IconOrientation.Bottom)
380 Layout = new LinearLayout()
382 LinearOrientation = LinearLayout.Orientation.Vertical,
383 HorizontalAlignment = itemHorizontalAlignment,
384 VerticalAlignment = itemVerticalAlignment,
385 CellPadding = cellPadding
392 if (overlayImage != null)
394 overlayImage.ExcludeLayouting = true;
399 private void UpdateSizeAndSpacing()
401 if (size == null || buttonIcon == null || buttonText == null)
406 LinearLayout layout = Layout as LinearLayout;
413 float lengthWithoutText = 0;
414 Size2D cellPadding = null;
415 Extents iconMargin = buttonIcon.Margin ?? new Extents(0);
416 Extents textMargin = buttonText.Margin ?? new Extents(0);
418 if (buttonIcon.Size.Width != 0 && buttonIcon.Size.Height != 0)
420 lengthWithoutText = buttonIcon.Size.Width;
422 if (!String.IsNullOrEmpty(buttonText.Text))
424 cellPadding = itemSpacing;
426 if (iconRelativeOrientation == IconOrientation.Left || iconRelativeOrientation == IconOrientation.Right)
428 lengthWithoutText += (itemSpacing?.Width ?? 0) + iconMargin.Start + iconMargin.End + textMargin.Start + textMargin.End;
432 lengthWithoutText += (itemSpacing?.Height ?? 0) + iconMargin.Top + iconMargin.Bottom + textMargin.Top + textMargin.Bottom;
437 layout.CellPadding = cellPadding ?? new Size2D(0, 0);
439 // If the button has fixed width and the text is not empty, the text should not exceed button boundary.
440 if (WidthSpecification != LayoutParamPolicies.WrapContent && !String.IsNullOrEmpty(buttonText.Text))
442 buttonText.MaximumSize = new Size2D((int)Math.Max(size.Width - lengthWithoutText, Math.Max(buttonText.MinimumSize.Width, 1)), (int)size.Height);
446 private void OnClickedInternal(ClickedEventArgs eventArgs, Touch touch)
448 // If GrabTouchAfterLeave is true, Up will result in Finished rather than Interrupted even if it is out of the button area.
449 // So, it is necessary to check whether it is Up in the button area.
450 if (GrabTouchAfterLeave == true)
452 Vector2 localPosition = touch.GetLocalPosition(0);
453 if ((localPosition != null && Size != null &&
454 0 <= localPosition.X && localPosition.X <= Size.Width &&
455 0 <= localPosition.Y && localPosition.Y <= Size.Height) == false)
460 OnClickedInternal(eventArgs);
463 private void OnClickedInternal(ClickedEventArgs eventArgs)
465 Command?.Execute(CommandParameter);
466 OnClicked(eventArgs);
467 Extension?.OnClicked(this, eventArgs);
469 ClickEventArgs nestedEventArgs = new ClickEventArgs();
470 ClickEvent?.Invoke(this, nestedEventArgs);
471 Clicked?.Invoke(this, eventArgs);