From 2972c4af4eb9300839170c6c59bdd95990747019 Mon Sep 17 00:00:00 2001 From: Fang Xiaohui Date: Fri, 20 Aug 2021 14:11:17 +0800 Subject: [PATCH] [Xaml] Support Trigger in Xaml --- .../src/internal/Xaml/ApplyPropertiesVisitor.cs | 5 +- .../src/internal/Xaml/TypeConversionExtensions.cs | 9 +- .../src/internal/Xaml/VisualState/VisualState.cs | 50 +++ .../internal/Xaml/VisualState/VisualStateGroup.cs | 77 +++++ .../Xaml/VisualState/VisualStateGroupList.cs | 130 +++++++ .../VisualState/VisualStateGroupListExtensions.cs | 40 +++ .../Xaml/VisualState/VisualStateManager.cs | 118 +++++++ .../src/internal/Xaml/VisualState/WatchAddList.cs | 100 ++++++ .../src/internal/Xaml/VisualStateManager.cs | 378 --------------------- .../src/internal/XamlBinding/MergedStyle.cs | 173 ++++++++++ .../src/internal/XamlBinding/StyleSheets/IStyle.cs | 13 + .../src/public/BaseComponents/ViewInternal.cs | 15 + .../src/public/XamlBinding/ResourceDictionary.cs | 15 + src/Tizen.NUI/src/public/XamlBinding/XamlStyle.cs | 212 ++++++++++++ 14 files changed, 954 insertions(+), 381 deletions(-) create mode 100755 src/Tizen.NUI/src/internal/Xaml/VisualState/VisualState.cs create mode 100755 src/Tizen.NUI/src/internal/Xaml/VisualState/VisualStateGroup.cs create mode 100755 src/Tizen.NUI/src/internal/Xaml/VisualState/VisualStateGroupList.cs create mode 100755 src/Tizen.NUI/src/internal/Xaml/VisualState/VisualStateGroupListExtensions.cs create mode 100755 src/Tizen.NUI/src/internal/Xaml/VisualState/VisualStateManager.cs create mode 100755 src/Tizen.NUI/src/internal/Xaml/VisualState/WatchAddList.cs delete mode 100755 src/Tizen.NUI/src/internal/Xaml/VisualStateManager.cs create mode 100755 src/Tizen.NUI/src/internal/XamlBinding/MergedStyle.cs create mode 100755 src/Tizen.NUI/src/internal/XamlBinding/StyleSheets/IStyle.cs create mode 100755 src/Tizen.NUI/src/public/XamlBinding/XamlStyle.cs diff --git a/src/Tizen.NUI/src/internal/Xaml/ApplyPropertiesVisitor.cs b/src/Tizen.NUI/src/internal/Xaml/ApplyPropertiesVisitor.cs index 2d4bc90..f2ffb5d 100755 --- a/src/Tizen.NUI/src/internal/Xaml/ApplyPropertiesVisitor.cs +++ b/src/Tizen.NUI/src/internal/Xaml/ApplyPropertiesVisitor.cs @@ -166,7 +166,8 @@ namespace Tizen.NUI.Xaml addMethod?.Invoke(source, new[] { value }); return; } - if (xpe == null && Context.Types[parentElement].GetRuntimeMethods().Any(mi => mi.Name == "Add" && mi.GetParameters().Length == 1)) + if (xpe == null && Context.Types[parentElement].GetRuntimeMethods().Any + (mi => mi.Name == "Add" && mi.GetParameters().Length == 1 && mi.GetParameters()[0].ParameterType.IsAssignableFrom(value.GetType()))) { //if there are similar parameters in the function, this will exist issue. var addMethod = Context.Types[parentElement].GetRuntimeMethods().First(mi => mi.Name == "Add" && mi.GetParameters().Length == 1); @@ -211,7 +212,7 @@ namespace Tizen.NUI.Xaml MethodInfo addMethod; if (xpe == null && (addMethod = collection.GetType().GetRuntimeMethods().First(mi => mi.Name == "Add" && mi.GetParameters().Length == 1)) != null) { - addMethod.Invoke(collection, new[] { Values[node] }); + addMethod.Invoke(collection, new[] { value }); return; } xpe = xpe ?? new XamlParseException($"Value of {parentList.XmlName.LocalName} does not have a Add() method", node); diff --git a/src/Tizen.NUI/src/internal/Xaml/TypeConversionExtensions.cs b/src/Tizen.NUI/src/internal/Xaml/TypeConversionExtensions.cs index fa6ff61..b0e25a5 100755 --- a/src/Tizen.NUI/src/internal/Xaml/TypeConversionExtensions.cs +++ b/src/Tizen.NUI/src/internal/Xaml/TypeConversionExtensions.cs @@ -93,7 +93,14 @@ namespace Tizen.NUI.Xaml if (converterTypeName == null) { - converterTypeName = toType.FullName + "TypeConverter"; + if (toType == typeof(Type)) + { + converterTypeName = typeof(TypeTypeConverter).FullName; + } + else + { + converterTypeName = toType.FullName + "TypeConverter"; + } } var convertertype = Type.GetType(converterTypeName); diff --git a/src/Tizen.NUI/src/internal/Xaml/VisualState/VisualState.cs b/src/Tizen.NUI/src/internal/Xaml/VisualState/VisualState.cs new file mode 100755 index 0000000..de3ccd6 --- /dev/null +++ b/src/Tizen.NUI/src/internal/Xaml/VisualState/VisualState.cs @@ -0,0 +1,50 @@ +/* + * Copyright(c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using Tizen.NUI; +using Tizen.NUI.Binding; + +namespace Tizen.NUI.Xaml +{ + [RuntimeNameProperty(nameof(Name))] + internal sealed class VisualState + { + public VisualState() + { + Setters = new ObservableCollection(); + } + + public string Name { get; set; } + public IList Setters { get; } + public Type TargetType { get; set; } + + internal VisualState Clone() + { + var clone = new VisualState { Name = Name, TargetType = TargetType }; + foreach (var setter in Setters) + { + clone.Setters.Add(setter); + } + + return clone; + } + } +} diff --git a/src/Tizen.NUI/src/internal/Xaml/VisualState/VisualStateGroup.cs b/src/Tizen.NUI/src/internal/Xaml/VisualState/VisualStateGroup.cs new file mode 100755 index 0000000..d55184b --- /dev/null +++ b/src/Tizen.NUI/src/internal/Xaml/VisualState/VisualStateGroup.cs @@ -0,0 +1,77 @@ +/* + * Copyright(c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using Tizen.NUI; +using Tizen.NUI.Binding; + +namespace Tizen.NUI.Xaml +{ + [RuntimeNameProperty(nameof(Name))] + [ContentProperty(nameof(States))] + internal sealed class VisualStateGroup + { + public VisualStateGroup() + { + States = new WatchAddList(OnStatesChanged); + } + + public Type TargetType { get; set; } + public string Name { get; set; } + public IList States { get; } + public VisualState CurrentState { get; internal set; } + + internal VisualState GetState(string name) + { + foreach (VisualState state in States) + { + if (string.CompareOrdinal(state.Name, name) == 0) + { + return state; + } + } + + return null; + } + + internal VisualStateGroup Clone() + { + var clone = new VisualStateGroup { TargetType = TargetType, Name = Name, CurrentState = CurrentState }; + foreach (VisualState state in States) + { + clone.States.Add(state.Clone()); + } + + return clone; + } + + internal event EventHandler StatesChanged; + + void OnStatesChanged(IList list) + { + if (list.Any(state => string.IsNullOrEmpty(state.Name))) + { + throw new InvalidOperationException("State names may not be null or empty"); + } + + StatesChanged?.Invoke(this, EventArgs.Empty); + } + } +} diff --git a/src/Tizen.NUI/src/internal/Xaml/VisualState/VisualStateGroupList.cs b/src/Tizen.NUI/src/internal/Xaml/VisualState/VisualStateGroupList.cs new file mode 100755 index 0000000..1faa931 --- /dev/null +++ b/src/Tizen.NUI/src/internal/Xaml/VisualState/VisualStateGroupList.cs @@ -0,0 +1,130 @@ +/* + * Copyright(c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using Tizen.NUI; +using Tizen.NUI.Binding; + +namespace Tizen.NUI.Xaml +{ + internal class VisualStateGroupList : IList + { + readonly IList _internalList; + + void Validate(IList groups) + { + // If we have 1 group, no need to worry about duplicate group names + if (groups.Count > 1) + { + if (groups.GroupBy(vsg => vsg.Name).Any(g => g.Count() > 1)) + { + throw new InvalidOperationException("VisualStateGroup Names must be unique"); + } + } + + // State names must be unique within this group list, so pull in all + // the states in all the groups, group them by name, and see if we have + // and duplicates + if (groups.SelectMany(group => group.States).GroupBy(state => state.Name).Any(g => g.Count() > 1)) + { + throw new InvalidOperationException("VisualState Names must be unique"); + } + } + + public VisualStateGroupList() + { + _internalList = new WatchAddList(Validate); + } + + void ValidateOnStatesChanged(object sender, EventArgs eventArgs) + { + Validate(_internalList); + } + + public IEnumerator GetEnumerator() + { + return _internalList.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)_internalList).GetEnumerator(); + } + + public void Add(VisualStateGroup item) + { + _internalList.Add(item); + item.StatesChanged += ValidateOnStatesChanged; + } + + public void Clear() + { + foreach (var group in _internalList) + { + group.StatesChanged -= ValidateOnStatesChanged; + } + + _internalList.Clear(); + } + + public bool Contains(VisualStateGroup item) + { + return _internalList.Contains(item); + } + + public void CopyTo(VisualStateGroup[] array, int arrayIndex) + { + _internalList.CopyTo(array, arrayIndex); + } + + public bool Remove(VisualStateGroup item) + { + item.StatesChanged -= ValidateOnStatesChanged; + return _internalList.Remove(item); + } + + public int Count => _internalList.Count; + + public bool IsReadOnly => false; + + public int IndexOf(VisualStateGroup item) + { + return _internalList.IndexOf(item); + } + + public void Insert(int index, VisualStateGroup item) + { + item.StatesChanged += ValidateOnStatesChanged; + _internalList.Insert(index, item); + } + + public void RemoveAt(int index) + { + _internalList[index].StatesChanged -= ValidateOnStatesChanged; + _internalList.RemoveAt(index); + } + + public VisualStateGroup this[int index] + { + get => _internalList[index]; + set => _internalList[index] = value; + } + } +} diff --git a/src/Tizen.NUI/src/internal/Xaml/VisualState/VisualStateGroupListExtensions.cs b/src/Tizen.NUI/src/internal/Xaml/VisualState/VisualStateGroupListExtensions.cs new file mode 100755 index 0000000..b25cb6a --- /dev/null +++ b/src/Tizen.NUI/src/internal/Xaml/VisualState/VisualStateGroupListExtensions.cs @@ -0,0 +1,40 @@ +/* + * Copyright(c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using Tizen.NUI; +using Tizen.NUI.Binding; + +namespace Tizen.NUI.Xaml +{ + internal static class VisualStateGroupListExtensions + { + internal static IList Clone(this IList groups) + { + var actual = new VisualStateGroupList(); + foreach (var group in groups) + { + actual.Add(group.Clone()); + } + + return actual; + } + } +} diff --git a/src/Tizen.NUI/src/internal/Xaml/VisualState/VisualStateManager.cs b/src/Tizen.NUI/src/internal/Xaml/VisualState/VisualStateManager.cs new file mode 100755 index 0000000..11d571d --- /dev/null +++ b/src/Tizen.NUI/src/internal/Xaml/VisualState/VisualStateManager.cs @@ -0,0 +1,118 @@ +/* + * Copyright(c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using Tizen.NUI; +using Tizen.NUI.Binding; + +namespace Tizen.NUI.Xaml +{ + internal static class VisualStateManager + { + internal class CommonStates + { + public const string Normal = "Normal"; + public const string Disabled = "Disabled"; + public const string Focused = "Focused"; + } + + private static VisualStateGroupList visualStateGroups; + public static readonly BindableProperty VisualStateGroupsProperty = + BindableProperty.CreateAttached("VisualStateGroups", typeof(VisualStateGroupList), typeof(/*VisualElement*/BaseHandle), + defaultValue: null, propertyChanged: VisualStateGroupsPropertyChanged, + defaultValueCreator: (BindableObject obj)=> + { + if (null == visualStateGroups) + { + visualStateGroups = new VisualStateGroupList(); + } + + return visualStateGroups; + }); + + static void VisualStateGroupsPropertyChanged(BindableObject bindable, object oldValue, object newValue) + { + GoToState((/*VisualElement*/BaseHandle)bindable, CommonStates.Normal); + } + + public static IList GetVisualStateGroups(/*VisualElement*/BaseHandle visualElement) + { + return (IList)visualElement.GetValue(VisualStateGroupsProperty); + } + + public static void SetVisualStateGroups(/*VisualElement*/BaseHandle visualElement, VisualStateGroupList value) + { + visualElement.SetValue(VisualStateGroupsProperty, value); + } + + public static bool GoToState(/*VisualElement*/BaseHandle visualElement, string name) + { + if (!visualElement.IsSet(VisualStateGroupsProperty)) + { + return false; + } + + var groups = (IList)visualElement.GetValue(VisualStateGroupsProperty); + + foreach (VisualStateGroup group in groups) + { + if (group.CurrentState?.Name == name) + { + // We're already in the target state; nothing else to do + return true; + } + + // See if this group contains the new state + var target = group.GetState(name); + if (target == null) + { + continue; + } + + // If we've got a new state to transition to, unapply the setters from the current state + if (group.CurrentState != null) + { + foreach (Setter setter in group.CurrentState.Setters) + { + setter.UnApply(visualElement); + } + } + + // Update the current state + group.CurrentState = target; + + // Apply the setters from the new state + foreach (Setter setter in target.Setters) + { + setter.Apply(visualElement); + } + + return true; + } + + return false; + } + + public static bool HasVisualStateGroups(this /*VisualElement*/BaseHandle element) + { + return element.IsSet(VisualStateGroupsProperty); + } + } +} diff --git a/src/Tizen.NUI/src/internal/Xaml/VisualState/WatchAddList.cs b/src/Tizen.NUI/src/internal/Xaml/VisualState/WatchAddList.cs new file mode 100755 index 0000000..cdff63f --- /dev/null +++ b/src/Tizen.NUI/src/internal/Xaml/VisualState/WatchAddList.cs @@ -0,0 +1,100 @@ +/* + * Copyright(c) 2021 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using Tizen.NUI; +using Tizen.NUI.Binding; + +namespace Tizen.NUI.Xaml +{ + internal class WatchAddList : IList + { + readonly Action> _onAdd; + readonly List _internalList; + + public WatchAddList(Action> onAdd) + { + _onAdd = onAdd; + _internalList = new List(); + } + + public IEnumerator GetEnumerator() + { + return _internalList.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)_internalList).GetEnumerator(); + } + + public void Add(T item) + { + _internalList.Add(item); + _onAdd(_internalList); + } + + public void Clear() + { + _internalList.Clear(); + } + + public bool Contains(T item) + { + return _internalList.Contains(item); + } + + public void CopyTo(T[] array, int arrayIndex) + { + _internalList.CopyTo(array, arrayIndex); + } + + public bool Remove(T item) + { + return _internalList.Remove(item); + } + + public int Count => _internalList.Count; + + public bool IsReadOnly => false; + + public int IndexOf(T item) + { + return _internalList.IndexOf(item); + } + + public void Insert(int index, T item) + { + _internalList.Insert(index, item); + _onAdd(_internalList); + } + + public void RemoveAt(int index) + { + _internalList.RemoveAt(index); + } + + public T this[int index] + { + get => _internalList[index]; + set => _internalList[index] = value; + } + } +} diff --git a/src/Tizen.NUI/src/internal/Xaml/VisualStateManager.cs b/src/Tizen.NUI/src/internal/Xaml/VisualStateManager.cs deleted file mode 100755 index e13521e..0000000 --- a/src/Tizen.NUI/src/internal/Xaml/VisualStateManager.cs +++ /dev/null @@ -1,378 +0,0 @@ -/* - * Copyright(c) 2021 Samsung Electronics Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using Tizen.NUI; -using Tizen.NUI.Binding; - -namespace Tizen.NUI.Xaml -{ - internal static class VisualStateManager - { - internal class CommonStates - { - public const string Normal = "Normal"; - public const string Disabled = "Disabled"; - public const string Focused = "Focused"; - } - - public static readonly BindableProperty VisualStateGroupsProperty = - BindableProperty.CreateAttached("VisualStateGroups", typeof(VisualStateGroupList), typeof(/*VisualElement*/BaseHandle), - defaultValue: null, propertyChanged: VisualStateGroupsPropertyChanged, - defaultValueCreator: bindable => new VisualStateGroupList()); - - static void VisualStateGroupsPropertyChanged(BindableObject bindable, object oldValue, object newValue) - { - GoToState((/*VisualElement*/BaseHandle)bindable, CommonStates.Normal); - } - - public static IList GetVisualStateGroups(/*VisualElement*/BaseHandle visualElement) - { - return (IList)visualElement.GetValue(VisualStateGroupsProperty); - } - - public static void SetVisualStateGroups(/*VisualElement*/BaseHandle visualElement, VisualStateGroupList value) - { - visualElement.SetValue(VisualStateGroupsProperty, value); - } - - public static bool GoToState(/*VisualElement*/BaseHandle visualElement, string name) - { - if (!visualElement.IsSet(VisualStateGroupsProperty)) - { - return false; - } - - var groups = (IList)visualElement.GetValue(VisualStateGroupsProperty); - - foreach (VisualStateGroup group in groups) - { - if (group.CurrentState?.Name == name) - { - // We're already in the target state; nothing else to do - return true; - } - - // See if this group contains the new state - var target = group.GetState(name); - if (target == null) - { - continue; - } - - // If we've got a new state to transition to, unapply the setters from the current state - if (group.CurrentState != null) - { - foreach (Setter setter in group.CurrentState.Setters) - { - setter.UnApply(visualElement); - } - } - - // Update the current state - group.CurrentState = target; - - // Apply the setters from the new state - foreach (Setter setter in target.Setters) - { - setter.Apply(visualElement); - } - - return true; - } - - return false; - } - - public static bool HasVisualStateGroups(this /*VisualElement*/BaseHandle element) - { - return element.IsSet(VisualStateGroupsProperty); - } - } - - internal class VisualStateGroupList : IList - { - readonly IList _internalList; - - void Validate(IList groups) - { - // If we have 1 group, no need to worry about duplicate group names - if (groups.Count > 1) - { - if (groups.GroupBy(vsg => vsg.Name).Any(g => g.Count() > 1)) - { - throw new InvalidOperationException("VisualStateGroup Names must be unique"); - } - } - - // State names must be unique within this group list, so pull in all - // the states in all the groups, group them by name, and see if we have - // and duplicates - if (groups.SelectMany(group => group.States) - .GroupBy(state => state.Name) - .Any(g => g.Count() > 1)) - { - throw new InvalidOperationException("VisualState Names must be unique"); - } - } - - public VisualStateGroupList() - { - _internalList = new WatchAddList(Validate); - } - - void ValidateOnStatesChanged(object sender, EventArgs eventArgs) - { - Validate(_internalList); - } - - public IEnumerator GetEnumerator() - { - return _internalList.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return ((IEnumerable)_internalList).GetEnumerator(); - } - - public void Add(VisualStateGroup item) - { - _internalList.Add(item); - item.StatesChanged += ValidateOnStatesChanged; - } - - public void Clear() - { - foreach (var group in _internalList) - { - group.StatesChanged -= ValidateOnStatesChanged; - } - - _internalList.Clear(); - } - - public bool Contains(VisualStateGroup item) - { - return _internalList.Contains(item); - } - - public void CopyTo(VisualStateGroup[] array, int arrayIndex) - { - _internalList.CopyTo(array, arrayIndex); - } - - public bool Remove(VisualStateGroup item) - { - item.StatesChanged -= ValidateOnStatesChanged; - return _internalList.Remove(item); - } - - public int Count => _internalList.Count; - - public bool IsReadOnly => false; - - public int IndexOf(VisualStateGroup item) - { - return _internalList.IndexOf(item); - } - - public void Insert(int index, VisualStateGroup item) - { - item.StatesChanged += ValidateOnStatesChanged; - _internalList.Insert(index, item); - } - - public void RemoveAt(int index) - { - _internalList[index].StatesChanged -= ValidateOnStatesChanged; - _internalList.RemoveAt(index); - } - - public VisualStateGroup this[int index] - { - get => _internalList[index]; - set => _internalList[index] = value; - } - } - - [RuntimeNameProperty(nameof(Name))] - [ContentProperty(nameof(States))] - internal sealed class VisualStateGroup - { - public VisualStateGroup() - { - States = new WatchAddList(OnStatesChanged); - } - - public Type TargetType { get; set; } - public string Name { get; set; } - public IList States { get; } - public VisualState CurrentState { get; internal set; } - - internal VisualState GetState(string name) - { - foreach (VisualState state in States) - { - if (string.CompareOrdinal(state.Name, name) == 0) - { - return state; - } - } - - return null; - } - - internal VisualStateGroup Clone() - { - var clone = new VisualStateGroup { TargetType = TargetType, Name = Name, CurrentState = CurrentState }; - foreach (VisualState state in States) - { - clone.States.Add(state.Clone()); - } - - return clone; - } - - internal event EventHandler StatesChanged; - - void OnStatesChanged(IList list) - { - if (list.Any(state => string.IsNullOrEmpty(state.Name))) - { - throw new InvalidOperationException("State names may not be null or empty"); - } - - StatesChanged?.Invoke(this, EventArgs.Empty); - } - } - - [RuntimeNameProperty(nameof(Name))] - internal sealed class VisualState - { - public VisualState() - { - Setters = new ObservableCollection(); - } - - public string Name { get; set; } - public IList Setters { get; } - public Type TargetType { get; set; } - - internal VisualState Clone() - { - var clone = new VisualState { Name = Name, TargetType = TargetType }; - foreach (var setter in Setters) - { - clone.Setters.Add(setter); - } - - return clone; - } - } - - internal static class VisualStateGroupListExtensions - { - internal static IList Clone(this IList groups) - { - var actual = new VisualStateGroupList(); - foreach (var group in groups) - { - actual.Add(group.Clone()); - } - - return actual; - } - } - - internal class WatchAddList : IList - { - readonly Action> _onAdd; - readonly List _internalList; - - public WatchAddList(Action> onAdd) - { - _onAdd = onAdd; - _internalList = new List(); - } - - public IEnumerator GetEnumerator() - { - return _internalList.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return ((IEnumerable)_internalList).GetEnumerator(); - } - - public void Add(T item) - { - _internalList.Add(item); - _onAdd(_internalList); - } - - public void Clear() - { - _internalList.Clear(); - } - - public bool Contains(T item) - { - return _internalList.Contains(item); - } - - public void CopyTo(T[] array, int arrayIndex) - { - _internalList.CopyTo(array, arrayIndex); - } - - public bool Remove(T item) - { - return _internalList.Remove(item); - } - - public int Count => _internalList.Count; - - public bool IsReadOnly => false; - - public int IndexOf(T item) - { - return _internalList.IndexOf(item); - } - - public void Insert(int index, T item) - { - _internalList.Insert(index, item); - _onAdd(_internalList); - } - - public void RemoveAt(int index) - { - _internalList.RemoveAt(index); - } - - public T this[int index] - { - get => _internalList[index]; - set => _internalList[index] = value; - } - } -} diff --git a/src/Tizen.NUI/src/internal/XamlBinding/MergedStyle.cs b/src/Tizen.NUI/src/internal/XamlBinding/MergedStyle.cs new file mode 100755 index 0000000..bf0c29b --- /dev/null +++ b/src/Tizen.NUI/src/internal/XamlBinding/MergedStyle.cs @@ -0,0 +1,173 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Tizen.NUI.StyleSheets; +using Tizen.NUI.BaseComponents; + +namespace Tizen.NUI.Binding +{ + internal sealed class MergedStyle : IStyle + { + ////If the base type is one of these, stop registering dynamic resources further + ////The last one (typeof(Element)) is a safety guard as we might be creating VisualElement directly in internal code + static readonly IList s_stopAtTypes = new List { typeof(View), typeof(Element) }; + + IList _classStyleProperties; + + readonly List _implicitStyles = new List(); + + IList _classStyles; + + IStyle _implicitStyle; + + IStyle _style; + + IList _styleClass; + + public MergedStyle(Type targetType, BindableObject target) + { + Target = target; + TargetType = targetType; + RegisterImplicitStyles(); + Apply(Target); + } + + public IStyle Style + { + get { return _style; } + set { SetStyle(ImplicitStyle, ClassStyles, value); } + } + + public IList StyleClass + { + get { return _styleClass; } + set + { + if (_styleClass == value) + return; + + if (_styleClass != null && _classStyles != null) + foreach (var classStyleProperty in _classStyleProperties) + Target.RemoveDynamicResource(classStyleProperty); + + _styleClass = value; + + if (_styleClass != null) + { + _classStyleProperties = new List(); + foreach (var styleClass in _styleClass) + { + var classStyleProperty = BindableProperty.Create("ClassStyle", typeof(IList), typeof(View), default(IList), + propertyChanged: (bindable, oldvalue, newvalue) => ((View)bindable)._mergedStyle.OnClassStyleChanged()); + _classStyleProperties.Add(classStyleProperty); + Target.OnSetDynamicResource(classStyleProperty, Tizen.NUI.Binding.XamlStyle.StyleClassPrefix + styleClass); + } + } + } + } + + public BindableObject Target { get; } + + IList ClassStyles + { + get { return _classStyles; } + set { SetStyle(ImplicitStyle, value, Style); } + } + + IStyle ImplicitStyle + { + get { return _implicitStyle; } + set { SetStyle(value, ClassStyles, Style); } + } + + public void Apply(BindableObject bindable) + { + ImplicitStyle?.Apply(bindable); + if (ClassStyles != null) + foreach (var classStyle in ClassStyles) + ((IStyle)classStyle)?.Apply(bindable); + Style?.Apply(bindable); + } + + public Type TargetType { get; } + + public void UnApply(BindableObject bindable) + { + Style?.UnApply(bindable); + if (ClassStyles != null) + foreach (var classStyle in ClassStyles) + ((IStyle)classStyle)?.UnApply(bindable); + ImplicitStyle?.UnApply(bindable); + } + + void OnClassStyleChanged() + { + ClassStyles = _classStyleProperties.Select(p => (Target.GetValue(p) as IList)?.FirstOrDefault(s => s.CanBeAppliedTo(TargetType))).ToList(); + } + + void OnImplicitStyleChanged() + { + var first = true; + foreach (BindableProperty implicitStyleProperty in _implicitStyles) + { + var implicitStyle = (XamlStyle)Target.GetValue(implicitStyleProperty); + if (implicitStyle != null) + { + if (first || implicitStyle.ApplyToDerivedTypes) + { + ImplicitStyle = implicitStyle; + return; + } + } + first = false; + } + } + + void RegisterImplicitStyles() + { + Type type = TargetType; + if (type == null) + { + return; + } + while (true) + { + BindableProperty implicitStyleProperty = BindableProperty.Create(nameof(ImplicitStyle), typeof(XamlStyle), typeof(View), default(XamlStyle), + propertyChanged: (bindable, oldvalue, newvalue) => OnImplicitStyleChanged()); + _implicitStyles.Add(implicitStyleProperty); + Target.SetDynamicResource(implicitStyleProperty, type.FullName); + type = type.GetTypeInfo().BaseType; + if (s_stopAtTypes.Contains(type)) + return; + } + } + + void SetStyle(IStyle implicitStyle, IList classStyles, IStyle style) + { + bool shouldReApplyStyle = implicitStyle != ImplicitStyle || classStyles != ClassStyles || Style != style; + bool shouldReApplyClassStyle = implicitStyle != ImplicitStyle || classStyles != ClassStyles; + bool shouldReApplyImplicitStyle = implicitStyle != ImplicitStyle && (Style as XamlStyle == null || ((XamlStyle)Style).CanCascade); + + if (shouldReApplyStyle) + Style?.UnApply(Target); + if (shouldReApplyClassStyle && ClassStyles != null) + foreach (var classStyle in ClassStyles) + ((IStyle)classStyle)?.UnApply(Target); + if (shouldReApplyImplicitStyle) + ImplicitStyle?.UnApply(Target); + + _implicitStyle = implicitStyle; + _classStyles = classStyles; + _style = style; + + if (shouldReApplyImplicitStyle) + ImplicitStyle?.Apply(Target); + if (shouldReApplyClassStyle && ClassStyles != null) + foreach (var classStyle in ClassStyles) + ((IStyle)classStyle)?.Apply(Target); + if (shouldReApplyStyle) + Style?.Apply(Target); + } + } +} \ No newline at end of file diff --git a/src/Tizen.NUI/src/internal/XamlBinding/StyleSheets/IStyle.cs b/src/Tizen.NUI/src/internal/XamlBinding/StyleSheets/IStyle.cs new file mode 100755 index 0000000..1cb59e3 --- /dev/null +++ b/src/Tizen.NUI/src/internal/XamlBinding/StyleSheets/IStyle.cs @@ -0,0 +1,13 @@ +using System; +using Tizen.NUI.Binding; + +namespace Tizen.NUI.StyleSheets +{ + internal interface IStyle + { + Type TargetType { get; } + + void Apply(BindableObject bindable); + void UnApply(BindableObject bindable); + } +} diff --git a/src/Tizen.NUI/src/public/BaseComponents/ViewInternal.cs b/src/Tizen.NUI/src/public/BaseComponents/ViewInternal.cs index dcc758f..f40cd3c 100755 --- a/src/Tizen.NUI/src/public/BaseComponents/ViewInternal.cs +++ b/src/Tizen.NUI/src/public/BaseComponents/ViewInternal.cs @@ -20,6 +20,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Runtime.CompilerServices; +using Tizen.NUI.Binding; namespace Tizen.NUI.BaseComponents { @@ -29,8 +30,22 @@ namespace Tizen.NUI.BaseComponents /// 3 public partial class View { + private MergedStyle mergedStyle = null; internal string styleName; + internal MergedStyle _mergedStyle + { + get + { + if (null == mergedStyle) + { + mergedStyle = new MergedStyle(GetType(), this); + } + + return mergedStyle; + } + } + internal class ThemeData { [Flags] diff --git a/src/Tizen.NUI/src/public/XamlBinding/ResourceDictionary.cs b/src/Tizen.NUI/src/public/XamlBinding/ResourceDictionary.cs index 9b34cd0..797436b 100755 --- a/src/Tizen.NUI/src/public/XamlBinding/ResourceDictionary.cs +++ b/src/Tizen.NUI/src/public/XamlBinding/ResourceDictionary.cs @@ -343,6 +343,21 @@ namespace Tizen.NUI.Binding remove { ValuesChanged -= value; } } + internal void Add(XamlStyle style) + { + if (string.IsNullOrEmpty(style.Class)) + Add(style.TargetType.FullName, style); + else + { + IList classes; + object outclasses; + if (!TryGetValue(XamlStyle.StyleClassPrefix + style.Class, out outclasses) || (classes = outclasses as IList) == null) + classes = new List(); + classes.Add(style); + this[XamlStyle.StyleClassPrefix + style.Class] = classes; + } + } + /// This will be public opened in tizen_5.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] public void Add(ResourceDictionary mergedResourceDictionary) diff --git a/src/Tizen.NUI/src/public/XamlBinding/XamlStyle.cs b/src/Tizen.NUI/src/public/XamlBinding/XamlStyle.cs new file mode 100755 index 0000000..c8f0fad --- /dev/null +++ b/src/Tizen.NUI/src/public/XamlBinding/XamlStyle.cs @@ -0,0 +1,212 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using Tizen.NUI.StyleSheets; +using System.ComponentModel; + +namespace Tizen.NUI.Binding +{ + /// 6 + /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. + [EditorBrowsable(EditorBrowsableState.Never)] + [ContentProperty("Setters")] + public sealed class XamlStyle : IStyle + { + internal const string StyleClassPrefix = "Tizen.NUI.Binding.StyleClass."; + + readonly BindableProperty _basedOnResourceProperty = BindableProperty.CreateAttached("BasedOnResource", typeof(XamlStyle), typeof(XamlStyle), default(XamlStyle), + propertyChanged: OnBasedOnResourceChanged); + + readonly List> _targets = new List>(4); + + XamlStyle _basedOnStyle; + + string _baseResourceKey; + + IList _behaviors; + + IList _triggers; + + /// 6 + /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. + [EditorBrowsable(EditorBrowsableState.Never)] + public XamlStyle([TypeConverter(typeof(TypeTypeConverter))][Parameter("TargetType")] Type targetType) + { + if (targetType == null) + throw new ArgumentNullException(nameof(targetType)); + + TargetType = targetType; + Setters = new List(); + } + + /// 6 + /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. + [EditorBrowsable(EditorBrowsableState.Never)] + public bool ApplyToDerivedTypes { get; set; } + + /// 6 + /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. + [EditorBrowsable(EditorBrowsableState.Never)] + public XamlStyle BasedOn + { + get { return _basedOnStyle; } + set + { + if (_basedOnStyle == value) + return; + if (!ValidateBasedOn(value)) + throw new ArgumentException("BasedOn.TargetType is not compatible with TargetType"); + XamlStyle oldValue = _basedOnStyle; + _basedOnStyle = value; + BasedOnChanged(oldValue, value); + if (value != null) + BaseResourceKey = null; + } + } + + /// 6 + /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. + [EditorBrowsable(EditorBrowsableState.Never)] + public string BaseResourceKey + { + get { return _baseResourceKey; } + set + { + if (_baseResourceKey == value) + return; + _baseResourceKey = value; + //update all DynamicResources + foreach (WeakReference bindableWr in _targets) + { + BindableObject target; + if (!bindableWr.TryGetTarget(out target)) + continue; + target.RemoveDynamicResource(_basedOnResourceProperty); + if (value != null) + target.SetDynamicResource(_basedOnResourceProperty, value); + } + if (value != null) + BasedOn = null; + } + } + + internal IList Behaviors + { + get { return _behaviors ?? (_behaviors = new AttachedCollection()); } + } + + /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. + [EditorBrowsable(EditorBrowsableState.Never)] + public bool CanCascade { get; set; } + + /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. + [EditorBrowsable(EditorBrowsableState.Never)] + public string Class { get; set; } + + /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. + [EditorBrowsable(EditorBrowsableState.Never)] + public IList Setters { get; } + + /// This will be public opened in tizen_next after ACR done. Before ACR, need to be hidden as inhouse API. + [EditorBrowsable(EditorBrowsableState.Never)] + public IList Triggers + { + get { return _triggers ?? (_triggers = new AttachedCollection()); } + } + + void IStyle.Apply(BindableObject bindable) + { + _targets.Add(new WeakReference(bindable)); + if (BaseResourceKey != null) + bindable.SetDynamicResource(_basedOnResourceProperty, BaseResourceKey); + ApplyCore(bindable, BasedOn ?? GetBasedOnResource(bindable)); + } + + /// 6 + /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. + [EditorBrowsable(EditorBrowsableState.Never)] + public Type TargetType { get; } + + void IStyle.UnApply(BindableObject bindable) + { + UnApplyCore(bindable, BasedOn ?? GetBasedOnResource(bindable)); + bindable.RemoveDynamicResource(_basedOnResourceProperty); + _targets.RemoveAll(wr => + { + BindableObject target; + return wr.TryGetTarget(out target) && target == bindable; + }); + } + + internal bool CanBeAppliedTo(Type targetType) + { + if (TargetType == targetType) + return true; + if (!ApplyToDerivedTypes) + return false; + do + { + targetType = targetType.GetTypeInfo().BaseType; + if (TargetType == targetType) + return true; + } while (targetType != typeof(Element)); + return false; + } + + void ApplyCore(BindableObject bindable, XamlStyle basedOn) + { + if (basedOn != null) + ((IStyle)basedOn).Apply(bindable); + + foreach (Setter setter in Setters) + setter.Apply(bindable, true); + ((AttachedCollection)Behaviors).AttachTo(bindable); + ((AttachedCollection)Triggers).AttachTo(bindable); + } + + void BasedOnChanged(XamlStyle oldValue, XamlStyle newValue) + { + foreach (WeakReference bindableRef in _targets) + { + BindableObject bindable; + if (!bindableRef.TryGetTarget(out bindable)) + continue; + + UnApplyCore(bindable, oldValue); + ApplyCore(bindable, newValue); + } + } + + XamlStyle GetBasedOnResource(BindableObject bindable) + { + return (XamlStyle)bindable.GetValue(_basedOnResourceProperty); + } + + static void OnBasedOnResourceChanged(BindableObject bindable, object oldValue, object newValue) + { + // Style style = (bindable as /*VisualElement*/BaseHandle).Style; + // if (style == null) + // return; + // style.UnApplyCore(bindable, (Style)oldValue); + // style.ApplyCore(bindable, (Style)newValue); + } + + void UnApplyCore(BindableObject bindable, XamlStyle basedOn) + { + ((AttachedCollection)Triggers).DetachFrom(bindable); + ((AttachedCollection)Behaviors).DetachFrom(bindable); + foreach (Setter setter in Setters) + setter.UnApply(bindable, true); + + if (basedOn != null) + ((IStyle)basedOn).UnApply(bindable); + } + + bool ValidateBasedOn(XamlStyle value) + { + if (value == null) + return true; + return value.TargetType.IsAssignableFrom(TargetType); + } + } +} -- 2.7.4