Extend some more IPC functions.

This commit is contained in:
Ottermandias 2022-06-04 20:46:50 +02:00
parent f0131dd5ba
commit cf79f47e08
4 changed files with 411 additions and 158 deletions

View file

@ -1,10 +1,12 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using Dalamud.Configuration;
using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.ClientState.Objects.Types;
using Lumina.Data; using Lumina.Data;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
namespace Penumbra.Api namespace Penumbra.Api;
{
public interface IPenumbraApiBase public interface IPenumbraApiBase
{ {
public int ApiVersion { get; } public int ApiVersion { get; }
@ -16,9 +18,16 @@ namespace Penumbra.Api
public interface IPenumbraApi : IPenumbraApiBase public interface IPenumbraApi : IPenumbraApiBase
{ {
// Obtain the currently set mod directory from the configuration.
public string GetModDirectory();
// Obtain the entire current penumbra configuration.
public IPluginConfiguration GetConfiguration();
// Triggered when the user hovers over a listed changed object in a mod tab. // Triggered when the user hovers over a listed changed object in a mod tab.
// Can be used to append tooltips. // Can be used to append tooltips.
public event ChangedItemHover? ChangedItemTooltip; public event ChangedItemHover? ChangedItemTooltip;
// Triggered when the user clicks a listed changed object in a mod tab. // Triggered when the user clicks a listed changed object in a mod tab.
public event ChangedItemClick? ChangedItemClicked; public event ChangedItemClick? ChangedItemClicked;
@ -47,5 +56,22 @@ namespace Penumbra.Api
// Gets a dictionary of effected items from a collection // Gets a dictionary of effected items from a collection
public IReadOnlyDictionary< string, object? > GetChangedItemsForCollection( string collectionName ); public IReadOnlyDictionary< string, object? > GetChangedItemsForCollection( string collectionName );
}
// Obtain a list of the names of all currently installed collections.
public IList< string > GetCollections();
// Obtain the name of the currently selected collection.
public string GetCurrentCollection();
// Obtain the name of the default collection.
public string GetDefaultCollection();
// Obtain the name of the collection associated with characterName and whether it is configured or inferred from default.
public (string, bool) GetCharacterCollection( string characterName );
// Obtain the game object associated with a given draw object and the name of the collection associated with this game object.
public (IntPtr, string) GetDrawObjectInfo( IntPtr drawObject );
// Obtain a list of all installed mods. The first string is their directory name, the second string is their mod name.
public IList< (string, string) > GetModList();
} }

View file

@ -3,9 +3,11 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Dalamud.Configuration;
using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Logging; using Dalamud.Logging;
using Lumina.Data; using Lumina.Data;
using Newtonsoft.Json;
using Penumbra.Collections; using Penumbra.Collections;
using Penumbra.GameData.ByteString; using Penumbra.GameData.ByteString;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
@ -36,6 +38,19 @@ public class PenumbraApi : IDisposable, IPenumbraApi
} }
public event ChangedItemClick? ChangedItemClicked; public event ChangedItemClick? ChangedItemClicked;
public string GetModDirectory()
{
CheckInitialized();
return Penumbra.Config.ModDirectory;
}
public IPluginConfiguration GetConfiguration()
{
CheckInitialized();
return JsonConvert.DeserializeObject< Configuration >( JsonConvert.SerializeObject( Penumbra.Config ) );
}
public event ChangedItemHover? ChangedItemTooltip; public event ChangedItemHover? ChangedItemTooltip;
internal bool HasTooltip internal bool HasTooltip
@ -59,21 +74,18 @@ public class PenumbraApi : IDisposable, IPenumbraApi
public void RedrawObject( string name, RedrawType setting ) public void RedrawObject( string name, RedrawType setting )
{ {
CheckInitialized(); CheckInitialized();
_penumbra!.ObjectReloader.RedrawObject( name, setting ); _penumbra!.ObjectReloader.RedrawObject( name, setting );
} }
public void RedrawObject( GameObject? gameObject, RedrawType setting ) public void RedrawObject( GameObject? gameObject, RedrawType setting )
{ {
CheckInitialized(); CheckInitialized();
_penumbra!.ObjectReloader.RedrawObject( gameObject, setting ); _penumbra!.ObjectReloader.RedrawObject( gameObject, setting );
} }
public void RedrawAll( RedrawType setting ) public void RedrawAll( RedrawType setting )
{ {
CheckInitialized(); CheckInitialized();
_penumbra!.ObjectReloader.RedrawAll( setting ); _penumbra!.ObjectReloader.RedrawAll( setting );
} }
@ -151,4 +163,43 @@ public class PenumbraApi : IDisposable, IPenumbraApi
throw; throw;
} }
} }
public IList< string > GetCollections()
{
CheckInitialized();
return Penumbra.CollectionManager.Skip( 1 ).Select( c => c.Name ).ToArray();
}
public string GetCurrentCollection()
{
CheckInitialized();
return Penumbra.CollectionManager.Current.Name;
}
public string GetDefaultCollection()
{
CheckInitialized();
return Penumbra.CollectionManager.Default.Name;
}
public (string, bool) GetCharacterCollection( string characterName )
{
CheckInitialized();
return Penumbra.CollectionManager.Characters.TryGetValue( characterName, out var collection )
? ( collection.Name, true )
: ( Penumbra.CollectionManager.Default.Name, false );
}
public (IntPtr, string) GetDrawObjectInfo( IntPtr drawObject )
{
CheckInitialized();
var (obj, collection) = _penumbra!.PathResolver.IdentifyDrawObject( drawObject );
return ( obj, collection.Name );
}
public IList< (string, string) > GetModList()
{
CheckInitialized();
return Penumbra.ModManager.Select( m => ( m.ModPath.Name, m.Name.Text ) ).ToArray();
}
} }

