浏览代码

Scene/Project Comments (#23)

* Base Work

* Comments in scene base work

* Comments Browser Window

* Updated Icon / List / Editor

* Filtering Comments + UsePOV

* GUI Styling

* Tweak Priorities

* Work on Labels

* Code cleanup

* Comment Editor

* Work on threads

* Formatting/Editring Messages

* Polishing UI

* Refactor comments to fix serialization

* Better property management, added filtering by name

* Refactored focus

* String filter

* Message UI fixes + edit button fix

* Handled Scene Change

* Updated Changelog

* Align with view button

* Add new Comment Button + Manage on Delete

* Toggle Preferences

* Comments are displayed as edit mode upon creation.

* Comment Assets

* Fixed Accessibility / Script Compilation

* Sorting Columns

* Added Priority Column
/main
GitHub 3 年前
当前提交
a25caceb
共有 30 个文件被更改,包括 1764 次插入4 次删除
  1. 1
      CHANGELOG.md
  2. 12
      Editor/AdvancedHierarchyView/AdvancedHierarchyView.cs
  3. 20
      Editor/SceneViewToolbar.cs
  4. 8
      Editor/Comments.meta
  5. 34
      Editor/GUIUtils.cs
  6. 11
      Editor/GUIUtils.cs.meta
  7. 4
      Icons/GUI/edit.png
  8. 116
      Icons/GUI/edit.png.meta
  9. 4
      Icons/GUI/reply.png
  10. 116
      Icons/GUI/reply.png.meta
  11. 46
      Icons/Misc/ic-comment.png
  12. 116
      Icons/Misc/ic-comment.png.meta
  13. 9
      Icons/Misc/ic-comment.png~
  14. 8
      Runtime/Comments.meta
  15. 41
      Editor/Comments/CommentAsset.cs
  16. 11
      Editor/Comments/CommentAsset.cs.meta
  17. 57
      Editor/Comments/CommentAssetEditor.cs
  18. 11
      Editor/Comments/CommentAssetEditor.cs.meta
  19. 434
      Editor/Comments/CommentEditor.cs
  20. 11
      Editor/Comments/CommentEditor.cs.meta
  21. 423
      Editor/Comments/CommentsWindow.cs
  22. 11
      Editor/Comments/CommentsWindow.cs.meta
  23. 73
      Editor/Comments/SceneCommentEditor.cs
  24. 11
      Editor/Comments/SceneCommentEditor.cs.meta
  25. 130
      Runtime/Comments/Comment.cs
  26. 11
      Runtime/Comments/Comment.cs.meta
  27. 28
      Runtime/Comments/SceneComment.cs
  28. 11
      Runtime/Comments/SceneComment.cs.meta

1
CHANGELOG.md


* Added Rigs to **Ingredients Explorer** : that view summarizes currently loaded rigs, and groups them in update groups.
* Added abstract `PingableEditor` class for MonoBehaviours that can be Pinged to stand out in the inspector.
* Added Help items in Menu, Added HelpURL class attributes
* Feature: Scene Comments
## 2020.2.2

12
Editor/AdvancedHierarchyView/AdvancedHierarchyView.cs


using UnityEngine.Playables;
using System.Linq;
using System.Reflection;
using GameplayIngredients.Comments;
namespace GameplayIngredients.Editor
{

RegisterComponentType( typeof(Text), "Text Icon");
RegisterComponentType( typeof(Button), "Button Icon");
RegisterComponentType( typeof(Folder), "Folder Icon");
RegisterComponentType( typeof(SceneComment), "Packages/net.peeweek.gameplay-ingredients/Icons/Misc/ic-comment.png");
foreach(var assembly in AppDomain.CurrentDomain.GetAssemblies())
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
try
{

var c = GUI.color;
bool isFolder = o.GetComponent<Folder>() != null;
bool isComment = o.GetComponent<SceneComment>() != null;
if(isFolder)
if(isFolder || isComment)
DrawIcon(fullRect, Contents.GetContent(typeof(Folder)), o.GetComponent<Folder>().Color);
if (isFolder)
DrawIcon(fullRect, Contents.GetContent(typeof(Folder)), o.GetComponent<Folder>().Color);
else
DrawIcon(fullRect, Contents.GetContent(typeof(SceneComment)), Color.white);
}
else
{

20
Editor/SceneViewToolbar.cs


using UnityEngine;
using UnityEditor;
using System;
using GameplayIngredients.Comments.Editor;
namespace GameplayIngredients.Editor
{

EditorWindow.GetWindow<CheckWindow>();
}
// Comments Window
GUILayout.Space(16);
if (GUILayout.Button(Contents.commentsWindow, EditorStyles.toolbarButton))
{
CommentsWindow.Open();
}
if (GUILayout.Button(Contents.addComment, EditorStyles.toolbarButton))
{
SceneCommentEditor.CreateComment();
}
// Custom Code here
if (OnSceneViewToolbarGUI != null)
OnSceneViewToolbarGUI.Invoke(sceneView);

GUILayout.Space(96);
}
}

public static GUIContent linkGameView;
public static GUIContent linkGameViewCinemachine;
public static GUIContent checkWindow;
public static GUIContent commentsWindow;
public static GUIContent addComment;
static Contents()
{

checkWindow = new GUIContent(EditorGUIUtility.IconContent("Valid"));
checkWindow.text = "Check";
commentsWindow = new GUIContent(EditorGUIUtility.IconContent("console.infoicon.inactive.sml"));
commentsWindow.text = "Comments";
addComment = new GUIContent("+");
}
}

8
Editor/Comments.meta


fileFormatVersion: 2
guid: a42fe31621978284c8c2c0c11c8516d5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

34
Editor/GUIUtils.cs


