2026-06-13 04:30:24 +01:00
/ * This Source Code Form is subject to the terms of the Mozilla Public
2020-12-22 03:13:43 +00:00
* License , v . 2.0 . If a copy of the MPL was not distributed with this
* file , You can obtain one at https : //mozilla.org/MPL/2.0/. */
2020-04-28 01:50:50 +01:00
using System ;
2020-05-22 22:34:08 +01:00
using System.Collections.Generic ;
2020-04-23 03:31:17 +01:00
using System.IO ;
2020-03-22 19:05:26 +00:00
using System.Linq ;
using System.Reflection ;
using UnityEditor ;
using UnityEngine ;
2020-04-14 04:08:33 +01:00
using static UnityEditor . EditorGUILayout ;
2020-03-22 19:05:26 +00:00
2021-03-31 04:07:11 +01:00
public abstract class DatabaseListView : EditorWindow
2020-03-22 19:05:26 +00:00
{
2021-03-31 04:07:11 +01:00
public static GUIStyle ListStyleOdd ;
public static GUIStyle ListStyleEven ;
2020-11-29 22:50:12 +00:00
2020-03-22 19:05:26 +00:00
public Guid SelectedItem ;
public GUIStyle SelectedStyle ;
2021-03-31 04:07:11 +01:00
protected CultCache cultCache ;
2020-03-22 19:05:26 +00:00
private static DatabaseInspector _inspector ;
2021-04-07 01:27:35 +01:00
private Dictionary < string , ( bool foldout , ( Type entryType , bool foldout ) [ ] types ) > _tables ;
//private Type[] _entryTypes;
private Type [ ] _globalTypes ;
2020-04-14 04:08:33 +01:00
private Vector2 _view ;
2021-03-31 04:07:11 +01:00
private HashSet < int > _groupFoldouts = new HashSet < int > ( ) ;
protected abstract string DatabaseName { get ; }
protected abstract string FilePath { get ; }
protected virtual DatabaseEntryGroup [ ] Groupers = > new DatabaseEntryGroup [ ] { } ;
protected class DatabaseEntryGroup < T , K > : DatabaseEntryGroup where T : DatabaseEntry
{
public Func < T , K > GroupSelector { get ; }
public Func < K , string > GroupLabel { get ; }
public Action < T , K > Activator { get ; }
public DatabaseEntryGroup ( Func < T , K > groupSelector , Func < K , string > groupLabel , Action < T , K > activator )
{
GroupSelector = groupSelector ;
GroupLabel = groupLabel ;
Activator = activator ;
}
public override bool CanGroup ( Type type )
{
return typeof ( T ) = = type ;
}
public override object SelectGroup ( DatabaseEntry o )
{
if ( o is T t ) return GroupSelector ( t ) ;
return null ;
}
public override string GetGroupName ( object o )
{
if ( o is K k ) return GroupLabel ( k ) ;
return "ERROR" ;
}
public override int HashGroup ( object o )
{
2021-04-07 01:27:35 +01:00
return o . GetHashCode ( ) ;
2021-03-31 04:07:11 +01:00
if ( o is K k ) return k . GetHashCode ( ) ;
return 0 ;
}
public override void Activate ( DatabaseEntry o , object g )
{
if ( o is T t & & g is K k ) Activator ( t , k ) ;
throw new ArgumentException (
$"Entry group activation failed! Expected: object type {typeof(T)}, group type {typeof(K)}; received: object type {o.GetType()}, group type {g.GetType()}" ) ;
}
}
protected abstract class DatabaseEntryGroup
{
public abstract bool CanGroup ( Type type ) ;
public abstract object SelectGroup ( DatabaseEntry o ) ;
public abstract string GetGroupName ( object o ) ;
public abstract int HashGroup ( object o ) ;
public abstract void Activate ( DatabaseEntry o , object g ) ;
}
2020-03-22 19:05:26 +00:00
2020-04-09 07:48:45 +01:00
public Color LabelColor = > EditorGUIUtility . isProSkin ? Color . white : Color . black ;
2021-03-31 04:07:11 +01:00
private bool _listItemStyle ;
2021-06-23 04:25:23 +01:00
private SingleFileMessagePackBackingStore _msgPackStore ;
2021-04-07 01:27:35 +01:00
public GUIStyle ListItemStyle = > ( _listItemStyle = ! _listItemStyle ) ? ListStyleEven : ListStyleOdd ;
2020-11-29 22:50:12 +00:00
2020-03-22 19:05:26 +00:00
void OnEnable ( )
{
2021-03-31 04:07:11 +01:00
ListStyleOdd = new GUIStyle
{
normal = { background = EditorGUIUtility . isProSkin ? XKCDColors . DarkGrey . ToTexture ( ) : XKCDColors . LightGrey . ToTexture ( ) } ,
margin = new RectOffset ( 0 , 0 , 0 , 0 )
} ;
ListStyleEven = new GUIStyle
{
normal = { background = EditorGUIUtility . isProSkin ? Color . Lerp ( XKCDColors . DarkGrey , Color . black , . 5f ) . ToTexture ( ) : Color . Lerp ( XKCDColors . LightGrey , Color . white , . 5f ) . ToTexture ( ) } ,
margin = new RectOffset ( 0 , 0 , 0 , 0 )
} ;
2020-05-22 22:34:08 +01:00
2021-06-23 04:25:23 +01:00
_msgPackStore = new SingleFileMessagePackBackingStore ( Path . Combine ( FilePath , "AetherDB.msgpack" ) ) ;
cultCache = new CultCache ( ) ;
cultCache . AddBackingStore ( _msgPackStore ) ;
cultCache . AddBackingStore ( new MultiFileMessagePackBackingStore ( FilePath ) , typeof ( NameFile ) ) ;
cultCache . PullAllBackingStores ( ) ;
cultCache . OnUpdate + = ( _ , __ ) = > EditorDispatcher . Dispatch ( Repaint ) ;
2021-03-31 04:07:11 +01:00
DatabaseInspector . CultCache = cultCache ;
2020-11-29 22:50:12 +00:00
_inspector = DatabaseInspector . Instance ;
_inspector . Show ( ) ;
2020-03-22 19:05:26 +00:00
2021-04-07 01:27:35 +01:00
_globalTypes = typeof ( DatabaseEntry ) . GetAllChildClasses ( )
. Where ( t = > t . GetCustomAttribute < InspectableAttribute > ( ) ! = null & & t . GetCustomAttribute < GlobalSettingsAttribute > ( ) ! = null ) . ToArray ( ) ;
_tables = typeof ( DatabaseEntry ) . GetAllChildClasses ( )
. Where ( t = > t . GetCustomAttribute < InspectableAttribute > ( ) ! = null & & t . GetCustomAttribute < GlobalSettingsAttribute > ( ) = = null )
2026-06-13 04:30:24 +01:00
. GroupBy ( t = > t . GetCustomAttribute < LegacyCatalogGroupAttribute > ( ) ? . TableName ? ? "Default" )
2021-04-07 01:27:35 +01:00
. ToDictionary ( group = > group . Key , group = > ( false , group . Select ( t = > ( t , false ) ) . ToArray ( ) ) ) ;
2020-03-22 19:05:26 +00:00
InitStyles ( ) ;
}
private void InitStyles ( )
{
SelectedStyle = new GUIStyle
{
2021-03-31 04:07:11 +01:00
normal = { background = EditorGUIUtility . isProSkin ? XKCDColors . DarkGreyBlue . ToTexture ( ) : XKCDColors . Greyblue . ToTexture ( ) } ,
2020-03-22 19:05:26 +00:00
margin = new RectOffset ( 0 , 0 , 0 , 0 )
} ;
}
public void OnGUI ( )
{
Event currentEvent = Event . current ;
EventType currentEventType = currentEvent . type ;
if ( currentEventType = = EventType . DragUpdated )
// Indicate that we don't accept drags ourselves
DragAndDrop . visualMode = DragAndDropVisualMode . Rejected ;
2021-04-07 01:27:35 +01:00
using ( new HorizontalScope ( ) )
2020-04-28 01:50:50 +01:00
{
2021-06-23 04:25:23 +01:00
GUILayout . Label ( "Export" ) ;
if ( GUILayout . Button ( "MsgPack" ) )
2020-11-27 22:47:33 +00:00
{
2021-06-23 04:25:23 +01:00
_msgPackStore . PushAll ( ) ;
Debug . Log ( "Exported DB to MessagePack!" ) ;
2020-11-27 22:47:33 +00:00
}
2020-03-22 19:05:26 +00:00
}
2020-05-10 00:16:33 +01:00
2020-03-22 19:05:26 +00:00
GUILayout . Space ( 5 ) ;
2020-05-10 00:16:33 +01:00
2021-03-15 11:40:46 +00:00
_view = BeginScrollView (
_view ,
false ,
false ,
GUIStyle . none ,
GUI . skin . verticalScrollbar ,
GUI . skin . scrollView ,
GUILayout . Width ( EditorGUIUtility . currentViewWidth ) ,
GUILayout . ExpandHeight ( true ) ) ;
2021-04-07 01:27:35 +01:00
foreach ( var globalType in _globalTypes )
2020-03-22 19:05:26 +00:00
{
2021-04-07 01:27:35 +01:00
var globalEntry = cultCache . GetGlobal ( globalType ) ;
var style = ListItemStyle ;
var selected = SelectedItem = = globalEntry . ID ;
using ( new HorizontalScope ( selected ? SelectedStyle : style ) )
2020-03-22 19:05:26 +00:00
{
2021-04-07 01:27:35 +01:00
using ( var h = new HorizontalScope ( ) )
{
if ( h . rect . Contains ( currentEvent . mousePosition ) )
{
if ( currentEventType = = EventType . MouseUp )
Select ( globalEntry ) ;
}
GUILayout . Label ( globalType . Name . SplitCamelCase ( ) , selected ? EditorStyles . whiteLabel : GUI . skin . label ) ;
}
2021-03-31 04:07:11 +01:00
}
2021-04-07 01:27:35 +01:00
}
2021-03-31 04:07:11 +01:00
2021-04-07 01:27:35 +01:00
void DisplayGroup ( Type entryType , IEnumerable < DatabaseEntry > entries , IEnumerable < DatabaseEntryGroup > nestedGroups , int indent = 2 , Action < DatabaseEntry > activatePreviousGroups = null , string previousGroupNames = "" )
{
var grouper = nestedGroups . FirstOrDefault ( ) ;
if ( grouper ! = null )
2021-03-31 04:07:11 +01:00
{
2021-04-07 01:27:35 +01:00
var groups = entries . GroupBy ( o = > grouper . SelectGroup ( o ) ) ;
foreach ( var group in groups )
2020-03-22 19:05:26 +00:00
{
2021-04-07 01:27:35 +01:00
//var groupHash = grouper.HashGroup(group.Key);
var groupName = grouper . GetGroupName ( group . Key ) ;
var groupHash = groupName . GetHashCode ( ) ;
using ( new HorizontalScope ( ListItemStyle ) )
2020-03-22 19:05:26 +00:00
{
2021-04-07 01:27:35 +01:00
GUILayout . Space ( indent * 10 ) ;
if ( Foldout ( _groupFoldouts . Contains ( groupHash ) , FormatTypeName ( groupName ) , true ) )
_groupFoldouts . Add ( groupHash ) ;
else _groupFoldouts . Remove ( groupHash ) ;
}
2020-05-22 22:34:08 +01:00
2021-04-07 01:27:35 +01:00
if ( _groupFoldouts . Contains ( groupHash ) )
{
Action < DatabaseEntry > activate = entry = >
2021-03-31 04:07:11 +01:00
{
2021-04-07 01:27:35 +01:00
activatePreviousGroups ? . Invoke ( entry ) ;
grouper . Activate ( entry , group . Key ) ;
} ;
DisplayGroup ( entryType , group , nestedGroups . Skip ( 1 ) , indent + 1 , activate , $"{groupName} {previousGroupNames}" ) ;
using ( var h = new HorizontalScope ( ListItemStyle ) )
{
if ( GUI . Button ( h . rect , GUIContent . none , GUIStyle . none ) )
CreateItem ( entryType , activate ) ;
GUILayout . Space ( ( indent + 1 ) * 10 ) ;
GUILayout . Label ( $"Create {groupName} {previousGroupNames}" ) ;
2020-03-22 19:05:26 +00:00
}
}
}
2021-04-07 01:27:35 +01:00
}
else
{
int index = 0 ;
foreach ( var entry in entries )
2020-03-22 19:05:26 +00:00
{
2021-04-07 01:27:35 +01:00
index + + ;
var style = ListItemStyle ;
var selected = SelectedItem = = entry . ID ;
using ( var h = new HorizontalScope ( selected ? SelectedStyle : style ) )
2020-03-22 19:05:26 +00:00
{
2021-04-07 01:27:35 +01:00
if ( h . rect . Contains ( currentEvent . mousePosition ) )
2020-03-22 19:05:26 +00:00
{
2021-04-07 01:27:35 +01:00
if ( currentEventType = = EventType . MouseDrag )
2020-03-22 19:05:26 +00:00
{
2021-04-07 01:27:35 +01:00
DragAndDrop . PrepareStartDrag ( ) ;
DragAndDrop . SetGenericData ( "Item" , entry . ID ) ;
DragAndDrop . StartDrag ( "Database Item" ) ;
2020-03-22 19:05:26 +00:00
}
2021-04-07 01:27:35 +01:00
else if ( currentEventType = = EventType . MouseUp )
Select ( entry ) ;
2020-03-22 19:05:26 +00:00
}
2021-04-07 01:27:35 +01:00
GUILayout . Space ( indent * 10 ) ;
GUILayout . Label ( ( entry as INamedEntry ) ? . EntryName ? ? index . ToString ( ) ,
selected ? EditorStyles . whiteLabel : GUI . skin . label ) ;
2020-03-22 19:05:26 +00:00
}
}
2021-04-07 01:27:35 +01:00
}
}
foreach ( var table in _tables . Keys . ToArray ( ) )
{
var ( tableFoldout , _ ) = _tables [ table ] ;
using ( new HorizontalScope ( ListItemStyle ) )
{
_tables [ table ] = ( Foldout ( tableFoldout = _tables [ table ] . foldout , table , true ) , _tables [ table ] . types ) ;
}
if ( tableFoldout )
{
for ( var i = 0 ; i < _tables [ table ] . types . Length ; i + + )
2020-03-22 19:05:26 +00:00
{
2021-04-07 01:27:35 +01:00
var ( entryType , typeFoldout ) = _tables [ table ] . types [ i ] ;
using ( new HorizontalScope ( ListItemStyle ) )
{
GUILayout . Space ( 10 ) ;
_tables [ table ] . types [ i ] = ( entryType , typeFoldout = Foldout ( typeFoldout ,
( ( NameAttribute ) entryType . GetCustomAttribute ( typeof ( NameAttribute ) ) ) ? . Name ? ? FormatTypeName ( entryType . Name ) ,
true ) ) ;
}
if ( typeFoldout )
{
var entries = cultCache . GetAll ( entryType )
. OrderBy ( entry = > entry is INamedEntry namedEntry ? namedEntry . EntryName : entry . ID . ToString ( ) ) ;
var groupers = Groupers . Where ( g = > g . CanGroup ( entryType ) ) ;
DisplayGroup ( entryType , entries , groupers ) ;
using ( var h = new HorizontalScope ( ListItemStyle ) )
{
if ( GUI . Button ( h . rect , GUIContent . none , GUIStyle . none ) )
CreateItem ( entryType ) ;
GUILayout . Space ( 20 ) ;
GUILayout . Label ( $"Create {FormatTypeName(entryType.Name)}" ) ;
}
}
2020-03-22 19:05:26 +00:00
}
}
}
2020-04-14 04:08:33 +01:00
EndScrollView ( ) ;
2020-03-22 19:05:26 +00:00
}
2021-04-07 01:27:35 +01:00
public static string FormatTypeName ( string typeName )
2020-04-09 07:48:45 +01:00
{
return ( typeName . EndsWith ( "Data" )
? typeName . Substring ( 0 , typeName . Length - 4 )
: typeName ) . SplitCamelCase ( ) ;
}
2020-05-22 22:34:08 +01:00
void Select ( DatabaseEntry item )
{
SelectedItem = item . ID ;
Repaint ( ) ;
2021-03-31 04:07:11 +01:00
_inspector . entry = cultCache . Get ( SelectedItem ) ;
2020-05-22 22:34:08 +01:00
_inspector . Repaint ( ) ;
}
2020-04-09 07:48:45 +01:00
2020-06-01 03:11:04 +01:00
private DatabaseEntry CreateItem ( Type type , Action < DatabaseEntry > onCreate = null )
2020-03-22 19:05:26 +00:00
{
var newEntry = ( DatabaseEntry ) Activator . CreateInstance ( type ) ;
if ( newEntry is INamedEntry entry )
2020-05-22 22:34:08 +01:00
entry . EntryName = $"New {FormatTypeName(type.Name)}" ;
2020-06-01 03:11:04 +01:00
onCreate ? . Invoke ( newEntry ) ;
2021-03-31 04:07:11 +01:00
cultCache . Add ( newEntry ) ;
2020-05-22 22:34:08 +01:00
Select ( newEntry ) ;
return newEntry ;
2020-03-22 19:05:26 +00:00
}
}