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 } }