using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.Pool;
using UnityEngine.UIElements;
namespace RosettaUI.UIToolkit
{
///
/// PopupFieldの拡張
///
/// 1. choicesの全要素が収まるようにwidthを広げるPopupField
/// BasePopupField.PopupTextElementを継承してDoMeasure()をoverrideするのがよさそうだが、
/// 継承したクラスをBasePopupField.m_TextElementにセットする方法がなさそうなので
/// styleでminWidthを指定する
///
/// 2. ドロップダウンメニューをanchoredをfalse(指定矩形の幅を使用しない)で呼ぶ
/// BasePopupFieldが行っているanchored==true(Unity6.3以降はDropdownMenuSizeMode.Fixed)で)呼ぶとvisualInputの幅でメニューが表示されるが、
/// これが十分な幅ではないためメニュー内にスクロールバーが出てしまう
/// 外側Windowが小さいサイズにされた場合はさらにvisualInputが小さくなってしまう
///
public class PopupFieldCustom : PopupField
{
#region choicesの全要素が収まるようにwidthを広げる
public override List choices
{
get => base.choices;
set
{
if (base.choices == value || (base.choices?.SequenceEqual(value) ?? false)) return;
base.choices = value;
AdjustMinWidthWithChoices();
}
}
private void AdjustMinWidthWithChoices()
{
if (choices == null || !choices.Any()) return;
if (textElement.panel == null)
{
textElement.RegisterCallback(OnGeometryChanged);
}
else
{
DoAdjustMinWidthWithChoices();
}
void OnGeometryChanged(GeometryChangedEvent _)
{
textElement.UnregisterCallback(OnGeometryChanged);
DoAdjustMinWidthWithChoices();
}
}
private void DoAdjustMinWidthWithChoices()
{
var maxWidth = choices
.Select(GetValueToDisplay)
.Select(str => textElement.MeasureTextSize(str, 0f, MeasureMode.Undefined, 0f, MeasureMode.Undefined).x)
.Max();
textElement.style.minWidth = maxWidth;
}
private string GetValueToDisplay(T str)
{
return formatListItemCallback?.Invoke(str) ??
(choices.Contains(str)
? (str?.ToString() ?? string.Empty)
: string.Empty);
}
#endregion
#region ドロップダウンメニューをanchorをfalse(指定矩形の幅を使用しない)で呼ぶ
private static readonly FieldInfo CreateMenuCallbackFieldInfo = typeof(BasePopupField).GetField("createMenuCallback", BindingFlags.NonPublic | BindingFlags.Instance);
public PopupFieldCustom()
{
CreateMenuCallbackFieldInfo.SetValue(this, (Func)CreateCustomGenericDropdownMenu);
}
#if UNITY_6000_3_OR_NEWER
/// Dropdown()の第3引数を無視するGenericDropdownMenu
private class GenericDropdownMenuIgnoreSizeMode : GenericDropdownMenu
{
public override void DropDown(Rect position, VisualElement element, DropdownMenuSizeMode _ = DropdownMenuSizeMode.Auto)
{
base.DropDown(position, element, DropdownMenuSizeMode.Content);
}
}
private static GenericDropdownMenu CreateCustomGenericDropdownMenu()
{
return new GenericDropdownMenuIgnoreSizeMode();
}
#else
private static GenericDropdownMenu CreateCustomGenericDropdownMenu()
{
return GenericDropdownMenuIgnoreAnchoredBuilder.CreateGenericDropdownMenuIgnoreAnchored();
}
#endif
#endregion
#region choisesにIEnumerableを渡せるようにする
public bool SetChoices(IEnumerable newChoices)
{
if (ReferenceEquals(choices, newChoices)) return false;
using var _ = ListPool.Get(out var newChoicesList);
newChoicesList.AddRange(newChoices ?? Array.Empty());
if (choices?.SequenceEqual(newChoicesList) ?? false) return false;
choices = new List(newChoicesList);
return true;
}
#endregion
}
}