UnityのUI Toolkitでコピー&ペーストなどのショートカットをハンドリングするCommand Eventの使い方についてまとめました。
Unity 2022.2.19
Command Eventとは
Command Eventをハンドリングすると、キーボードショートカットやメニューの実行により、コピーやペーストなどが行われた時の処理を記述できます。
このイベントはUnityEditor専用のイベントとなり、ランタイムでは使えないのでご注意ください。
コピー&ペーストをハンドリングする
それでは実際にCommand Eventを使ってコピー&ペーストをハンドリングします。
Command Eventをハンドリングするには、ValidateCommandEvent
とExecuteCommandEvent
の二つのイベントについてのコールバックを登録します。
using System; using System.Linq; using UnityEditor; using UnityEngine; using UnityEngine.UIElements; public sealed class ExampleWindow : EditorWindow { private readonly Color[] _sourceColors = { Color.Lerp(Color.red, Color.black, 0.25f), Color.Lerp(Color.green, Color.black, 0.25f), Color.Lerp(Color.blue, Color.black, 0.25f), Color.Lerp(Color.yellow, Color.black, 0.25f), Color.Lerp(Color.cyan, Color.black, 0.25f), Color.Lerp(Color.magenta, Color.black, 0.25f) }; private ListView _listView; public void CreateGUI() { _listView = new ListView(); _listView.makeItem = () => { var root = new VisualElement { style = { height = 20, flexDirection = new StyleEnum<FlexDirection>(FlexDirection.Row) } }; // アイコン var icon = new VisualElement { name = "icon", style = { width = 16, height = 16, marginBottom = 2, marginLeft = 2, marginRight = 2, marginTop = 2 } }; root.Add(icon); // ラベル var label = new Label { name = "label", style = { unityTextAlign = TextAnchor.MiddleCenter } }; root.Add(label); return root; }; _listView.bindItem = (element, i) => { var color = (Color)_listView.itemsSource[i]; // アイコンを設定 var icon = element.Q("icon"); icon.style.backgroundColor = color; // ラベルを設定 var label = element.Q<Label>("label"); label.text = ColorToString(color); }; _listView.selectionType = SelectionType.Single; _listView.itemsSource = _sourceColors; _listView.RegisterCallback<ValidateCommandEvent>(OnValidateCommand); _listView.RegisterCallback<ExecuteCommandEvent>(OnExecuteCommand); rootVisualElement.Add(_listView); } [MenuItem("Window/Example")] private static void Open() { GetWindow<ExampleWindow>(); } // コマンドが実行可能かどうかを判定する // 当該コマンドが実行可能だったらStopPropagationを呼ぶ private void OnValidateCommand(ValidateCommandEvent evt) { switch (evt.commandName) { case "Copy" when _listView.selectedIndices.Any(): { Debug.Log("stop"); evt.StopPropagation(); break; } case "Paste" when IsColorString(EditorGUIUtility.systemCopyBuffer): { evt.StopPropagation(); break; } } } // コマンドを実行する // コマンドが実行されたらStopPropagationを呼ぶprivate void OnExecuteCommand(ExecuteCommandEvent evt) { switch (evt.commandName) { case "Copy": { evt.StopPropagation(); var selectedColor = (Color)_listView.itemsSource[_listView.selectedIndex]; EditorGUIUtility.systemCopyBuffer = ColorToString(selectedColor); break; } case "Paste": { evt.StopPropagation(); var color = StringToColor(EditorGUIUtility.systemCopyBuffer); _listView.itemsSource[_listView.selectedIndex] = color; _listView.RefreshItem(_listView.selectedIndex); break; } } } private static string ColorToString(Color color) { return $"#{ColorUtility.ToHtmlStringRGB(color)}"; } private static Color StringToColor(string colorString) { if (!ColorUtility.TryParseHtmlString(colorString, out var color)) throw new Exception($"Invalid color string: {colorString}"); return color; } private static bool IsColorString(string colorString) { return ColorUtility.TryParseHtmlString(colorString, out _); } }
説明はコメントに書いた通りです。
コマンドが発行されるとまず、ValidateCommandEvent
が送られます。
このコマンドが実行可能だったら、StopPropagation
を呼んで伝播を止めます。
ValidateCommandEvent
の伝播を止めた場合には、その後にExecuteCommandEvent
が送られます。
ExecuteCommandEvent
にはコマンドの実行処理を書いて、StopPropagation
してイベントの伝播を止めます。
動作確認
Example > Windowからウィンドウを開くと以下の結果が得られます。
いずれかの行を選択してコピーし他の行にペーストすると、行の色が貼り付けられることを確認できます。
対応しているコマンド
マニュアルによるとコマンドには以下の種類があるようです。(詳細は調べてません)
Copy
Cut
Paste
Delete
SoftDelete
Duplicate
FrameSelected
FrameSelectedWithLock
SelectAll
Find
FocusProjectWindow