Skip to content

KeyHandler with a custom window

PostCyberPunk edited this page Sep 15, 2023 · 2 revisions

SVBMData.cs
using UnityEngine;
using PCP.WhichKey.Types;
using Unity.Mathematics;
using UnityEditor;
using System.Collections.Generic;
[System.Serializable]
public struct SVBMSet
{
    public WkKeySeq Key;
    public string Hint;
    [SerializeReference]
    public Texture2D Texture;

    [HideInInspector]
    public Vector3 Pos;
    [HideInInspector]
    public quaternion Rot;
    [HideInInspector]
    public bool Mode;
    [HideInInspector]
    public float Size;
    public SVBMSet(int key, SceneView sv, Texture2D tex)
    {
        //wkkeyseq has implicit operator from int and int[],so we can use int directly
        Key = key;
        Hint = "Bookmark";
        Pos = sv.pivot;
        Rot = sv.rotation;
        Size = sv.size;
        Mode = sv.in2DMode;
        Texture = tex;
    }
}

public static class SVBMExtension
{
    public static bool HasKey(this SVBMSet[] array, int key)
    {
        foreach (var set in array)
        {
            if (set.Key == key)
                return true;
        }
        return false;
    }
}

[CreateAssetMenu(fileName = "SVBMData", menuName = "WhichkeyDemo/SVBMData")]
public class SVBMData : ScriptableObject
{
    public List<SVBMSet> mSVBMSets=new();
}
SVBMWin.cs
using System.Collections.Generic;
using UnityEngine.UIElements;
using PCP.WhichKey.Types;
using UnityEngine;
public class SVBMWin : WkBaseWindow
{
	private readonly float itemWidth = 400;
	private readonly float itemHeight = 200;
	public List<SVBMSet> Bookmarks { get; set; }
	protected override void ShowHints()
	{
		if (Bookmarks == null)
			return;
		rootVisualElement.style.flexDirection = FlexDirection.Row;
		rootVisualElement.style.flexWrap = Wrap.Wrap;
		rootVisualElement.style.backgroundColor = new Color(0.118f, 0.118f, 0.18f);
		foreach (var set in Bookmarks)
		{
			var e = CreateItem(set.Key.KeyLabel, set.Hint, set.Texture);
			rootVisualElement.Add(e);
		}
		// the size of the window is based on mHeight and mWidth
		//
		winWidth = 800;
		winHeight = Mathf.CeilToInt(Bookmarks.Count / 2f) * itemHeight;
		//You can use this method to make your window follow the Preferences
		SetWindowPosition();
		//alternatively,you can set the position manually
	}
	// you should use uxml or some custom controls for this.
	private VisualElement CreateItem(string key, string hint, Texture2D tex)
	{
		var e = new VisualElement();
		e.style.width = itemWidth;
		e.style.height = itemHeight;

		var keyl = new Label(key);
		var img = new Image() { image = tex };
		var hintl = new Label(hint);

		hintl.style.fontSize = 20;
		hintl.style.alignSelf = Align.Center;

		keyl.style.fontSize = 40;
		keyl.style.unityTextAlign = TextAnchor.MiddleCenter;
		keyl.style.alignSelf = Align.Center;
		keyl.style.color = new Color(0.651f, 0.89f, 0.631f);
		keyl.style.backgroundColor = new Color(0, 0, 0, 0.5f);

		img.style.flexGrow = 1;
		img.style.justifyContent = Justify.Center;
		img.Add(keyl);
		e.Add(img);
		e.Add(hintl);
		return e;
	}
}
SVBMHandler.cs
using UnityEngine;
using UnityEditor;
using UnityEditorInternal;
using PCP.WhichKey.Types;
using System.Collections.Generic;

