[NUI] Initialize feedback instance only when the Feedback property is on.
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / Control.cs
1 /*
2  * Copyright(c) 2019 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 using System;
19 using System.Diagnostics;
20 using System.ComponentModel;
21 using Tizen.NUI.BaseComponents;
22 using Tizen.NUI.Binding;
23 using System.Windows.Input;
24 using Tizen.System;
25
26 namespace Tizen.NUI.Components
27 {
28     /// <summary>
29     /// The control component is base class of tv nui components. It's abstract class, so cann't instantiate and can only be inherited.
30     /// </summary>
31     /// <since_tizen> 6 </since_tizen>
32     /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
33     [EditorBrowsable(EditorBrowsableState.Never)]
34     public class Control : VisualView
35     {
36         /// Internal used.
37         [EditorBrowsable(EditorBrowsableState.Never)]
38         public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(Control), null, propertyChanged: (bo, o, n) => ((Control)bo).OnCommandChanged());
39
40         /// Internal used.
41         [EditorBrowsable(EditorBrowsableState.Never)]
42         public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create(nameof(CommandParameter), typeof(object), typeof(Button), null,
43             propertyChanged: (bindable, oldvalue, newvalue) => ((Button)bindable).CommandCanExecuteChanged(bindable, EventArgs.Empty));
44
45         private bool onThemeChangedEventOverrideChecker;
46
47         private Feedback feedback = null;
48
49         private TapGestureDetector tapGestureDetector = null;
50
51         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
52         [EditorBrowsable(EditorBrowsableState.Never)]
53         public ControlStyle Style => (ControlStyle)ViewStyle.Clone();
54
55         static Control() { }
56
57         /// <summary>
58         /// Construct an empty Control.
59         /// </summary>
60         /// <since_tizen> 6 </since_tizen>
61         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
62         [EditorBrowsable(EditorBrowsableState.Never)]
63         public Control() : this((ControlStyle)null)
64         {
65         }
66
67         /// <summary>
68         /// Construct with style.
69         /// </summary>
70         /// <param name="style">Create control with style.</param>
71         /// <since_tizen> 6 </since_tizen>
72         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
73         [EditorBrowsable(EditorBrowsableState.Never)]
74         public Control(ControlStyle style) : base(style)
75         {
76             Initialize();
77         }
78
79         /// <summary>
80         /// Construct with style name
81         /// </summary>
82         /// <param name="styleName">The name of style in the current theme to be applied</param>
83         /// <since_tizen> 6 </since_tizen>
84         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
85         [EditorBrowsable(EditorBrowsableState.Never)]
86         public Control(string styleName) : this(ThemeManager.GetStyle(styleName) as ControlStyle)
87         {
88             if (ThemeManager.GetStyle(styleName) == null)
89             {
90                 throw new InvalidOperationException($"There is no style {styleName}");
91             }
92
93             this.styleName = styleName;
94             ThemeChangeSensitive = true;
95         }
96
97         /// <summary>
98         /// Enable/Disable a sound feedback when tap gesture detected.
99         /// </summary>
100         [EditorBrowsable(EditorBrowsableState.Never)]
101         public bool Feedback
102         {
103             get => feedback != null;
104             set
105             {
106                 if (value == (feedback != null))
107                 {
108                     return;
109                 }
110
111                 if (value)
112                 {
113                     Debug.Assert(feedback == null && tapGestureDetector == null);
114
115                     tapGestureDetector = new TapGestureDetector();
116                     tapGestureDetector.Attach(this);
117                     tapGestureDetector.Detected += OnTapGestureDetected;
118                     feedback = new Feedback();
119                 }
120                 else
121                 {
122                     Debug.Assert(feedback != null && tapGestureDetector != null);
123
124                     feedback.Stop();
125                     feedback = null;
126
127                     tapGestureDetector.Detected -= OnTapGestureDetected;
128                     tapGestureDetector.Detach(this);
129                     tapGestureDetector = null;
130                 }
131             }
132         }
133
134         /// Internal used.
135         [EditorBrowsable(EditorBrowsableState.Never)]
136         public ICommand Command
137         {
138             get { return (ICommand)GetValue(CommandProperty); }
139             set { SetValue(CommandProperty, value); }
140         }
141
142         /// Internal used.
143         [EditorBrowsable(EditorBrowsableState.Never)]
144         public object CommandParameter
145         {
146             get { return GetValue(CommandParameterProperty); }
147             set { SetValue(CommandParameterProperty, value); }
148         }
149
150         /// <summary>
151         /// Whether focusable when touch
152         /// </summary>
153         /// <since_tizen> 6 </since_tizen>
154         internal bool StateFocusableOnTouchMode { get; set; }
155
156         internal bool IsFocused { get; set; } = false;
157
158         internal void CommandCanExecuteChanged(object sender, EventArgs eventArgs)
159         {
160             ICommand cmd = Command;
161             if (cmd != null)
162                 cmd.CanExecute(CommandParameter);
163         }
164
165         internal void OnCommandChanged()
166         {
167             if (Command != null)
168             {
169                 Command.CanExecuteChanged += CommandCanExecuteChanged;
170                 CommandCanExecuteChanged(this, EventArgs.Empty);
171             }
172         }
173
174         /// <summary>
175         /// Dispose Control and all children on it.
176         /// </summary>
177         /// <param name="type">Dispose type.</param>
178         /// <since_tizen> 6 </since_tizen>
179         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
180         [EditorBrowsable(EditorBrowsableState.Never)]
181         protected override void Dispose(DisposeTypes type)
182         {
183             if (disposed)
184             {
185                 return;
186             }
187
188             if (type == DisposeTypes.Explicit)
189             {
190                 Feedback = false; // Release feedback resources.
191             }
192
193             base.Dispose(type);
194         }
195
196         /// <summary>
197         /// Called after a key event is received by the view that has had its focus set.
198         /// </summary>
199         /// <param name="key">The key event.</param>
200         /// <returns>True if the key event should be consumed.</returns>
201         /// <since_tizen> 6 </since_tizen>
202         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
203         [EditorBrowsable(EditorBrowsableState.Never)]
204         public override bool OnKey(Key key)
205         {
206             return false;
207         }
208
209         /// <summary>
210         /// Called after the size negotiation has been finished for this control.<br />
211         /// The control is expected to assign this given size to itself or its children.<br />
212         /// Should be overridden by derived classes if they need to layout views differently after certain operations like add or remove views, resize, or after changing specific properties.<br />
213         /// As this function is called from inside the size negotiation algorithm, you cannot call RequestRelayout (the call would just be ignored).<br />
214         /// </summary>
215         /// <param name="size">The allocated size.</param>
216         /// <param name="container">The control should add views to this container that it is not able to allocate a size for.</param>
217         /// <since_tizen> 6 </since_tizen>
218         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
219         [EditorBrowsable(EditorBrowsableState.Never)]
220         public override void OnRelayout(Vector2 size, RelayoutContainer container)
221         {
222             base.OnRelayout(size, container);
223             OnUpdate();
224         }
225
226         /// <summary>
227         /// Called when the control gain key input focus. Should be overridden by derived classes if they need to customize what happens when the focus is gained.
228         /// </summary>
229         /// <since_tizen> 6 </since_tizen>
230         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
231         [EditorBrowsable(EditorBrowsableState.Never)]
232         public override void OnFocusGained()
233         {
234             IsFocused = true;
235         }
236
237         /// <summary>
238         /// Called when the control loses key input focus. Should be overridden by derived classes if they need to customize what happens when the focus is lost.
239         /// </summary>
240         /// <since_tizen> 6 </since_tizen>
241         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
242         [EditorBrowsable(EditorBrowsableState.Never)]
243         public override void OnFocusLost()
244         {
245             IsFocused = false;
246         }
247
248         /// <summary>
249         /// Tap gesture callback.
250         /// </summary>
251         /// <param name="source">The sender</param>
252         /// <param name="e">The tap gesture event data</param>
253         /// <since_tizen> 6 </since_tizen>
254         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
255         [EditorBrowsable(EditorBrowsableState.Never)]
256         protected virtual void OnTapGestureDetected(object source, TapGestureDetector.DetectedEventArgs e)
257         {
258             if (Feedback && e?.TapGesture?.State == Gesture.StateType.Started)
259             {
260                 if (feedback != null && feedback.IsSupportedPattern(FeedbackType.Sound, "Tap"))
261                 {
262                     feedback.Play(FeedbackType.Sound, "Tap");
263                 }
264             }
265         }
266
267         /// <summary>
268         /// Update by style.
269         /// </summary>
270         /// <since_tizen> 6 </since_tizen>
271         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
272         [EditorBrowsable(EditorBrowsableState.Never)]
273         protected virtual void OnUpdate()
274         {
275         }
276
277         /// <summary>
278         /// Theme change callback when theme is changed, this callback will be trigger.
279         /// Note that it is deprecated API.Please use OnThemeChanged instead.
280         /// </summary>
281         /// <param name="sender">The sender</param>
282         /// <param name="e">The event data</param>
283         /// <since_tizen> 6 </since_tizen>
284         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
285         [EditorBrowsable(EditorBrowsableState.Never)]
286         protected virtual void OnThemeChangedEvent(object sender, StyleManager.ThemeChangeEventArgs e)
287         {
288             onThemeChangedEventOverrideChecker = false;
289         }
290
291         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
292         [EditorBrowsable(EditorBrowsableState.Never)]
293         protected override ViewStyle CreateViewStyle()
294         {
295             return new ControlStyle();
296         }
297
298         /// <inheritdoc/>
299         [EditorBrowsable(EditorBrowsableState.Never)]
300         protected override void OnThemeChanged(object sender, ThemeChangedEventArgs e)
301         {
302             // TODO Remove checker after update Tizen.FH.NUI.
303             onThemeChangedEventOverrideChecker = true;
304
305             OnThemeChangedEvent(sender, new StyleManager.ThemeChangeEventArgs { CurrentTheme = e.ThemeId });
306
307             if (onThemeChangedEventOverrideChecker) return;
308
309             // If the OnThemeChangedEvent is not implemented, ApplyStyle()
310             base.OnThemeChanged(sender, e);
311         }
312
313         private void Initialize()
314         {
315             LeaveRequired = true;
316
317             StateFocusableOnTouchMode = false;
318
319             EnableControlState = true;
320         }
321     }
322 }