59a9cda07418652a1309aad652cb03689713270e
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / XamlBinding / Element.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Collections.ObjectModel;
4 using System.Collections.Specialized;
5 using System.ComponentModel;
6 using System.Runtime.CompilerServices;
7 using System.Xml;
8 using Tizen.NUI.Binding.Internals;
9
10 namespace Tizen.NUI.Binding
11 {
12     /// <summary>
13     /// Provides the base class for all Tizen.NUI.Binding hierarchal elements. This class contains all the methods and properties required to represent an element in the Tizen.NUI.Binding hierarchy.
14     /// </summary>
15     [EditorBrowsable(EditorBrowsableState.Never)]
16     public abstract partial class Element : BindableObject, IElement, INameScope, IElementController
17     {
18
19         // public static readonly BindableProperty MenuProperty = BindableProperty.CreateAttached(nameof(Menu), typeof(Menu), typeof(Element), null);
20
21         // public static Menu GetMenu(BindableObject bindable)
22         // {
23         //   return (Menu)bindable.GetValue(MenuProperty);
24         // }
25
26         // public static void SetMenu(BindableObject bindable, Menu menu)
27         // {
28         //   bindable.SetValue(MenuProperty, menu);
29         // }
30
31         internal static readonly ReadOnlyCollection<Element> EmptyChildren = new ReadOnlyCollection<Element>(new Element[0]);
32
33         /// <summary>
34         /// Identifies the ClassId bindable property.
35         /// </summary>
36         internal static readonly BindableProperty ClassIdProperty = BindableProperty.Create("ClassId", typeof(string), typeof(Tizen.NUI.BaseComponents.View), null);
37
38         string _automationId;
39
40         IList<BindableObject> _bindableResources;
41
42         List<Action<object, ResourcesChangedEventArgs>> _changeHandlers;
43
44         Dictionary<BindableProperty, string> _dynamicResources;
45
46         IEffectControlProvider _effectControlProvider;
47
48         TrackableCollection<Effect> _effects;
49
50         Guid? _id;
51
52         Element _parentOverride;
53
54         IPlatform _platform;
55
56         string _styleId;
57
58         /// <summary>
59         /// Gets or sets a value that allows the automation framework to find and interact with this element.
60         /// </summary>
61         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
62         [EditorBrowsable(EditorBrowsableState.Never)]
63         public string AutomationId
64         {
65             get { return _automationId; }
66             set
67             {
68                 if (_automationId != null)
69                     throw new InvalidOperationException("AutomationId may only be set one time");
70                 _automationId = value;
71             }
72         }
73
74         /// <summary>
75         /// Gets or sets a value used to identify a collection of semantically similar elements.
76         /// </summary>
77         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
78         [EditorBrowsable(EditorBrowsableState.Never)]
79         public string ClassId
80         {
81             get { return (string)GetValue(ClassIdProperty); }
82             set { SetValue(ClassIdProperty, value); }
83         }
84
85         internal IList<Effect> Effects
86         {
87             get
88             {
89                 if (_effects == null)
90                 {
91                     _effects = new TrackableCollection<Effect>();
92                     _effects.CollectionChanged += EffectsOnCollectionChanged;
93                     _effects.Clearing += EffectsOnClearing;
94                 }
95                 return _effects;
96             }
97         }
98
99         /// <summary>
100         /// Gets a value that can be used to uniquely identify an element through the run of an application.
101         /// </summary>
102         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
103         [EditorBrowsable(EditorBrowsableState.Never)]
104         public Guid Id
105         {
106             get
107             {
108                 if (!_id.HasValue)
109                     _id = Guid.NewGuid();
110                 return _id.Value;
111             }
112         }
113
114         /// <summary>
115         /// Gets the element which is the closest ancestor of this element that is a BaseHandle.
116         /// </summary>
117         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
118         [EditorBrowsable(EditorBrowsableState.Never)]
119         [Obsolete("ParentView is obsolete as of version 2.1.0. Please use Parent instead.")]
120         public BaseHandle ParentView
121         {
122             get
123             {
124                 Element parent = Parent;
125                 while (parent != null)
126                 {
127                     var parentView = parent as BaseHandle;
128                     if (parentView != null)
129                         return parentView;
130                     parent = parent.RealParent;
131                 }
132                 return null;
133             }
134         }
135
136         /// <summary>
137         /// Gets or sets a user defined value to uniquely identify the element.
138         /// </summary>
139         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
140         [EditorBrowsable(EditorBrowsableState.Never)]
141         public string StyleId
142         {
143             get { return _styleId; }
144             set
145             {
146                 if (_styleId == value)
147                     return;
148
149                 OnPropertyChanging();
150                 _styleId = value;
151                 OnPropertyChanged();
152             }
153         }
154
155         internal virtual ReadOnlyCollection<Element> LogicalChildrenInternal => EmptyChildren;
156
157         /// <summary>
158         /// For internal use.
159         /// </summary>
160         [EditorBrowsable(EditorBrowsableState.Never)]
161         public ReadOnlyCollection<Element> LogicalChildren => LogicalChildrenInternal;
162
163         internal bool Owned { get; set; }
164
165         internal Element ParentOverride
166         {
167             get { return _parentOverride; }
168             set
169             {
170                 if (_parentOverride == value)
171                     return;
172
173                 bool emitChange = Parent != value;
174
175                 if (emitChange)
176                     OnPropertyChanging(nameof(Parent));
177
178                 _parentOverride = value;
179
180                 if (emitChange)
181                     OnPropertyChanged(nameof(Parent));
182             }
183         }
184
185         /// <summary>
186         /// For internal use.
187         /// </summary>
188         internal IPlatform Platform
189         {
190             get
191             {
192                 if (_platform == null && RealParent != null)
193                     return RealParent.Platform;
194                 return _platform;
195             }
196             set
197             {
198                 if (_platform == value)
199                     return;
200                 _platform = value;
201                 PlatformSet?.Invoke(this, EventArgs.Empty);
202                 foreach (Element descendant in Descendants())
203                 {
204                     descendant._platform = _platform;
205                     descendant.PlatformSet?.Invoke(this, EventArgs.Empty);
206                 }
207             }
208         }
209
210         /// <summary>
211         /// For internal use.
212         /// </summary>
213         [EditorBrowsable(EditorBrowsableState.Never)]
214         public Element RealParent { get; private set; }
215
216         Dictionary<BindableProperty, string> DynamicResources
217         {
218             get { return _dynamicResources ?? (_dynamicResources = new Dictionary<BindableProperty, string>()); }
219         }
220
221         void IElement.AddResourcesChangedListener(Action<object, ResourcesChangedEventArgs> onchanged)
222         {
223             _changeHandlers = _changeHandlers ?? new List<Action<object, ResourcesChangedEventArgs>>(2);
224             _changeHandlers.Add(onchanged);
225         }
226
227         /// <summary>
228         /// Gets or sets the parent element of the element.
229         /// </summary>
230         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
231         [EditorBrowsable(EditorBrowsableState.Never)]
232         public Element Parent
233         {
234             get { return _parentOverride ?? RealParent; }
235             set
236             {
237                 if (RealParent == value)
238                     return;
239
240                 OnPropertyChanging();
241
242                 if (RealParent != null)
243                     ((IElement)RealParent).RemoveResourcesChangedListener(OnParentResourcesChanged);
244                 RealParent = value;
245                 if (RealParent != null)
246                 {
247                     OnParentResourcesChanged(RealParent?.GetMergedResources());
248                     ((IElement)RealParent).AddResourcesChangedListener(OnParentResourcesChanged);
249                 }
250
251                 object context = value != null ? value.BindingContext : null;
252                 if (value != null)
253                 {
254                     value.SetChildInheritedBindingContext(this, context);
255                 }
256                 else
257                 {
258                     SetInheritedBindingContext(this, null);
259                 }
260
261                 OnParentSet();
262
263                 if (RealParent != null)
264                 {
265                     IPlatform platform = RealParent.Platform;
266                     if (platform != null)
267                         Platform = platform;
268                 }
269
270                 OnPropertyChanged();
271             }
272         }
273
274         void IElement.RemoveResourcesChangedListener(Action<object, ResourcesChangedEventArgs> onchanged)
275         {
276             if (_changeHandlers == null)
277                 return;
278             _changeHandlers.Remove(onchanged);
279         }
280
281         /// <summary>
282         /// For internal use.
283         /// </summary>
284         internal IEffectControlProvider EffectControlProvider
285         {
286             get { return _effectControlProvider; }
287             set
288             {
289                 if (_effectControlProvider == value)
290                     return;
291                 if (_effectControlProvider != null && _effects != null)
292                 {
293                     foreach (Effect effect in _effects)
294                         effect?.SendDetached();
295                 }
296                 _effectControlProvider = value;
297                 if (_effectControlProvider != null && _effects != null)
298                 {
299                     foreach (Effect effect in _effects)
300                     {
301                         if (effect != null)
302                             AttachEffect(effect);
303                     }
304                 }
305             }
306         }
307
308         //void IElementController.SetValueFromRenderer(BindableProperty property, object value) => SetValueFromRenderer(property, value);
309
310         /// <summary>
311         /// Sets the value of the specified property.
312         /// </summary>
313         /// <param name="property">The BindableProperty on which to assign a value.</param>
314         /// <param name="value">The value to set.</param>
315         internal void SetValueFromRenderer(BindableProperty property, object value)
316         {
317             SetValueCore(property, value);
318         }
319
320         /// <summary>
321         /// Sets the value of the propertyKey.
322         /// </summary>
323         /// <param name="property">The BindablePropertyKey on which to assign a value.</param>
324         /// <param name="value">The value to set.</param>
325         internal void SetValueFromRenderer(BindablePropertyKey property, object value)
326         {
327             SetValueCore(property, value);
328         }
329
330         /// <summary>
331         /// For internal use.
332         /// </summary>
333         /// <param name="name">The nameof the effect</param>
334         /// <returns>true if attached</returns>
335         [EditorBrowsable(EditorBrowsableState.Never)]
336         public bool EffectIsAttached(string name)
337         {
338             foreach (var effect in Effects)
339             {
340                 if (effect.ResolveId == name)
341                     return true;
342             }
343             return false;
344         }
345
346         object INameScope.FindByName(string name)
347         {
348             INameScope namescope = GetNameScope();
349             if (namescope == null)
350             {
351                 return null;
352             }
353             else
354             {
355                 return namescope.FindByName(name);
356             }
357         }
358
359         void INameScope.RegisterName(string name, object scopedElement)
360         {
361             INameScope namescope = GetNameScope();
362             if (namescope == null)
363                 throw new InvalidOperationException("this element is not in a namescope");
364             namescope.RegisterName(name, scopedElement);
365         }
366
367         [Obsolete]
368         void INameScope.RegisterName(string name, object scopedElement, IXmlLineInfo xmlLineInfo)
369         {
370             INameScope namescope = GetNameScope();
371             if (namescope == null)
372                 throw new InvalidOperationException("this element is not in a namescope");
373             namescope.RegisterName(name, scopedElement, xmlLineInfo);
374         }
375
376         void INameScope.UnregisterName(string name)
377         {
378             INameScope namescope = GetNameScope();
379             if (namescope == null)
380                 throw new InvalidOperationException("this element is not in a namescope");
381             namescope.UnregisterName(name);
382         }
383
384         internal event EventHandler<ElementEventArgs> ChildAdded;
385
386         internal event EventHandler<ElementEventArgs> ChildRemoved;
387
388         internal event EventHandler<ElementEventArgs> DescendantAdded;
389
390         internal event EventHandler<ElementEventArgs> DescendantRemoved;
391
392         /// <summary>
393         /// Removes a previously set dynamic resource.
394         /// </summary>
395         /// <param name="property">The BindableProperty from which to remove the DynamicResource.</param>
396         internal new void RemoveDynamicResource(BindableProperty property)
397         {
398             base.RemoveDynamicResource(property);
399         }
400
401         /// <summary>
402         /// Sets the BindableProperty property of this element to be updated via the DynamicResource with the provided key.
403         /// </summary>
404         /// <param name="property">The BindableProperty.</param>
405         /// <param name="key">The key of the DynamicResource</param>
406         internal new void SetDynamicResource(BindableProperty property, string key)
407         {
408             base.SetDynamicResource(property, key);
409         }
410
411         /// <summary>
412         /// Invoked whenever the binding context of the element changes. Implement this method to add class handling for this event.
413         /// </summary>
414         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
415         [EditorBrowsable(EditorBrowsableState.Never)]
416         protected override void OnBindingContextChanged()
417         {
418             var gotBindingContext = false;
419             object bc = null;
420
421             for (var index = 0; index < LogicalChildrenInternal.Count; index++)
422             {
423                 Element child = LogicalChildrenInternal[index];
424
425                 if (!gotBindingContext)
426                 {
427                     bc = BindingContext;
428                     gotBindingContext = true;
429                 }
430
431                 SetChildInheritedBindingContext(child, bc);
432             }
433
434             if (_bindableResources != null)
435                 foreach (BindableObject item in _bindableResources)
436                 {
437                     SetInheritedBindingContext(item, BindingContext);
438                 }
439
440             base.OnBindingContextChanged();
441         }
442
443         /// <summary>
444         /// Invoked whenever the ChildAdded event needs to be emitted.Implement this method to add class handling for this event.
445         /// </summary>
446         /// <param name="child">The element that was added.</param>
447         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
448         [EditorBrowsable(EditorBrowsableState.Never)]
449         protected virtual void OnChildAdded(Element child)
450         {
451             child.Parent = this;
452             if (Platform != null)
453                 child.Platform = Platform;
454
455             child.ApplyBindings(skipBindingContext: false, fromBindingContextChanged:true);
456
457             ChildAdded?.Invoke(this, new ElementEventArgs(child));
458
459             OnDescendantAdded(child);
460             foreach (Element element in child.Descendants())
461                 OnDescendantAdded(element);
462         }
463
464         /// <summary>
465         /// Invoked whenever the ChildRemoved event needs to be emitted.Implement this method to add class handling for this event.
466         /// </summary>
467         /// <param name="child">The element that was removed.</param>
468         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
469         [EditorBrowsable(EditorBrowsableState.Never)]
470         protected virtual void OnChildRemoved(Element child)
471         {
472             child.Parent = null;
473
474             ChildRemoved?.Invoke(child, new ElementEventArgs(child));
475
476             OnDescendantRemoved(child);
477             foreach (Element element in child.Descendants())
478                 OnDescendantRemoved(element);
479         }
480
481         /// <summary>
482         /// Invoked whenever the Parent of an element is set.Implement this method in order to add behavior when the element is added to a parent.
483         /// </summary>
484         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
485         [EditorBrowsable(EditorBrowsableState.Never)]
486         protected virtual void OnParentSet()
487         {
488             ParentSet?.Invoke(this, EventArgs.Empty);
489             // ApplyStyleSheetsOnParentSet();
490         }
491
492         /// <summary>
493         /// Method that is called when a bound property is changed.
494         /// </summary>
495         /// <param name="propertyName">The name of the bound property that changed.</param>
496         /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API.
497         [EditorBrowsable(EditorBrowsableState.Never)]
498         protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
499         {
500             base.OnPropertyChanged(propertyName);
501
502             if (_effects == null || _effects.Count == 0)
503                 return;
504
505             var args = new PropertyChangedEventArgs(propertyName);
506             foreach (Effect effect in _effects)
507             {
508                 effect?.SendOnElementPropertyChanged(args);
509             }
510         }
511
512         /// <summary>
513         /// For internal use.
514         /// </summary>
515         /// <returns>the elements</returns>
516         [EditorBrowsable(EditorBrowsableState.Never)]
517         public IEnumerable<Element> Descendants()
518         {
519             var queue = new Queue<Element>(16);
520             queue.Enqueue(this);
521
522             while (queue.Count > 0)
523             {
524                 ReadOnlyCollection<Element> children = queue.Dequeue().LogicalChildrenInternal;
525                 for (var i = 0; i < children.Count; i++)
526                 {
527                     Element child = children[i];
528                     yield return child;
529                     queue.Enqueue(child);
530                 }
531             }
532         }
533
534         internal virtual void OnParentResourcesChanged(object sender, ResourcesChangedEventArgs e)
535         {
536             // if (e == ResourcesChangedEventArgs.StyleSheets)
537             //  // ApplyStyleSheetsOnParentSet();
538             // else
539             //  OnParentResourcesChanged(e.Values);
540         }
541
542         internal virtual void OnParentResourcesChanged(IEnumerable<KeyValuePair<string, object>> values)
543         {
544             OnResourcesChanged(values);
545         }
546
547         internal override void OnRemoveDynamicResource(BindableProperty property)
548         {
549             DynamicResources.Remove(property);
550
551             if (DynamicResources.Count == 0)
552                 _dynamicResources = null;
553             base.OnRemoveDynamicResource(property);
554         }
555
556         internal virtual void OnResourcesChanged(object sender, ResourcesChangedEventArgs e)
557         {
558             OnResourcesChanged(e.Values);
559         }
560
561         internal void OnResourcesChanged(IEnumerable<KeyValuePair<string, object>> values)
562         {
563             if (values == null)
564                 return;
565             if (_changeHandlers != null)
566                 foreach (Action<object, ResourcesChangedEventArgs> handler in _changeHandlers)
567                     handler(this, new ResourcesChangedEventArgs(values));
568             if (_dynamicResources == null)
569                 return;
570             if (_bindableResources == null)
571                 _bindableResources = new List<BindableObject>();
572             foreach (KeyValuePair<string, object> value in values)
573             {
574                 List<BindableProperty> changedResources = null;
575                 foreach (KeyValuePair<BindableProperty, string> dynR in DynamicResources)
576                 {
577                     // when the DynamicResource bound to a BindableProperty is
578                     // changing then the BindableProperty needs to be refreshed;
579                     // The .Value is the name of DynamicResouce to which the BindableProperty is bound.
580                     // The .Key is the name of the DynamicResource whose value is changing.
581                     if (dynR.Value != value.Key)
582                         continue;
583                     changedResources = changedResources ?? new List<BindableProperty>();
584                     changedResources.Add(dynR.Key);
585                 }
586                 if (changedResources == null)
587                     continue;
588                 foreach (BindableProperty changedResource in changedResources)
589                     OnResourceChanged(changedResource, value.Value);
590
591                 var bindableObject = value.Value as BindableObject;
592                 if (bindableObject != null && (bindableObject as Element)?.Parent == null)
593                 {
594                     if (!_bindableResources.Contains(bindableObject))
595                         _bindableResources.Add(bindableObject);
596                     SetInheritedBindingContext(bindableObject, BindingContext);
597                 }
598             }
599         }
600
601         internal override void OnSetDynamicResource(BindableProperty property, string key)
602         {
603             base.OnSetDynamicResource(property, key);
604             DynamicResources[property] = key;
605             object value;
606             if (this.TryGetResource(key, out value))
607                 OnResourceChanged(property, value);
608
609             Tizen.NUI.Application.AddResourceChangedCallback(this, (this as Element).OnResourcesChanged);
610         }
611
612         internal event EventHandler ParentSet;
613
614         internal static void SetFlowDirectionFromParent(Element child)
615         {
616         }
617
618         /// <summary>
619         /// For internal use.
620         /// </summary>
621         [EditorBrowsable(EditorBrowsableState.Never)]
622         public event EventHandler PlatformSet;
623
624         internal virtual void SetChildInheritedBindingContext(Element child, object context)
625         {
626             SetInheritedBindingContext(child, context);
627         }
628
629         internal IEnumerable<Element> VisibleDescendants()
630         {
631             var queue = new Queue<Element>(16);
632             queue.Enqueue(this);
633
634             while (queue.Count > 0)
635             {
636                 ReadOnlyCollection<Element> children = queue.Dequeue().LogicalChildrenInternal;
637                 for (var i = 0; i < children.Count; i++)
638                 {
639                     var child = children[i] as BaseHandle;
640                     if (child == null)
641                     {
642                         continue;
643                     }
644                     yield return child;
645                     queue.Enqueue(child);
646                 }
647             }
648         }
649
650         void AttachEffect(Effect effect)
651         {
652             if (_effectControlProvider == null)
653                 return;
654             if (effect.IsAttached)
655                 throw new InvalidOperationException("Cannot attach Effect to multiple sources");
656
657             Effect effectToRegister = effect;
658             if (effect is RoutingEffect)
659                 effectToRegister = ((RoutingEffect)effect).Inner;
660             _effectControlProvider.RegisterEffect(effectToRegister);
661             effectToRegister.Element = this;
662             effect.SendAttached();
663         }
664
665         void EffectsOnClearing(object sender, EventArgs eventArgs)
666         {
667             foreach (Effect effect in _effects)
668             {
669                 effect?.ClearEffect();
670             }
671         }
672
673         void EffectsOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
674         {
675             switch (e.Action)
676             {
677                 case NotifyCollectionChangedAction.Add:
678                     foreach (Effect effect in e.NewItems)
679                     {
680                         AttachEffect(effect);
681                     }
682                     break;
683                 case NotifyCollectionChangedAction.Move:
684                     break;
685                 case NotifyCollectionChangedAction.Remove:
686                     foreach (Effect effect in e.OldItems)
687                     {
688                         effect.ClearEffect();
689                     }
690                     break;
691                 case NotifyCollectionChangedAction.Replace:
692                     foreach (Effect effect in e.NewItems)
693                     {
694                         AttachEffect(effect);
695                     }
696                     foreach (Effect effect in e.OldItems)
697                     {
698                         effect.ClearEffect();
699                     }
700                     break;
701                 case NotifyCollectionChangedAction.Reset:
702                     if (e.NewItems != null)
703                     {
704                         foreach (Effect effect in e.NewItems)
705                         {
706                             AttachEffect(effect);
707                         }
708                     }
709                     if (e.OldItems != null)
710                     {
711                         foreach (Effect effect in e.OldItems)
712                         {
713                             effect.ClearEffect();
714                         }
715                     }
716                     break;
717                 default:
718                     throw new ArgumentOutOfRangeException();
719             }
720         }
721
722         INameScope GetNameScope()
723         {
724             INameScope namescope = NameScope.GetNameScope(this);
725             Element p = RealParent;
726             while (namescope == null && p != null)
727             {
728                 namescope = NameScope.GetNameScope(p);
729                 p = p.RealParent;
730             }
731             return namescope;
732         }
733
734         void OnDescendantAdded(Element child)
735         {
736             DescendantAdded?.Invoke(this, new ElementEventArgs(child));
737
738             if (RealParent != null)
739                 RealParent.OnDescendantAdded(child);
740         }
741
742         void OnDescendantRemoved(Element child)
743         {
744             DescendantRemoved?.Invoke(this, new ElementEventArgs(child));
745
746             if (RealParent != null)
747                 RealParent.OnDescendantRemoved(child);
748         }
749
750         void OnResourceChanged(BindableProperty property, object value)
751         {
752             SetValueCore(property, value, SetValueFlags.ClearOneWayBindings | SetValueFlags.ClearTwoWayBindings);
753         }
754     }
755 }