Merge remote-tracking branch 'origin/master' into tizen
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / XamlBinding / XamlStyle.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Reflection;
4 using Tizen.NUI.StyleSheets;
5 using System.ComponentModel;
6 using Tizen.NUI.BaseComponents;
7
8 namespace Tizen.NUI.Binding
9 {
10     [EditorBrowsable(EditorBrowsableState.Never)]
11     [ContentProperty("Setters")]
12     public sealed class XamlStyle : IStyle
13     {
14         internal const string StyleClassPrefix = "Tizen.NUI.Binding.StyleClass.";
15
16                 const int CleanupTrigger = 128;
17                 int cleanupThreshold = CleanupTrigger;
18
19         readonly BindableProperty basedOnResourceProperty = BindableProperty.CreateAttached("BasedOnResource", typeof(XamlStyle), typeof(XamlStyle), default(XamlStyle),
20             propertyChanged: OnBasedOnResourceChanged);
21
22         readonly List<WeakReference<BindableObject>> targets = new List<WeakReference<BindableObject>>(4);
23
24         XamlStyle basedOnStyle;
25
26         string baseResourceKey;
27
28         IList<Behavior> behaviors;
29
30         IList<TriggerBase> triggers;
31
32         [EditorBrowsable(EditorBrowsableState.Never)]
33         public XamlStyle([TypeConverter(typeof(TypeTypeConverter))][Parameter("TargetType")] Type targetType)
34         {
35             if (targetType == null)
36                 throw new ArgumentNullException(nameof(targetType));
37
38             TargetType = targetType;
39             Setters = new List<Setter>();
40         }
41
42         [EditorBrowsable(EditorBrowsableState.Never)]
43         public bool ApplyToDerivedTypes { get; set; }
44
45         [EditorBrowsable(EditorBrowsableState.Never)]
46         public XamlStyle BasedOn
47         {
48             get { return basedOnStyle; }
49             set
50             {
51                 if (basedOnStyle == value)
52                     return;
53                 if (!ValidateBasedOn(value))
54                     throw new ArgumentException("BasedOn.TargetType is not compatible with TargetType");
55                 XamlStyle oldValue = basedOnStyle;
56                 basedOnStyle = value;
57                 BasedOnChanged(oldValue, value);
58                 if (value != null)
59                     BaseResourceKey = null;
60             }
61         }
62
63         [EditorBrowsable(EditorBrowsableState.Never)]
64         public string BaseResourceKey
65         {
66             get { return baseResourceKey; }
67             set
68             {
69                 if (baseResourceKey == value)
70                     return;
71                 baseResourceKey = value;
72                 //update all DynamicResources
73                 foreach (WeakReference<BindableObject> bindableWr in targets)
74                 {
75                     BindableObject target;
76                     if (!bindableWr.TryGetTarget(out target))
77                         continue;
78                     target.RemoveDynamicResource(basedOnResourceProperty);
79                     if (value != null)
80                         target.SetDynamicResource(basedOnResourceProperty, value);
81                 }
82                 if (value != null)
83                     BasedOn = null;
84             }
85         }
86
87         internal IList<Behavior> Behaviors
88         {
89             get { return behaviors ?? (behaviors = new AttachedCollection<Behavior>()); }
90         }
91
92         [EditorBrowsable(EditorBrowsableState.Never)]
93         public bool CanCascade { get; set; }
94
95         [EditorBrowsable(EditorBrowsableState.Never)]
96         public string Class { get; set; }
97
98         [EditorBrowsable(EditorBrowsableState.Never)]
99         public IList<Setter> Setters { get; }
100
101         [EditorBrowsable(EditorBrowsableState.Never)]
102         public IList<TriggerBase> Triggers
103         {
104             get { return triggers ?? (triggers = new AttachedCollection<TriggerBase>()); }
105         }
106
107         void IStyle.Apply(BindableObject bindable)
108         {
109             targets.Add(new WeakReference<BindableObject>(bindable));
110             if (BaseResourceKey != null)
111                 bindable.SetDynamicResource(basedOnResourceProperty, BaseResourceKey);
112             ApplyCore(bindable, BasedOn ?? GetBasedOnResource(bindable));
113             CleanUpWeakReferences();
114         }
115
116         [EditorBrowsable(EditorBrowsableState.Never)]
117         public Type TargetType { get; }
118
119         void IStyle.UnApply(BindableObject bindable)
120         {
121             UnApplyCore(bindable, BasedOn ?? GetBasedOnResource(bindable));
122             bindable.RemoveDynamicResource(basedOnResourceProperty);
123             targets.RemoveAll(wr =>
124             {
125                 BindableObject target;
126                 return wr.TryGetTarget(out target) && target == bindable;
127             });
128         }
129
130         internal bool CanBeAppliedTo(Type targetType)
131         {
132             if (TargetType == targetType)
133                 return true;
134             if (!ApplyToDerivedTypes)
135                 return false;
136             do
137             {
138                 targetType = targetType.GetTypeInfo().BaseType;
139                 if (TargetType == targetType)
140                     return true;
141             } while (targetType != typeof(Element));
142             return false;
143         }
144
145         void ApplyCore(BindableObject bindable, XamlStyle basedOn)
146         {
147             if (basedOn != null)
148                 ((IStyle)basedOn).Apply(bindable);
149
150             foreach (Setter setter in Setters)
151                 setter.Apply(bindable, true);
152             ((AttachedCollection<Behavior>)Behaviors).AttachTo(bindable);
153             ((AttachedCollection<TriggerBase>)Triggers).AttachTo(bindable);
154         }
155
156         void BasedOnChanged(XamlStyle oldValue, XamlStyle newValue)
157         {
158             foreach (WeakReference<BindableObject> bindableRef in targets)
159             {
160                 BindableObject bindable;
161                 if (!bindableRef.TryGetTarget(out bindable))
162                     continue;
163
164                 UnApplyCore(bindable, oldValue);
165                 ApplyCore(bindable, newValue);
166             }
167         }
168
169         XamlStyle GetBasedOnResource(BindableObject bindable)
170         {
171             return (XamlStyle)bindable.GetValue(basedOnResourceProperty);
172         }
173
174         static void OnBasedOnResourceChanged(BindableObject bindable, object oldValue, object newValue)
175         {
176             XamlStyle style = (bindable as View).XamlStyle;
177             if (style == null)
178                 return;
179             style.UnApplyCore(bindable, (XamlStyle)oldValue);
180             style.ApplyCore(bindable, (XamlStyle)newValue);
181         }
182
183         void UnApplyCore(BindableObject bindable, XamlStyle basedOn)
184         {
185             ((AttachedCollection<TriggerBase>)Triggers).DetachFrom(bindable);
186             ((AttachedCollection<Behavior>)Behaviors).DetachFrom(bindable);
187             foreach (Setter setter in Setters)
188                 setter.UnApply(bindable, true);
189
190             if (basedOn != null)
191                 ((IStyle)basedOn).UnApply(bindable);
192         }
193
194         bool ValidateBasedOn(XamlStyle value)
195         {
196             if (value == null)
197                 return true;
198             return value.TargetType.IsAssignableFrom(TargetType);
199         }
200
201                 void CleanUpWeakReferences()
202                 {
203                         if (targets.Count < cleanupThreshold)
204                         {
205                                 return;
206                         }
207
208                         targets.RemoveAll(t => !t.TryGetTarget(out _));
209                         cleanupThreshold = targets.Count + CleanupTrigger;
210                 }
211         }
212 }