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 /// 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);
190 [EditorBrowsable(EditorBrowsableState.Never)]
191 protected override void OnEnabled(bool enabled)
193 base.OnEnabled(enabled);
198 /// Update Button State.
200 /// <since_tizen> 6 </since_tizen>
201 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
202 [EditorBrowsable(EditorBrowsableState.Never)]
203 protected void UpdateState()
205 if (!styleApplied) return;
207 ControlState sourceState = ControlState;
208 ControlState targetState;
211 targetState = IsEnabled ? ControlState.Normal : ControlState.Disabled;
213 // Selected, DisabledSelected
214 if (IsSelected) targetState += ControlState.Selected;
216 // Pressed, PressedSelected
217 if (isPressed) targetState += ControlState.Pressed;
219 // Focused, FocusedPressed, FocusedPressedSelected, DisabledFocused, DisabledSelectedFocused
220 if (IsFocused) targetState += ControlState.Focused;
222 if (sourceState != targetState)
224 ControlState = targetState;
227 StateChangedEventArgs e = new StateChangedEventArgs
229 PreviousState = ControlStatesExtension.FromControlStateClass(sourceState),
230 CurrentState = ControlStatesExtension.FromControlStateClass(targetState)
232 stateChangeHandler?.Invoke(this, e);
234 Extension?.OnControlStateChanged(this, new ControlStateChangedEventArgs(sourceState, targetState));
239 /// Dispose Button and all children on it.
241 /// <param name="type">Dispose type.</param>
242 /// <since_tizen> 6 </since_tizen>
243 protected override void Dispose(DisposeTypes type)
250 if (type == DisposeTypes.Explicit)
252 Extension?.OnDispose(this);
254 if (buttonIcon != null)
256 Utility.Dispose(buttonIcon);
258 if (buttonText != null)
260 Utility.Dispose(buttonText);
262 if (overlayImage != null)
264 Utility.Dispose(overlayImage);
272 /// Initializes AT-SPI object.
274 [EditorBrowsable(EditorBrowsableState.Never)]
275 public override void OnInitialize()
278 SetAccessibilityConstructor(Role.PushButton);
280 AccessibilityHighlightable = true;
281 EnableControlStatePropagation = true;
283 AccessibilityManager.Instance.SetAccessibilityAttribute(this, AccessibilityManager.AccessibilityAttribute.Trait, "Button");
285 buttonText = CreateText();
286 buttonIcon = CreateIcon();
295 [EditorBrowsable(EditorBrowsableState.Never)]
296 public override void OnRelayout(Vector2 size, RelayoutContainer container)
298 if (size == null) return;
300 if (size.Equals(this.size))
305 this.size = new Vector2(size);
307 UpdateSizeAndSpacing();
311 [EditorBrowsable(EditorBrowsableState.Never)]
312 protected override void OnControlStateChanged(ControlStateChangedEventArgs controlStateChangedInfo)
314 base.OnControlStateChanged(controlStateChangedInfo);
316 var stateEnabled = !controlStateChangedInfo.CurrentState.Contains(ControlState.Disabled);
318 if (IsEnabled != stateEnabled)
320 IsEnabled = stateEnabled;
323 var statePressed = controlStateChangedInfo.CurrentState.Contains(ControlState.Pressed);
325 if (isPressed != statePressed)
327 isPressed = statePressed;
332 /// Put sub items (e.g. buttonText, buttonIcon) to the right place.
334 [EditorBrowsable(EditorBrowsableState.Never)]
335 protected virtual void LayoutItems()
337 if (buttonIcon == null || buttonText == null)
342 buttonIcon.Unparent();
343 buttonText.Unparent();
344 overlayImage?.Unparent();
346 #pragma warning disable CA2000
347 Size2D cellPadding = String.IsNullOrEmpty(buttonText.Text) ? new Size2D(0, 0) : itemSpacing;
348 #pragma warning restore CA2000
350 if (IconRelativeOrientation == IconOrientation.Left)
352 Layout = new LinearLayout()
354 LinearOrientation = LinearLayout.Orientation.Horizontal,
355 LinearAlignment = itemAlignment,
356 CellPadding = cellPadding
362 else if (IconRelativeOrientation == IconOrientation.Right)
364 Layout = new LinearLayout()
366 LinearOrientation = LinearLayout.Orientation.Horizontal,
367 LinearAlignment = itemAlignment,
368 CellPadding = cellPadding
374 else if (IconRelativeOrientation == IconOrientation.Top)
376 Layout = new LinearLayout()
378 LinearOrientation = LinearLayout.Orientation.Vertical,
379 LinearAlignment = itemAlignment,
380 CellPadding = cellPadding
386 else if (IconRelativeOrientation == IconOrientation.Bottom)
388 Layout = new LinearLayout()
390 LinearOrientation = LinearLayout.Orientation.Vertical,
391 LinearAlignment = itemAlignment,
392 CellPadding = cellPadding
399 if (overlayImage != null)
401 overlayImage.ExcludeLayouting = true;
406 private void UpdateSizeAndSpacing()
408 if (size == null || buttonIcon == null || buttonText == null)
413 LinearLayout layout = Layout as LinearLayout;
420 float lengthWithoutText = 0;
421 Size2D cellPadding = null;
422 Extents iconMargin = buttonIcon.Margin ?? new Extents(0);
423 Extents textMargin = buttonText.Margin ?? new Extents(0);
425 if (buttonIcon.Size.Width != 0 && buttonIcon.Size.Height != 0)
427 lengthWithoutText = buttonIcon.Size.Width;
429 if (!String.IsNullOrEmpty(buttonText.Text))
431 cellPadding = itemSpacing;
433 if (iconRelativeOrientation == IconOrientation.Left || iconRelativeOrientation == IconOrientation.Right)
435 lengthWithoutText += (itemSpacing?.Width ?? 0) + iconMargin.Start + iconMargin.End + textMargin.Start + textMargin.End;
439 lengthWithoutText += (itemSpacing?.Height ?? 0) + iconMargin.Top + iconMargin.Bottom + textMargin.Top + textMargin.Bottom;
444 layout.CellPadding = cellPadding ?? new Size2D(0, 0);
446 // If the button has fixed width and the text is not empty, the text should not exceed button boundary.
447 if (WidthSpecification != LayoutParamPolicies.WrapContent && !String.IsNullOrEmpty(buttonText.Text))
449 buttonText.MaximumSize = new Size2D((int)Math.Max(size.Width - lengthWithoutText, Math.Max(buttonText.MinimumSize.Width, 1)), (int)size.Height);
453 private void OnClickedInternal(ClickedEventArgs eventArgs)
455 Command?.Execute(CommandParameter);
456 OnClicked(eventArgs);
457 Extension?.OnClicked(this, eventArgs);
459 ClickEventArgs nestedEventArgs = new ClickEventArgs();
460 ClickEvent?.Invoke(this, nestedEventArgs);
461 Clicked?.Invoke(this, eventArgs);