Files
XRLib/Assets/Sample/UIToolkit/UTKStyleGuideSample.Icon.cs

512 lines
19 KiB
C#
Raw Normal View History

2026-01-23 19:04:12 +09:00
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UIElements;
using UVC.UIToolkit;
/// <summary>
/// UTKStyleGuideSample의 Icon 카테고리 Initialize 메서드들
/// </summary>
public partial class UTKStyleGuideSample
{
#region Icon Initializers
private void InitializeUTKMaterialIconsSample(VisualElement root)
{
var font = UTKMaterialIcons.LoadFont();
if (font == null)
{
root.Add(new Label("Error: UTKMaterialIcons 폰트를 로드할 수 없습니다."));
return;
}
// Icon 코드 블록 참조 저장
_materialIconCodeBlock = root.Q<UTKCodeBlock>();
if (_materialIconCodeBlock != null && _materialIconCodeBlock.Title == "Icon")
{
UpdateMaterialIconCodeBlock("Home");
}
_iconNameList = UTKMaterialIcons.GetAllIconNames().ToList();
_filteredIconNameList = _iconNameList;
_iconCountLabel = root.Q<Label>("icon-count-label");
if (_iconCountLabel != null)
{
_iconCountLabel.text = $"총 {UTKMaterialIcons.Count}개의 아이콘 (가상화 적용)";
}
var searchField = root.Q<UTKInputField>("icon-search");
var gridContainer = root.Q<VisualElement>("icon-grid-container");
if (gridContainer != null)
{
var rowData = CreateIconRowData(_filteredIconNameList);
_iconListView = new ListView();
_iconListView.style.flexGrow = 1;
_iconListView.style.maxHeight = 500;
_iconListView.fixedItemHeight = IconItemHeight + IconItemMargin;
_iconListView.itemsSource = rowData;
_iconListView.makeItem = MakeIconRow;
_iconListView.bindItem = BindIconRow;
_iconListView.selectionType = SelectionType.None;
_iconListView.virtualizationMethod = CollectionVirtualizationMethod.FixedHeight;
gridContainer.Add(_iconListView);
}
if (searchField != null)
{
void PerformSearch(string searchValue)
{
if (_iconNameList == null || _iconListView == null || _iconCountLabel == null) return;
_filteredIconNameList = string.IsNullOrEmpty(searchValue)
? _iconNameList
: _iconNameList.FindAll(name => name.Contains(searchValue, StringComparison.OrdinalIgnoreCase));
var newRowData = CreateIconRowData(_filteredIconNameList);
_iconListView.itemsSource = newRowData;
_iconListView.Rebuild();
_iconCountLabel.text = string.IsNullOrEmpty(searchValue)
? $"총 {UTKMaterialIcons.Count}개의 아이콘 (가상화 적용)"
: $"{_filteredIconNameList.Count}개 / {UTKMaterialIcons.Count}개 아이콘 (가상화 적용)";
}
searchField.OnSubmit += PerformSearch;
searchField.OnBlurred += () => PerformSearch(searchField.Value);
}
}
private List<List<string>> CreateIconRowData(List<string> iconNames)
{
var rows = new List<List<string>>();
for (int i = 0; i < iconNames.Count; i += IconsPerRow)
{
var row = new List<string>();
for (int j = 0; j < IconsPerRow && i + j < iconNames.Count; j++)
{
row.Add(iconNames[i + j]);
}
rows.Add(row);
}
return rows;
}
private VisualElement MakeIconRow()
{
var row = new VisualElement();
row.style.flexDirection = FlexDirection.Row;
row.style.height = IconItemHeight;
for (int i = 0; i < IconsPerRow; i++)
{
var iconSlot = CreateIconSlot();
row.Add(iconSlot);
}
return row;
}
private VisualElement CreateIconSlot()
{
var item = new VisualElement();
item.name = "icon-slot";
item.style.width = IconItemWidth;
item.style.height = IconItemHeight;
item.style.marginRight = IconItemMargin;
item.style.alignItems = Align.Center;
item.style.justifyContent = Justify.Center;
item.style.borderTopLeftRadius = 4;
item.style.borderTopRightRadius = 4;
item.style.borderBottomLeftRadius = 4;
item.style.borderBottomRightRadius = 4;
item.AddToClassList("utk-icon-slot");
var iconLabel = new Label();
iconLabel.name = "icon-label";
UTKMaterialIcons.ApplyIconStyle(iconLabel, 28);
iconLabel.AddToClassList("utk-icon-slot__icon");
iconLabel.style.marginBottom = 4;
item.Add(iconLabel);
var nameLabel = new Label();
nameLabel.name = "name-label";
nameLabel.style.fontSize = 8;
nameLabel.style.whiteSpace = WhiteSpace.NoWrap;
nameLabel.style.overflow = Overflow.Hidden;
nameLabel.style.textOverflow = TextOverflow.Ellipsis;
nameLabel.style.maxWidth = IconItemWidth - 4;
nameLabel.style.unityTextAlign = TextAnchor.MiddleCenter;
nameLabel.AddToClassList("utk-icon-slot__name");
item.Add(nameLabel);
return item;
}
private void BindIconRow(VisualElement row, int index)
{
if (_iconListView?.itemsSource is not List<List<string>> rowData) return;
if (index < 0 || index >= rowData.Count) return;
var iconNames = rowData[index];
var slots = row.Query<VisualElement>("icon-slot").ToList();
for (int i = 0; i < slots.Count; i++)
{
var slot = slots[i];
if (i < iconNames.Count)
{
var iconName = iconNames[i];
var iconChar = UTKMaterialIcons.GetIcon(iconName);
slot.style.display = DisplayStyle.Flex;
var iconLabel = slot.Q<Label>("icon-label");
if (iconLabel != null)
iconLabel.text = iconChar;
var nameLabel = slot.Q<Label>("name-label");
if (nameLabel != null)
nameLabel.text = iconName;
slot.tooltip = iconName;
slot.userData = iconName;
slot.UnregisterCallback<ClickEvent>(OnIconSlotClicked);
slot.RegisterCallback<ClickEvent>(OnIconSlotClicked);
}
else
{
slot.style.display = DisplayStyle.None;
}
}
}
private void OnIconSlotClicked(ClickEvent evt)
{
if (evt.currentTarget is VisualElement slot && slot.userData is string iconName)
{
UpdateMaterialIconCodeBlock(iconName);
var csharpCode = $"UTKMaterialIcons.{iconName}";
GUIUtility.systemCopyBuffer = csharpCode;
UTKToast.Show($"복사됨: {csharpCode}");
}
}
private void UpdateMaterialIconCodeBlock(string iconName)
{
if (_materialIconCodeBlock == null) return;
var iconChar = UTKMaterialIcons.GetIcon(iconName);
if (string.IsNullOrEmpty(iconChar))
{
_materialIconCodeBlock.Code = $"// 아이콘을 찾을 수 없습니다: {iconName}";
return;
}
var iconDisplayName = UTKMaterialIcons.GetIconNameByChar(iconChar);
//첫글자만 대문자로 바꾸기
var iconDisplayNameCapitalized = char.ToUpper(iconDisplayName[0]) + iconDisplayName.Substring(1);
var unicodeHex = $"\\u{((int)iconChar[0]):x4}";
var code = $"// C# 코드\n" +
$"// Material Icon과 텍스트 함께 사용\n" +
$"var iconLabel = new UTKLabel(\"\", UTKMaterialIcons.{iconDisplayNameCapitalized});\n" +
$"\n" +
$"// Material Icon만 사용\n" +
$"var iconOnly = new UTKLabel(UTKMaterialIcons.{iconDisplayNameCapitalized}, 12);\n" +
$"<utk:UTKLabel material-icon=\"{iconDisplayName}\" icon-size=\"12\" />\n" +
$"var iconLabel = new UTKLabel(\"\", UTKMaterialIcons.{iconDisplayNameCapitalized});\n" +
$"<utk:UTKLabel text=\"\" material-icon=\"{iconDisplayName}\" />\n" +
$"\n" +
$"var circleBtn1 = new UTKButton(\"\", UTKMaterialIcons.{iconDisplayNameCapitalized}, ButtonVariant.Text, 12) {{ IconOnly = true }};\n" +
$"<utk:UTKButton icon=\"{iconDisplayName}\" icon-only=\"true\" icon-size=\"12\" variant=\"Text\" />\n" +
$"\n" +
$"var label = new Label(UTKMaterialIcons.{iconDisplayNameCapitalized});\n" +
$"UTKMaterialIcons.ApplyIconStyle(label);\n" +
$"\n" +
$"/* 유니코드: {unicodeHex} */";
_materialIconCodeBlock.Code = code;
}
private void InitializeImageIconsSample(VisualElement root)
{
// Icon 코드 블록 참조 저장
var codeBlocks = root.Query<UTKCodeBlock>().ToList();
_imageIconCodeBlock = codeBlocks.FirstOrDefault(cb => cb.Title == "Icon");
if (_imageIconCodeBlock != null && _imageIconNameList != null && _imageIconNameList.Count > 0)
{
UpdateImageIconCodeBlock(_imageIconNameList[0]);
}
_imageIconNameList = UTKImageIcons.GetAllIconNames().ToList();
_filteredImageIconNameList = _imageIconNameList;
_imageIconCountLabel = root.Q<Label>("image-icon-count-label");
if (_imageIconCountLabel != null)
{
_imageIconCountLabel.text = $"총 {UTKImageIcons.Count}개의 이미지 아이콘 (가상화 적용)";
}
var searchField = root.Q<UTKInputField>("image-icon-search");
var gridContainer = root.Q<VisualElement>("image-icon-grid-container");
if (gridContainer != null)
{
var rowData = CreateImageIconRowData(_filteredImageIconNameList);
_imageIconListView = new ListView();
_imageIconListView.style.flexGrow = 1;
_imageIconListView.style.maxHeight = 500;
_imageIconListView.fixedItemHeight = ImageIconItemHeight + ImageIconItemMargin;
_imageIconListView.itemsSource = rowData;
_imageIconListView.makeItem = MakeImageIconRow;
_imageIconListView.bindItem = BindImageIconRow;
_imageIconListView.selectionType = SelectionType.None;
_imageIconListView.virtualizationMethod = CollectionVirtualizationMethod.FixedHeight;
gridContainer.Add(_imageIconListView);
}
if (searchField != null)
{
void PerformSearch(string searchValue)
{
if (_imageIconNameList == null || _imageIconListView == null || _imageIconCountLabel == null) return;
_filteredImageIconNameList = string.IsNullOrEmpty(searchValue)
? _imageIconNameList
: _imageIconNameList.FindAll(name => name.Contains(searchValue, StringComparison.OrdinalIgnoreCase));
var newRowData = CreateImageIconRowData(_filteredImageIconNameList);
_imageIconListView.itemsSource = newRowData;
_imageIconListView.Rebuild();
_imageIconCountLabel.text = string.IsNullOrEmpty(searchValue)
? $"총 {UTKImageIcons.Count}개의 이미지 아이콘 (가상화 적용)"
: $"{_filteredImageIconNameList.Count}개 / {UTKImageIcons.Count}개 이미지 아이콘 (가상화 적용)";
}
searchField.OnSubmit += PerformSearch;
searchField.OnBlurred += () => PerformSearch(searchField.Value);
}
}
private List<List<string>> CreateImageIconRowData(List<string> iconNames)
{
var rows = new List<List<string>>();
for (int i = 0; i < iconNames.Count; i += ImageIconsPerRow)
{
var row = new List<string>();
for (int j = 0; j < ImageIconsPerRow && i + j < iconNames.Count; j++)
{
row.Add(iconNames[i + j]);
}
rows.Add(row);
}
return rows;
}
private VisualElement MakeImageIconRow()
{
var row = new VisualElement();
row.style.flexDirection = FlexDirection.Row;
row.style.height = ImageIconItemHeight;
for (int i = 0; i < ImageIconsPerRow; i++)
{
var iconSlot = CreateImageIconSlot();
row.Add(iconSlot);
}
return row;
}
private VisualElement CreateImageIconSlot()
{
var item = new VisualElement();
item.name = "image-icon-slot";
item.style.width = ImageIconItemWidth;
item.style.height = ImageIconItemHeight;
item.style.marginRight = ImageIconItemMargin;
item.style.alignItems = Align.Center;
item.style.justifyContent = Justify.Center;
item.style.borderTopLeftRadius = 4;
item.style.borderTopRightRadius = 4;
item.style.borderBottomLeftRadius = 4;
item.style.borderBottomRightRadius = 4;
item.AddToClassList("utk-icon-slot");
var iconImage = new VisualElement();
iconImage.name = "icon-image";
iconImage.style.width = 32;
iconImage.style.height = 32;
iconImage.style.marginBottom = 4;
iconImage.AddToClassList("utk-icon-slot__image");
item.Add(iconImage);
var nameLabel = new Label();
nameLabel.name = "name-label";
nameLabel.style.fontSize = 8;
nameLabel.style.whiteSpace = WhiteSpace.NoWrap;
nameLabel.style.overflow = Overflow.Hidden;
nameLabel.style.textOverflow = TextOverflow.Ellipsis;
nameLabel.style.maxWidth = ImageIconItemWidth - 4;
nameLabel.style.unityTextAlign = TextAnchor.MiddleCenter;
nameLabel.AddToClassList("utk-icon-slot__name");
item.Add(nameLabel);
return item;
}
private void BindImageIconRow(VisualElement row, int index)
{
if (_imageIconListView?.itemsSource is not List<List<string>> rowData) return;
if (index < 0 || index >= rowData.Count) return;
var iconNames = rowData[index];
var slots = row.Query<VisualElement>("image-icon-slot").ToList();
for (int i = 0; i < slots.Count; i++)
{
var slot = slots[i];
if (i < iconNames.Count)
{
var iconName = iconNames[i];
var iconPath = UTKImageIcons.GetPath(iconName);
slot.style.display = DisplayStyle.Flex;
var iconImage = slot.Q<VisualElement>("icon-image");
if (iconImage != null)
{
var texture = UTKImageIcons.LoadTextureByName(iconName);
if (texture != null)
{
iconImage.style.backgroundImage = new StyleBackground(texture);
}
else
{
iconImage.style.backgroundImage = StyleKeyword.None;
}
}
var nameLabel = slot.Q<Label>("name-label");
if (nameLabel != null)
nameLabel.text = iconName;
slot.tooltip = $"{iconName}\n{iconPath}";
slot.userData = iconName;
slot.UnregisterCallback<ClickEvent>(OnImageIconSlotClicked);
slot.RegisterCallback<ClickEvent>(OnImageIconSlotClicked);
}
else
{
slot.style.display = DisplayStyle.None;
}
}
}
private void OnImageIconSlotClicked(ClickEvent evt)
{
if (evt.currentTarget is VisualElement slot && slot.userData is string iconName)
{
UpdateImageIconCodeBlock(iconName);
var csharpCode = $"UTKImageIcons.{iconName}";
GUIUtility.systemCopyBuffer = csharpCode;
UTKToast.Show($"복사됨: {csharpCode}");
}
}
private void UpdateImageIconCodeBlock(string iconName)
{
if (_imageIconCodeBlock == null) return;
var iconPath = UTKImageIcons.GetPath(iconName);
if (string.IsNullOrEmpty(iconPath))
{
_imageIconCodeBlock.Code = $"// 아이콘을 찾을 수 없습니다: {iconName}";
return;
}
//PascalCase을 camelCase로 변환 btn_cancel_64 -> BtnCancel64
var iconDisplayNameCapitalized = "";
var words = iconName.Split('_');
for (int i = 0; i < words.Length; i++)
{
words[i] = char.ToUpper(words[i][0]) + words[i].Substring(1);
}
iconDisplayNameCapitalized = string.Join("", words);
var code = $"// C# 코드\n" +
$"// Sprite 로드\n" +
$"Sprite sprite = UTKImageIcons.{iconDisplayNameCapitalized};\n" +
$"var image = new Image {{ sprite = sprite }};\n" +
$"\n" +
$"// 또는 이름으로 로드\n" +
$"Sprite sprite2 = UTKImageIcons.LoadSpriteByName(\"{iconName}\");\n" +
$"\n" +
$"// Texture2D 로드\n" +
$"Texture2D texture = UTKImageIcons.LoadTextureByName(\"{iconName}\");\n" +
$"var image2 = new Image();\n" +
$"image2.style.backgroundImage = new StyleBackground(texture);\n" +
$"\n" +
$"// Image Icon만 사용\n" +
$"var imgOnly = new UTKLabel(UTKImageIcons.{iconDisplayNameCapitalized}, isImageIcon: true);\n" +
$"<utk:UTKLabel text=\"\" image-icon=\"{iconName}\" />\n" +
$"<utk:UTKLabel image-icon=\"{iconName}\" icon-size=\"22\" />\n" +
$"\n" +
$"var circleBtn1 = new UTKButton(\"\", UTKImageIcons.{iconDisplayNameCapitalized}, ButtonVariant.Text, 12) {{ IconOnly = true }};\n" +
$"<utk:UTKButton icon=\"{iconName}\" icon-only=\"true\" icon-size=\"12\" variant=\"Text\" />\n" +
$"\n" +
$"/* 경로: {iconPath} */\n" +
$"\n" +
$"/* UXML에서 사용 */\n" +
$"<utk:UTKLabel imageIcon=\"{iconPath}\" iconSize=\"22\" />";
_imageIconCodeBlock.Code = code;
}
private void InitializeUTKImageSample(VisualElement root)
{
// 외부 이미지 로드 버튼
var btnLoadExternal = root.Q<UTKButton>("btn-load-external");
var imgExternal = root.Q<UTKImage>("img-external");
var imgExternalStatus = root.Q<Label>("img-external-status");
if (btnLoadExternal != null && imgExternal != null)
{
btnLoadExternal.RegisterCallback<ClickEvent>(_ =>
{
if (imgExternalStatus != null)
imgExternalStatus.text = "로드 중...";
// 샘플 외부 이미지 URL (placeholder 이미지)
const string sampleUrl = "https://picsum.photos/200";
imgExternal.OnImageLoaded += (texture) =>
{
if (imgExternalStatus != null)
imgExternalStatus.text = $"로드 완료 ({texture.width}x{texture.height})";
};
imgExternal.OnImageFailed += (error) =>
{
if (imgExternalStatus != null)
imgExternalStatus.text = $"로드 실패: {error}";
};
imgExternal.Src = sampleUrl;
});
}
}
#endregion
}