using UnityEngine;
using UnityEditor;
namespace GameplayIngredients
{
static class GUIUtils
{
public static void ColoredLabel(string text, Color color)
{
GUIContent label = new GUIContent(text);
Rect r = GUILayoutUtility.GetRect(label, Styles.coloredLabel);
EditorGUI.DrawRect(r, color);
var r2 = new RectOffset(1, 1, 1, 1).Remove(r);
EditorGUI.DrawRect(r2, color * new Color(.5f, .5f, .5f, 1f));
GUI.contentColor = color * 2;
GUI.Label(r, label, Styles.coloredLabel);
GUI.contentColor = Color.white;
}
static class Styles
{
public static GUIStyle coloredLabel;
static Styles()
{
coloredLabel = new GUIStyle(EditorStyles.label);
coloredLabel.fontStyle = FontStyle.Bold;
coloredLabel.fontSize = 10;
coloredLabel.padding = new RectOffset(6, 6, 2, 2);
}
}
}
}

11
Editor/GUIUtils.cs.meta


fileFormatVersion: 2
guid: e9a280e27f38ebe4984b39d341cacd4f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

4
Icons/GUI/edit.png

之前 之后
宽度: 36  |  高度: 36  |  大小: 206 B

116
Icons/GUI/edit.png.meta


fileFormatVersion: 2
guid: 98d201cc66f05344ebd036a7a493e5c7
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: 1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: -1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

4
Icons/GUI/reply.png

之前 之后
宽度: 36  |  高度: 36  |  大小: 303 B

116
Icons/GUI/reply.png.meta


fileFormatVersion: 2
guid: 773f17336a081c741a9568d4b2fe5867
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: 1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: -1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

46
Icons/Misc/ic-comment.png

之前 之后
宽度: 96  |  高度: 96  |  大小: 3.0 KiB

116
Icons/Misc/ic-comment.png.meta


fileFormatVersion: 2
guid: 2db418d0b9151704f87ace549b922c16
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: 1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: -1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

9
Icons/Misc/ic-comment.png~

之前 之后
宽度: 96  |  高度: 96  |  大小: 1.2 KiB

8
Runtime/Comments.meta


fileFormatVersion: 2
guid: 0f301a8303ad194429c4dccd1ba83a6b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

41
Editor/Comments/CommentAsset.cs


using GameplayIngredients.Editor;
using UnityEngine;
using UnityEditor;
namespace GameplayIngredients.Comments.Editor
{
class CommentAsset : ScriptableObject
{
public Comment comment => m_Comment;
[SerializeField]
Comment m_Comment;
[SerializeField, HideInInspector]
public bool firstTimeEdit;
private void Reset()
{
SetDefault();
firstTimeEdit = true;
}
public void SetDefault()
{
m_Comment.title = "New Comment";
m_Comment.message.body = "This is a new Comment, it can describe a problem in the scene, a note to the attention of other user, or a bug encountered.";
m_Comment.message.from = Comment.currentUser;
m_Comment.message.type = CommentType.Info;
m_Comment.message.priority = CommentPriority.Low;
m_Comment.message.state = CommentState.Open;
}
[MenuItem("Assets/Create/Comment")]
static void CreateAsset()
{
AssetFactory.CreateAssetInProjectWindow<CommentAsset>("Packages/net.peeweek.gameplay-ingredients/Icons/Misc/ic-comment.png", "New Comment.asset");
}
}
}

11
Editor/Comments/CommentAsset.cs.meta


fileFormatVersion: 2
guid: 953948be2cd259646acba568317eaf8c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 2db418d0b9151704f87ace549b922c16, type: 3}
userData:
assetBundleName:
assetBundleVariant:

57
Editor/Comments/CommentAssetEditor.cs


using UnityEditor;
using UnityEngine;
namespace GameplayIngredients.Comments.Editor
{
[CustomEditor(typeof(CommentAsset))]
public class CommentAssetEditor : UnityEditor.Editor
{
[SerializeField]
CommentAsset commentAsset;
[SerializeField]
SerializedProperty m_Comment;
[SerializeField]
CommentEditor m_CommentEditor;
public static void RequestEdit() { m_NeedEditNext = true; }
public static bool m_NeedEditNext = false;
private void OnEnable()
{
UpdateComment();
if (commentAsset.comment.focus)
EditorGUIUtility.PingObject(commentAsset);
}
void UpdateComment()
{
if (m_Comment == null)
m_Comment = serializedObject.FindProperty("m_Comment");
if (m_CommentEditor == null)
m_CommentEditor = new CommentEditor(serializedObject, m_Comment, true);
commentAsset = (serializedObject.targetObject as CommentAsset);
if (commentAsset.firstTimeEdit)
RequestEdit();
}
public override void OnInspectorGUI()
{
UpdateComment();
GUILayout.Space(4);
m_CommentEditor.DrawComment(commentAsset.comment, m_NeedEditNext);
if (m_NeedEditNext)
{
m_NeedEditNext = false;
commentAsset.firstTimeEdit = false;
}
GUILayout.Space(16);
}
}
}

11
Editor/Comments/CommentAssetEditor.cs.meta


fileFormatVersion: 2
guid: 59a242602deca20438f9668ce3b67225
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

434
Editor/Comments/CommentEditor.cs


