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 internal int styleApplying = 0;
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 > 0) 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 if (Extension != null)
245 Extension.OnDispose(this);
249 if (buttonIcon != null)
251 Utility.Dispose(buttonIcon);
254 if (buttonText != null)
256 Utility.Dispose(buttonText);
259 if (overlayImage != null)
261 Utility.Dispose(overlayImage);
270 /// Initializes AT-SPI object.
272 [EditorBrowsable(EditorBrowsableState.Never)]
273 public override void OnInitialize()
277 AccessibilityRole = Role.PushButton;
278 AccessibilityHighlightable = true;
279 EnableControlStatePropagation = true;
281 AccessibilityManager.Instance.SetAccessibilityAttribute(this, AccessibilityManager.AccessibilityAttribute.Trait, "Button");
283 buttonText = CreateText();
284 buttonIcon = CreateIcon();
291 [EditorBrowsable(EditorBrowsableState.Never)]
292 public override void OnRelayout(Vector2 size, RelayoutContainer container)
294 if (size == null) return;
296 if (size.Equals(this.size))
301 this.size = new Vector2(size);
303 UpdateSizeAndSpacing();
307 [EditorBrowsable(EditorBrowsableState.Never)]
308 protected override void OnControlStateChanged(ControlStateChangedEventArgs controlStateChangedInfo)
310 base.OnControlStateChanged(controlStateChangedInfo);
312 var stateEnabled = !controlStateChangedInfo.CurrentState.Contains(ControlState.Disabled);
314 if (IsEnabled != stateEnabled)
316 IsEnabled = stateEnabled;
319 var statePressed = controlStateChangedInfo.CurrentState.Contains(ControlState.Pressed);
321 if (isPressed != statePressed)
323 isPressed = statePressed;
328 var stateSelected = controlStateChangedInfo.CurrentState.Contains(ControlState.Selected);
330 if (IsSelected != stateSelected)
332 IsSelected = stateSelected;
338 /// Put sub items (e.g. buttonText, buttonIcon) to the right place.
340 [EditorBrowsable(EditorBrowsableState.Never)]
341 protected virtual void LayoutItems()
343 if (buttonIcon == null || buttonText == null)
348 buttonIcon.Unparent();
349 buttonText.Unparent();
350 overlayImage?.Unparent();
352 #pragma warning disable CA2000
353 Size2D cellPadding = String.IsNullOrEmpty(buttonText.Text) ? new Size2D(0, 0) : itemSpacing;
354 #pragma warning restore CA2000
356 var linearLayout = Layout as LinearLayout;
357 if (linearLayout == null) Layout = (linearLayout = new LinearLayout());
359 if (IconRelativeOrientation == IconOrientation.Left)
361 linearLayout.LinearOrientation = LinearLayout.Orientation.Horizontal;
362 linearLayout.HorizontalAlignment = itemHorizontalAlignment;
363 linearLayout.VerticalAlignment = itemVerticalAlignment;
364 linearLayout.CellPadding = cellPadding;
369 else if (IconRelativeOrientation == IconOrientation.Right)
371 linearLayout.LinearOrientation = LinearLayout.Orientation.Horizontal;
372 linearLayout.HorizontalAlignment = itemHorizontalAlignment;
373 linearLayout.VerticalAlignment = itemVerticalAlignment;
374 linearLayout.CellPadding = cellPadding;
379 else if (IconRelativeOrientation == IconOrientation.Top)
381 linearLayout.LinearOrientation = LinearLayout.Orientation.Vertical;
382 linearLayout.HorizontalAlignment = itemHorizontalAlignment;
383 linearLayout.VerticalAlignment = itemVerticalAlignment;
384 linearLayout.CellPadding = cellPadding;
389 else if (IconRelativeOrientation == IconOrientation.Bottom)
391 linearLayout.LinearOrientation = LinearLayout.Orientation.Vertical;
392 linearLayout.HorizontalAlignment = itemHorizontalAlignment;
393 linearLayout.VerticalAlignment = itemVerticalAlignment;
394 linearLayout.CellPadding = cellPadding;
400 if (overlayImage != null)
402 overlayImage.ExcludeLayouting = true;
407 private void UpdateSizeAndSpacing()
409 if (size == null || buttonIcon == null || buttonText == null)
414 LinearLayout layout = Layout as LinearLayout;
421 float lengthWithoutText = 0;
422 Size2D cellPadding = null;
423 Extents iconMargin = buttonIcon.Margin ?? new Extents(0);
424 Extents textMargin = buttonText.Margin ?? new Extents(0);
426 if (buttonIcon.Size.Width != 0 && buttonIcon.Size.Height != 0)
428 lengthWithoutText = buttonIcon.Size.Width;
430 if (!String.IsNullOrEmpty(buttonText.Text))
432 cellPadding = itemSpacing;
434 if (iconRelativeOrientation == IconOrientation.Left || iconRelativeOrientation == IconOrientation.Right)
436 lengthWithoutText += (itemSpacing?.Width ?? 0) + iconMargin.Start + iconMargin.End + textMargin.Start + textMargin.End + Padding.Start + Padding.End;
440 lengthWithoutText += (itemSpacing?.Height ?? 0) + iconMargin.Top + iconMargin.Bottom + textMargin.Top + textMargin.Bottom + Padding.Top + Padding.Bottom;
445 layout.CellPadding = cellPadding ?? new Size2D(0, 0);
447 // If the button has fixed width and the text is not empty, the text should not exceed button boundary.
448 if (WidthSpecification != LayoutParamPolicies.WrapContent && !String.IsNullOrEmpty(buttonText.Text))
450 buttonText.MaximumSize = new Size2D((int)Math.Max(size.Width - lengthWithoutText, Math.Max(buttonText.MinimumSize.Width, 1)), (int)size.Height);
454 private void OnClickedInternal(ClickedEventArgs eventArgs, Touch touch)
456 // If GrabTouchAfterLeave is true, Up will result in Finished rather than Interrupted even if it is out of the button area.
457 // So, it is necessary to check whether it is Up in the button area.
458 if (GrabTouchAfterLeave == true)
460 Vector2 localPosition = touch.GetLocalPosition(0);
461 if ((localPosition != null && Size != null &&
462 0 <= localPosition.X && localPosition.X <= Size.Width &&
463 0 <= localPosition.Y && localPosition.Y <= Size.Height) == false)
468 OnClickedInternal(eventArgs);
471 private void OnClickedInternal(ClickedEventArgs eventArgs)
473 Command?.Execute(CommandParameter);
474 OnClicked(eventArgs);
475 Extension?.OnClicked(this, eventArgs);
477 ClickEventArgs nestedEventArgs = new ClickEventArgs();
478 ClickEvent?.Invoke(this, nestedEventArgs);
479 Clicked?.Invoke(this, eventArgs);