public class SVBMHandler : WindowKeyHandler<SVBMWin>
{
	private SVBMData mSVBMDATA;
	private List<SVBMSet> bookmarks => mSVBMDATA.mSVBMSets;
	public bool Save { get; set; }
	//!!!Minimal example,for demonstration only,this is a very BAD way to save Bookmarks
	//you can use EditorPrefs or ScriptableObject to save data
	//if you dont know how ,take a look at extra wkExtraManager.cs and SceneNav
	//every project is different,there are many way to save bookmark data,even use mono script,you need find a way that fits your project
	public SVBMHandler()
	{
		mSVBMDATA = AssetDatabase.LoadAssetAtPath<SVBMData>("Assets/SVBMData.asset");
		if (mSVBMDATA == null)
		{
			AssetDatabase.CreateAsset(ScriptableObject.CreateInstance<SVBMData>(), "Assets/SVBMData.asset");
			AssetDatabase.Refresh();
			mSVBMDATA = AssetDatabase.LoadAssetAtPath<SVBMData>("Assets/SVBMData.asset");
		}
	}
	//this method will be called before handler processing key,so we can assign data to window or reset some data
	//HINT: whichkey use PopuuWindow,the window will be an new instance when you active this handler,so you need to assign window data here or use a static data.
	public override void OnActive()
	{
		window.Bookmarks = bookmarks;
	}
	//the int key is ASCII code of the key pressed,there is some change,you can take a look at core/editor/utils/keycodeExtesnion.cs
	public override void HandleKey(int key)
	{
		if (mSVBMDATA == null)
			Debug.Log("SVBMData not found");
		if (SceneView.lastActiveSceneView == null)
			Debug.Log("No SceneView found");
		else if (Save)
			SaveBookmark(key);
		else
			LoadBookmark(key);
		//!!You MUST close the window after everything is done!
		//if want use a multi-layer handle,you can update your hints first ,then call window.UpdateHints() to notify window to update
		window.Close();
	}
	private void LoadBookmark(int key)
	{
		foreach (SVBMSet set in bookmarks)
		{
			if (set.Key == key)
			{
				var sv = SceneView.lastActiveSceneView;
				sv.in2DMode = set.Mode;
				sv.pivot = set.Pos;
				if (!set.Mode)
					sv.rotation = set.Rot;
				sv.size = set.Size;
				return;
			}
		}
		Debug.Log("No bookmark found");
	}
	private void SaveBookmark(int key)
	{
		for (int i = 0; i < bookmarks.Count; i++)
		{
			if (bookmarks[i].Key == key)
			{
				bookmarks[i] = new SVBMSet(key, SceneView.lastActiveSceneView, CreateThumbnail(key));
				return;
			}
		}
		bookmarks.Add(new SVBMSet(key, SceneView.lastActiveSceneView, CreateThumbnail(key)));
	}
	//an utils function to create thumbnail,
	private Texture2D CreateThumbnail(int key)
	{
		var sv = SceneView.lastActiveSceneView;
		//create texturea
		sv.Show();
		var rect = sv.position;
		Texture2D tex = new((int)rect.width, (int)rect.height);
		tex.SetPixels(InternalEditorUtility.ReadScreenPixel(new Vector2(rect.x, rect.y), tex.width, tex.height));
		//save to sprite
		var bytes = tex.EncodeToPNG();
		var path = "Assets/WhichKeyDemo/" + key.ToString() + ".png";
		System.IO.File.WriteAllBytes(path, bytes);
		AssetDatabase.Refresh();
		tex = AssetDatabase.LoadAssetAtPath<Texture2D>(path);
		return tex;
	}
}
SVBMCmd.cs
using PCP.WhichKey.Types;
using UnityEngine;

public class SVBMCommand : ChangeWinHandlerCmd
{
	//lets create a static handler for this command,because every factory will create a command object,we only need one handler
	public static SVBMHandler handler = new SVBMHandler();
	//assgin the handler to the command
	public override BaseWinKeyHandler Handler => handler;
	// add a bool so we can know if the bookmark is save or load
	public bool Save;
	//this method will be called before handler processing key
	public override void OnActive()
	{
		handler.Save = Save;
	}
}
public class SVBMCmdFactory : WKCommandFactory
{

	//the id of this Factory, must be unique,this is the CmdType of keyset.
	//since i may develop some addtinal commands,so lets start from 101
	public override int TID => 101;
	//the name of the command, the name that shows in Type Dropdown
	public override string CommandName => "SceneViewBookMark";
	//we dont need arg for this command,i mean you can set Commaand save or load by using arg,but lets keep arg empty in case we may need it later
	//i will add some serialization later,so you can use multiple arg with defined type,stay tune for update notes
	public override WKCommand CreateCommand(string arg)
	{
		return new SVBMCommand();
	}
}
public class SVBMSaveCmdFactory : WKCommandFactory
{
	//dont forget to change your factory id
	public override int TID => 102;
	public override string CommandName => "SceneViewBookMarkSave";
	//in this command we set Save to true
	public override WKCommand CreateCommand(string arg)
	{
		return new SVBMCommand() { Save = true };
	}
}
Clone this wiki locally