Editor/sandmod.libraryplus/Widget/LibraryManagerDock.cs
using System;
using System.IO;
using System.Linq;
using System.Threading;
using Editor;
using Sandbox;
using Sandbox.UI;
using FileSystem = Editor.FileSystem;
namespace LibraryPlus;
[Dock( "Editor", "Library Plus Manager", "extension" )]
public class LibraryManagerDock : Widget
{
internal Action<string> OnValueChanged;
private NavigationView View;
public LibraryManagerDock( Widget parent ) : base( parent )
{
Layout = Layout.Row();
Layout.Margin = 4;
Layout.Spacing = 4;
FocusMode = FocusMode.TabOrClickOrWheel;
}
[EditorEvent.Hotload]
private void Rebuild()
{
Layout.Clear( true );
View = new NavigationView( this );
View.AddSectionHeader( "Project" );
View.AddPage( "Installed", "folder",
new InstalledLibrariesWidget( this ) { ContentMargins = new Margin( 8, 0, 0, 0 ) } );
View.AddSectionHeader( "Install" );
View.AddPage( "sbox.game", "language",
new AvailableLibrariesWidget( this ) { ContentMargins = new Margin( 8, 0, 0, 0 ) } );
{
var row = View.MenuTop.AddRow();
row.Spacing = 8;
var textEdit = row.Add( new LineEdit( this )
{
FixedHeight = Theme.RowHeight, PlaceholderText = "⌕ Search", ToolTip = "Search"
} );
textEdit.TextChanged += txt =>
{
OnValueChanged?.Invoke( txt );
};
textEdit.SetStyles( "background-color: #000;" );
row.Add( new IconButton( "folder" )
{
ToolTip = "Open Library Folder",
OnClick = () => EditorUtility.OpenFolder( FileSystem.Libraries.GetFullPath( "/" ) )
} );
row.Add( new IconButton( "add_circle" ) { ToolTip = "Create New Library", OnClick = CreateNewLibrary } );
}
Layout.Add( View );
}
private void CreateNewLibrary()
{
Dialog.AskString( CreateNewLibrary, "What would you like to call your new library?", "Create",
title: "Create a Library" );
}
private void CreateNewLibrary( string libraryName )
{
// make libraryName folder safe
var folderName = string.Concat( libraryName.Where( char.IsAsciiLetterOrDigit ) );
if ( string.IsNullOrWhiteSpace( folderName ) )
{
return;
}
void FinalizeLibrary( string name )
{
FileSystem.Libraries.CreateDirectory( name );
var dir = FileSystem.Libraries.GetFullPath( name );
CopyFolder( FileSystem.Root.GetFullPath( "templates/library.minimal" ), dir, libraryName, name.ToLower() );
_ = LibrarySystem.Add( name, CancellationToken.None );
}
// Check to make sure the ident doesn't match the game's ident or any other library
var sharesIdent = string.Equals( folderName, Project.Current.Package.Ident,
StringComparison.CurrentCultureIgnoreCase );
if ( !sharesIdent )
{
sharesIdent = FileSystem.Libraries.FindDirectory( "", folderName )?.Count() > 0;
}
if ( sharesIdent )
{
var newIdentNum = 2;
var newFolderName = $"{folderName}_{newIdentNum}";
while ( (FileSystem.Libraries.FindDirectory( "", newFolderName )?.Count() ?? 0) > 0 )
{
newIdentNum++;
newFolderName = $"{folderName}_{newIdentNum}";
}
folderName = newFolderName;
Dialog.AskConfirm( () => { FinalizeLibrary( newFolderName ); },
$"The library cannot share it's Ident with another Package. Would you like to continue with the ident \"{newFolderName}\"?",
"Error creating Library", "OK" );
return;
}
FinalizeLibrary( folderName );
}
private static void CopyFolder( string sourceDir, string targetDir, string libraryName, string libraryFolder )
{
if ( sourceDir.Contains( "\\.", StringComparison.OrdinalIgnoreCase ) )
{
return;
}
Directory.CreateDirectory( targetDir );
foreach ( var file in Directory.GetFiles( sourceDir ) )
{
CopyAndProcessFile( file, targetDir, libraryName, libraryFolder );
}
foreach ( var directory in Directory.GetDirectories( sourceDir ) )
{
CopyFolder( directory, Path.Combine( targetDir, Path.GetFileName( directory ) ), libraryName,
libraryFolder );
}
}
private static void CopyAndProcessFile( string file, string targetDir, string libraryName, string libraryFolder )
{
var targetname = Path.Combine( targetDir, Path.GetFileName( file ) );
// Replace $ident with our ident in file name
targetname = targetname.Replace( "$title", libraryName );
targetname = targetname.Replace( "$ident", libraryFolder );
if ( file.EndsWith( ".cs" ) || file.EndsWith( ".json" ) || file.EndsWith( ".sbproj" ) )
{
var txt = File.ReadAllText( file );
txt = txt.Replace( "$title", libraryName );
txt = txt.Replace( "$ident", libraryFolder );
File.WriteAllText( targetname, txt );
}
else
{
File.Copy( file, targetname );
}
}
[EditorEvent.Frame]
public void OnFrame()
{
if ( SetContentHash( ContentHash, 0.5f ) )
{
Rebuild();
}
}
private int ContentHash()
{
return HashCode.Combine( 0 );
}
}
public class InstalledLibrariesWidget : Widget
{
private readonly Layout ContentLayout;
public InstalledLibrariesWidget( LibraryManagerDock manager )
{
Layout = Layout.Row();
var left = new LibraryList( null ) { ShowInstalled = true };
Layout.Add( left, 1 );
ContentLayout = Layout.AddColumn();
left.OnLibrarySelected = library =>
{
ContentLayout.Clear( true );
ContentLayout.Add( new LibraryDetail( library ) );
};
manager.OnValueChanged = txt =>
{
left.Filter = txt;
};
}
}
public class AvailableLibrariesWidget : Widget
{
private readonly Layout ContentLayout;
public AvailableLibrariesWidget( LibraryManagerDock manager )
{
Layout = Layout.Row();
var left = new LibraryList( null ) { ShowAvailable = true };
Layout.Add( left, 1 );
ContentLayout = Layout.AddColumn();
left.OnLibrarySelected = library =>
{
ContentLayout.Clear( true );
ContentLayout.Add( new LibraryDetail( library ) );
};
manager.OnValueChanged = txt =>
{
left.Filter = txt;
};
}
}