View file

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Dalamud.Configuration;
using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Logging; using Dalamud.Logging;
using Dalamud.Plugin; using Dalamud.Plugin;
@ -8,63 +9,49 @@ using Penumbra.GameData.Enums;
namespace Penumbra.Api; namespace Penumbra.Api;
public class PenumbraIpc : IDisposable public partial class PenumbraIpc : IDisposable
{ {
public const string LabelProviderInitialized = "Penumbra.Initialized";
public const string LabelProviderDisposed = "Penumbra.Disposed";
public const string LabelProviderApiVersion = "Penumbra.ApiVersion";
public const string LabelProviderRedrawName = "Penumbra.RedrawObjectByName";
public const string LabelProviderRedrawObject = "Penumbra.RedrawObject";
public const string LabelProviderRedrawAll = "Penumbra.RedrawAll";
public const string LabelProviderResolveDefault = "Penumbra.ResolveDefaultPath";
public const string LabelProviderResolveCharacter = "Penumbra.ResolveCharacterPath";
public const string LabelProviderChangedItemTooltip = "Penumbra.ChangedItemTooltip";
public const string LabelProviderChangedItemClick = "Penumbra.ChangedItemClick";
public const string LabelProviderGetChangedItems = "Penumbra.GetChangedItems";
internal ICallGateProvider< object? >? ProviderInitialized;
internal ICallGateProvider< object? >? ProviderDisposed;
internal ICallGateProvider< int >? ProviderApiVersion;
internal ICallGateProvider< string, int, object >? ProviderRedrawName;
internal ICallGateProvider< GameObject, int, object >? ProviderRedrawObject;
internal ICallGateProvider< int, object >? ProviderRedrawAll;
internal ICallGateProvider< string, string >? ProviderResolveDefault;
internal ICallGateProvider< string, string, string >? ProviderResolveCharacter;
internal ICallGateProvider< ChangedItemType, uint, object >? ProviderChangedItemTooltip;
internal ICallGateProvider< MouseButton, ChangedItemType, uint, object >? ProviderChangedItemClick;
internal ICallGateProvider< string, IReadOnlyDictionary< string, object? > >? ProviderGetChangedItems;
internal readonly IPenumbraApi Api; internal readonly IPenumbraApi Api;
private static RedrawType CheckRedrawType( int value )
{
var type = ( RedrawType )value;
if( Enum.IsDefined( type ) )
{
return type;
}
throw new Exception( "The integer provided for a Redraw Function was not a valid RedrawType." );
}
private void OnClick( MouseButton click, object? item )
{
var (type, id) = ChangedItemExtensions.ChangedItemToTypeAndId( item );
ProviderChangedItemClick?.SendMessage( click, type, id );
}
private void OnTooltip( object? item )
{
var (type, id) = ChangedItemExtensions.ChangedItemToTypeAndId( item );
ProviderChangedItemTooltip?.SendMessage( type, id );
}
public PenumbraIpc( DalamudPluginInterface pi, IPenumbraApi api ) public PenumbraIpc( DalamudPluginInterface pi, IPenumbraApi api )
{ {
Api = api; Api = api;
InitializeGeneralProviders( pi );
InitializeResolveProviders( pi );
InitializeRedrawProviders( pi );
InitializeChangedItemProviders( pi );
InitializeDataProviders( pi );
ProviderInitialized?.SendMessage();
}
public void Dispose()
{
DisposeDataProviders();
DisposeChangedItemProviders();
DisposeRedrawProviders();
DisposeResolveProviders();
DisposeGeneralProviders();
ProviderDisposed?.SendMessage();
}
}
public partial class PenumbraIpc
{
public const string LabelProviderInitialized = "Penumbra.Initialized";
public const string LabelProviderDisposed = "Penumbra.Disposed";
public const string LabelProviderApiVersion = "Penumbra.ApiVersion";
public const string LabelProviderGetModDirectory = "Penumbra.GetModDirectory";
public const string LabelProviderGetConfiguration = "Penumbra.GetConfiguration";
internal ICallGateProvider< object? >? ProviderInitialized;
internal ICallGateProvider< object? >? ProviderDisposed;
internal ICallGateProvider< int >? ProviderApiVersion;
internal ICallGateProvider< string >? ProviderGetModDirectory;
internal ICallGateProvider< IPluginConfiguration >? ProviderGetConfiguration;
private void InitializeGeneralProviders( DalamudPluginInterface pi )
{
try try
{ {
ProviderInitialized = pi.GetIpcProvider< object? >( LabelProviderInitialized ); ProviderInitialized = pi.GetIpcProvider< object? >( LabelProviderInitialized );
@ -86,17 +73,69 @@ public class PenumbraIpc : IDisposable
try try
{ {
ProviderApiVersion = pi.GetIpcProvider< int >( LabelProviderApiVersion ); ProviderApiVersion = pi.GetIpcProvider< int >( LabelProviderApiVersion );
ProviderApiVersion.RegisterFunc( () => api.ApiVersion ); ProviderApiVersion.RegisterFunc( () => Api.ApiVersion );
} }
catch( Exception e ) catch( Exception e )
{ {
PluginLog.Error( $"Error registering IPC provider for {LabelProviderApiVersion}:\n{e}" ); PluginLog.Error( $"Error registering IPC provider for {LabelProviderApiVersion}:\n{e}" );
} }
try
{
ProviderGetModDirectory = pi.GetIpcProvider< string >( LabelProviderGetModDirectory );
ProviderGetModDirectory.RegisterFunc( Api.GetModDirectory );
}
catch( Exception e )
{
PluginLog.Error( $"Error registering IPC provider for {LabelProviderGetModDirectory}:\n{e}" );
}
try
{
ProviderGetConfiguration = pi.GetIpcProvider< IPluginConfiguration >( LabelProviderGetConfiguration );
ProviderGetConfiguration.RegisterFunc( Api.GetConfiguration );
}
catch( Exception e )
{
PluginLog.Error( $"Error registering IPC provider for {LabelProviderGetConfiguration}:\n{e}" );
}
}
private void DisposeGeneralProviders()
{
ProviderGetConfiguration?.UnregisterFunc();
ProviderGetModDirectory?.UnregisterFunc();
ProviderApiVersion?.UnregisterFunc();
}
}
public partial class PenumbraIpc
{
public const string LabelProviderRedrawName = "Penumbra.RedrawObjectByName";
public const string LabelProviderRedrawObject = "Penumbra.RedrawObject";
public const string LabelProviderRedrawAll = "Penumbra.RedrawAll";
internal ICallGateProvider< string, int, object >? ProviderRedrawName;
internal ICallGateProvider< GameObject, int, object >? ProviderRedrawObject;
internal ICallGateProvider< int, object >? ProviderRedrawAll;
private static RedrawType CheckRedrawType( int value )
{
var type = ( RedrawType )value;
if( Enum.IsDefined( type ) )
{
return type;
}
throw new Exception( "The integer provided for a Redraw Function was not a valid RedrawType." );
}
private void InitializeRedrawProviders( DalamudPluginInterface pi )
{
try try
{ {
ProviderRedrawName = pi.GetIpcProvider< string, int, object >( LabelProviderRedrawName ); ProviderRedrawName = pi.GetIpcProvider< string, int, object >( LabelProviderRedrawName );
ProviderRedrawName.RegisterAction( ( s, i ) => api.RedrawObject( s, CheckRedrawType( i ) ) ); ProviderRedrawName.RegisterAction( ( s, i ) => Api.RedrawObject( s, CheckRedrawType( i ) ) );
} }
catch( Exception e ) catch( Exception e )
{ {
@ -106,7 +145,7 @@ public class PenumbraIpc : IDisposable
try try
{ {
ProviderRedrawObject = pi.GetIpcProvider< GameObject, int, object >( LabelProviderRedrawObject ); ProviderRedrawObject = pi.GetIpcProvider< GameObject, int, object >( LabelProviderRedrawObject );
ProviderRedrawObject.RegisterAction( ( o, i ) => api.RedrawObject( o, CheckRedrawType( i ) ) ); ProviderRedrawObject.RegisterAction( ( o, i ) => Api.RedrawObject( o, CheckRedrawType( i ) ) );
} }
catch( Exception e ) catch( Exception e )
{ {
@ -116,17 +155,38 @@ public class PenumbraIpc : IDisposable
try try
{ {
ProviderRedrawAll = pi.GetIpcProvider< int, object >( LabelProviderRedrawAll ); ProviderRedrawAll = pi.GetIpcProvider< int, object >( LabelProviderRedrawAll );
ProviderRedrawAll.RegisterAction( i => api.RedrawAll( CheckRedrawType( i ) ) ); ProviderRedrawAll.RegisterAction( i => Api.RedrawAll( CheckRedrawType( i ) ) );
} }
catch( Exception e ) catch( Exception e )
{ {
PluginLog.Error( $"Error registering IPC provider for {LabelProviderRedrawAll}:\n{e}" ); PluginLog.Error( $"Error registering IPC provider for {LabelProviderRedrawAll}:\n{e}" );
} }
}
private void DisposeRedrawProviders()
{
ProviderRedrawName?.UnregisterAction();
ProviderRedrawObject?.UnregisterAction();
ProviderRedrawAll?.UnregisterAction();
}
}
public partial class PenumbraIpc
{
public const string LabelProviderResolveDefault = "Penumbra.ResolveDefaultPath";
public const string LabelProviderResolveCharacter = "Penumbra.ResolveCharacterPath";
public const string LabelProviderGetDrawObjectInfo = "Penumbra.GetDrawObjectInfo";
internal ICallGateProvider< string, string >? ProviderResolveDefault;
internal ICallGateProvider< string, string, string >? ProviderResolveCharacter;
internal ICallGateProvider< IntPtr, (IntPtr, string) >? ProviderGetDrawObjectInfo;
private void InitializeResolveProviders( DalamudPluginInterface pi )
{
try try
{ {
ProviderResolveDefault = pi.GetIpcProvider< string, string >( LabelProviderResolveDefault ); ProviderResolveDefault = pi.GetIpcProvider< string, string >( LabelProviderResolveDefault );
ProviderResolveDefault.RegisterFunc( api.ResolvePath ); ProviderResolveDefault.RegisterFunc( Api.ResolvePath );
} }
catch( Exception e ) catch( Exception e )
{ {
@ -136,17 +196,60 @@ public class PenumbraIpc : IDisposable
try try
{ {
ProviderResolveCharacter = pi.GetIpcProvider< string, string, string >( LabelProviderResolveCharacter ); ProviderResolveCharacter = pi.GetIpcProvider< string, string, string >( LabelProviderResolveCharacter );
ProviderResolveCharacter.RegisterFunc( api.ResolvePath ); ProviderResolveCharacter.RegisterFunc( Api.ResolvePath );
} }
catch( Exception e ) catch( Exception e )
{ {
PluginLog.Error( $"Error registering IPC provider for {LabelProviderResolveCharacter}:\n{e}" ); PluginLog.Error( $"Error registering IPC provider for {LabelProviderResolveCharacter}:\n{e}" );
} }
try
{
ProviderGetDrawObjectInfo = pi.GetIpcProvider< IntPtr, (IntPtr, string) >( LabelProviderGetDrawObjectInfo );
ProviderGetDrawObjectInfo.RegisterFunc( Api.GetDrawObjectInfo );
}
catch( Exception e )
{
PluginLog.Error( $"Error registering IPC provider for {LabelProviderGetDrawObjectInfo}:\n{e}" );
}
}
private void DisposeResolveProviders()
{
ProviderGetDrawObjectInfo?.UnregisterFunc();
ProviderResolveDefault?.UnregisterFunc();
ProviderResolveCharacter?.UnregisterFunc();
}
}
public partial class PenumbraIpc
{
public const string LabelProviderChangedItemTooltip = "Penumbra.ChangedItemTooltip";
public const string LabelProviderChangedItemClick = "Penumbra.ChangedItemClick";
public const string LabelProviderGetChangedItems = "Penumbra.GetChangedItems";
internal ICallGateProvider< ChangedItemType, uint, object >? ProviderChangedItemTooltip;
internal ICallGateProvider< MouseButton, ChangedItemType, uint, object >? ProviderChangedItemClick;
internal ICallGateProvider< string, IReadOnlyDictionary< string, object? > >? ProviderGetChangedItems;
private void OnClick( MouseButton click, object? item )
{
var (type, id) = ChangedItemExtensions.ChangedItemToTypeAndId( item );
ProviderChangedItemClick?.SendMessage( click, type, id );
}
private void OnTooltip( object? item )
{
var (type, id) = ChangedItemExtensions.ChangedItemToTypeAndId( item );
ProviderChangedItemTooltip?.SendMessage( type, id );
}
private void InitializeChangedItemProviders( DalamudPluginInterface pi )
{
try try
{ {
ProviderChangedItemTooltip = pi.GetIpcProvider< ChangedItemType, uint, object >( LabelProviderChangedItemTooltip ); ProviderChangedItemTooltip = pi.GetIpcProvider< ChangedItemType, uint, object >( LabelProviderChangedItemTooltip );
api.ChangedItemTooltip += OnTooltip; Api.ChangedItemTooltip += OnTooltip;
} }
catch( Exception e ) catch( Exception e )
{ {
@ -156,7 +259,7 @@ public class PenumbraIpc : IDisposable
try try
{ {
ProviderChangedItemClick = pi.GetIpcProvider< MouseButton, ChangedItemType, uint, object >( LabelProviderChangedItemClick ); ProviderChangedItemClick = pi.GetIpcProvider< MouseButton, ChangedItemType, uint, object >( LabelProviderChangedItemClick );
api.ChangedItemClicked += OnClick; Api.ChangedItemClicked += OnClick;
} }
catch( Exception e ) catch( Exception e )
{ {
@ -166,28 +269,95 @@ public class PenumbraIpc : IDisposable
try try
{ {
ProviderGetChangedItems = pi.GetIpcProvider< string, IReadOnlyDictionary< string, object? > >( LabelProviderGetChangedItems ); ProviderGetChangedItems = pi.GetIpcProvider< string, IReadOnlyDictionary< string, object? > >( LabelProviderGetChangedItems );
ProviderGetChangedItems.RegisterFunc( api.GetChangedItemsForCollection ); ProviderGetChangedItems.RegisterFunc( Api.GetChangedItemsForCollection );
}
catch( Exception e )
{
PluginLog.Error( $"Error registering IPC provider for {LabelProviderChangedItemClick}:\n{e}" );
}
}
private void DisposeChangedItemProviders()
{
ProviderGetChangedItems?.UnregisterFunc();
Api.ChangedItemClicked -= OnClick;
Api.ChangedItemTooltip -= OnTooltip;
}
}
public partial class PenumbraIpc
{
public const string LabelProviderGetMods = "Penumbra.GetMods";
public const string LabelProviderGetCollections = "Penumbra.GetCollections";
public const string LabelProviderCurrentCollectionName = "Penumbra.GetCurrentCollectionName";
public const string LabelProviderDefaultCollectionName = "Penumbra.GetDefaultCollectionName";
public const string LabelProviderCharacterCollectionName = "Penumbra.GetCharacterCollectionName";
internal ICallGateProvider< IList< (string, string) > >? ProviderGetMods;
internal ICallGateProvider< IList< string > >? ProviderGetCollections;
internal ICallGateProvider< string >? ProviderCurrentCollectionName;
internal ICallGateProvider< string >? ProviderDefaultCollectionName;
internal ICallGateProvider< string, (string, bool) >? ProviderCharacterCollectionName;
private void InitializeDataProviders( DalamudPluginInterface pi )
{
try
{
ProviderGetMods = pi.GetIpcProvider< IList< (string, string) > >( LabelProviderGetMods );
ProviderGetMods.RegisterFunc( Api.GetModList );
} }
catch( Exception e ) catch( Exception e )
{ {
PluginLog.Error( $"Error registering IPC provider for {LabelProviderChangedItemClick}:\n{e}" ); PluginLog.Error( $"Error registering IPC provider for {LabelProviderChangedItemClick}:\n{e}" );
} }
ProviderInitialized?.SendMessage(); try
{
ProviderGetCollections = pi.GetIpcProvider< IList< string > >( LabelProviderGetCollections );
ProviderGetCollections.RegisterFunc( Api.GetCollections );
}
catch( Exception e )
{
PluginLog.Error( $"Error registering IPC provider for {LabelProviderChangedItemClick}:\n{e}" );
} }
public void Dispose() try
{ {
ProviderDisposed?.SendMessage(); ProviderCurrentCollectionName = pi.GetIpcProvider< string >( LabelProviderCurrentCollectionName );
ProviderInitialized?.UnregisterFunc(); ProviderCurrentCollectionName.RegisterFunc( Api.GetCurrentCollection );
ProviderApiVersion?.UnregisterFunc(); }
ProviderRedrawName?.UnregisterAction(); catch( Exception e )
ProviderRedrawObject?.UnregisterAction(); {
ProviderRedrawAll?.UnregisterAction(); PluginLog.Error( $"Error registering IPC provider for {LabelProviderChangedItemClick}:\n{e}" );
ProviderResolveDefault?.UnregisterFunc(); }
ProviderResolveCharacter?.UnregisterFunc();
ProviderGetChangedItems?.UnregisterFunc(); try
Api.ChangedItemClicked -= OnClick; {
Api.ChangedItemTooltip -= OnTooltip; ProviderDefaultCollectionName = pi.GetIpcProvider< string >( LabelProviderDefaultCollectionName );
ProviderDefaultCollectionName.RegisterFunc( Api.GetDefaultCollection );
}
catch( Exception e )
{
PluginLog.Error( $"Error registering IPC provider for {LabelProviderChangedItemClick}:\n{e}" );
}
try
{
ProviderCharacterCollectionName = pi.GetIpcProvider< string, ( string, bool) >( LabelProviderCharacterCollectionName );
ProviderCharacterCollectionName.RegisterFunc( Api.GetCharacterCollection );
}
catch( Exception e )
{
PluginLog.Error( $"Error registering IPC provider for {LabelProviderChangedItemClick}:\n{e}" );
}
}
private void DisposeDataProviders()
{
ProviderGetMods?.UnregisterFunc();
ProviderGetCollections?.UnregisterFunc();
ProviderCurrentCollectionName?.UnregisterFunc();
ProviderDefaultCollectionName?.UnregisterFunc();
ProviderCharacterCollectionName?.UnregisterFunc();
} }
} }

View file

@ -138,4 +138,10 @@ public partial class PathResolver : IDisposable
DisposeDataHooks(); DisposeDataHooks();
DisposeMetaHooks(); DisposeMetaHooks();
} }
public unsafe (IntPtr, ModCollection) IdentifyDrawObject( IntPtr drawObject )
{
var parent = FindParent( drawObject, out var collection );
return ( ( IntPtr )parent, collection );
}
} }