init
This commit is contained in:
8
Assets/Oculus/Voice/Features/Hub/Scripts.meta
Normal file
8
Assets/Oculus/Voice/Features/Hub/Scripts.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 79bd2993a06d3ea47abef1290db75547
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Oculus/Voice/Features/Hub/Scripts/Editor.meta
Normal file
8
Assets/Oculus/Voice/Features/Hub/Scripts/Editor.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 36165b1ffe137194cb78c4bfa33954fb
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
48
Assets/Oculus/Voice/Features/Hub/Scripts/Editor/ImagePage.cs
Normal file
48
Assets/Oculus/Voice/Features/Hub/Scripts/Editor/ImagePage.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
using Meta.Voice.Hub.Attributes;
|
||||
using Meta.Voice.Hub.Interfaces;
|
||||
using Meta.Voice.Hub.UIComponents;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Meta.Voice.Hub
|
||||
{
|
||||
[MetaHubPageScriptableObject]
|
||||
public class ImagePage : ScriptableObject
|
||||
{
|
||||
[SerializeField] public Texture2D image;
|
||||
}
|
||||
|
||||
[CustomEditor(typeof(ImagePage))]
|
||||
public class ImageDisplayScriptableObjectEditor : Editor
|
||||
{
|
||||
private ImagePage _imageDisplay;
|
||||
private ImageView _imageView;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
_imageDisplay = (ImagePage)target;
|
||||
_imageView = new ImageView(this);
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
if (_imageDisplay.image)
|
||||
{
|
||||
_imageView.Draw(_imageDisplay.image);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Draw the default properties
|
||||
base.OnInspectorGUI();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a249c5c9df69431f935e9e49b478eab4
|
||||
timeCreated: 1680673274
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8d6e8cb15dbf44a0b9fb35536ecd9d5f
|
||||
timeCreated: 1680898102
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
namespace Meta.Voice.Hub.Interfaces
|
||||
{
|
||||
public interface IOverrideSize
|
||||
{
|
||||
float OverrideWidth { get; set; }
|
||||
float OverrideHeight { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ab13218e15145a792b14452a94dcf16
|
||||
timeCreated: 1680898110
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 61bdbee982945e342b8e4e3766304221
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using System.Text.RegularExpressions;
|
||||
using Meta.Voice.Hub.Interfaces;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace Meta.Voice.Hub.Markdown
|
||||
{
|
||||
[CustomEditor(typeof(MarkdownPage))]
|
||||
public class MarkdownInspector : Editor, IOverrideSize
|
||||
{
|
||||
private GUIStyle _linkStyle;
|
||||
private GUIStyle _normalTextStyle;
|
||||
private GUIStyle _imageLabelStyle;
|
||||
private Dictionary<string, Texture2D> _cachedImages = new Dictionary<string, Texture2D>();
|
||||
private Texture2D _badImageTex;
|
||||
|
||||
private Vector2 scrollView;
|
||||
|
||||
public float OverrideWidth { get; set; } = -1;
|
||||
public float OverrideHeight { get; set; } = -1;
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
float padding = 55;
|
||||
var markdownPage = ((MarkdownPage)target);
|
||||
if (!markdownPage)
|
||||
{
|
||||
base.OnInspectorGUI();
|
||||
return;
|
||||
}
|
||||
|
||||
var markdownFile = markdownPage.MarkdownFile;
|
||||
if (!markdownFile)
|
||||
{
|
||||
base.OnInspectorGUI();
|
||||
return;
|
||||
}
|
||||
|
||||
var text = markdownFile.text;
|
||||
|
||||
if (_linkStyle == null)
|
||||
{
|
||||
_linkStyle = new GUIStyle(GUI.skin.label)
|
||||
{
|
||||
richText = true,
|
||||
wordWrap = true,
|
||||
alignment = TextAnchor.MiddleLeft
|
||||
};
|
||||
}
|
||||
|
||||
if (_normalTextStyle == null)
|
||||
{
|
||||
_normalTextStyle = new GUIStyle(GUI.skin.label)
|
||||
{
|
||||
wordWrap = true,
|
||||
richText = true,
|
||||
alignment = TextAnchor.MiddleLeft
|
||||
};
|
||||
}
|
||||
|
||||
Event currentEvent = Event.current;
|
||||
Regex urlRegex = new Regex(@"(https?://[^\s]+)");
|
||||
Regex imageRegex = new Regex(@"!\[(.*?)\]\((.*?)\)");
|
||||
Regex splitRegex = new Regex(@"(!\[.*?\]\(.*?\))|(https?://[^\s]+)");
|
||||
string[] parts = splitRegex.Split(text);
|
||||
|
||||
scrollView = GUILayout.BeginScrollView(scrollView);
|
||||
var windowWidth = (OverrideWidth > 0 ? OverrideWidth : EditorGUIUtility.currentViewWidth) - padding;
|
||||
GUILayout.BeginVertical(GUILayout.Width(windowWidth));
|
||||
foreach (string part in parts)
|
||||
{
|
||||
if (imageRegex.IsMatch(part))
|
||||
{
|
||||
Match imageMatch = imageRegex.Match(part);
|
||||
|
||||
if (imageMatch.Success)
|
||||
{
|
||||
string imagePath = imageMatch.Groups[2].Value;
|
||||
|
||||
if (!_cachedImages.ContainsKey(imagePath))
|
||||
{
|
||||
if (urlRegex.IsMatch(imagePath))
|
||||
{
|
||||
LoadImageFromUrl(imagePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
var path = AssetDatabase.GetAssetPath(markdownPage);
|
||||
var dir = Path.GetDirectoryName(path);
|
||||
Texture2D image = AssetDatabase.LoadAssetAtPath<Texture2D>(dir + "/" + imagePath);
|
||||
if (!image)
|
||||
{
|
||||
// Get the path of target markdown file
|
||||
string markdownPath = AssetDatabase.GetAssetPath(markdownFile);
|
||||
// Get the directory of the markdown file
|
||||
string markdownDir = System.IO.Path.GetDirectoryName(markdownPath);
|
||||
image = AssetDatabase.LoadAssetAtPath<Texture2D>(Path.Combine(markdownDir,
|
||||
imagePath));
|
||||
if (!image) image = _badImageTex;
|
||||
}
|
||||
|
||||
_cachedImages[imagePath] = image;
|
||||
}
|
||||
}
|
||||
|
||||
if (_cachedImages.TryGetValue(imagePath, out Texture2D img) && img && img != _badImageTex)
|
||||
{
|
||||
float aspectRatio = 1;
|
||||
float width = img.width;
|
||||
float height = img.height;
|
||||
if (img.width > windowWidth - padding)
|
||||
{
|
||||
width = windowWidth - padding;
|
||||
aspectRatio = img.width / (float) img.height;
|
||||
height = width / aspectRatio;
|
||||
}
|
||||
|
||||
if (null == _imageLabelStyle)
|
||||
{
|
||||
_imageLabelStyle = new GUIStyle(GUI.skin.label)
|
||||
{
|
||||
alignment = TextAnchor.MiddleCenter,
|
||||
imagePosition = ImagePosition.ImageAbove
|
||||
};
|
||||
}
|
||||
|
||||
GUIContent content = new GUIContent(img);
|
||||
Rect imageLabelRect = GUILayoutUtility.GetRect(content, _imageLabelStyle,
|
||||
GUILayout.Height(height), GUILayout.Width(width));
|
||||
|
||||
if (GUI.Button(imageLabelRect, content, _imageLabelStyle))
|
||||
{
|
||||
ImageViewer.ShowWindow(img, Path.GetFileNameWithoutExtension(imagePath));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (urlRegex.IsMatch(part))
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
GUILayout.Space(EditorGUI.indentLevel * 15);
|
||||
GUILayout.Label("<color=blue>" + part + "</color>", _linkStyle, GUILayout.MaxWidth(windowWidth));
|
||||
|
||||
Rect linkRect = GUILayoutUtility.GetLastRect();
|
||||
if (currentEvent.type == EventType.MouseDown && linkRect.Contains(currentEvent.mousePosition))
|
||||
{
|
||||
Application.OpenURL(part);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.LabelField(ParseMarkdown(part), _normalTextStyle, GUILayout.MaxWidth(windowWidth));
|
||||
}
|
||||
}
|
||||
|
||||
GUILayout.EndVertical();
|
||||
GUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
public static string ParseMarkdown(string markdown)
|
||||
{
|
||||
// Headers
|
||||
markdown = Regex.Replace(markdown, @"^######\s(.*?)$", "<size=14><b>$1</b></size>", RegexOptions.Multiline);
|
||||
markdown = Regex.Replace(markdown, @"^#####\s(.*?)$", "<size=16><b>$1</b></size>", RegexOptions.Multiline);
|
||||
markdown = Regex.Replace(markdown, @"^####\s(.*?)$", "<size=18><b>$1</b></size>", RegexOptions.Multiline);
|
||||
markdown = Regex.Replace(markdown, @"^###\s(.*?)$", "<size=20><b>$1</b></size>", RegexOptions.Multiline);
|
||||
markdown = Regex.Replace(markdown, @"^##\s(.*?)$", "<size=22><b>$1</b></size>", RegexOptions.Multiline);
|
||||
markdown = Regex.Replace(markdown, @"^#\s(.*?)$", "<size=24><b>$1</b></size>", RegexOptions.Multiline);
|
||||
|
||||
// Bold
|
||||
markdown = Regex.Replace(markdown, @"\*\*(.*?)\*\*", "<b>$1</b>", RegexOptions.Multiline);
|
||||
|
||||
// Italic
|
||||
markdown = Regex.Replace(markdown, @"\*(.*?)\*", "<i>$1</i>", RegexOptions.Multiline);
|
||||
|
||||
// Code blocks
|
||||
markdown = Regex.Replace(markdown, @"(?s)```(.*?)```", m =>
|
||||
{
|
||||
var codeLines = m.Groups[1].Value.Trim().Split('\n');
|
||||
string result = string.Empty;
|
||||
foreach (var line in codeLines)
|
||||
{
|
||||
result += $" <color=#a1b56c>{line}</color>\n";
|
||||
}
|
||||
|
||||
return result;
|
||||
}, RegexOptions.Multiline);
|
||||
|
||||
// Raw Urls
|
||||
markdown = Regex.Replace(markdown, @"(https?:\/\/[^\s""'<>]+)",
|
||||
"<link><color=#a1b56c><u>$1</u></color></link>", RegexOptions.Multiline);
|
||||
|
||||
// Unordered lists
|
||||
markdown = Regex.Replace(markdown, @"^\s*\*\s(.*?)$", "• $1", RegexOptions.Multiline);
|
||||
|
||||
// Ordered lists
|
||||
markdown = Regex.Replace(markdown, @"^(\d+)\.\s(.*?)$", "$1. $2", RegexOptions.Multiline);
|
||||
|
||||
return markdown;
|
||||
}
|
||||
|
||||
private void LoadImageFromUrl(string url)
|
||||
{
|
||||
_cachedImages[url] = _badImageTex;
|
||||
UnityWebRequest request = UnityWebRequestTexture.GetTexture(url);
|
||||
request.SendWebRequest().completed += operation =>
|
||||
{
|
||||
if (request.responseCode == 200)
|
||||
{
|
||||
Texture2D texture = DownloadHandlerTexture.GetContent(request);
|
||||
_cachedImages[url] = texture;
|
||||
Repaint();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Failed to load image from URL [Error {request.responseCode}]: {url}");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e1014ec949564436944663d476cf2c48
|
||||
timeCreated: 1680838351
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
using Meta.Voice.Hub;
|
||||
using Meta.Voice.Hub.Attributes;
|
||||
using Meta.Voice.Hub.Interfaces;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Meta.Voice.Hub.Markdown
|
||||
{
|
||||
[MetaHubPageScriptableObject]
|
||||
public class MarkdownPage : ScriptableObject, IPageInfo
|
||||
{
|
||||
[SerializeField] private string _displayName;
|
||||
[SerializeField] private string _prefix;
|
||||
[SerializeField] private MetaHubContext _context;
|
||||
[SerializeField] private TextAsset _markdownFile;
|
||||
[SerializeField] private int _priority = 0;
|
||||
|
||||
internal TextAsset MarkdownFile => _markdownFile;
|
||||
public string Name => _displayName ?? name;
|
||||
public string Context => _context.Name;
|
||||
public int Priority => _priority;
|
||||
public string Prefix => _prefix;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 98bfdca232b84c3db2527e4b5c9d3b9d
|
||||
timeCreated: 1680839218
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "Meta.Voice.Hub.Editor",
|
||||
"rootNamespace": "Meta.Voice.Hub",
|
||||
"references": [
|
||||
"GUID:8e054d88729a3fb4bbe539499f824363"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a4f6d7d3afe393a4c8034af2ef0f36a8
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
496
Assets/Oculus/Voice/Features/Hub/Scripts/Editor/MetaHub.cs
Normal file
496
Assets/Oculus/Voice/Features/Hub/Scripts/Editor/MetaHub.cs
Normal file
@@ -0,0 +1,496 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Eventing.Reader;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Meta.Voice.Hub.Attributes;
|
||||
using Meta.Voice.Hub.Interfaces;
|
||||
using Meta.Voice.Hub.UIComponents;
|
||||
using Meta.Voice.Hub.Utilities;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Meta.Voice.Hub
|
||||
{
|
||||
public class MetaHub : EditorWindow
|
||||
{
|
||||
[SerializeField] private Texture2D _icon;
|
||||
[SerializeField] private Texture2D _logoImage;
|
||||
|
||||
private int _leftPanelWidth = 200;
|
||||
|
||||
private List<string> _contextFilter = new List<string>();
|
||||
|
||||
private List<MetaHubContext> _contexts = new List<MetaHubContext>();
|
||||
private Dictionary<string, MetaHubContext> _contextMap = new Dictionary<string, MetaHubContext>();
|
||||
|
||||
private List<PageGroup> _pageGroups = new List<PageGroup>();
|
||||
private Dictionary<MetaHubContext, PageGroup> _pageGroupMap = new Dictionary<MetaHubContext, PageGroup>();
|
||||
|
||||
public MetaHubContext PrimaryContext
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ContextFilter.Count > 0)
|
||||
{
|
||||
var filter = ContextFilter.First();
|
||||
if (_contextMap.TryGetValue(filter, out var context))
|
||||
{
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return _contexts[0];
|
||||
}
|
||||
}
|
||||
|
||||
public GUIContent TitleContent => new GUIContent(PrimaryContext.Title, PrimaryContext.Icon);
|
||||
public Texture2D LogoImage => PrimaryContext.LogoImage ? PrimaryContext.LogoImage : _logoImage;
|
||||
|
||||
public const string DEFAULT_CONTEXT = "";
|
||||
|
||||
public virtual List<string> ContextFilter => _contextFilter;
|
||||
public string SelectedPage { get; set; } = "";
|
||||
|
||||
private PageGroup _rootPageGroup;
|
||||
|
||||
private class PageGroup
|
||||
{
|
||||
private MetaHubContext _context;
|
||||
private List<PageReference> _pages = new List<PageReference>();
|
||||
private HashSet<string> _addedPages = new HashSet<string>();
|
||||
private FoldoutHierarchy<PageReference> _foldoutHierarchy = new FoldoutHierarchy<PageReference>();
|
||||
private readonly Action<PageReference> _onDrawPage;
|
||||
|
||||
public MetaHubContext Context => _context;
|
||||
public IEnumerable<PageReference> Pages => _pages;
|
||||
public int PageCount => _pages.Count;
|
||||
public FoldoutHierarchy<PageReference> Hierarchy => _foldoutHierarchy;
|
||||
|
||||
public PageGroup(MetaHubContext context, Action<PageReference> onDrawPage)
|
||||
{
|
||||
_context = context;
|
||||
_onDrawPage = onDrawPage;
|
||||
}
|
||||
|
||||
public void AddPage(PageReference page)
|
||||
{
|
||||
var pageId = page.PageId;
|
||||
if (!_addedPages.Contains(pageId))
|
||||
{
|
||||
_addedPages.Add(pageId);
|
||||
_pages.Add(page);
|
||||
var prefix = page.info.Prefix?.Trim(new char[] { '/' });
|
||||
if (prefix.Length > 0)
|
||||
{
|
||||
prefix += "/";
|
||||
}
|
||||
var path = "/" + prefix + page.info.Name;
|
||||
_foldoutHierarchy.Add(path, new FoldoutHierarchyItem<PageReference> {
|
||||
path = path,
|
||||
item = page,
|
||||
onDraw = _onDrawPage
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void Sort()
|
||||
{
|
||||
Sort(_pages);
|
||||
}
|
||||
|
||||
public void Sort(List<PageReference> pages)
|
||||
{
|
||||
pages.Sort((a, b) =>
|
||||
{
|
||||
int compare = a.info.Priority.CompareTo(b.info.Priority);
|
||||
if (compare == 0) compare = string.Compare(a.info.Name, b.info.Name);
|
||||
return compare;
|
||||
});
|
||||
_foldoutHierarchy = new FoldoutHierarchy<PageReference>();
|
||||
foreach (var page in _pages)
|
||||
{
|
||||
var path = "/" + page.info.Prefix + page.info.Name;
|
||||
_foldoutHierarchy.Add(path, new FoldoutHierarchyItem<PageReference> {
|
||||
path = path,
|
||||
item = page,
|
||||
onDraw = _onDrawPage
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct PageReference
|
||||
{
|
||||
public IMetaHubPage page;
|
||||
public IPageInfo info;
|
||||
public string PageId => info.Context + "::" + info.Name;
|
||||
}
|
||||
|
||||
private string _searchString = "";
|
||||
private IMetaHubPage _selectedPage;
|
||||
private Vector2 _scroll;
|
||||
private Vector2 _leftScroll;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
UpdateContextFilter();
|
||||
|
||||
minSize = new Vector2(400, 400);
|
||||
}
|
||||
|
||||
public void UpdateContextFilter()
|
||||
{
|
||||
|
||||
if(null == _rootPageGroup) _rootPageGroup = new PageGroup(null, DrawPageEntry);
|
||||
_contexts = ContextFinder.FindAllContextAssets<MetaHubContext>();
|
||||
_contexts.Sort((a, b) => a.Priority.CompareTo(b.Priority));
|
||||
foreach (var context in _contexts)
|
||||
{
|
||||
_contextMap[context.Name] = context;
|
||||
var pageGroup = new PageGroup(context, DrawPageEntry);
|
||||
if (!_pageGroupMap.ContainsKey(context))
|
||||
{
|
||||
_pageGroups.Add(pageGroup);
|
||||
_pageGroupMap[context] = pageGroup;
|
||||
|
||||
foreach (var soPage in context.ScriptableObjectReflectionPages)
|
||||
{
|
||||
var pages = PageFinder.FindPages(soPage.scriptableObjectType);
|
||||
foreach (var so in pages)
|
||||
{
|
||||
var page = new ScriptableObjectPage(so, context.Name, prefix: soPage.namePrefix, priority: soPage.priorityModifier);
|
||||
AddPage(new PageReference
|
||||
{
|
||||
page = page,
|
||||
info = page
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var page in ContextFinder.FindAllContextAssets<MetaHubPage>())
|
||||
{
|
||||
AddPage(new PageReference
|
||||
{
|
||||
page = page,
|
||||
info = page
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var pageType in PageFinder.FindPages())
|
||||
{
|
||||
var pageInfo = PageFinder.GetPageInfo(pageType);
|
||||
|
||||
if (pageInfo is MetaHubPageScriptableObjectAttribute)
|
||||
{
|
||||
var pages = PageFinder.FindPages(pageType);
|
||||
foreach (var page in pages)
|
||||
{
|
||||
var soPage = new ScriptableObjectPage(page, pageInfo);
|
||||
AddPage(new PageReference
|
||||
{
|
||||
page = soPage,
|
||||
info = soPage
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
IMetaHubPage page;
|
||||
if (pageType.IsSubclassOf(typeof(ScriptableObject)))
|
||||
{
|
||||
page = (IMetaHubPage) ScriptableObject.CreateInstance(pageType);
|
||||
}
|
||||
else
|
||||
{
|
||||
page = Activator.CreateInstance(pageType) as IMetaHubPage;
|
||||
}
|
||||
if(page is IPageInfo info) AddPage(new PageReference { page = page, info = info});
|
||||
else AddPage(new PageReference { page = page, info = pageInfo});
|
||||
|
||||
var method = page.GetType().GetMethod("OnEnable", BindingFlags.Default | BindingFlags.Public);
|
||||
method?.Invoke(page, new object[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the pages by priority then alpha
|
||||
foreach (var group in _pageGroupMap.Values)
|
||||
{
|
||||
group.Sort();
|
||||
}
|
||||
}
|
||||
|
||||
private void AddPage(PageReference page)
|
||||
{
|
||||
if (string.IsNullOrEmpty(page.info.Context)) _rootPageGroup.AddPage(page);
|
||||
else _pageGroupMap[_contextMap[page.info.Context]].AddPage(page);
|
||||
}
|
||||
|
||||
protected virtual void OnGUI()
|
||||
{
|
||||
titleContent = TitleContent;
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
_searchString = EditorGUILayout.TextField(_searchString, GUI.skin.FindStyle("ToolbarSeachTextField"));
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
DrawLeftPanel();
|
||||
DrawRightPanel();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private void DrawLeftPanel()
|
||||
{
|
||||
EditorGUILayout.BeginVertical(GUILayout.Width(_leftPanelWidth));
|
||||
|
||||
var logo = LogoImage;
|
||||
// Draw logo image
|
||||
if (logo)
|
||||
{
|
||||
float aspectRatio = logo.width / (float) logo.height;
|
||||
GUILayout.Box(logo, GUILayout.Width(_leftPanelWidth), GUILayout.Height(_leftPanelWidth / aspectRatio));
|
||||
}
|
||||
|
||||
_leftScroll = GUILayout.BeginScrollView(_leftScroll);
|
||||
DrawPageGroup(_rootPageGroup);
|
||||
foreach (var context in _pageGroups)
|
||||
{
|
||||
DrawPageGroup(context);
|
||||
}
|
||||
GUILayout.EndScrollView();
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
private void DrawPageGroup(PageGroup group)
|
||||
{
|
||||
if (!IsGroupVisible(group)) return;
|
||||
|
||||
var searchMatchedGroupContext = ContextFilter.Count != 1 && IsGroupInSearch(group);
|
||||
|
||||
List<PageReference> pages = new List<PageReference>();
|
||||
if (!string.IsNullOrEmpty(_searchString) && !searchMatchedGroupContext)
|
||||
{
|
||||
foreach (var page in group.Pages)
|
||||
{
|
||||
if (PageInSearch(page))
|
||||
{
|
||||
pages.Add(page);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ContextFilter.Count == 0 && (string.IsNullOrEmpty(_searchString) || pages.Count > 0))
|
||||
{
|
||||
if (null != group.Context &&
|
||||
(string.IsNullOrEmpty(_searchString) && group.PageCount > 0 || pages.Count > 0) &&
|
||||
!string.IsNullOrEmpty(group.Context.Name) && group.Context.ShowPageGroupTitle)
|
||||
{
|
||||
GUILayout.Space(8);
|
||||
GUILayout.Label(group.Context.Name, EditorStyles.boldLabel);
|
||||
}
|
||||
}
|
||||
|
||||
if(!string.IsNullOrEmpty(_searchString))
|
||||
{
|
||||
for (int i = 0; i < pages.Count; i++)
|
||||
{
|
||||
DrawPageEntry(pages[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
group.Hierarchy.Draw();
|
||||
}
|
||||
}
|
||||
|
||||
private bool PageInSearch(PageReference page)
|
||||
{
|
||||
#if UNITY_2021_1_OR_NEWER
|
||||
return page.info.Name.Contains(_searchString, StringComparison.OrdinalIgnoreCase);
|
||||
#else
|
||||
return page.info.Name.ToLower().Contains(_searchString.ToLower());
|
||||
#endif
|
||||
}
|
||||
|
||||
private bool IsGroupInSearch(PageGroup group)
|
||||
{
|
||||
#if UNITY_2021_1_OR_NEWER
|
||||
return group.Context && group.Context.Name.Contains(_searchString,
|
||||
StringComparison.OrdinalIgnoreCase);
|
||||
#else
|
||||
return group.Context && group.Context.Name.ToLower().Contains(_searchString.ToLower());
|
||||
#endif
|
||||
}
|
||||
|
||||
private bool IsGroupVisible(PageGroup group)
|
||||
{
|
||||
return group.PageCount > 0 &&
|
||||
ContextFilter.Count == 0 && (!group.Context || group.Context.AllowWithoutContextFilter) ||
|
||||
ContextFilter.Contains(group.Context ? group.Context.Name : "");
|
||||
}
|
||||
|
||||
private void DrawPageEntry(PageReference page)
|
||||
{
|
||||
GUIStyle optionStyle = new GUIStyle(GUI.skin.label);
|
||||
optionStyle.normal.background = null;
|
||||
optionStyle.normal.textColor = _selectedPage == page.page ? Color.white : GUI.skin.label.normal.textColor;
|
||||
|
||||
if (null == _selectedPage)
|
||||
{
|
||||
// TODO: We will need to improve this logic.
|
||||
if (!string.IsNullOrEmpty(SelectedPage) && page.PageId == SelectedPage) _selectedPage = page.page;
|
||||
else if(string.IsNullOrEmpty(SelectedPage)) _selectedPage = page.page;
|
||||
}
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
{
|
||||
Rect optionRect = GUILayoutUtility.GetRect(GUIContent.none, optionStyle, GUILayout.ExpandWidth(true), GUILayout.Height(20));
|
||||
|
||||
bool isHover = optionRect.Contains(Event.current.mousePosition);
|
||||
if (isHover)
|
||||
{
|
||||
EditorGUIUtility.AddCursorRect(optionRect, MouseCursor.Link);
|
||||
}
|
||||
|
||||
Color backgroundColor;
|
||||
if (page.page == _selectedPage)
|
||||
{
|
||||
backgroundColor = EditorGUIUtility.isProSkin ? new Color(0.22f, 0.44f, 0.88f) : new Color(0.24f, 0.48f, 0.90f);
|
||||
}
|
||||
else
|
||||
{
|
||||
backgroundColor = Color.clear;
|
||||
}
|
||||
|
||||
EditorGUI.DrawRect(optionRect, backgroundColor);
|
||||
GUI.Label(optionRect, new GUIContent(page.info.Name, page.info.Name), optionStyle);
|
||||
|
||||
if (Event.current.type == EventType.MouseDown && isHover)
|
||||
{
|
||||
_selectedPage = page.page;
|
||||
Event.current.Use();
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
|
||||
protected virtual void DrawRightPanel()
|
||||
{
|
||||
// Create a GUIStyle with a darker background color
|
||||
GUIStyle darkBackgroundStyle = new GUIStyle();
|
||||
Texture2D backgroundTexture = new Texture2D(1, 1);
|
||||
backgroundTexture.SetPixel(0, 0, new Color(0f, 0f, 0f, .25f));
|
||||
backgroundTexture.Apply();
|
||||
darkBackgroundStyle.normal.background = backgroundTexture;
|
||||
|
||||
// Apply the dark background style to the right panel
|
||||
EditorGUILayout.BeginVertical(darkBackgroundStyle, GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true));
|
||||
|
||||
if (_selectedPage is ScriptableObjectPage soPage)
|
||||
{
|
||||
if(soPage.Editor is IOverrideSize size) {
|
||||
size.OverrideWidth = EditorGUIUtility.currentViewWidth - _leftPanelWidth;
|
||||
}
|
||||
}
|
||||
_selectedPage?.OnGUI();
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
public static T ShowWindow<T>(params string[] contexts) where T : MetaHub
|
||||
{
|
||||
var window = EditorWindow.GetWindow<T>();
|
||||
window._selectedPage = null;
|
||||
window.titleContent = new GUIContent("Meta Hub");
|
||||
window.ContextFilter.Clear();
|
||||
foreach (var context in contexts)
|
||||
{
|
||||
window.ContextFilter.Add(context);
|
||||
}
|
||||
window.UpdateContextFilter();
|
||||
window.Show();
|
||||
return window;
|
||||
}
|
||||
}
|
||||
|
||||
internal class ScriptableObjectPage : IMetaHubPage, IPageInfo
|
||||
{
|
||||
private readonly ScriptableObject _page;
|
||||
private string _context;
|
||||
private Editor _editor;
|
||||
private string _name;
|
||||
private string _prefix = "";
|
||||
private int _priority;
|
||||
|
||||
public string Name => _name;
|
||||
public string Prefix => _prefix;
|
||||
public string Context => _context;
|
||||
public Editor Editor => _editor;
|
||||
public int Priority => _priority;
|
||||
|
||||
public ScriptableObjectPage(ScriptableObject page, MetaHubPageAttribute pageInfo)
|
||||
{
|
||||
_page = page;
|
||||
_context = pageInfo.Context;
|
||||
_priority = pageInfo.Priority;
|
||||
_prefix = pageInfo.Prefix;
|
||||
|
||||
UpdatePageInfo();
|
||||
}
|
||||
|
||||
public ScriptableObjectPage(ScriptableObject page, string context, string prefix = "", int priority = 0)
|
||||
{
|
||||
_page = page;
|
||||
_context = context;
|
||||
_priority = priority;
|
||||
_prefix = prefix;
|
||||
|
||||
UpdatePageInfo();
|
||||
}
|
||||
|
||||
private void UpdatePageInfo()
|
||||
{
|
||||
if (_page is IPageInfo info)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(info.Name)) _name = info.Name;
|
||||
if (!string.IsNullOrEmpty(info.Context)) _context = info.Context;
|
||||
if (!string.IsNullOrEmpty(info.Prefix)) _prefix = info.Prefix;
|
||||
if (info.Priority != 0) _priority = info.Priority;
|
||||
}
|
||||
else
|
||||
{
|
||||
_name = _page.name;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnGUI()
|
||||
{
|
||||
if (_page)
|
||||
{
|
||||
// Create an editor for the assigned ScriptableObject
|
||||
if (_editor == null || _editor.target != _page)
|
||||
{
|
||||
_editor = Editor.CreateEditor(_page);
|
||||
}
|
||||
|
||||
// Render the ScriptableObject with its default editor
|
||||
_editor.OnInspectorGUI();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 271f6b1cb92788340a1dae6d90354a0c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences:
|
||||
- m_ViewDataDictionary: {instanceID: 0}
|
||||
- _icon: {fileID: 2800000, guid: 439d6ccc4069b824cae61eb7ac48e64d, type: 3}
|
||||
- _logoImage: {fileID: 2800000, guid: d42013762536e8042a5211ecbc89b7f9, type: 3}
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Meta.Voice.Hub
|
||||
{
|
||||
public class MetaHubContext : ScriptableObject
|
||||
{
|
||||
[Header("Context Configuration")]
|
||||
[Tooltip("The title of the window if this is the primary context")]
|
||||
[SerializeField] private string _windowTitle;
|
||||
[Tooltip("The logo to use if this is the primary context")]
|
||||
[SerializeField] private Texture2D _logo;
|
||||
[Tooltip("The icon to use if this is the primary context")]
|
||||
[SerializeField] private Texture2D _icon;
|
||||
[Tooltip("The priority of this context. Lower number means higher probability that this will be the primary context.")]
|
||||
[SerializeField] private int _priority = 1000;
|
||||
[Tooltip("If there are no context filters you will have")]
|
||||
[SerializeField] private bool _allowWithoutContextFilter = true;
|
||||
|
||||
[Header("Page Content")]
|
||||
[SerializeField] private bool showPageGroupTitle = true;
|
||||
[SerializeField] private MetaHubPage[] _pages;
|
||||
[SerializeField] private ScriptableObjectReflectionPage[] _scriptableObjectPages;
|
||||
[SerializeField] private string _defaultPage;
|
||||
|
||||
public virtual string Name => name;
|
||||
public virtual int Priority => _priority;
|
||||
public virtual Texture2D LogoImage => _logo;
|
||||
public virtual Texture2D Icon => _icon;
|
||||
public virtual string DefaultPage => _defaultPage;
|
||||
public virtual string Title => _windowTitle;
|
||||
public virtual bool ShowPageGroupTitle => showPageGroupTitle;
|
||||
public virtual bool AllowWithoutContextFilter => _allowWithoutContextFilter;
|
||||
|
||||
public virtual ScriptableObjectReflectionPage[] ScriptableObjectReflectionPages => _scriptableObjectPages;
|
||||
|
||||
[Serializable]
|
||||
public class ScriptableObjectReflectionPage
|
||||
{
|
||||
[SerializeField] public string scriptableObjectType;
|
||||
[SerializeField] public string namePrefix;
|
||||
[Tooltip("A modifier the priority of all pages of this type.")]
|
||||
[SerializeField] public int priorityModifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1a05db9a1dc84904b336c0c789666114
|
||||
timeCreated: 1680655384
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
using Meta.Voice.Hub.Interfaces;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Meta.Voice.Hub
|
||||
{
|
||||
public class MetaHubPage : ScriptableObject, IMetaHubPage, IPageInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The context this page will fall under
|
||||
/// </summary>
|
||||
[SerializeField] private string _context;
|
||||
/// <summary>
|
||||
/// A prefix that will show up before the name of the page. This is a good place to insert page hierarchy etc.
|
||||
/// </summary>
|
||||
[SerializeField] private string _prefix;
|
||||
/// <summary>
|
||||
/// The sorting priority of the page
|
||||
/// </summary>
|
||||
[SerializeField] private int _priority;
|
||||
|
||||
public virtual string Name => name;
|
||||
public virtual string Context => _context;
|
||||
public virtual int Priority => _priority;
|
||||
public virtual string Prefix => _context;
|
||||
|
||||
public virtual void OnGUI()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 731e6f73e53947cba9d7d0c6caf7d0a8
|
||||
timeCreated: 1680655880
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eedddfe631e0492d931ec56bb2d2eec4
|
||||
timeCreated: 1680888150
|
||||
@@ -0,0 +1,135 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Eventing.Reader;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Meta.Voice.Hub.UIComponents
|
||||
{
|
||||
|
||||
|
||||
public class FoldoutHierarchy<T>
|
||||
{
|
||||
private Dictionary<string, FoldoutGroup<T>> _groups = new Dictionary<string, FoldoutGroup<T>>();
|
||||
private List<FoldoutGroup<T>> _orderedGroups = new List<FoldoutGroup<T>>();
|
||||
|
||||
public void Add(string path, FoldoutHierarchyItem<T> item)
|
||||
{
|
||||
string[] parts = path.Split('/');
|
||||
FoldoutGroup<T> currentGroup = null;
|
||||
|
||||
|
||||
for (int i = 0; i < parts.Length; i++)
|
||||
{
|
||||
string key = string.Join("/", parts, 0, i + 1);
|
||||
|
||||
if (!_groups.ContainsKey(key))
|
||||
{
|
||||
FoldoutGroup<T> newGroup = new FoldoutGroup<T>(parts[i]);
|
||||
_groups.Add(key, newGroup);
|
||||
_orderedGroups.Add(newGroup);
|
||||
|
||||
if (currentGroup != null)
|
||||
{
|
||||
currentGroup.AddChild(newGroup, item, i == parts.Length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
currentGroup = _groups[key];
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
foreach (var group in _orderedGroups)
|
||||
{
|
||||
if (group.Parent == null)
|
||||
{
|
||||
group.Draw();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class FoldoutHierarchyItem<T>
|
||||
{
|
||||
public string path;
|
||||
public T item;
|
||||
public Action<T> onDraw;
|
||||
}
|
||||
|
||||
public class FoldoutGroup<T>
|
||||
{
|
||||
private string _name;
|
||||
private FoldoutGroup<T> _parent;
|
||||
private List<object> _children = new List<object>();
|
||||
private List<FoldoutHierarchyItem<T>> _data = new List<FoldoutHierarchyItem<T>>();
|
||||
private int _indentSpace = 10;
|
||||
|
||||
private bool _isFoldedOut = false;
|
||||
|
||||
public string Name => _name;
|
||||
public FoldoutGroup<T> Parent => _parent;
|
||||
|
||||
public FoldoutGroup(string name)
|
||||
{
|
||||
this._name = name;
|
||||
}
|
||||
|
||||
public void AddChild(FoldoutGroup<T> child, FoldoutHierarchyItem<T> data, bool isLeaf)
|
||||
{
|
||||
child._parent = this;
|
||||
_data.Add(data);
|
||||
if(isLeaf) _children.Add(data);
|
||||
else _children.Add(child);
|
||||
}
|
||||
|
||||
public void Draw(int indentLevel = 0)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_name))
|
||||
{
|
||||
DrawExpanded(indentLevel);
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
if (indentLevel >= 0)
|
||||
{
|
||||
GUILayout.Space(_indentSpace);
|
||||
}
|
||||
GUILayout.BeginVertical();
|
||||
_isFoldedOut = EditorGUILayout.Foldout(_isFoldedOut, _name, true);
|
||||
|
||||
if (_isFoldedOut)
|
||||
{
|
||||
DrawExpanded(indentLevel);
|
||||
}
|
||||
|
||||
GUILayout.EndVertical();
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawExpanded(int indentLevel)
|
||||
{
|
||||
foreach (var child in _children)
|
||||
{
|
||||
if (child is FoldoutGroup<T> foldoutGroup)
|
||||
{
|
||||
foldoutGroup.Draw(indentLevel);
|
||||
} else if (child is FoldoutHierarchyItem<T> leaf)
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
if (indentLevel >= 0)
|
||||
{
|
||||
GUILayout.Space(_indentSpace);
|
||||
}
|
||||
GUILayout.BeginVertical();
|
||||
leaf.onDraw(leaf.item);
|
||||
GUILayout.EndVertical();
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5b3b26502f774dd2b597ed1657a8f0a4
|
||||
timeCreated: 1681274028
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace Meta.Voice.Hub.UIComponents
|
||||
{
|
||||
public class ImageView
|
||||
{
|
||||
private EditorWindow _editorWindow;
|
||||
private Editor _editor;
|
||||
private Vector2 pan;
|
||||
private float zoom = -1f;
|
||||
|
||||
public ImageView(EditorWindow editorWindow) => _editorWindow = editorWindow;
|
||||
public ImageView(Editor editor) => _editor = editor;
|
||||
|
||||
private float ViewHeight => _editorWindow ? _editorWindow.position.height : Screen.height;
|
||||
private float ViewWidth => _editorWindow ? _editorWindow.position.width : EditorGUIUtility.currentViewWidth;
|
||||
|
||||
private void Repaint()
|
||||
{
|
||||
if(_editorWindow) _editorWindow.Repaint();
|
||||
else if (_editor) _editor.Repaint();
|
||||
}
|
||||
|
||||
public void Draw(Texture2D image)
|
||||
{
|
||||
GUILayout.Box("",GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true));
|
||||
|
||||
var windowRect = GUILayoutUtility.GetLastRect();
|
||||
if (windowRect.width <= 1 && windowRect.height <= 1) return;
|
||||
|
||||
if (image == null)
|
||||
{
|
||||
EditorGUILayout.HelpBox("No Texture2D assigned.", MessageType.Info);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle input for panning and zooming
|
||||
HandleInput();
|
||||
|
||||
GUI.BeginGroup(windowRect);
|
||||
|
||||
var imageWidth = image.width * zoom;
|
||||
var imageHeight = image.height * zoom;
|
||||
|
||||
if (zoom < 0 || imageWidth < windowRect.width && imageHeight < windowRect.height)
|
||||
{
|
||||
float widthScale = windowRect.width / image.width;
|
||||
float heightScale = windowRect.height / image.height;
|
||||
zoom = Mathf.Min(widthScale, heightScale);
|
||||
}
|
||||
|
||||
if (imageWidth < windowRect.width) pan.x = (windowRect.width - imageWidth) / 2.0f;
|
||||
else if (pan.x + imageWidth < windowRect.width) pan.x += windowRect.width - (pan.x + imageWidth);
|
||||
|
||||
if (imageHeight < windowRect.height) pan.y = (windowRect.height - imageHeight) / 2.0f;
|
||||
else if (pan.y + imageHeight < windowRect.height) pan.y += windowRect.height - (pan.y + imageHeight);
|
||||
|
||||
if (pan.x > 0) pan.x = 0;
|
||||
if (pan.y > 0) pan.y = 0;
|
||||
|
||||
if (imageHeight < windowRect.height) pan.y = (windowRect.height - imageHeight) / 2.0f;
|
||||
|
||||
GUI.DrawTexture(new Rect(pan.x, pan.y, image.width * zoom, image.height * zoom), image, ScaleMode.ScaleAndCrop);
|
||||
|
||||
GUI.EndGroup();
|
||||
}
|
||||
|
||||
private void HandleInput()
|
||||
{
|
||||
Event e = Event.current;
|
||||
|
||||
// Panning
|
||||
if (e.type == EventType.MouseDown)
|
||||
{
|
||||
e.Use();
|
||||
}
|
||||
|
||||
if (e.type == EventType.MouseDrag)
|
||||
{
|
||||
pan += e.delta;
|
||||
e.Use();
|
||||
}
|
||||
|
||||
// Zooming
|
||||
if (e.type == EventType.ScrollWheel)
|
||||
{
|
||||
float zoomDelta = -e.delta.y * 0.01f;
|
||||
zoom = Mathf.Clamp(zoom + zoomDelta, 0.1f, 10f);
|
||||
e.Use();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1767e34a605743579a6cb935eff19e64
|
||||
timeCreated: 1680888171
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d10f6297f38d44b3b93a5b848ab5da32
|
||||
timeCreated: 1680655605
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Meta.Voice.Hub.Utilities
|
||||
{
|
||||
public static class ContextFinder
|
||||
{
|
||||
public static List<T> FindAllContextAssets<T>() where T: ScriptableObject
|
||||
{
|
||||
string[] guids = AssetDatabase.FindAssets("t:" + typeof(T).Name);
|
||||
List<T> assets = new List<T>();
|
||||
|
||||
foreach (string guid in guids)
|
||||
{
|
||||
string assetPath = AssetDatabase.GUIDToAssetPath(guid);
|
||||
T asset = AssetDatabase.LoadAssetAtPath<T>(assetPath);
|
||||
if (asset != null)
|
||||
{
|
||||
if (!assets.Contains(asset))
|
||||
{
|
||||
assets.Add(asset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return assets;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c235113b0b6c484a8f361e84786d7467
|
||||
timeCreated: 1680655637
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Meta.Voice.Hub.Attributes;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Meta.Voice.Hub.Utilities
|
||||
{
|
||||
internal static class PageFinder
|
||||
{
|
||||
private static List<Type> _pages;
|
||||
internal static List<Type> FindPages()
|
||||
{
|
||||
if (null == _pages)
|
||||
{
|
||||
_pages = ReflectionUtils.GetTypesWithAttribute<MetaHubPageAttribute>();
|
||||
}
|
||||
|
||||
return _pages;
|
||||
}
|
||||
|
||||
internal static MetaHubPageAttribute GetPageInfo(Type type)
|
||||
{
|
||||
var attributes = type.GetCustomAttributes(typeof(MetaHubPageAttribute), false);
|
||||
return attributes.Length > 0 ? (MetaHubPageAttribute) attributes[0] : null;
|
||||
}
|
||||
|
||||
internal static List<ScriptableObject> FindPages(Type t)
|
||||
{
|
||||
if (!typeof(ScriptableObject).IsAssignableFrom(t))
|
||||
{
|
||||
throw new ArgumentException("The specified type must be a ScriptableObject.");
|
||||
}
|
||||
|
||||
return FindPages(t.Name);
|
||||
}
|
||||
|
||||
public static List<ScriptableObject> FindPages(string type)
|
||||
{
|
||||
List<ScriptableObject> pages = new List<ScriptableObject>();
|
||||
string[] guids = AssetDatabase.FindAssets($"t:{type}");
|
||||
|
||||
foreach (string guid in guids)
|
||||
{
|
||||
string assetPath = AssetDatabase.GUIDToAssetPath(guid);
|
||||
ScriptableObject asset = AssetDatabase.LoadAssetAtPath<ScriptableObject>(assetPath);
|
||||
if (asset != null)
|
||||
{
|
||||
pages.Add(asset);
|
||||
}
|
||||
}
|
||||
|
||||
return pages;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b5b71dcbc6c2475a831e119b0d8701cf
|
||||
timeCreated: 1680655836
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Meta.Voice.Hub.Utilities
|
||||
{
|
||||
internal static class ReflectionUtils
|
||||
{
|
||||
private const string NAMESPACE_PREFIX = "Meta";
|
||||
|
||||
internal static bool IsValidNamespace(Type type) =>
|
||||
type.Namespace != null && type.Namespace.StartsWith(NAMESPACE_PREFIX);
|
||||
|
||||
internal static List<Type> GetTypesWithAttribute<T>() where T : Attribute
|
||||
{
|
||||
var attributeType = typeof(T);
|
||||
|
||||
return AppDomain.CurrentDomain.GetAssemblies()
|
||||
.SelectMany(assembly => assembly.GetTypes())
|
||||
.Where(type => IsValidNamespace(type))
|
||||
.Where(type => type.GetCustomAttributes(attributeType, false).Length > 0)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c9dfa99b5c354fc9b6cd8f560ca1ff02
|
||||
timeCreated: 1680790475
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 122d847b98c549f696fcfa52128264bb
|
||||
timeCreated: 1680889697
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
using Meta.Voice.Hub.UIComponents;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
public class ImageViewer : EditorWindow
|
||||
{
|
||||
private Texture2D _image;
|
||||
private ImageView _imageView;
|
||||
|
||||
public static void ShowWindow(Texture2D image, string title)
|
||||
{
|
||||
ImageViewer window = CreateInstance<ImageViewer>();
|
||||
window._image = image;
|
||||
window.titleContent = new GUIContent(title);
|
||||
window.Show();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (_image == null)
|
||||
{
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
if(null == _imageView) _imageView = new ImageView(this);
|
||||
_imageView.Draw(_image);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e775846ae33248d98afc58bedcbd6c12
|
||||
timeCreated: 1680847262
|
||||
8
Assets/Oculus/Voice/Features/Hub/Scripts/Runtime.meta
Normal file
8
Assets/Oculus/Voice/Features/Hub/Scripts/Runtime.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 61f50677da1080242ace79cb69dc6bdb
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 259e970f8c624d50adde3e97c28d4db2
|
||||
timeCreated: 1680653496
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace Meta.Voice.Hub.Attributes
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class MetaHubContextAttribute : Attribute
|
||||
{
|
||||
public string Context { get; private set; }
|
||||
public int Priority { get; private set; }
|
||||
public string LogoPath { get; private set; }
|
||||
|
||||
public MetaHubContextAttribute(string context, int priority = 1000, string pathToLogo = "")
|
||||
{
|
||||
Context = context;
|
||||
Priority = priority;
|
||||
LogoPath = pathToLogo;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: adaa391766724cfba11a9c2223e738b0
|
||||
timeCreated: 1680653506
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using Meta.Voice.Hub.Interfaces;
|
||||
|
||||
namespace Meta.Voice.Hub.Attributes
|
||||
{
|
||||
public class MetaHubPageAttribute : Attribute, IPageInfo
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
public string Context { get; private set; }
|
||||
public int Priority { get; private set; }
|
||||
public string Prefix { get; private set; }
|
||||
|
||||
|
||||
public MetaHubPageAttribute(string name = null, string context = "", string prefix = "", int priority = 0)
|
||||
{
|
||||
Name = name;
|
||||
Context = context;
|
||||
Priority = priority;
|
||||
Prefix = prefix;
|
||||
}
|
||||
}
|
||||
|
||||
public class MetaHubPageScriptableObjectAttribute : MetaHubPageAttribute
|
||||
{
|
||||
public MetaHubPageScriptableObjectAttribute(string context = "") : base(context: context)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b7abd7a0ff8e45bc9cad665bffea5fae
|
||||
timeCreated: 1680653617
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fa9a1019478641b2a667674cd4a94eb8
|
||||
timeCreated: 1680672606
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
namespace Meta.Voice.Hub.Interfaces
|
||||
{
|
||||
public interface IMetaHubPage
|
||||
{
|
||||
void OnGUI();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 816e6d4cdf174cf1981d648ac98b115c
|
||||
timeCreated: 1680672614
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
namespace Meta.Voice.Hub.Interfaces
|
||||
{
|
||||
public interface IPageInfo
|
||||
{
|
||||
string Name { get; }
|
||||
string Context { get; }
|
||||
int Priority { get; }
|
||||
string Prefix { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 33e5585f3c294fddbdf1f2c3b8e6786b
|
||||
timeCreated: 1680672632
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "Meta.Voice.Hub.Runtime",
|
||||
"rootNamespace": "Meta.Voice.Hub",
|
||||
"references": [],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8e054d88729a3fb4bbe539499f824363
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user