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();
286 [EditorBrowsable(EditorBrowsableState.Never)]
287 public override void OnRelayout(Vector2 size, RelayoutContainer container)
289 if (size == null) return;
291 if (size.Equals(this.size))
296 this.size = new Vector2(size);
298 UpdateSizeAndSpacing();
302 [EditorBrowsable(EditorBrowsableState.Never)]
303 protected override void OnControlStateChanged(ControlStateChangedEventArgs controlStateChangedInfo)
305 base.OnControlStateChanged(controlStateChangedInfo);
307 var stateEnabled = !controlStateChangedInfo.CurrentState.Contains(ControlState.Disabled);
309 if (IsEnabled != stateEnabled)
311 IsEnabled = stateEnabled;
314 var statePressed = controlStateChangedInfo.CurrentState.Contains(ControlState.Pressed);
316 if (isPressed != statePressed)
318 isPressed = statePressed;
323 /// Put sub items (e.g. buttonText, buttonIcon) to the right place.
325 [EditorBrowsable(EditorBrowsableState.Never)]
326 protected virtual void LayoutItems()
328 if (buttonIcon == null || buttonText == null)
333 buttonIcon.Unparent();
334 buttonText.Unparent();
335 overlayImage?.Unparent();
337 #pragma warning disable CA2000
338 Size2D cellPadding = String.IsNullOrEmpty(buttonText.Text) ? new Size2D(0, 0) : itemSpacing;
339 #pragma warning restore CA2000
341 if (IconRelativeOrientation == IconOrientation.Left)
343 Layout = new LinearLayout()
345 LinearOrientation = LinearLayout.Orientation.Horizontal,
346 HorizontalAlignment = itemHorizontalAlignment,
347 VerticalAlignment = itemVerticalAlignment,
348 CellPadding = cellPadding
354 else if (IconRelativeOrientation == IconOrientation.Right)
356 Layout = new LinearLayout()
358 LinearOrientation = LinearLayout.Orientation.Horizontal,
359 HorizontalAlignment = itemHorizontalAlignment,
360 VerticalAlignment = itemVerticalAlignment,
361 CellPadding = cellPadding
367 else if (IconRelativeOrientation == IconOrientation.Top)
369 Layout = new LinearLayout()
371 LinearOrientation = LinearLayout.Orientation.Vertical,
372 HorizontalAlignment = itemHorizontalAlignment,
373 VerticalAlignment = itemVerticalAlignment,
374 CellPadding = cellPadding
380 else if (IconRelativeOrientation == IconOrientation.Bottom)
382 Layout = new LinearLayout()
384 LinearOrientation = LinearLayout.Orientation.Vertical,
385 HorizontalAlignment = itemHorizontalAlignment,
386 VerticalAlignment = itemVerticalAlignment,
387 CellPadding = cellPadding
394 if (overlayImage != null)
396 overlayImage.ExcludeLayouting = true;
401 private void UpdateSizeAndSpacing()
403 if (size == null || buttonIcon == null || buttonText == null)
408 LinearLayout layout = Layout as LinearLayout;
415 float lengthWithoutText = 0;
416 Size2D cellPadding = null;
417 Extents iconMargin = buttonIcon.Margin ?? new Extents(0);
418 Extents textMargin = buttonText.Margin ?? new Extents(0);
420 if (buttonIcon.Size.Width != 0 && buttonIcon.Size.Height != 0)
422 lengthWithoutText = buttonIcon.Size.Width;
424 if (!String.IsNullOrEmpty(buttonText.Text))
426 cellPadding = itemSpacing;
428 if (iconRelativeOrientation == IconOrientation.Left || iconRelativeOrientation == IconOrientation.Right)
430 lengthWithoutText += (itemSpacing?.Width ?? 0) + iconMargin.Start + iconMargin.End + textMargin.Start + textMargin.End;
434 lengthWithoutText += (itemSpacing?.Height ?? 0) + iconMargin.Top + iconMargin.Bottom + textMargin.Top + textMargin.Bottom;
439 layout.CellPadding = cellPadding ?? new Size2D(0, 0);
441 // If the button has fixed width and the text is not empty, the text should not exceed button boundary.
442 if (WidthSpecification != LayoutParamPolicies.WrapContent && !String.IsNullOrEmpty(buttonText.Text))
444 buttonText.MaximumSize = new Size2D((int)Math.Max(size.Width - lengthWithoutText, Math.Max(buttonText.MinimumSize.Width, 1)), (int)size.Height);
448 private void OnClickedInternal(ClickedEventArgs eventArgs, Touch touch)
450 // If GrabTouchAfterLeave is true, Up will result in Finished rather than Interrupted even if it is out of the button area.
451 // So, it is necessary to check whether it is Up in the button area.
452 if (GrabTouchAfterLeave == true)
454 Vector2 localPosition = touch.GetLocalPosition(0);
455 if ((localPosition != null && Size != null &&
456 0 <= localPosition.X && localPosition.X <= Size.Width &&
457 0 <= localPosition.Y && localPosition.Y <= Size.Height) == false)
462 OnClickedInternal(eventArgs);
465 private void OnClickedInternal(ClickedEventArgs eventArgs)
467 Command?.Execute(CommandParameter);
468 OnClicked(eventArgs);
469 Extension?.OnClicked(this, eventArgs);
471 ClickEventArgs nestedEventArgs = new ClickEventArgs();
472 ClickEvent?.Invoke(this, nestedEventArgs);
473 Clicked?.Invoke(this, eventArgs);