/* * Copyright(c) 2020-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.Generic; using System.ComponentModel; using System.Linq; namespace Tizen.NUI.BaseComponents { /// /// Class for describing the states of control. /// If a non-control view class would want to get the control state, please refer . /// /// 9 [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"); /// /// Normal State. /// /// 9 public static readonly ControlState Normal = Create("Normal"); /// /// Focused State. /// /// 9 public static readonly ControlState Focused = Create("Focused"); /// /// Pressed State. /// /// 9 public static readonly ControlState Pressed = Create("Pressed"); /// /// Disabled State. /// /// 9 public static readonly ControlState Disabled = Create("Disabled"); /// /// Selected State. /// /// 9 public static readonly ControlState Selected = Create("Selected"); /// /// SelectedPressed State. /// [EditorBrowsable(EditorBrowsableState.Never)] public static readonly ControlState SelectedPressed = Selected + Pressed; /// /// DisabledSelected State. /// [EditorBrowsable(EditorBrowsableState.Never)] public static readonly ControlState DisabledSelected = Disabled + Selected; /// /// DisabledFocused State. /// [EditorBrowsable(EditorBrowsableState.Never)] public static readonly ControlState DisabledFocused = Disabled + Focused; /// /// SelectedFocused State. /// [EditorBrowsable(EditorBrowsableState.Never)] public static readonly ControlState SelectedFocused = Selected + Focused; /// /// 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"); private List stateList = new List(); private readonly string name = ""; /// /// Gets or sets a value indicating whether it has combined states. /// [EditorBrowsable(EditorBrowsableState.Never)] public bool IsCombined => stateList.Count > 1; private ControlState() { } private ControlState(string name) : this() => this.name = name; /// /// Create an instance of the with state name. /// /// The state name. /// The instance which has single state. /// Thrown when the given name is null. /// Thrown when the given name is invalid. /// 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; } /// /// Create an instance of the with combined states. /// /// The control state array. /// The instance which has combined state. [EditorBrowsable(EditorBrowsableState.Never)] public static ControlState Create(params ControlState[] states) { if (states.Length == 1) return states[0]; ControlState newState = new ControlState(); for (int i = 0; i < states.Length; i++) { if (states[i] == Normal) continue; 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]; } return newState; } /// /// Determines whether a state contains a specified state. /// /// The state to search for /// true if the state contain a specified state, otherwise, false. /// Thrown when the given state is null. /// 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; } /// [EditorBrowsable(EditorBrowsableState.Never)] public bool Equals(ControlState other) { if (other is null || stateList.Count != other.stateList.Count) return false; return Contains(other); } /// /// 9 public override bool Equals(object other) => this.Equals(other as ControlState); /// [EditorBrowsable(EditorBrowsableState.Never)] public override int GetHashCode() => (name.GetHashCode() * 397) ^ IsCombined.GetHashCode(); /// [EditorBrowsable(EditorBrowsableState.Never)] public override string ToString() { string name = ""; for (int i = 0; i < stateList.Count; i++) { name += ((i == 0) ? "" : ", ") + stateList[i].name; } return name; } /// /// Compares whether the two ControlStates are same or not. /// /// A on the left hand side. /// A on the right hand side. /// true if the ControlStates are equal; otherwise, false. /// 9 public static bool operator ==(ControlState lhs, ControlState rhs) { // Check for null on left side. if (lhs is null) { if (rhs is null) { // null == null = true. return true; } // Only the left side is null. return false; } // Equals handles case of null on right side. return lhs.Equals(rhs); } /// /// Compares whether the two ControlStates are different or not. /// /// A on the left hand side. /// A on the right hand side. /// true if the ControlStates are not equal; otherwise, false. /// 9 public static bool operator !=(ControlState lhs, ControlState rhs) => !(lhs == rhs); /// /// The addition operator. /// /// A on the left hand side. /// 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); /// /// The substraction operator. /// /// A on the left hand side. /// A on the right hand side. /// The containing the result of the substraction. /// Thrown when lhs or rhs is null. [EditorBrowsable(EditorBrowsableState.Never)] public static ControlState operator -(ControlState lhs, ControlState rhs) { if (null == lhs) { throw new ArgumentNullException(nameof(lhs)); } else if (null == rhs) { throw new ArgumentNullException(nameof(rhs)); } if (!lhs.IsCombined) { return ReferenceEquals(lhs, rhs) ? Normal : lhs; } var rest = lhs.stateList.Except(rhs.stateList); var count = rest.Count(); if (count == 0) { return Normal; } if (count == 1) { return rest.First(); } ControlState newState = new ControlState(); newState.stateList.AddRange(rest); return newState; } class ControlStateTypeConverter : Binding.TypeConverter { public override object ConvertFromInvariantString(string value) { if (value != null) { value = value.Trim(); ControlState convertedState = new ControlState(); string[] parts = value.Split(','); foreach (string part in parts) { convertedState += Create(part); } return convertedState; } throw new InvalidOperationException($"Cannot convert \"{value}\" into {typeof(ControlState)}"); } } } }