oni-priority-ux/mod/PLibOptions/SelectOneOptionsEntry.cs

180 lines
5.8 KiB
C#

/*
* Copyright 2022 Peter Han
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Collections.Generic;
using System.Reflection;
using PeterHan.PLib.UI;
using UnityEngine;
namespace PeterHan.PLib.Options {
/// <summary>
/// An options entry which represents Enum and displays a spinner with text options.
/// </summary>
public class SelectOneOptionsEntry : OptionsEntry {
/// <summary>
/// Obtains the title and tool tip for an enumeration value.
/// </summary>
/// <param name="enumValue">The value in the enumeration.</param>
/// <param name="fieldType">The type of the Enum field.</param>
/// <returns>The matching Option</returns>
private static EnumOption GetAttribute(object enumValue, Type fieldType) {
if (enumValue == null)
throw new ArgumentNullException(nameof(enumValue));
string valueName = enumValue.ToString(), title = valueName, tooltip = "";
foreach (var enumField in fieldType.GetMember(valueName, BindingFlags.Public |
BindingFlags.Static))
if (enumField.DeclaringType == fieldType) {
// Search for OptionsAttribute
foreach (var attrib in enumField.GetCustomAttributes(false))
if (attrib is IOptionSpec spec) {
if (string.IsNullOrEmpty(spec.Title))
spec = HandleDefaults(spec, enumField);
title = LookInStrings(spec.Title);
tooltip = LookInStrings(spec.Tooltip);
break;
}
break;
}
return new EnumOption(title, tooltip, enumValue);
}
public override object Value {
get {
return chosen?.Value;
}
set {
// Find a matching value, if possible
string valueStr = value?.ToString() ?? "";
foreach (var option in options)
if (valueStr == option.Value.ToString()) {
chosen = option;
Update();
break;
}
}
}
/// <summary>
/// The chosen item in the array.
/// </summary>
protected EnumOption chosen;
/// <summary>
/// The realized item label.
/// </summary>
private GameObject comboBox;
/// <summary>
/// The available options to cycle through.
/// </summary>
protected readonly IList<EnumOption> options;
public SelectOneOptionsEntry(string field, IOptionSpec spec, Type fieldType) :
base(field, spec) {
var eval = Enum.GetValues(fieldType);
if (eval == null)
throw new ArgumentException("No values, or invalid values, for enum");
int n = eval.Length;
if (n == 0)
throw new ArgumentException("Enum has no declared members");
chosen = null;
comboBox = null;
options = new List<EnumOption>(n);
for (int i = 0; i < n; i++)
options.Add(GetAttribute(eval.GetValue(i), fieldType));
}
public override GameObject GetUIComponent() {
// Find largest option to size the label appropriately, using em width this time!
EnumOption firstOption = null;
int maxLen = 0;
foreach (var option in options) {
int len = option.Title?.Trim()?.Length ?? 0;
// Kerning each string is slow, so estimate based on em width instead
if (firstOption == null && len > 0)
firstOption = option;
if (len > maxLen)
maxLen = len;
}
comboBox = new PComboBox<EnumOption>("Select") {
BackColor = PUITuning.Colors.ButtonPinkStyle, InitialItem = firstOption,
Content = options, EntryColor = PUITuning.Colors.ButtonBlueStyle,
TextStyle = PUITuning.Fonts.TextLightStyle, OnOptionSelected = UpdateValue,
}.SetMinWidthInCharacters(maxLen).Build();
Update();
return comboBox;
}
/// <summary>
/// Updates the displayed text to match the current item.
/// </summary>
private void Update() {
if (comboBox != null && chosen != null)
PComboBox<EnumOption>.SetSelectedItem(comboBox, chosen, false);
}
/// <summary>
/// Triggered when the value chosen from the combo box has been changed.
/// </summary>
/// <param name="selected">The value selected by the user.</param>
private void UpdateValue(GameObject _, EnumOption selected) {
if (selected != null)
chosen = selected;
}
/// <summary>
/// Represents a selectable option.
/// </summary>
protected sealed class EnumOption : ITooltipListableOption {
/// <summary>
/// The option title.
/// </summary>
public string Title { get; }
/// <summary>
/// The option tool tip.
/// </summary>
public string ToolTip { get; }
/// <summary>
/// The value to assign if this option is chosen.
/// </summary>
public object Value { get; }
public EnumOption(string title, string toolTip, object value) {
Title = title ?? throw new ArgumentNullException(nameof(title));
ToolTip = toolTip;
Value = value;
}
public string GetProperName() {
return Title;
}
public string GetToolTipText() {
return ToolTip;
}
public override string ToString() {
return string.Format("Option[Title={0},Value={1}]", Title, Value);
}
}
}
}