diff --git a/src/Tizen.NUI/src/internal/Common/ControlStateUtility.cs b/src/Tizen.NUI/src/internal/Common/ControlStateUtility.cs new file mode 100644 index 00000000000..c87903425fe --- /dev/null +++ b/src/Tizen.NUI/src/internal/Common/ControlStateUtility.cs @@ -0,0 +1,72 @@ +/* + * Copyright(c) 2025 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.Generic; + +namespace Tizen.NUI +{ + /// + /// Manages state name and bit mask. + /// + internal static class ControlStateUtility + { + private const int MaxBitWidth = 32; + private static readonly Dictionary registeredStates = new Dictionary(); + private static int nextBitPosition = 0; + + /// + /// + public static long FullMask => (1L << MaxBitWidth) - 1L; + + /// + /// + public static IEnumerable<(string, long)> RegisteredStates() + { + foreach (var (key, value) in registeredStates) + { + yield return (key, value); + } + } + + public static long Register(string stateName) + { + if (stateName == null) + throw new ArgumentNullException($"{nameof(stateName)} cannot be null.", nameof(stateName)); + + if (string.IsNullOrWhiteSpace(stateName)) + throw new ArgumentException($"{nameof(stateName)} cannot be whitespace.", nameof(stateName)); + + string trimmed = stateName.Trim().ToLowerInvariant(); + + if (!registeredStates.TryGetValue(trimmed, out long bitMask)) + { + if (nextBitPosition + 1 > MaxBitWidth) + { + throw new ArgumentException($"The given state name '{stateName}' is not acceptable since there is no more room to register a new state."); + } + + bitMask = 1L << nextBitPosition; + registeredStates.Add(trimmed, bitMask); + + nextBitPosition++; + } + + return bitMask; + } + } +} \ No newline at end of file diff --git a/src/Tizen.NUI/src/public/BaseComponents/ControlState.cs b/src/Tizen.NUI/src/public/BaseComponents/ControlState.cs index 9c557e14b74..9b633b49144 100755 --- a/src/Tizen.NUI/src/public/BaseComponents/ControlState.cs +++ b/src/Tizen.NUI/src/public/BaseComponents/ControlState.cs @@ -1,5 +1,5 @@ /* - * Copyright(c) 2020-2021 Samsung Electronics Co., Ltd. + * Copyright(c) 2020-2025 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. @@ -16,9 +16,8 @@ */ using System; -using System.Collections.Generic; +using System.Text; using System.ComponentModel; -using System.Linq; namespace Tizen.NUI.BaseComponents { @@ -30,38 +29,37 @@ namespace Tizen.NUI.BaseComponents [Binding.TypeConverter(typeof(ControlStateTypeConverter))] public class ControlState : IEquatable { - private static readonly Dictionary stateDictionary = new Dictionary(); //Default States /// /// The All state is used in a selector class. It represents all states, so if this state is defined in a selector, the other states are ignored. /// /// 9 - public static readonly ControlState All = Create("All"); + public static readonly ControlState All = new ControlState(ControlStateUtility.FullMask); /// /// Normal State. /// /// 9 - public static readonly ControlState Normal = Create("Normal"); + public static readonly ControlState Normal = new ControlState(0L); /// /// Focused State. /// /// 9 - public static readonly ControlState Focused = Create("Focused"); + public static readonly ControlState Focused = new ControlState(nameof(Focused)); /// /// Pressed State. /// /// 9 - public static readonly ControlState Pressed = Create("Pressed"); + public static readonly ControlState Pressed = new ControlState(nameof(Pressed)); /// /// Disabled State. /// /// 9 - public static readonly ControlState Disabled = Create("Disabled"); + public static readonly ControlState Disabled = new ControlState(nameof(Disabled)); /// /// Selected State. /// /// 9 - public static readonly ControlState Selected = Create("Selected"); + public static readonly ControlState Selected = new ControlState(nameof(Selected)); /// /// SelectedPressed State. /// @@ -86,20 +84,24 @@ public class ControlState : IEquatable /// This is used in a selector class. It represents all other states except for states that are already defined in a selector. /// /// 9 - public static readonly ControlState Other = Create("Other"); + public static readonly ControlState Other = new ControlState(nameof(Other)); - private List stateList = new List(); - private readonly string name = ""; + readonly long bitFlags; /// /// Gets or sets a value indicating whether it has combined states. /// [EditorBrowsable(EditorBrowsableState.Never)] - public bool IsCombined => stateList.Count > 1; + public bool IsCombined => bitFlags != 0L && Math.Ceiling(Math.Log2(bitFlags)) != Math.Floor(Math.Log2(bitFlags)); - private ControlState() { } + ControlState(long bitMask) + { + bitFlags = bitMask; + } - private ControlState(string name) : this() => this.name = name; + private ControlState(string name) : this(ControlStateUtility.Register(name)) + { + } /// /// Create an instance of the with state name. @@ -111,20 +113,7 @@ private ControlState() { } /// 9 public static ControlState Create(string name) { - if (name == null) - throw new ArgumentNullException(nameof(name)); - if (string.IsNullOrWhiteSpace(name)) - throw new ArgumentException("name cannot be empty string", nameof(name)); - - name = name.Trim(); - - if (stateDictionary.TryGetValue(name, out ControlState state)) - return state; - - state = new ControlState(name); - state.stateList.Add(state); - stateDictionary.Add(name, state); - return state; + return new ControlState(name); } /// @@ -138,7 +127,8 @@ public static ControlState Create(params ControlState[] states) if (states.Length == 1) return states[0]; - ControlState newState = new ControlState(); + var newState = new ControlState(0L); + for (int i = 0; i < states.Length; i++) { if (states[i] == Normal) @@ -147,17 +137,7 @@ public static ControlState Create(params ControlState[] states) if (states[i] == All) return All; - newState.stateList.AddRange(states[i].stateList); - } - - if (newState.stateList.Count == 0) - return Normal; - - newState.stateList = newState.stateList.Distinct().ToList(); - - if (newState.stateList.Count == 1) - { - return newState.stateList[0]; + newState += states[i]; } return newState; @@ -172,38 +152,16 @@ public static ControlState Create(params ControlState[] states) /// 9 public bool Contains(ControlState state) { - if (state == null) - throw new ArgumentNullException(nameof(state)); - - if (!IsCombined) - return ReferenceEquals(this, state); - - bool found; - for (int i = 0; i < state.stateList.Count; i++) - { - found = false; - for (int j = 0; j < stateList.Count; j++) - { - if (ReferenceEquals(state.stateList[i], stateList[j])) - { - found = true; - break; - } - } - if (!found) return false; - } - - return true; + if (state is null) return false; + return (bitFlags & state.bitFlags) == state.bitFlags; } /// [EditorBrowsable(EditorBrowsableState.Never)] public bool Equals(ControlState other) { - if (other is null || stateList.Count != other.stateList.Count) - return false; - - return Contains(other); + if (other is null) return false; + return this.bitFlags == other.bitFlags; } /// @@ -212,18 +170,24 @@ public bool Equals(ControlState other) /// [EditorBrowsable(EditorBrowsableState.Never)] - public override int GetHashCode() => (name.GetHashCode() * 397) ^ IsCombined.GetHashCode(); + public override int GetHashCode() => bitFlags.GetHashCode(); /// [EditorBrowsable(EditorBrowsableState.Never)] public override string ToString() { - string name = ""; - for (int i = 0; i < stateList.Count; i++) + var sbuilder = new StringBuilder(); + + foreach (var (name, bitMask) in ControlStateUtility.RegisteredStates()) { - name += ((i == 0) ? "" : ", ") + stateList[i].name; + if ((bitFlags & bitMask) > 0) + { + if (sbuilder.Length != 0) sbuilder.Append(", "); + sbuilder.Append(name); + } } - return name; + + return sbuilder.ToString(); } /// @@ -247,7 +211,6 @@ public override string ToString() // Only the left side is null. return false; } - // Equals handles case of null on right side. return lhs.Equals(rhs); } @@ -267,7 +230,10 @@ public override string ToString() /// A on the right hand side. /// The containing the result of the addition. /// 9 - public static ControlState operator +(ControlState lhs, ControlState rhs) => Create(lhs, rhs); + public static ControlState operator +(ControlState lhs, ControlState rhs) + { + return Add(lhs, rhs); + } /// /// The substraction operator. @@ -279,36 +245,55 @@ public override string ToString() [EditorBrowsable(EditorBrowsableState.Never)] public static ControlState operator -(ControlState lhs, ControlState rhs) { - if (null == lhs) + return Remove(lhs, rhs); + } + + /// + /// Add multiple states. + /// + /// The first operand object. + /// The second operand object. + /// The rest operand objects. + /// The operation result. + static ControlState Add(ControlState operand1, ControlState operand2, params ControlState[] rest) + { + if (operand1 is null) { - throw new ArgumentNullException(nameof(lhs)); + throw new ArgumentNullException(nameof(operand1)); } - else if (null == rhs) + if (operand2 is null) { - throw new ArgumentNullException(nameof(rhs)); + throw new ArgumentNullException(nameof(operand2)); } - if (!lhs.IsCombined) + long newBitFlags = operand1.bitFlags | operand2.bitFlags; + + foreach (var state in rest) { - return ReferenceEquals(lhs, rhs) ? Normal : lhs; + newBitFlags |= state.bitFlags; } - var rest = lhs.stateList.Except(rhs.stateList); - var count = rest.Count(); + return new ControlState(newBitFlags); + } - if (count == 0) + /// + /// Remove a state from another. + /// + /// The first operand object. + /// The second operand object. + /// The operation result. + static ControlState Remove(ControlState operand1, ControlState operand2) + { + if (operand1 is null) { - return Normal; + throw new ArgumentNullException(nameof(operand1)); } - - if (count == 1) + if (operand2 is null) { - return rest.First(); + throw new ArgumentNullException(nameof(operand2)); } - ControlState newState = new ControlState(); - newState.stateList.AddRange(rest); - return newState; + return new ControlState(operand1.bitFlags & ~(operand2.bitFlags)); } class ControlStateTypeConverter : Binding.TypeConverter @@ -319,7 +304,7 @@ public override object ConvertFromInvariantString(string value) { value = value.Trim(); - ControlState convertedState = new ControlState(); + ControlState convertedState = new ControlState(0L); string[] parts = value.Split(','); foreach (string part in parts) {