├── .gitignore ├── Documents~ └── imgs │ ├── projectnotes_main_window.png │ ├── projectnotes_note_editing_window.png │ └── projectnotes_toolbar_entry.gif ├── Editor.meta ├── Editor ├── CategoryEntryToggle.cs ├── CategoryEntryToggle.cs.meta ├── CategoryInfo.cs ├── CategoryInfo.cs.meta ├── EditorToggleGroup.cs ├── EditorToggleGroup.cs.meta ├── GBG.ProjectNotes.Editor.asmdef ├── GBG.ProjectNotes.Editor.asmdef.meta ├── NoteContentView.cs ├── NoteContentView.cs.meta ├── NoteEditWindow.cs ├── NoteEditWindow.cs.meta ├── NoteEntry.cs ├── NoteEntry.cs.meta ├── NoteHistory.cs ├── NoteHistory.cs.meta ├── NoteKey.cs ├── NoteKey.cs.meta ├── NoteListViewItemLabel.cs ├── NoteListViewItemLabel.cs.meta ├── Picker.cs ├── Picker.cs.meta ├── ProjectNotesLocalCache.cs ├── ProjectNotesLocalCache.cs.meta ├── ProjectNotesSettings.cs ├── ProjectNotesSettings.cs.meta ├── ProjectNotesSettingsEditor.cs ├── ProjectNotesSettingsEditor.cs.meta ├── ProjectNotesWindow.GUI.cs ├── ProjectNotesWindow.GUI.cs.meta ├── ProjectNotesWindow.cs ├── ProjectNotesWindow.cs.meta ├── Utility.cs └── Utility.cs.meta ├── LICENSE ├── LICENSE.meta ├── README.md ├── README.md.meta ├── README_CN.md ├── README_CN.md.meta ├── package.json └── package.json.meta /.gitignore: -------------------------------------------------------------------------------- 1 | # This .gitignore file should be placed at the root of your Unity project directory 2 | # 3 | # Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore 4 | # 5 | /[Ll]ibrary/ 6 | /[Tt]emp/ 7 | /[Oo]bj/ 8 | /[Bb]uild/ 9 | /[Bb]uilds/ 10 | /[Ll]ogs/ 11 | /[Uu]ser[Ss]ettings/ 12 | 13 | # MemoryCaptures can get excessive in size. 14 | # They also could contain extremely sensitive data 15 | /[Mm]emoryCaptures/ 16 | 17 | # Recordings can get excessive in size 18 | /[Rr]ecordings/ 19 | 20 | # Uncomment this line if you wish to ignore the asset store tools plugin 21 | # /[Aa]ssets/AssetStoreTools* 22 | 23 | # Autogenerated Jetbrains Rider plugin 24 | /[Aa]ssets/Plugins/Editor/JetBrains* 25 | 26 | # Visual Studio cache directory 27 | .vs/ 28 | 29 | # Gradle cache directory 30 | .gradle/ 31 | 32 | # Autogenerated VS/MD/Consulo solution and project files 33 | ExportedObj/ 34 | .consulo/ 35 | *.csproj 36 | *.unityproj 37 | *.sln 38 | *.suo 39 | *.tmp 40 | *.user 41 | *.userprefs 42 | *.pidb 43 | *.booproj 44 | *.svd 45 | *.pdb 46 | *.mdb 47 | *.opendb 48 | *.VC.db 49 | 50 | # Unity3D generated meta files 51 | *.pidb.meta 52 | *.pdb.meta 53 | *.mdb.meta 54 | 55 | # Unity3D generated file on crash reports 56 | sysinfo.txt 57 | 58 | # Builds 59 | *.apk 60 | *.aab 61 | *.unitypackage 62 | *.app 63 | 64 | # Crashlytics generated file 65 | crashlytics-build.properties 66 | 67 | # Packed Addressables 68 | /[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin* 69 | 70 | # Temporary auto-generated Android Assets 71 | /[Aa]ssets/[Ss]treamingAssets/aa.meta 72 | /[Aa]ssets/[Ss]treamingAssets/aa/* 73 | -------------------------------------------------------------------------------- /Documents~/imgs/projectnotes_main_window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SolarianZ/UnityProjectNotes/64f2600de3849807ee49868601bfdd3e0dc137d8/Documents~/imgs/projectnotes_main_window.png -------------------------------------------------------------------------------- /Documents~/imgs/projectnotes_note_editing_window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SolarianZ/UnityProjectNotes/64f2600de3849807ee49868601bfdd3e0dc137d8/Documents~/imgs/projectnotes_note_editing_window.png -------------------------------------------------------------------------------- /Documents~/imgs/projectnotes_toolbar_entry.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SolarianZ/UnityProjectNotes/64f2600de3849807ee49868601bfdd3e0dc137d8/Documents~/imgs/projectnotes_toolbar_entry.gif -------------------------------------------------------------------------------- /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2c911cb83761e42489b862d62ab06481 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/CategoryEntryToggle.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | using UnityEditor.UIElements; 3 | using UnityEngine; 4 | using UnityEngine.UIElements; 5 | 6 | namespace GBG.ProjectNotes.Editor 7 | { 8 | public class CategoryEntryToggle : ToolbarToggle 9 | { 10 | private Image _redDotIcon; 11 | 12 | public bool redDotIconVisible 13 | { 14 | get 15 | { 16 | return _redDotIcon != null 17 | ? _redDotIcon.style.display == DisplayStyle.Flex 18 | : false; 19 | } 20 | set 21 | { 22 | if (value) 23 | { 24 | if (_redDotIcon == null) 25 | { 26 | CreateRedDotIcon(); 27 | } 28 | 29 | _redDotIcon.style.display = DisplayStyle.Flex; 30 | } 31 | else if (_redDotIcon != null) 32 | { 33 | _redDotIcon.style.display = DisplayStyle.None; 34 | } 35 | } 36 | } 37 | 38 | 39 | public CategoryEntryToggle(string text = null) 40 | { 41 | this.text = text; 42 | } 43 | 44 | private void CreateRedDotIcon() 45 | { 46 | _redDotIcon = new Image 47 | { 48 | image = EditorGUIUtility.Load(Utility.RedDotIconName) as Texture, 49 | style = 50 | { 51 | width = 8, 52 | height = 8, 53 | } 54 | }; 55 | Add(_redDotIcon); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /Editor/CategoryEntryToggle.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1499c1992bdd4544287bd8cbedb3e71f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/CategoryInfo.cs: -------------------------------------------------------------------------------- 1 | namespace GBG.ProjectNotes.Editor 2 | { 3 | public struct CategoryInfo 4 | { 5 | public string category; 6 | public int maxPriority; 7 | public bool hasUnreadNotes; 8 | } 9 | } -------------------------------------------------------------------------------- /Editor/CategoryInfo.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a379b152a5358224f9a440b5a27e080d 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/EditorToggleGroup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using UnityEngine.UIElements; 4 | 5 | namespace GBG.ProjectNotes.Editor 6 | { 7 | public class EditorToggleGroup : GroupBox 8 | { 9 | private readonly List _toggles = new List(); 10 | private Toggle _activeToggle; 11 | 12 | public event Action activeToggleChanged; 13 | 14 | 15 | public new void Add(VisualElement child) 16 | { 17 | if (child is Toggle toggle && !_toggles.Contains(toggle)) 18 | { 19 | _toggles.Add(toggle); 20 | toggle.RegisterValueChangedCallback(OnToggleValueChanged); 21 | } 22 | 23 | base.Add(child); 24 | } 25 | 26 | public new void Remove(VisualElement child) 27 | { 28 | if (child is Toggle toggle) 29 | { 30 | _toggles.Remove(toggle); 31 | toggle.UnregisterValueChangedCallback(OnToggleValueChanged); 32 | } 33 | 34 | base.Remove(child); 35 | } 36 | 37 | public new void Clear() 38 | { 39 | foreach (Toggle toggle in _toggles) 40 | { 41 | toggle.UnregisterValueChangedCallback(OnToggleValueChanged); 42 | } 43 | _toggles.Clear(); 44 | 45 | base.Clear(); 46 | } 47 | 48 | 49 | private void OnToggleValueChanged(ChangeEvent evt) 50 | { 51 | if (!evt.newValue) 52 | { 53 | if (evt.target == _activeToggle) 54 | { 55 | _activeToggle.SetValueWithoutNotify(true); 56 | } 57 | 58 | return; 59 | } 60 | 61 | _activeToggle = evt.target as Toggle; 62 | foreach (Toggle toggle in _toggles) 63 | { 64 | if (toggle != _activeToggle) 65 | { 66 | toggle.SetValueWithoutNotify(false); 67 | } 68 | } 69 | 70 | activeToggleChanged?.Invoke(_activeToggle); 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /Editor/EditorToggleGroup.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 125aed3a2e1bd134fa81085208996783 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/GBG.ProjectNotes.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "GBG.ProjectNotes.Editor", 3 | "rootNamespace": "GBG.ProjectNotes.Editor", 4 | "references": [], 5 | "includePlatforms": [ 6 | "Editor" 7 | ], 8 | "excludePlatforms": [], 9 | "allowUnsafeCode": true, 10 | "overrideReferences": false, 11 | "precompiledReferences": [], 12 | "autoReferenced": true, 13 | "defineConstraints": [], 14 | "versionDefines": [], 15 | "noEngineReferences": false 16 | } -------------------------------------------------------------------------------- /Editor/GBG.ProjectNotes.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1eb1750d028517c498c04064b42e5db8 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/NoteContentView.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using UnityEditor; 4 | using UnityEngine; 5 | using UnityEngine.UIElements; 6 | #if !UNITY_2022_3_OR_NEWER 7 | using UnityEditor.UIElements; // PopupField 8 | #endif 9 | using UDebug = UnityEngine.Debug; 10 | 11 | namespace GBG.ProjectNotes.Editor 12 | { 13 | public class NoteContentView : VisualElement 14 | { 15 | public static Color draftLabelBorderColor = new Color32(255, 180, 0, 255); 16 | public static Color unreadLabelBorderColor = new Color32(240, 0, 0, 255); 17 | public static float statusLabelBorderWidth = 2; 18 | public static float statusLabelBorderRadius = 4; 19 | public static float contentLabelBorderSize = 1; 20 | public static Color contentLabelBorderColorDark = new Color32(44, 44, 44, 255); 21 | public static Color contentLabelBorderColorLight = new Color32(177, 177, 177, 255); 22 | 23 | private readonly Label _versionLabel; 24 | private readonly Label _unreadLabel; 25 | private readonly Label _titleLabel; 26 | private readonly Label _authorLabel; 27 | private readonly PopupField _historyPopup; 28 | private readonly List _historyTimestamps = new List(); 29 | private readonly Label _contentLabel; 30 | private readonly Button _markButton; 31 | private readonly Button _opDropdownButton; 32 | private NoteEntry _note; 33 | 34 | public event Action readStatusChanged; 35 | public event Action wantsToEditNote; 36 | public event Action wantsToDeleteNote; 37 | 38 | 39 | public NoteContentView() 40 | { 41 | style.paddingLeft = 8; 42 | style.paddingRight = 6; 43 | style.paddingTop = 4; 44 | style.paddingBottom = 4; 45 | 46 | VisualElement statusContainer = new VisualElement 47 | { 48 | style = 49 | { 50 | flexDirection = FlexDirection.Row, 51 | } 52 | }; 53 | Add(statusContainer); 54 | 55 | _versionLabel = new Label 56 | { 57 | text = "Draft", 58 | style = 59 | { 60 | display = DisplayStyle.None, 61 | marginRight = 2, 62 | paddingLeft= 2, 63 | paddingRight= 2, 64 | borderLeftWidth = statusLabelBorderWidth, 65 | borderRightWidth = statusLabelBorderWidth, 66 | borderTopWidth = statusLabelBorderWidth, 67 | borderBottomWidth = statusLabelBorderWidth, 68 | borderTopLeftRadius = statusLabelBorderRadius , 69 | borderTopRightRadius = statusLabelBorderRadius , 70 | borderBottomLeftRadius = statusLabelBorderRadius , 71 | borderBottomRightRadius = statusLabelBorderRadius , 72 | borderLeftColor = draftLabelBorderColor, 73 | borderRightColor = draftLabelBorderColor, 74 | borderTopColor = draftLabelBorderColor, 75 | borderBottomColor = draftLabelBorderColor, 76 | unityTextAlign = TextAnchor.MiddleCenter, 77 | } 78 | }; 79 | statusContainer.Add(_versionLabel); 80 | 81 | _unreadLabel = new Label 82 | { 83 | text = "Unread", 84 | style = 85 | { 86 | display = DisplayStyle.None, 87 | marginLeft = 2, 88 | paddingLeft= 2, 89 | paddingRight= 2, 90 | borderLeftWidth = statusLabelBorderWidth, 91 | borderRightWidth = statusLabelBorderWidth, 92 | borderTopWidth = statusLabelBorderWidth, 93 | borderBottomWidth = statusLabelBorderWidth, 94 | borderTopLeftRadius = statusLabelBorderRadius, 95 | borderTopRightRadius = statusLabelBorderRadius, 96 | borderBottomLeftRadius = statusLabelBorderRadius, 97 | borderBottomRightRadius = statusLabelBorderRadius, 98 | borderLeftColor = unreadLabelBorderColor, 99 | borderRightColor = unreadLabelBorderColor, 100 | borderTopColor = unreadLabelBorderColor, 101 | borderBottomColor = unreadLabelBorderColor, 102 | unityTextAlign = TextAnchor.MiddleCenter, 103 | } 104 | }; 105 | statusContainer.Add(_unreadLabel); 106 | 107 | _titleLabel = new Label 108 | { 109 | text = "-", 110 | enableRichText = true, 111 | style = 112 | { 113 | fontSize = 16, 114 | } 115 | }; 116 | #if UNITY_2022_3_OR_NEWER 117 | ((ITextSelection)_titleLabel).isSelectable = true; 118 | #endif 119 | Add(_titleLabel); 120 | 121 | _authorLabel = new Label 122 | { 123 | text = "-", 124 | enableRichText = true, 125 | style = 126 | { 127 | marginLeft = 2, 128 | fontSize = 11, 129 | unityFontStyleAndWeight = FontStyle.Italic, 130 | } 131 | }; 132 | #if UNITY_2022_3_OR_NEWER 133 | ((ITextSelection)_authorLabel).isSelectable = true; 134 | #endif 135 | Add(_authorLabel); 136 | 137 | _historyPopup = new PopupField 138 | { 139 | choices = _historyTimestamps, 140 | value = 0, 141 | formatSelectedValueCallback = Utility.FormatTimestamp, 142 | formatListItemCallback = Utility.FormatTimestamp, 143 | style = 144 | { 145 | marginLeft = 0, 146 | alignSelf = Align.FlexStart, 147 | } 148 | }; 149 | _historyPopup.RegisterValueChangedCallback(SelectHistory); 150 | Add(_historyPopup); 151 | 152 | ScrollView contentScrollView = new ScrollView 153 | { 154 | style = 155 | { 156 | flexGrow = 1, 157 | marginTop = 10, 158 | } 159 | }; 160 | Add(contentScrollView); 161 | _contentLabel = new Label 162 | { 163 | text = "-", 164 | enableRichText = true, 165 | style = 166 | { 167 | flexGrow = 1, 168 | fontSize = 14, 169 | whiteSpace = WhiteSpace.Normal, 170 | //borderLeftWidth = contentLabelBorderSize, 171 | //borderRightWidth = contentLabelBorderSize, 172 | //borderTopWidth = contentLabelBorderSize, 173 | //borderBottomWidth = contentLabelBorderSize, 174 | //borderTopLeftRadius = contentLabelBorderSize, 175 | //borderTopRightRadius = contentLabelBorderSize, 176 | //borderBottomLeftRadius = contentLabelBorderSize, 177 | //borderBottomRightRadius = contentLabelBorderSize, 178 | //borderLeftColor = EditorGUIUtility.isProSkin ? contentLabelBorderColorDark : contentLabelBorderColorLight, 179 | //borderRightColor = EditorGUIUtility.isProSkin ? contentLabelBorderColorDark : contentLabelBorderColorLight, 180 | //borderTopColor = EditorGUIUtility.isProSkin ? contentLabelBorderColorDark : contentLabelBorderColorLight, 181 | //borderBottomColor = EditorGUIUtility.isProSkin ? contentLabelBorderColorDark : contentLabelBorderColorLight, 182 | } 183 | }; 184 | #if UNITY_2022_3_OR_NEWER 185 | ((ITextSelection)_contentLabel).isSelectable = true; 186 | #endif 187 | contentScrollView.Add(_contentLabel); 188 | 189 | VisualElement opContainer = new VisualElement 190 | { 191 | style = 192 | { 193 | flexDirection = FlexDirection.Row, 194 | justifyContent = Justify.FlexEnd, 195 | minHeight= 22, 196 | maxHeight= 22, 197 | marginBottom = 2, 198 | }, 199 | }; 200 | Add(opContainer); 201 | 202 | _markButton = new Button(MarkStatus) 203 | { 204 | text = "-", 205 | style = 206 | { 207 | width = 110, 208 | marginRight = 0, 209 | paddingRight = 0, 210 | borderTopLeftRadius = Utility.ButtonBorderRadius, 211 | borderTopRightRadius = Utility.ButtonBorderRadius, 212 | borderBottomLeftRadius = Utility.ButtonBorderRadius, 213 | borderBottomRightRadius = Utility.ButtonBorderRadius, 214 | } 215 | }; 216 | opContainer.Add(_markButton); 217 | 218 | _opDropdownButton = new Button(CreateOpDropdownMenu) 219 | { 220 | style = 221 | { 222 | backgroundImage = EditorGUIUtility.isProSkin 223 | ? EditorGUIUtility.Load("d_icon dropdown@2x") as Texture2D 224 | : EditorGUIUtility.Load("icon dropdown@2x") as Texture2D, 225 | width = 14, 226 | marginLeft = 0, 227 | paddingLeft = 0, 228 | borderTopLeftRadius = Utility.ButtonBorderRadius, 229 | borderTopRightRadius = Utility.ButtonBorderRadius, 230 | borderBottomLeftRadius = Utility.ButtonBorderRadius, 231 | borderBottomRightRadius = Utility.ButtonBorderRadius, 232 | } 233 | }; 234 | opContainer.Add(_opDropdownButton); 235 | } 236 | 237 | public void SetNote(NoteEntry note) 238 | { 239 | _note = note; 240 | _versionLabel.text = _note?.isDraft ?? false 241 | ? "Draft" 242 | : null; 243 | _versionLabel.style.display = _note?.isDraft ?? false 244 | ? DisplayStyle.Flex 245 | : DisplayStyle.None; 246 | _unreadLabel.style.display = _note != null && ProjectNotesLocalCache.instance.IsUnread(_note.GetKey()) 247 | ? DisplayStyle.Flex 248 | : DisplayStyle.None; 249 | _titleLabel.text = _note?.title ?? "TITLE"; 250 | _authorLabel.text = _note == null ? "AUTHOR" : $"by {_note.author}"; 251 | _note.CollectHistoryTimestamps(_historyTimestamps); 252 | _historyPopup.SetValueWithoutNotify(_note?.timestamp ?? 0L); 253 | _contentLabel.text = _note?.content ?? "CONTENT"; 254 | UpdateOpButtons(); 255 | } 256 | 257 | public void RefreshView() 258 | { 259 | SetNote(_note); 260 | } 261 | 262 | private void CreateOpDropdownMenu() 263 | { 264 | if (_note == null) 265 | { 266 | return; 267 | } 268 | 269 | GenericMenu menu = new GenericMenu(); 270 | if (_note.timestamp == _historyPopup.value) 271 | { 272 | menu.AddItem(new GUIContent("Edit"), false, OnWantsToEditNote); 273 | menu.AddSeparator(""); 274 | menu.AddItem(new GUIContent("Delete"), false, OnWantsToDeleteNote); 275 | menu.DropDown(_opDropdownButton.worldBound); 276 | } 277 | else 278 | { 279 | menu.AddItem(new GUIContent("Delete Old Version"), false, OnWantsToDeleteNote); 280 | menu.DropDown(_opDropdownButton.worldBound); 281 | } 282 | } 283 | 284 | private void SelectHistory(ChangeEvent evt) 285 | { 286 | long timestamp = evt.newValue; 287 | if (timestamp == _note.timestamp) 288 | { 289 | RefreshView(); 290 | return; 291 | } 292 | 293 | foreach (NoteHistory history in _note.contentHistory) 294 | { 295 | if (history.timestamp == timestamp) 296 | { 297 | _versionLabel.text = "Old Version"; 298 | _versionLabel.style.display = DisplayStyle.Flex; 299 | _unreadLabel.style.display = DisplayStyle.None; 300 | _contentLabel.text = history.content; 301 | UpdateOpButtons(); 302 | return; 303 | } 304 | } 305 | 306 | UDebug.LogError($"[Project Notes] Unable to find historical version for timestamp '{timestamp}'."); 307 | RefreshView(); 308 | } 309 | 310 | private void UpdateOpButtons() 311 | { 312 | _markButton.text = _note == null || _note.timestamp != _historyPopup.value 313 | ? "MARK" 314 | : ProjectNotesLocalCache.instance.IsUnread(_note.GetKey()) 315 | ? "Mark as Read" 316 | : "Mark as Unread"; 317 | _markButton.SetEnabled(_note != null && _note.timestamp == _historyPopup.value); 318 | _opDropdownButton.SetEnabled(_note != null); 319 | } 320 | 321 | private void MarkStatus() 322 | { 323 | if (_note == null) 324 | { 325 | return; 326 | } 327 | 328 | bool unread = ProjectNotesLocalCache.instance.IsUnread(_note.GetKey()); 329 | if (unread) 330 | { 331 | ProjectNotesLocalCache.instance.MarkAsRead(_note.GetKey()); 332 | } 333 | else 334 | { 335 | ProjectNotesLocalCache.instance.MarkAsUnread(_note.GetKey()); 336 | } 337 | 338 | RefreshView(); 339 | 340 | readStatusChanged?.Invoke(_note); 341 | } 342 | 343 | private void OnWantsToEditNote() 344 | { 345 | if (_note == null) 346 | { 347 | return; 348 | } 349 | 350 | wantsToEditNote?.Invoke(_note); 351 | } 352 | 353 | private void OnWantsToDeleteNote() 354 | { 355 | if (_note == null) 356 | { 357 | return; 358 | } 359 | 360 | bool oldVersion = _note.timestamp != _historyPopup.value; 361 | string title = oldVersion 362 | ? "Delete old version of the selected note?" 363 | : "Delete selected note?"; 364 | string messageHeader = oldVersion 365 | ? $"{_note.title}\nVersion: {Utility.FormatTimestamp(_historyPopup.value)}\n\n" 366 | : $"{_note.title}\n\n"; 367 | string message = messageHeader + 368 | $"This operation cannot be undone.\n" + 369 | $"Once the settings is synced to the version control system, this note will be removed from the project of all team members."; 370 | if (EditorUtility.DisplayDialog(title, message, "Delete", "Cancel")) 371 | { 372 | wantsToDeleteNote?.Invoke(_note, _historyPopup.value); 373 | } 374 | } 375 | } 376 | } -------------------------------------------------------------------------------- /Editor/NoteContentView.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3c14d4bb7c00a9e4796c8d8891a12eb0 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/NoteEditWindow.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using UnityEditor; 5 | using UnityEditor.UIElements; 6 | using UnityEngine; 7 | using UnityEngine.UIElements; 8 | 9 | namespace GBG.ProjectNotes.Editor 10 | { 11 | public class NoteEditWindow : EditorWindow 12 | { 13 | public static NoteEditWindow Open(NoteEntry note, SaveNoteHandler onSave, IEnumerable existingCategories) 14 | { 15 | NoteEditWindow window = GetWindow(true); 16 | window.Initialize(note, onSave, existingCategories); // Called after CreateGUI 17 | //window.ShowModalUtility(); Conflict with undo/redo 18 | return window; 19 | } 20 | 21 | //private static NoteEditWindow _instance; 22 | 23 | [Flags] 24 | enum InvalidStatus 25 | { 26 | Category = 1 << 0, 27 | Author = 1 << 1, 28 | Title = 1 << 2, 29 | Content = 1 << 3, 30 | } 31 | 32 | 33 | public delegate void SaveNoteHandler(NoteEntry note, bool isNewNote); 34 | 35 | public const float FieldLabelWidth = 70; 36 | public const float AlertLabelOffset = 80; 37 | public static Color WarningTextColor = Color.yellow;// new Color32(255, 180, 0, 255); 38 | public static Color ErrorTextColor = new Color32(240, 0, 0, 255); 39 | 40 | private SaveNoteHandler _onSave; 41 | private bool _isNewNote; 42 | private InvalidStatus _invalidStatus; 43 | [SerializeField] 44 | private NoteEntry _serializedNote; // for undo/redo use 45 | private SerializedObject _serializedObject; 46 | 47 | private TextField _guidField; 48 | private TextField _timestampField; 49 | private TextField _categoryField; 50 | private ToolbarMenu _existingCategoryMenu; 51 | private Label _categoryAlertLabel; 52 | private TextField _authorField; 53 | private Label _authorAlertLabel; 54 | private Toggle _draftField; 55 | private IntegerField _priorityField; 56 | private TextField _titleField; 57 | private Label _titleAlertLabel; 58 | private TextField _contentField; 59 | private Label _contentAlertLabel; 60 | 61 | 62 | private void OnEnable() 63 | { 64 | //_instance = this; 65 | 66 | SetWindowTitle(); 67 | minSize = new Vector2(400, 400); 68 | 69 | AssemblyReloadEvents.beforeAssemblyReload -= OnBeforeAssemblyReload; 70 | AssemblyReloadEvents.beforeAssemblyReload += OnBeforeAssemblyReload; 71 | } 72 | 73 | private void CreateGUI() 74 | { 75 | VisualElement root = rootVisualElement; 76 | 77 | ScrollView scrollView = new ScrollView 78 | { 79 | style = { flexGrow = 1 }, 80 | }; 81 | root.Add(scrollView); 82 | 83 | _guidField = new TextField("Guid") 84 | { 85 | isReadOnly = true, 86 | style = { unityTextAlign = TextAnchor.MiddleRight, }, 87 | }; 88 | _guidField.Q