3145db3dc3f9758ff06f8f37187831c648a1ae4f
[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.ComponentModel;
20 using Tizen.NUI.BaseComponents;
21 using Tizen.NUI.Binding;
22 using System.Windows.Input;
23 using Tizen.System;
24
25 namespace Tizen.NUI.Components
26 {
27     /// <summary>
28     /// The control component is base class of tv nui components. It's abstract class, so can't instantiate and can only be inherited.
29     /// </summary>
30     /// <since_tizen> 6 </since_tizen>
31     /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
32     [EditorBrowsable(EditorBrowsableState.Never)]
33     public class Control : VisualView
34     {
35         /// <summary>
36         /// FeedbackProperty
37         /// </summary>
38         [EditorBrowsable(EditorBrowsableState.Never)]
39         public static readonly BindableProperty FeedbackProperty = BindableProperty.Create(nameof(Feedback), typeof(bool), typeof(Control), default(bool), propertyChanged: (bindable, oldValue, newValue) =>
40         {
41             var instance = (Control)bindable;
42             if (newValue != null)
43             {
44                 instance.InternalFeedback = (bool)newValue;
45             }
46         },
47         defaultValueCreator: (bindable) =>
48         {
49             var instance = (Control)bindable;
50             return instance.InternalFeedback;
51         });
52
53         /// Internal used.
54         [EditorBrowsable(EditorBrowsableState.Never)]
55         public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(Control), null, propertyChanged: (bo, o, n) => ((Control)bo).OnCommandChanged());
56
57         /// Internal used.
58         [EditorBrowsable(EditorBrowsableState.Never)]
59         public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create(nameof(CommandParameter), typeof(object), typeof(Button), null,
60             propertyChanged: (bindable, oldvalue, newvalue) => ((Button)bindable).CommandCanExecuteChanged(bindable, EventArgs.Empty));
61
62         private bool onThemeChangedEventOverrideChecker;
63
64         private Feedback feedback = null;
65
66         static Control()
67         {
68             ThemeManager.AddPackageTheme(DefaultThemeCreator.Instance);
69         }
70
71         /// <summary>
72         /// This is used to improve theme performance.
73         /// </summary>
74         [EditorBrowsable(EditorBrowsableState.Never)]
75         static public void Preload()
76         {
77             DefaultThemeCreator.Preload();
78         }
79
80         /// <summary>
81         /// Construct an empty Control.
82         /// </summary>
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() : this((ControlStyle)null)
87         {
88         }
89
90         /// <summary>
91         /// Construct with style.
92         /// </summary>
93         /// <param name="style">Create control with style.</param>
94         /// <since_tizen> 6 </since_tizen>
95         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
96         [EditorBrowsable(EditorBrowsableState.Never)]
97         public Control(ControlStyle style) : base(style)
98         {
99         }
100
101         /// <summary>
102         /// Construct with style name
103         /// </summary>
104         /// <param name="styleName">The name of style in the current theme to be applied</param>
105         /// <since_tizen> 6 </since_tizen>
106         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
107         [EditorBrowsable(EditorBrowsableState.Never)]
108         public Control(string styleName) : base(ThemeManager.GetInitialStyleWithoutClone(styleName) ?? throw new InvalidOperationException($"There is no style {styleName}"))
109         {
110             this.styleName = styleName;
111
112             SetThemeApplied();
113         }
114
115         /// <summary>
116         /// Enable/Disable a sound feedback when tap gesture detected.
117         /// </summary>
118         [EditorBrowsable(EditorBrowsableState.Never)]
119         public bool Feedback
120         {
121             get
122             {
123                 return (bool)GetValue(FeedbackProperty);
124             }
125             set
126             {
127                 SetValue(FeedbackProperty, value);
128                 NotifyPropertyChanged();
129             }
130         }
131         private bool InternalFeedback
132         {
133             get => feedback != null;
134             set
135             {
136                 if (value == (feedback != null))
137                 {
138                     return;
139                 }
140
141                 if (value)
142                 {
143                     try
144                     {
145                         feedback = new Feedback();
146                         this.TouchEvent += OnTouchPlayFeedback;
147                     }
148                     catch (NotSupportedException e)
149                     {
150                         Log.Error("NUI", $"[ERROR] No support of Feedback: {e}");
151                     }
152                     catch (InvalidOperationException e)
153                     {
154                         Log.Error("NUI", $"[ERROR] Fail to initialize Feedback: {e}");
155                     }
156                 }
157                 else
158                 {
159                     this.TouchEvent -= OnTouchPlayFeedback;
160                     feedback = null;
161                 }
162             }
163         }
164
165         private bool OnTouchPlayFeedback(object source, TouchEventArgs e)
166         {
167             if (Feedback && e?.Touch.GetState(0) == PointStateType.Down)
168             {
169                 if (feedback != null && feedback.IsSupportedPattern(FeedbackType.Sound, "Tap"))
170                 {
171                     feedback.Play(FeedbackType.Sound, "Tap");
172                 }
173             }
174             return false;
175         }
176
177         /// Internal used.
178         [EditorBrowsable(EditorBrowsableState.Never)]
179         public ICommand Command
180         {
181             get { return (ICommand)GetValue(CommandProperty); }
182             set { SetValue(CommandProperty, value); }
183         }
184
185         /// Internal used.
186         [EditorBrowsable(EditorBrowsableState.Never)]
187         public object CommandParameter
188         {
189             get { return GetValue(CommandParameterProperty); }
190             set { SetValue(CommandParameterProperty, value); }
191         }
192
193         /// <summary>
194         /// Whether focusable when touch
195         /// </summary>
196         /// <since_tizen> 6 </since_tizen>
197         internal bool StateFocusableOnTouchMode { get; set; }
198
199         internal bool IsFocused { get; set; } = false;
200
201         internal void CommandCanExecuteChanged(object sender, EventArgs eventArgs)
202         {
203             ICommand cmd = Command;
204             if (cmd != null)
205                 cmd.CanExecute(CommandParameter);
206         }
207
208         internal void OnCommandChanged()
209         {
210             if (Command != null)
211             {
212                 Command.CanExecuteChanged += CommandCanExecuteChanged;
213                 CommandCanExecuteChanged(this, EventArgs.Empty);
214             }
215         }
216
217         /// <summary>
218         /// Dispose Control and all children on it.
219         /// </summary>
220         /// <param name="type">Dispose type.</param>
221         /// <since_tizen> 6 </since_tizen>
222         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
223         [EditorBrowsable(EditorBrowsableState.Never)]
224         protected override void Dispose(DisposeTypes type)
225         {
226             if (disposed)
227             {
228                 return;
229             }
230
231             feedback = null;
232             this.TouchEvent -= OnTouchPlayFeedback;
233
234             if (type == DisposeTypes.Explicit)
235             {
236             }
237
238             base.Dispose(type);
239         }
240
241         /// <inheritdoc/>
242         public override void OnInitialize()
243         {
244             base.OnInitialize();
245
246             LeaveRequired = true;
247             StateFocusableOnTouchMode = false;
248             EnableControlState = true;
249         }
250
251         /// <summary>
252         /// Called after a key event is received by the view that has had its focus set.
253         /// </summary>
254         /// <param name="key">The key event.</param>
255         /// <returns>True if the key event should be consumed.</returns>
256         /// <since_tizen> 6 </since_tizen>
257         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
258         [EditorBrowsable(EditorBrowsableState.Never)]
259         public override bool OnKey(Key key)
260         {
261             return false;
262         }
263
264         /// <summary>
265         /// Called after the size negotiation has been finished for this control.<br />
266         /// The control is expected to assign this given size to itself or its children.<br />
267         /// 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 />
268         /// As this function is called from inside the size negotiation algorithm, you cannot call RequestRelayout (the call would just be ignored).<br />
269         /// </summary>
270         /// <param name="size">The allocated size.</param>
271         /// <param name="container">The control should add views to this container that it is not able to allocate a size for.</param>
272         /// <since_tizen> 6 </since_tizen>
273         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
274         [EditorBrowsable(EditorBrowsableState.Never)]
275         public override void OnRelayout(Vector2 size, RelayoutContainer container)
276         {
277             base.OnRelayout(size, container);
278             OnUpdate();
279         }
280
281         /// <summary>
282         /// 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.
283         /// </summary>
284         /// <since_tizen> 6 </since_tizen>
285         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
286         [EditorBrowsable(EditorBrowsableState.Never)]
287         public override void OnFocusGained()
288         {
289             IsFocused = true;
290         }
291
292         /// <summary>
293         /// 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.
294         /// </summary>
295         /// <since_tizen> 6 </since_tizen>
296         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
297         [EditorBrowsable(EditorBrowsableState.Never)]
298         public override void OnFocusLost()
299         {
300             IsFocused = false;
301         }
302
303         /// <summary>
304         /// Update by style.
305         /// </summary>
306         /// <since_tizen> 6 </since_tizen>
307         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
308         [EditorBrowsable(EditorBrowsableState.Never)]
309         protected virtual void OnUpdate()
310         {
311         }
312
313         /// <summary>
314         /// Theme change callback when theme is changed, this callback will be trigger.
315         /// Note that it is deprecated API.Please use OnThemeChanged instead.
316         /// </summary>
317         /// <param name="sender">The sender</param>
318         /// <param name="e">The event data</param>
319         /// <since_tizen> 6 </since_tizen>
320         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
321         [EditorBrowsable(EditorBrowsableState.Never)]
322         protected virtual void OnThemeChangedEvent(object sender, StyleManager.ThemeChangeEventArgs e)
323         {
324             onThemeChangedEventOverrideChecker = false;
325         }
326
327         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
328         [EditorBrowsable(EditorBrowsableState.Never)]
329         protected override ViewStyle CreateViewStyle()
330         {
331             return new ControlStyle();
332         }
333
334         /// <inheritdoc/>
335         [EditorBrowsable(EditorBrowsableState.Never)]
336         protected override void OnThemeChanged(object sender, ThemeChangedEventArgs e)
337         {
338             // TODO Remove checker after update Tizen.FH.NUI.
339             onThemeChangedEventOverrideChecker = true;
340
341             OnThemeChangedEvent(sender, new StyleManager.ThemeChangeEventArgs { CurrentTheme = e.ThemeId });
342
343             if (onThemeChangedEventOverrideChecker) return;
344
345             // If the OnThemeChangedEvent is not implemented, ApplyStyle()
346             base.OnThemeChanged(sender, e);
347         }
348
349         /// <summary>
350         /// when the derived class of Control is used as container and itself is not Focusable, this can be used when calling SetCurrentFocusView()
351         /// this can return Focusable View inside of itself. this can be utilized when default algorithm is enabled and when the case of setting first key focus in container.
352         /// </summary>
353         /// <returns>Focusable View inside of container</returns>
354         [EditorBrowsable(EditorBrowsableState.Never)]
355         protected internal virtual View PassFocusableViewInsideIfNeeded()
356         {
357             return this;
358         }
359
360     }
361 }