/*
* 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)}");
}
}
}
}