using GameplayIngredients.Editor;
using UnityEditor;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
namespace GameplayIngredients.Comments.Editor
{
public class CommentEditor
{
SerializedObject serializedObject;
SerializedProperty rootMessage;
SerializedProperty replies;
SerializedProperty title;
SerializedProperty focus;
string editMessagePath;
bool editRoot => editMessagePath == rootMessage.propertyPath;
bool m_IsAsset;
public CommentEditor(SerializedObject serializedObject, SerializedProperty comment, bool isAsset = false)
{
this.serializedObject = serializedObject;
title = comment.FindPropertyRelative("title");
rootMessage = comment.FindPropertyRelative("message");
replies = comment.FindPropertyRelative("replies");
focus = comment.FindPropertyRelative("focus");
m_IsAsset = isAsset;
}
public bool DrawEditButton(bool edit)
{
return GUILayout.Toggle(edit, edit ? "Close" : "Edit", EditorStyles.miniButton, GUILayout.Width(64));
}
public void DrawComment(Comment comment, bool requireEdit = false)
{
if(requireEdit)
{
editMessagePath = rootMessage.propertyPath;
requireEdit = false;
}
using (new GUILayout.HorizontalScope())
{
TypeLabel(comment.computedType);
StateLabel(comment.computedState);
PriorityLabel(comment.computedPriority);
GUILayout.FlexibleSpace();
EditorGUI.BeginChangeCheck();
EditorGUI.BeginDisabledGroup(comment.message.from != CommentsWindow.user);
bool editRoot = DrawEditButton(this.editRoot);
EditorGUI.EndDisabledGroup();
if(EditorGUI.EndChangeCheck())
{
if (editRoot)
editMessagePath = rootMessage.propertyPath;
else
editMessagePath = string.Empty;
}
}
GUILayout.Space(6);
if(editRoot)
{
serializedObject.Update();
EditorGUILayout.PropertyField(title);
using(new GUILayout.HorizontalScope())
{
EditorGUILayout.PropertyField(focus);
if(!m_IsAsset && GUILayout.Button("Align to SceneView", GUILayout.ExpandWidth(true)))
{
focus.boolValue = true;
if(SceneView.lastActiveSceneView != null)
{
SceneComment c = serializedObject.targetObject as SceneComment;
c.gameObject.transform.position = SceneView.lastActiveSceneView.camera.transform.position;
c.gameObject.transform.rotation = SceneView.lastActiveSceneView.camera.transform.rotation;
}
}
}
serializedObject.ApplyModifiedProperties();
CommentsWindow.RequestRefresh();
}
else
{
GUILayout.Space(8);
using (new GUILayout.HorizontalScope())
{
GUILayout.Label(CommentEditor.GetPriorityContent(" " + title.stringValue, comment.computedPriority), Styles.title);
GUILayout.FlexibleSpace();
}
}
GUILayout.Space(6);
DrawMessage(rootMessage, -1);
int replyCount = replies.arraySize;
using(new GUILayout.HorizontalScope())
{
GUILayout.Space(16);
using(new GUILayout.VerticalScope())
{
for (int i = 0; i < replyCount; i++)
{
DrawMessage(replies.GetArrayElementAtIndex(i), i);
}
}
}
}
void DrawMessage(SerializedProperty message, int replyIndex)
{
using(new EditorGUILayout.VerticalScope(Styles.message))
{
SerializedProperty body = message.FindPropertyRelative("body");
SerializedProperty URL = message.FindPropertyRelative("URL");
SerializedProperty from = message.FindPropertyRelative("from");
SerializedProperty targets = message.FindPropertyRelative("attachedObjects");
SerializedProperty changeType = message.FindPropertyRelative("changeType");
SerializedProperty changeState = message.FindPropertyRelative("changeState");
SerializedProperty changePriority = message.FindPropertyRelative("changePriority");
SerializedProperty type = message.FindPropertyRelative("type");
SerializedProperty state = message.FindPropertyRelative("state");
SerializedProperty priority = message.FindPropertyRelative("priority");
if (editMessagePath == message.propertyPath)
{
message.serializedObject.Update();
EditorGUILayout.LabelField("Edit Message", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(body);
EditorGUILayout.PropertyField(URL);
EditorGUILayout.PropertyField(from);
using (new EditorGUI.IndentLevelScope(1))
{
EditorGUILayout.PropertyField(targets);
}
if(replyIndex >= 0)
{
EditorGUILayout.PropertyField(changeType);
EditorGUILayout.PropertyField(changeState);
EditorGUILayout.PropertyField(changePriority);
}
if(changeType.boolValue || replyIndex == -1)
EditorGUILayout.PropertyField(type);
if (changeState.boolValue || replyIndex == -1)
EditorGUILayout.PropertyField(state);
if (changePriority.boolValue || replyIndex == -1)
EditorGUILayout.PropertyField(priority);
using (new GUILayout.HorizontalScope())
{
GUILayout.FlexibleSpace();
if (editMessagePath == message.propertyPath && GUILayout.Button("Apply", Styles.miniButton))
{
editMessagePath = string.Empty;
}
if (replyIndex == replies.arraySize - 1 && from.stringValue == CommentsWindow.user && GUILayout.Button("Delete", Styles.miniButton))
{
replies.serializedObject.Update();
replies.DeleteArrayElementAtIndex(replyIndex);
replies.serializedObject.ApplyModifiedProperties();
CommentsWindow.RequestRefresh();
}
}
GUILayout.Space(2);
message.serializedObject.ApplyModifiedProperties();
CommentsWindow.RequestRefresh();
}
else // Display Message
{
using (new GUILayout.HorizontalScope())
{
GUILayout.Label($"<b>From:</b> <color={(from.stringValue == CommentsWindow.user ? "lime" : "white")}><b>{from.stringValue}</b></color>", Styles.from);
GUILayout.FlexibleSpace();
if (replyIndex > -1 && from.stringValue == CommentsWindow.user
&& GUILayout.Button(Styles.edit, Styles.miniButton, GUILayout.Width(32)))
{
editMessagePath = message.propertyPath;
}
if (GUILayout.Button(Styles.reply, Styles.miniButton, GUILayout.Width(32)))
{
replies.serializedObject.Update();
int index = replies.arraySize;
replies.InsertArrayElementAtIndex(index);
var reply = replies.GetArrayElementAtIndex(index);
editMessagePath = reply.propertyPath;
reply.FindPropertyRelative("body").stringValue = string.Empty;
reply.FindPropertyRelative("URL").stringValue = string.Empty;
reply.FindPropertyRelative("from").stringValue = CommentsWindow.user;
reply.FindPropertyRelative("attachedObjects").ClearArray();
replies.serializedObject.ApplyModifiedProperties();
CommentsWindow.RequestRefresh();
}
}
EditorGUILayout.Space();
EditorGUILayout.LabelField(body.stringValue, Styles.multiline);
if (!string.IsNullOrEmpty(URL.stringValue))
{
GUILayout.Space(12);
GUILayout.Label("URL:", EditorStyles.boldLabel, GUILayout.Width(32));
Color b = GUI.backgroundColor;
GUI.backgroundColor = new Color(0, 0, 0, 0.2f);
if (GUILayout.Button($"<color=#44AAFFFF>{URL.stringValue}</color>", Styles.link))
{
Application.OpenURL(URL.stringValue);
}
GUI.backgroundColor = b;
}
if (targets.arraySize > 0)
{
GUILayout.Space(12);
EditorGUILayout.LabelField("Attached Objects:", EditorStyles.boldLabel);
EditorGUI.BeginDisabledGroup(true);
for (int i = 0; i < targets.arraySize; i++)
{
EditorGUILayout.ObjectField(targets.GetArrayElementAtIndex(i));
}
EditorGUI.EndDisabledGroup();
}
if(replyIndex == -1
|| changeType.boolValue
|| changeState.boolValue
|| changePriority.boolValue)
{
GUILayout.Space(12);
using (new GUILayout.HorizontalScope())
{
GUILayout.Label("<b>Set Properties :</b>", Styles.multiline);
if (replyIndex == -1 || changeType.boolValue)
TypeLabel((CommentType)type.intValue);
if (replyIndex == -1 || changeState.boolValue)
StateLabel((CommentState)state.intValue);
if (replyIndex == -1 || changePriority.boolValue)
PriorityLabel((CommentPriority)priority.intValue);
GUILayout.FlexibleSpace();
}
}
}
}
GUI.contentColor = Color.white;
}
void Separator()
{
Rect r = GUILayoutUtility.GetRect(0, 1, GUILayout.ExpandWidth(true));
EditorGUI.DrawRect(r, Color.gray);
}
public static Color GetTypeColor(CommentType type)
{
float sat = 0.3f;
float v = 1f;
switch (type)
{
default:
case CommentType.Info:
return Color.HSVToRGB(0.4f, 0, v);
case CommentType.Bug:
return Color.HSVToRGB(0.05f, sat, v);
case CommentType.Request:
return Color.HSVToRGB(0.15f, sat, v);
case CommentType.ToDo:
return Color.HSVToRGB(0.25f, sat, v);
}
}
public static Color GetStateColor(CommentState state)
{
float sat = 0.8f;
float v = 1f;
switch (state)
{
default:
case CommentState.Open:
return Color.HSVToRGB(0.3f, sat, v);
case CommentState.Blocked:
return Color.HSVToRGB(0.05f, sat, v);
case CommentState.Resolved:
return Color.HSVToRGB(0.5f, sat, v);
case CommentState.WontFix:
return Color.HSVToRGB(0.05f, sat, v);
case CommentState.Closed:
return Color.HSVToRGB(0.7f, 0f, v);
}
}
public static Color GetPriorityColor(CommentPriority priority)
{
float sat = 0.9f;
float v = 1f;
switch (priority)
{
default:
case CommentPriority.High:
return Color.HSVToRGB(0.02f, sat, v);
case CommentPriority.Medium:
return Color.HSVToRGB(0.15f, sat, v);
case CommentPriority.Low:
return Color.HSVToRGB(0.3f, sat, v);
}
}
public static void TypeLabel(CommentType value)
{
GUIUtils.ColoredLabel(value.ToString(), GetTypeColor(value));
}
public static void StateLabel(CommentState value)
{
GUIUtils.ColoredLabel(value.ToString(), GetStateColor(value));
}
public static void PriorityLabel(CommentPriority value)
{
GUIUtils.ColoredLabel(value.ToString(), GetPriorityColor(value));
}
public static GUIContent GetPriorityContent(string text, CommentPriority priority)
{
return new GUIContent(text, GetPriorityTexture(priority));
}
public static Texture GetPriorityTexture(CommentPriority priority)
{
switch (priority)
{
case CommentPriority.High:
return CheckResult.GetIcon(CheckResult.Result.Failed);
case CommentPriority.Medium:
return CheckResult.GetIcon(CheckResult.Result.Warning);
default:
case CommentPriority.Low:
return CheckResult.GetIcon(CheckResult.Result.Notice);
}
}
static class Styles
{
public static GUIStyle title;
public static GUIStyle from;
public static GUIStyle link;
public static GUIStyle multiline;
public static GUIStyle coloredLabel;
public static GUIStyle message;
public static GUIStyle miniButton;
public static GUIContent reply;
public static GUIContent edit;
static Styles()
{
title = new GUIStyle(EditorStyles.boldLabel);
title.fontSize = 16;
from = new GUIStyle(EditorStyles.label);
from.fontSize = 12;
from.richText = true;
link = new GUIStyle(EditorStyles.label);
link.fontSize = 12;
link.richText = true;
link.margin = new RectOffset(0, 0, 4, 4);
link.padding = new RectOffset(2, 2, 2, 2);
SetWhiteBG(link);
multiline = new GUIStyle(EditorStyles.label);
multiline.wordWrap = true;
multiline.richText = true;
multiline.fontSize = 13;
coloredLabel = new GUIStyle(EditorStyles.label);
coloredLabel.fontSize = 12;
coloredLabel.padding = new RectOffset(12, 12, 2, 2);
message = new GUIStyle(EditorStyles.helpBox);
SetWhiteBG(message);
miniButton = new GUIStyle(EditorStyles.miniButton);
miniButton.fontSize = 10;
miniButton.fixedHeight = 16;
SetWhiteBG(miniButton);
edit = new GUIContent(EditorGUIUtility.Load("Packages/net.peeweek.gameplay-ingredients/Icons/GUI/edit.png") as Texture);
reply = new GUIContent(EditorGUIUtility.Load("Packages/net.peeweek.gameplay-ingredients/Icons/GUI/reply.png") as Texture);
}
static Texture2D flat;
static void SetWhiteBG(GUIStyle style)
{
if(flat == null)
{
flat = new Texture2D(1, 1, DefaultFormat.LDR, TextureCreationFlags.None);
flat.SetPixel(0, 0, new Color(0.5f, 0.5f, 0.5f, 0.3f));
flat.Apply();
}
SetBGTexture(style, flat);
}
static void SetBGTexture(GUIStyle style, Texture2D texture)
{
style.onActive.background = texture;
style.active.background = texture;
style.onFocused.background = texture;
style.focused.background = texture;
style.onHover.background = texture;
style.hover.background = texture;
style.onNormal.background = texture;
style.normal.background = texture;
}
}
}
}

11
Editor/Comments/CommentEditor.cs.meta


fileFormatVersion: 2
guid: c6a9a77cca5b7ac4ba42c73ad1cc400a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

423
Editor/Comments/CommentsWindow.cs


using GameplayIngredients.Editor;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace GameplayIngredients.Comments.Editor
{
public class CommentsWindow : EditorWindow
{
static CommentsWindow s_Instance;
[MenuItem("Window/Gameplay Ingredients/Comments")]
public static void Open()
{
s_Instance = EditorWindow.GetWindow<CommentsWindow>();
}
private void OnEnable()
{
titleContent = EditorGUIUtility.IconContent("console.infoicon.inactive.sml");
titleContent.text = "Comments";
minSize = new Vector2(680, 180);
Refresh();
EditorSceneManager.sceneOpened += EditorSceneManager_sceneOpened;
EditorSceneManager.sceneClosed += EditorSceneManager_sceneClosed;
EditorSceneSetup.onSetupLoaded += EditorSceneSetup_onSetupLoaded;
EditorSceneManager.sceneLoaded += SceneManager_sceneLoaded;
EditorSceneManager.sceneUnloaded += SceneManager_sceneUnloaded;
}
private void EditorSceneSetup_onSetupLoaded(EditorSceneSetup setup)
{
Refresh();
}
private void EditorSceneManager_sceneClosed(Scene scene)
{
Refresh();
}
private void EditorSceneManager_sceneOpened(Scene scene, OpenSceneMode mode)
{
Refresh();
}
private void SceneManager_sceneUnloaded(Scene arg0)
{
Refresh();
}
private void SceneManager_sceneLoaded(Scene arg0, LoadSceneMode arg1)
{
Refresh();
}
private void OnDisable()
{
EditorSceneManager.sceneOpened -= EditorSceneManager_sceneOpened;
EditorSceneManager.sceneClosed -= EditorSceneManager_sceneClosed;
EditorSceneSetup.onSetupLoaded -= EditorSceneSetup_onSetupLoaded;
EditorSceneManager.sceneLoaded -= SceneManager_sceneLoaded;
EditorSceneManager.sceneUnloaded -= SceneManager_sceneUnloaded;
}
enum SortMode
{
None,
Name,
Description,
Location,
From,
Type,
Priority,
State
}
SortMode sortMode = SortMode.None;
public enum UserFilter
{
MyComments,
AllComments,
}
const string kPrefixPreference = "GameplayIngredients.Comments.";
static readonly string kUserFilterPreference = $"{kPrefixPreference}UserFilter";
static readonly string kUserPreference = $"{kPrefixPreference}User";
static UserFilter userFilter
{
get { return (UserFilter)EditorPrefs.GetInt(kUserFilterPreference, 0); }
set { EditorPrefs.SetInt(kUserFilterPreference, (int)value); }
}
[InitializeOnLoadMethod]
static void SetDefaultUser()
{
var user = EditorPrefs.GetString(kUserPreference, "");
if(user == "")
{
user = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
}
}
public static string user
{
get { return EditorPrefs.GetString(kUserPreference, ""); }
set { EditorPrefs.SetString(kUserPreference, value); }
}
bool GetShowPref(Enum item)
{
string name = $"{kPrefixPreference}.Show.{item.GetType().Name}.{item}";
return EditorPrefs.GetBool(name, true);
}
void SetShowPref(Enum item, bool value)
{
EditorPrefs.SetBool($"{kPrefixPreference}.Show.{item.GetType().Name}.{item}", value);
}
void ToggleShowPref(Enum item)
{
SetShowPref(item, !GetShowPref(item));
}
void MenuToggleShowPref(object item)
{
ToggleShowPref(item as Enum);
}
int highCount => results == null ? 0 : results.Count(o => o.Item1.computedPriority == CommentPriority.High);
int mediumCount => results == null ? 0 : results.Count(o => o.Item1.computedPriority == CommentPriority.Medium);
int lowCount => results == null ? 0 : results.Count(o => o.Item1.computedPriority == CommentPriority.Low);
string searchFilter;
Vector2 scrollPosition;
bool MatchFilter(UnityEngine.Object obj, Comment comment, string filter)
{
filter = filter.ToLowerInvariant();
return obj.name.Contains(filter)
|| MatchFilter(comment, filter)
;
}
bool MatchFilter(Comment comment, string filter)
{
return comment.title.ToLowerInvariant().Contains(filter)
|| comment.users.Any(o => o.ToLowerInvariant().Contains(filter))
|| MatchFilter(comment.message, filter)
|| comment.replies.Any(m => MatchFilter(m, filter))
;
}
bool MatchFilter(CommentMessage message, string filter)
{
return message.body.ToLowerInvariant().Contains(filter)
|| message.from.ToLowerInvariant().Contains(filter)
|| message.attachedObjects.Any(o => o.name.ToLowerInvariant().Contains(filter))
|| message.URL.ToLowerInvariant().Contains(filter)
;
}
private void OnGUI()
{
// Toolbar
using (new GUILayout.HorizontalScope(EditorStyles.toolbar))
{
if (GUILayout.Button("+", EditorStyles.toolbarButton, GUILayout.Width(24)))
{
SceneCommentEditor.CreateComment();
Refresh();
}
if (GUILayout.Button(EditorGUIUtility.IconContent("Refresh"), EditorStyles.toolbarButton, GUILayout.Width(24)))
Refresh();
searchFilter = EditorGUILayout.DelayedTextField(searchFilter, EditorStyles.toolbarSearchField, GUILayout.ExpandWidth(true));
userFilter = (UserFilter)EditorGUILayout.EnumPopup(userFilter, EditorStyles.toolbarDropDown, GUILayout.Width(128));
if (GUILayout.Button("Type", EditorStyles.toolbarDropDown, GUILayout.Width(64)))
{
var menu = new GenericMenu();
menu.AddItem(new GUIContent(CommentType.Bug.ToString()), GetShowPref(CommentType.Bug), MenuToggleShowPref, CommentType.Bug);
menu.AddItem(new GUIContent(CommentType.Info.ToString()), GetShowPref(CommentType.Info), MenuToggleShowPref, CommentType.Info);
menu.AddItem(new GUIContent(CommentType.Request.ToString()), GetShowPref(CommentType.Request), MenuToggleShowPref, CommentType.Request);
menu.AddItem(new GUIContent(CommentType.ToDo.ToString()), GetShowPref(CommentType.ToDo), MenuToggleShowPref, CommentType.ToDo);
menu.DropDown(new Rect(position.width - 240, 10, 12, 12));
}
if (GUILayout.Button("State", EditorStyles.toolbarDropDown, GUILayout.Width(64)))
{
var menu = new GenericMenu();
menu.AddItem(new GUIContent(CommentState.Open.ToString()), GetShowPref(CommentState.Open), MenuToggleShowPref, CommentState.Open);
menu.AddItem(new GUIContent(CommentState.Resolved.ToString()), GetShowPref(CommentState.Resolved), MenuToggleShowPref, CommentState.Resolved);
menu.AddItem(new GUIContent(CommentState.Closed.ToString()), GetShowPref(CommentState.Closed), MenuToggleShowPref, CommentState.Closed);
menu.AddItem(new GUIContent(CommentState.WontFix.ToString()), GetShowPref(CommentState.WontFix), MenuToggleShowPref, CommentState.WontFix);
menu.AddItem(new GUIContent(CommentState.Blocked.ToString()), GetShowPref(CommentState.Blocked), MenuToggleShowPref, CommentState.Blocked);
menu.DropDown(new Rect(position.width - 176, 10, 12, 12));
}
GUILayout.Space(16);
EditorGUI.BeginChangeCheck();
bool showHigh = GUILayout.Toggle(GetShowPref(CommentPriority.High), CommentEditor.GetPriorityContent(highCount.ToString(), CommentPriority.High), EditorStyles.toolbarButton, GUILayout.Width(32));
bool showMedium = GUILayout.Toggle(GetShowPref(CommentPriority.Medium), CommentEditor.GetPriorityContent(mediumCount.ToString(), CommentPriority.Medium), EditorStyles.toolbarButton, GUILayout.Width(32));
bool showLow = GUILayout.Toggle(GetShowPref(CommentPriority.Low), CommentEditor.GetPriorityContent(lowCount.ToString(), CommentPriority.Low), EditorStyles.toolbarButton, GUILayout.Width(32));
if(EditorGUI.EndChangeCheck())
{
SetShowPref(CommentPriority.High, showHigh);
SetShowPref(CommentPriority.Medium, showMedium);
SetShowPref(CommentPriority.Low, showMedium);
}
}
GUI.backgroundColor = Color.white * 1.25f;
// Header
using (new GUILayout.HorizontalScope(EditorStyles.toolbar))
{
SortButton("Commment", SortMode.Name, 180);
SortButton("Description", SortMode.Description, position.width - 541);
SortButton("Location", SortMode.Location, 100);
SortButton("From", SortMode.From, 80);
SortButton("Type", SortMode.Type, 50);
SortButton("Priority", SortMode.Priority, 60);
SortButton("State", SortMode.State, 70);
}
GUI.backgroundColor = Color.white;
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
int i = 0;
// Lines
foreach (var comment in results)
{
if(comment.Item2 == null)
{
Refresh();
break;
}
if(comment.Item2 is SceneComment)
{
if (!DrawComment(comment.Item1, i, (comment.Item2 as SceneComment).gameObject))
continue;
}
else
{
if (!DrawComment(comment.Item1, i, comment.Item2 as CommentAsset))
continue;
}
i++;
}
EditorGUILayout.EndScrollView();
}
void SortButton(string label, SortMode mode, float width)
{
if (GUILayout.Button(label, sortMode == mode ? Styles.sortHeader : Styles.header, GUILayout.Width(width)))
{
if(Event.current.shift)
sortMode = SortMode.None;
else if (sortMode != mode)
sortMode = mode;
}
SortResults();
}
bool DrawComment(Comment comment, int index, UnityEngine.Object parent)
{
if (comment.computedState == CommentState.Open && !GetShowPref(CommentState.Open)) return false;
if (comment.computedState == CommentState.Resolved && !GetShowPref(CommentState.Resolved)) return false;
if (comment.computedState == CommentState.Blocked && !GetShowPref(CommentState.Blocked)) return false;
if (comment.computedState == CommentState.WontFix && !GetShowPref(CommentState.WontFix)) return false;
if (comment.computedState == CommentState.Closed && !GetShowPref(CommentState.Closed)) return false;
if (comment.computedType == CommentType.Bug && !GetShowPref(CommentType.Bug)) return false;
if (comment.computedType == CommentType.Info && !GetShowPref(CommentType.Info)) return false;
if (comment.computedType == CommentType.Request && !GetShowPref(CommentType.Request)) return false;
if (comment.computedType == CommentType.ToDo && !GetShowPref(CommentType.ToDo)) return false;
if (comment.computedPriority == CommentPriority.High && !GetShowPref(CommentPriority.High)) return false;
if (comment.computedPriority == CommentPriority.Medium && !GetShowPref(CommentPriority.Medium)) return false;
if (comment.computedPriority == CommentPriority.Low && !GetShowPref(CommentPriority.Low)) return false;
if (userFilter == UserFilter.MyComments && !comment.users.Contains(user))
return false;
if (!string.IsNullOrEmpty(searchFilter) && !MatchFilter(parent, comment, searchFilter))
return false;
GUI.backgroundColor = (index % 2 == 0) ? Color.white : Color.white * 0.9f;
using (new GUILayout.HorizontalScope(EditorStyles.toolbar))
{
if (GUILayout.Button(CommentEditor.GetPriorityContent(comment.title, comment.computedPriority), Styles.line, GUILayout.Width(180)))
Selection.activeObject = parent;
GUILayout.Label(comment.message.body, Styles.line, GUILayout.Width(position.width - 541));
if(parent is GameObject)
GUILayout.Label((parent as GameObject).scene.name, Styles.line, GUILayout.Width(100));
else
GUILayout.Label((parent as CommentAsset).name, Styles.line, GUILayout.Width(100));
GUILayout.Label(comment.message.from, Styles.line, GUILayout.Width(80));
GUILayout.Label(comment.computedType.ToString(), Styles.line, GUILayout.Width(50));
GUILayout.Label(comment.computedPriority.ToString(), Styles.line, GUILayout.Width(60));
GUILayout.Label(comment.computedState.ToString(), Styles.line, GUILayout.Width(70));
}
return true;
}
List<Tuple<Comment, UnityEngine.Object>> results;
public static void RequestRefresh()
{
if (s_Instance != null)
s_Instance.Refresh();
}
void Refresh()
{
if (results == null)
results = new List<Tuple<Comment, UnityEngine.Object>>();
else
results.Clear();
foreach(var obj in Resources.FindObjectsOfTypeAll(typeof(SceneComment)))
{
results.Add(new Tuple<Comment, UnityEngine.Object>((obj as SceneComment).comment, obj));
}
foreach (var guid in AssetDatabase.FindAssets($"t:{typeof(CommentAsset).Name}"))
{
CommentAsset ca = AssetDatabase.LoadAssetAtPath<CommentAsset>(AssetDatabase.GUIDToAssetPath(guid));
results.Add(new Tuple<Comment, UnityEngine.Object>(ca.comment, ca));
}
SortResults();
Repaint();
}
void SortResults()
{
if (results == null)
return;
switch (sortMode)
{
case SortMode.None:
break;
case SortMode.Name:
results = results.OrderBy(o => o.Item2.name).ToList();
break;
case SortMode.Description:
results = results.OrderBy(o => o.Item1.title).ToList();
break;
case SortMode.Location:
results = results.OrderBy(o => o.Item2 is GameObject ? (o.Item2 as GameObject).scene.name : o.Item2.name ).ToList();
break;
case SortMode.From:
results = results.OrderBy(o => o.Item1.message.from).ToList();
break;
case SortMode.Type:
results = results.OrderBy(o => o.Item1.computedType).ToList();
break;
case SortMode.Priority:
results = results.OrderBy(o => o.Item1.computedPriority).ToList();
break;
case SortMode.State:
results = results.OrderBy(o => o.Item1.computedState).ToList();
break;
default:
break;
}
}
static class Styles
{
public static GUIStyle header;
public static GUIStyle sortHeader;
public static GUIStyle line;
static Styles()
{
header = new GUIStyle(EditorStyles.toolbarButton);
header.alignment = TextAnchor.MiddleLeft;
header.fontStyle = FontStyle.Bold;
sortHeader = new GUIStyle(EditorStyles.toolbarDropDown);
sortHeader.alignment = TextAnchor.MiddleLeft;
sortHeader.fontStyle = FontStyle.Bold;
line = new GUIStyle(EditorStyles.toolbarButton);
line.padding = new RectOffset();
line.contentOffset = new Vector2(6, 2);
line.alignment = TextAnchor.UpperLeft;
line.wordWrap = false;
}
}
[SettingsProvider]
public static SettingsProvider CommentsPreferences()
{
return new SettingsProvider("Preferences/Gameplay Ingredients/Comments", SettingsScope.User)
{
label = "Comments",
guiHandler = (searchContext) =>
{
user = EditorGUILayout.DelayedTextField("Project User Nickname", user);
}
};
}
}
}

11
Editor/Comments/CommentsWindow.cs.meta


fileFormatVersion: 2
guid: 40737dcad5a16c842aab1be8a72b04ce
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

73
Editor/Comments/SceneCommentEditor.cs


using UnityEditor;
using UnityEngine;
namespace GameplayIngredients.Comments.Editor
{
[CustomEditor(typeof(SceneComment))]
public class SceneCommentEditor : UnityEditor.Editor
{
[SerializeField]
SceneComment sceneComment;
[SerializeField]
SerializedProperty m_Comment;
[SerializeField]
CommentEditor m_CommentEditor;
public static void RequestEdit() { m_NeedEditNext = true; }
public static bool m_NeedEditNext = false;
private void OnEnable()
{
UpdateComment();
if(sceneComment.comment.focus && SceneView.lastActiveSceneView != null)
SceneView.lastActiveSceneView.AlignViewToObject(sceneComment.transform);
}
void UpdateComment()
{
if (m_Comment == null)
m_Comment = serializedObject.FindProperty("m_Comment");
if (m_CommentEditor == null)
m_CommentEditor = new CommentEditor(serializedObject, m_Comment);
sceneComment = (serializedObject.targetObject as SceneComment);
}
public override void OnInspectorGUI()
{
UpdateComment();
GUILayout.Space(4);
m_CommentEditor.DrawComment(sceneComment.comment, m_NeedEditNext);
if (m_NeedEditNext)
m_NeedEditNext = false;
GUILayout.Space(16);
}
[MenuItem("GameObject/Comment", false, 10)]
public static void CreateComment()
{
var go = new GameObject("Comment", typeof(SceneComment));
if (Selection.activeGameObject != null && Selection.activeGameObject.scene != null)
{
go.transform.parent = Selection.activeGameObject.transform;
}
if (SceneView.lastActiveSceneView != null)
{
var cam = SceneView.lastActiveSceneView.camera;
go.transform.position = cam.transform.position;
go.transform.rotation = cam.transform.rotation;
}
Selection.activeGameObject = go;
go.GetComponent<SceneComment>().SetDefault();
SceneCommentEditor.RequestEdit();
}
}
}

11
Editor/Comments/SceneCommentEditor.cs.meta


fileFormatVersion: 2
guid: cbc9f694094322c4386b7945966415a7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

130
Runtime/Comments/Comment.cs


using NaughtyAttributes;
using System;
using System.Linq;
using UnityEngine;
namespace GameplayIngredients.Comments
{
[Serializable]
public struct Comment
{
#if UNITY_EDITOR
const string kUserPreference = "GameplayIngredients.Comments.User";
public static string currentUser => UnityEditor.EditorPrefs.GetString(kUserPreference, "user");
#endif
public string title;
public CommentMessage message;
public CommentMessage[] replies;
public bool focus;
public string[] users
{
get
{
var users = replies.Select(r => r.from).ToList();
users.Add(message.from);
return users.ToArray();
}
}
public CommentType computedType
{
get
{
CommentType currentType = message.type;
if(replies != null)
{
foreach (var reply in replies)
{
if (reply.changeType)
currentType = reply.type;
}
}
return currentType;
}
}
public CommentState computedState
{
get
{
CommentState currentState = message.state;
if (replies != null)
{
foreach (var reply in replies)
{
if (reply.changeState)
currentState = reply.state;
}
}
return currentState;
}
}
public CommentPriority computedPriority
{
get
{
CommentPriority currentPriority = message.priority;
if (replies != null)
{
foreach (var reply in replies)
{
if (reply.changePriority)
currentPriority = reply.priority;
}
}
return currentPriority;
}
}
}
[Serializable]
public struct CommentMessage
{
public string from;
public string URL;
[Multiline]
public string body;
[ReorderableList, NoLabel]
public UnityEngine.Object[] attachedObjects;
public bool changeType;
public bool changeState;
public bool changePriority;
public CommentType type;
public CommentState state;
public CommentPriority priority;
}
[Serializable]
public enum CommentType
{
Info,
Bug,
Request,
ToDo,
}
[Serializable]
public enum CommentState
{
Open,
Blocked,
Resolved,
WontFix,
Closed,
}
[Serializable]
public enum CommentPriority
{
High,
Medium,
Low,
}
}

11
Runtime/Comments/Comment.cs.meta


fileFormatVersion: 2
guid: 8f47d9309b8127a41965c4dc518a1d5f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

28
Runtime/Comments/SceneComment.cs


using UnityEngine;
namespace GameplayIngredients.Comments
{
public class SceneComment : MonoBehaviour
{
#if UNITY_EDITOR
public Comment comment => m_Comment;
[SerializeField]
Comment m_Comment;
private void Reset()
{
m_Comment.message.from = Comment.currentUser;
transform.hideFlags = HideFlags.HideInInspector;
}
public void SetDefault()
{
m_Comment.title = "New Comment";
m_Comment.message.body = "This is a new Comment, it can describe a problem in the scene, a note to the attention of other user, or a bug encountered.";
m_Comment.message.from = Comment.currentUser;
m_Comment.message.type = CommentType.Info;
m_Comment.message.priority = CommentPriority.Low;
m_Comment.message.state = CommentState.Open;
}
#endif
}
}

11
Runtime/Comments/SceneComment.cs.meta


fileFormatVersion: 2
guid: c1e2df2465e50ca41bb6e114feab4401
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 2db418d0b9151704f87ace549b922c16, type: 3}
userData:
assetBundleName:
assetBundleVariant:
正在加载...
取消
保存