All Linebreaks to LF.

This commit is contained in:
Ottermandias 2021-02-15 12:55:04 +01:00
parent 82dff6be25
commit de8930c574
32 changed files with 1356 additions and 1356 deletions

View file

@ -1,4 +1,4 @@
using Dalamud.Configuration; using Dalamud.Configuration;
using Dalamud.Plugin; using Dalamud.Plugin;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -13,7 +13,7 @@ namespace Penumbra
public bool IsEnabled { get; set; } = true; public bool IsEnabled { get; set; } = true;
public bool ShowAdvanced { get; set; } public bool ShowAdvanced { get; set; }
public bool DisableFileSystemNotifications { get; set; } public bool DisableFileSystemNotifications { get; set; }
public bool EnableHttpApi { get; set; } public bool EnableHttpApi { get; set; }

View file

@ -1,54 +1,54 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Dalamud.Game.ClientState.Actors; using Dalamud.Game.ClientState.Actors;
using Dalamud.Game.ClientState.Actors.Types; using Dalamud.Game.ClientState.Actors.Types;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Penumbra namespace Penumbra
{ {
public static class RefreshActors public static class RefreshActors
{ {
private const int RenderModeOffset = 0x0104; private const int RenderModeOffset = 0x0104;
private const int RenderTaskPlayerDelay = 75; private const int RenderTaskPlayerDelay = 75;
private const int RenderTaskOtherDelay = 25; private const int RenderTaskOtherDelay = 25;
private const int ModelInvisibilityFlag = 0b10; private const int ModelInvisibilityFlag = 0b10;
private static async void Redraw(Actor actor) private static async void Redraw(Actor actor)
{ {
var ptr = actor.Address; var ptr = actor.Address;
var renderModePtr = ptr + RenderModeOffset; var renderModePtr = ptr + RenderModeOffset;
var renderStatus = Marshal.ReadInt32(renderModePtr); var renderStatus = Marshal.ReadInt32(renderModePtr);
async void DrawObject(int delay) async void DrawObject(int delay)
{ {
Marshal.WriteInt32(renderModePtr, renderStatus | ModelInvisibilityFlag); Marshal.WriteInt32(renderModePtr, renderStatus | ModelInvisibilityFlag);
await Task.Delay(delay); await Task.Delay(delay);
Marshal.WriteInt32(renderModePtr, renderStatus & ~ModelInvisibilityFlag); Marshal.WriteInt32(renderModePtr, renderStatus & ~ModelInvisibilityFlag);
} }
if (actor.ObjectKind == Dalamud.Game.ClientState.Actors.ObjectKind.Player) if (actor.ObjectKind == Dalamud.Game.ClientState.Actors.ObjectKind.Player)
{ {
DrawObject(RenderTaskPlayerDelay); DrawObject(RenderTaskPlayerDelay);
await Task.Delay(RenderTaskPlayerDelay); await Task.Delay(RenderTaskPlayerDelay);
} }
else else
DrawObject(RenderTaskOtherDelay); DrawObject(RenderTaskOtherDelay);
} }
public static void RedrawSpecific(ActorTable actors, string name) public static void RedrawSpecific(ActorTable actors, string name)
{ {
if (name?.Length == 0) if (name?.Length == 0)
RedrawAll(actors); RedrawAll(actors);
foreach (var actor in actors) foreach (var actor in actors)
if (actor.Name == name) if (actor.Name == name)
Redraw(actor); Redraw(actor);
} }
public static void RedrawAll(ActorTable actors) public static void RedrawAll(ActorTable actors)
{ {
foreach (var actor in actors) foreach (var actor in actors)
Redraw(actor); Redraw(actor);
} }
} }
} }

View file

@ -55,9 +55,9 @@ namespace Penumbra.Importer
case ".ttmp": case ".ttmp":
case ".ttmp2": case ".ttmp2":
VerifyVersionAndImport(modPackFile); VerifyVersionAndImport(modPackFile);
break; break;
default: default:
throw new ArgumentException( $"Unrecognized modpack format: {modPackFile.Extension}", nameof(modPackFile) ); throw new ArgumentException( $"Unrecognized modpack format: {modPackFile.Extension}", nameof(modPackFile) );
} }
@ -100,7 +100,7 @@ namespace Penumbra.Importer
ImportV2ModPack(modPackFile, extractedModPack, modRaw); ImportV2ModPack(modPackFile, extractedModPack, modRaw);
} }
else else
{ {
if (modPackFile.Extension != ".ttmp") if (modPackFile.Extension != ".ttmp")
PluginLog.Warning($"File {modPackFile.FullName} seems to be a V1 TTMP, but has the wrong extension."); PluginLog.Warning($"File {modPackFile.FullName} seems to be a V1 TTMP, but has the wrong extension.");
ImportV1ModPack(modPackFile, extractedModPack, modRaw); ImportV1ModPack(modPackFile, extractedModPack, modRaw);
@ -223,11 +223,11 @@ namespace Penumbra.Importer
// Iterate through all pages // Iterate through all pages
foreach( var page in modList.ModPackPages) foreach( var page in modList.ModPackPages)
{ {
foreach(var group in page.ModGroups) foreach(var group in page.ModGroups)
{ {
var groupFolder = new DirectoryInfo(Path.Combine(newModFolder.FullName, group.GroupName.ReplaceInvalidPathSymbols())); var groupFolder = new DirectoryInfo(Path.Combine(newModFolder.FullName, group.GroupName.ReplaceInvalidPathSymbols()));
foreach(var option in group.OptionList) foreach(var option in group.OptionList)
{ {
var optionFolder = new DirectoryInfo( Path.Combine( groupFolder.FullName, option.Name.ReplaceInvalidPathSymbols()) ); var optionFolder = new DirectoryInfo( Path.Combine( groupFolder.FullName, option.Name.ReplaceInvalidPathSymbols()) );
ExtractSimpleModList( optionFolder, option.ModsJsons, modData ); ExtractSimpleModList( optionFolder, option.ModsJsons, modData );
} }
@ -249,7 +249,7 @@ namespace Penumbra.Importer
GroupName = group.GroupName, GroupName = group.GroupName,
Options = new List<Option>(), Options = new List<Option>(),
}; };
foreach( var opt in group.OptionList ) foreach( var opt in group.OptionList )
{ {
var optio = new Option var optio = new Option
{ {
@ -258,13 +258,13 @@ namespace Penumbra.Importer
OptionFiles = new Dictionary<string, HashSet<string>>() OptionFiles = new Dictionary<string, HashSet<string>>()
}; };
var optDir = new DirectoryInfo(Path.Combine( groupFolder.FullName, opt.Name.ReplaceInvalidPathSymbols())); var optDir = new DirectoryInfo(Path.Combine( groupFolder.FullName, opt.Name.ReplaceInvalidPathSymbols()));
if (optDir.Exists) if (optDir.Exists)
{ {
foreach ( var file in optDir.EnumerateFiles("*.*", SearchOption.AllDirectories) ) foreach ( var file in optDir.EnumerateFiles("*.*", SearchOption.AllDirectories) )
{ {
optio.AddFile(file.FullName.Substring(baseFolder.FullName.Length).TrimStart('\\'), file.FullName.Substring(optDir.FullName.Length).TrimStart('\\').Replace('\\','/')); optio.AddFile(file.FullName.Substring(baseFolder.FullName.Length).TrimStart('\\'), file.FullName.Substring(optDir.FullName.Length).TrimStart('\\').Replace('\\','/'));
} }
} }
Inf.Options.Add( optio ); Inf.Options.Add( optio );
} }
meta.Groups.Add( group.GroupName, Inf ); meta.Groups.Add( group.GroupName, Inf );

View file

@ -1,163 +1,163 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Collections; using System.Collections;
using Dalamud.Plugin; using Dalamud.Plugin;
namespace Penumbra.Models
{
public class Deduplicator
{
private DirectoryInfo baseDir;
private int baseDirLength;
private ModMeta mod;
private SHA256 hasher = null;
private Dictionary<long, List<FileInfo>> filesBySize;
private ref SHA256 Sha()
{
if (hasher == null)
hasher = SHA256.Create();
return ref hasher;
}
public Deduplicator(DirectoryInfo baseDir, ModMeta mod)
{
this.baseDir = baseDir;
this.baseDirLength = baseDir.FullName.Length;
this.mod = mod;
filesBySize = new();
BuildDict(); namespace Penumbra.Models
} {
public class Deduplicator
private void BuildDict() {
{ private DirectoryInfo baseDir;
foreach( var file in baseDir.EnumerateFiles( "*.*", SearchOption.AllDirectories ) ) private int baseDirLength;
{ private ModMeta mod;
var fileLength = file.Length; private SHA256 hasher = null;
if (filesBySize.TryGetValue(fileLength, out var files))
files.Add(file); private Dictionary<long, List<FileInfo>> filesBySize;
else
filesBySize[fileLength] = new(){ file }; private ref SHA256 Sha()
} {
} if (hasher == null)
hasher = SHA256.Create();
public void Run() return ref hasher;
{ }
foreach (var pair in filesBySize)
{ public Deduplicator(DirectoryInfo baseDir, ModMeta mod)
if (pair.Value.Count < 2) {
continue; this.baseDir = baseDir;
this.baseDirLength = baseDir.FullName.Length;
if (pair.Value.Count == 2) this.mod = mod;
{ filesBySize = new();
if (CompareFilesDirectly(pair.Value[0], pair.Value[1]))
ReplaceFile(pair.Value[0], pair.Value[1]); BuildDict();
} }
else
{ private void BuildDict()
var deleted = Enumerable.Repeat(false, pair.Value.Count).ToArray(); {
var hashes = pair.Value.Select( F => ComputeHash(F)).ToArray(); foreach( var file in baseDir.EnumerateFiles( "*.*", SearchOption.AllDirectories ) )
{
for (var i = 0; i < pair.Value.Count; ++i) var fileLength = file.Length;
{ if (filesBySize.TryGetValue(fileLength, out var files))
if (deleted[i]) files.Add(file);
continue; else
filesBySize[fileLength] = new(){ file };
for (var j = i + 1; j < pair.Value.Count; ++j) }
{ }
if (deleted[j])
continue; public void Run()
{
if (!CompareHashes(hashes[i], hashes[j])) foreach (var pair in filesBySize)
continue; {
if (pair.Value.Count < 2)
ReplaceFile(pair.Value[i], pair.Value[j]); continue;
deleted[j] = true;
} if (pair.Value.Count == 2)
} {
} if (CompareFilesDirectly(pair.Value[0], pair.Value[1]))
} ReplaceFile(pair.Value[0], pair.Value[1]);
ClearEmptySubDirectories(baseDir); }
} else
{
private void ReplaceFile(FileInfo f1, FileInfo f2) var deleted = Enumerable.Repeat(false, pair.Value.Count).ToArray();
{ var hashes = pair.Value.Select( F => ComputeHash(F)).ToArray();
var relName1 = f1.FullName.Substring(baseDirLength).TrimStart('\\');
var relName2 = f2.FullName.Substring(baseDirLength).TrimStart('\\'); for (var i = 0; i < pair.Value.Count; ++i)
{
var inOption = false; if (deleted[i])
foreach (var group in mod.Groups.Select( g => g.Value.Options)) continue;
{
foreach (var option in group) for (var j = i + 1; j < pair.Value.Count; ++j)
{ {
if (option.OptionFiles.TryGetValue(relName2, out var values)) if (deleted[j])
{ continue;
inOption = true;
foreach (var value in values) if (!CompareHashes(hashes[i], hashes[j]))
option.AddFile(relName1, value); continue;
option.OptionFiles.Remove(relName2);
} ReplaceFile(pair.Value[i], pair.Value[j]);
} deleted[j] = true;
} }
if (!inOption) }
{ }
const string duplicates = "Duplicates"; }
if (!mod.Groups.ContainsKey(duplicates)) ClearEmptySubDirectories(baseDir);
{ }
InstallerInfo info = new()
{ private void ReplaceFile(FileInfo f1, FileInfo f2)
GroupName = duplicates, {
SelectionType = SelectType.Single, var relName1 = f1.FullName.Substring(baseDirLength).TrimStart('\\');
Options = new() var relName2 = f2.FullName.Substring(baseDirLength).TrimStart('\\');
{
new() var inOption = false;
{ foreach (var group in mod.Groups.Select( g => g.Value.Options))
OptionName = "Required", {
OptionDesc = "", foreach (var option in group)
OptionFiles = new() {
} if (option.OptionFiles.TryGetValue(relName2, out var values))
} {
}; inOption = true;
mod.Groups.Add(duplicates, info); foreach (var value in values)
} option.AddFile(relName1, value);
mod.Groups[duplicates].Options[0].AddFile(relName1, relName2.Replace('\\', '/')); option.OptionFiles.Remove(relName2);
mod.Groups[duplicates].Options[0].AddFile(relName1, relName1.Replace('\\', '/')); }
} }
PluginLog.Information($"File {relName1} and {relName2} are identical. Deleting the second."); }
f2.Delete(); if (!inOption)
} {
const string duplicates = "Duplicates";
public static bool CompareFilesDirectly(FileInfo f1, FileInfo f2) if (!mod.Groups.ContainsKey(duplicates))
{ {
return File.ReadAllBytes(f1.FullName).SequenceEqual(File.ReadAllBytes(f2.FullName)); InstallerInfo info = new()
} {
GroupName = duplicates,
public static bool CompareHashes(byte[] f1, byte[] f2) SelectionType = SelectType.Single,
{ Options = new()
return StructuralComparisons.StructuralEqualityComparer.Equals(f1, f2); {
} new()
{
public byte[] ComputeHash(FileInfo f) OptionName = "Required",
{ OptionDesc = "",
var stream = File.OpenRead( f.FullName ); OptionFiles = new()
var ret = Sha().ComputeHash(stream); }
stream.Dispose(); }
return ret; };
} mod.Groups.Add(duplicates, info);
}
// Does not delete the base directory itself even if it is completely empty at the end. mod.Groups[duplicates].Options[0].AddFile(relName1, relName2.Replace('\\', '/'));
public static void ClearEmptySubDirectories(DirectoryInfo baseDir) mod.Groups[duplicates].Options[0].AddFile(relName1, relName1.Replace('\\', '/'));
{ }
foreach (var subDir in baseDir.GetDirectories()) PluginLog.Information($"File {relName1} and {relName2} are identical. Deleting the second.");
{ f2.Delete();
ClearEmptySubDirectories(subDir); }
if (subDir.GetFiles().Length == 0 && subDir.GetDirectories().Length == 0)
subDir.Delete(); public static bool CompareFilesDirectly(FileInfo f1, FileInfo f2)
} {
} return File.ReadAllBytes(f1.FullName).SequenceEqual(File.ReadAllBytes(f2.FullName));
}
public static bool CompareHashes(byte[] f1, byte[] f2)
{
return StructuralComparisons.StructuralEqualityComparer.Equals(f1, f2);
}
public byte[] ComputeHash(FileInfo f)
{
var stream = File.OpenRead( f.FullName );
var ret = Sha().ComputeHash(stream);
stream.Dispose();
return ret;
}
// Does not delete the base directory itself even if it is completely empty at the end.
public static void ClearEmptySubDirectories(DirectoryInfo baseDir)
{
foreach (var subDir in baseDir.GetDirectories())
{
ClearEmptySubDirectories(subDir);
if (subDir.GetFiles().Length == 0 && subDir.GetDirectories().Length == 0)
subDir.Delete();
}
}
} }
} }

View file

@ -20,7 +20,7 @@ namespace Penumbra.Models
if (OptionFiles.TryGetValue(filePath, out var set)) if (OptionFiles.TryGetValue(filePath, out var set))
return set.Add(gamePath); return set.Add(gamePath);
else else
OptionFiles[filePath] = new(){ gamePath }; OptionFiles[filePath] = new(){ gamePath };
return true; return true;
} }
} }

View file

@ -1,7 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Linq; using System.Linq;
using System.IO; using System.IO;
using System; using System;
namespace Penumbra.Models namespace Penumbra.Models
@ -21,25 +21,25 @@ namespace Penumbra.Models
public Dictionary< string, string > FileSwaps { get; } = new(); public Dictionary< string, string > FileSwaps { get; } = new();
public Dictionary<string, InstallerInfo> Groups { get; set; } = new(); public Dictionary<string, InstallerInfo> Groups { get; set; } = new();
[JsonIgnore] [JsonIgnore]
public bool HasGroupWithConfig { get; set; } = false; public bool HasGroupWithConfig { get; set; } = false;
public static ModMeta LoadFromFile(string filePath) public static ModMeta LoadFromFile(string filePath)
{ {
try try
{ {
var meta = JsonConvert.DeserializeObject< ModMeta >( File.ReadAllText( filePath ) ); var meta = JsonConvert.DeserializeObject< ModMeta >( File.ReadAllText( filePath ) );
meta.HasGroupWithConfig = meta.Groups != null && meta.Groups.Count > 0 meta.HasGroupWithConfig = meta.Groups != null && meta.Groups.Count > 0
&& meta.Groups.Values.Any( G => G.SelectionType == SelectType.Multi || G.Options.Count > 1); && meta.Groups.Values.Any( G => G.SelectionType == SelectType.Multi || G.Options.Count > 1);
return meta; return meta;
} }
catch( Exception) catch( Exception)
{ {
return null; return null;
// todo: handle broken mods properly // todo: handle broken mods properly
} }
} }
} }
} }

View file

@ -246,17 +246,17 @@ namespace Penumbra.Mods
} }
public void DeleteMod( ResourceMod mod ) public void DeleteMod( ResourceMod mod )
{ {
if (mod?.ModBasePath?.Exists ?? false) if (mod?.ModBasePath?.Exists ?? false)
{ {
try try
{ {
Directory.Delete(mod.ModBasePath.FullName, true); Directory.Delete(mod.ModBasePath.FullName, true);
} }
catch( Exception e ) catch( Exception e )
{ {
PluginLog.Error($"Could not delete the mod {mod.ModBasePath.Name}:\n{e}"); PluginLog.Error($"Could not delete the mod {mod.ModBasePath.Name}:\n{e}");
} }
} }
DiscoverMods(); DiscoverMods();
} }

View file

@ -22,7 +22,7 @@ namespace Penumbra.Mods
PluginLog.LogError( "no basepath has been set on {ResourceModName}", Meta.Name ); PluginLog.LogError( "no basepath has been set on {ResourceModName}", Meta.Name );
return; return;
} }
ModFiles.Clear(); ModFiles.Clear();
// we don't care about any _files_ in the root dir, but any folders should be a game folder/file combo // we don't care about any _files_ in the root dir, but any folders should be a game folder/file combo
foreach( var dir in ModBasePath.EnumerateDirectories() ) foreach( var dir in ModBasePath.EnumerateDirectories() )
@ -35,7 +35,7 @@ namespace Penumbra.Mods
// Only add if not in a sub-folder, otherwise it was already added. // Only add if not in a sub-folder, otherwise it was already added.
//foreach( var pair in Meta.Groups.FileToGameAndGroup ) //foreach( var pair in Meta.Groups.FileToGameAndGroup )
// if (pair.Key.IndexOfAny(new[]{'/', '\\'}) < 0) // if (pair.Key.IndexOfAny(new[]{'/', '\\'}) < 0)
// ModFiles.Add( new FileInfo(Path.Combine(ModBasePath.FullName, pair.Key)) ); // ModFiles.Add( new FileInfo(Path.Combine(ModBasePath.FullName, pair.Key)) );
} }

View file

@ -1,4 +1,4 @@
using System.Linq; using System.Linq;
using Dalamud.Game.Command; using Dalamud.Game.Command;
using Dalamud.Plugin; using Dalamud.Plugin;
using EmbedIO; using EmbedIO;
@ -70,7 +70,7 @@ namespace Penumbra
public void CreateWebServer() public void CreateWebServer()
{ {
var prefix = "http://localhost:42069/"; var prefix = "http://localhost:42069/";
ShutdownWebServer(); ShutdownWebServer();
_webServer = new WebServer( o => o _webServer = new WebServer( o => o
@ -119,14 +119,14 @@ namespace Penumbra
$"Reloaded Penumbra mods. You have {ModManager.Mods.ModSettings.Count} mods, {ModManager.Mods.EnabledMods.Length} of which are enabled." $"Reloaded Penumbra mods. You have {ModManager.Mods.ModSettings.Count} mods, {ModManager.Mods.EnabledMods.Length} of which are enabled."
); );
break; break;
} }
case "redraw": case "redraw":
{ {
if (args.Length > 1) if (args.Length > 1)
RefreshActors.RedrawSpecific(PluginInterface.ClientState.Actors, string.Join(" ", args.Skip(1))); RefreshActors.RedrawSpecific(PluginInterface.ClientState.Actors, string.Join(" ", args.Skip(1)));
else else
RefreshActors.RedrawAll(PluginInterface.ClientState.Actors); RefreshActors.RedrawAll(PluginInterface.ClientState.Actors);
break; break;
} }
} }

View file

@ -1,4 +1,4 @@
using System; using System;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
@ -127,10 +127,10 @@ namespace Penumbra
if( LogAllFiles ) if( LogAllFiles )
{ {
PluginLog.Log( "[GetResourceHandler] {0}", gameFsPath ); PluginLog.Log( "[GetResourceHandler] {0}", gameFsPath );
} }
if (Plugin.Configuration.IsEnabled) if (Plugin.Configuration.IsEnabled)
{ {
var replacementPath = Plugin.ModManager.ResolveSwappedOrReplacementFilePath( gameFsPath ); var replacementPath = Plugin.ModManager.ResolveSwappedOrReplacementFilePath( gameFsPath );
// path must be < 260 because statically defined array length :( // path must be < 260 because statically defined array length :(
@ -152,9 +152,9 @@ namespace Penumbra
#if DEBUG #if DEBUG
PluginLog.Log( "[GetResourceHandler] resolved {GamePath} to {NewPath}", gameFsPath, replacementPath ); PluginLog.Log( "[GetResourceHandler] resolved {GamePath} to {NewPath}", gameFsPath, replacementPath );
#endif #endif
} }
return CallOriginalHandler( isSync, pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown, isUnknown ); return CallOriginalHandler( isSync, pFileManager, pCategoryId, pResourceType, pResourceHash, pPath, pUnknown, isUnknown );
} }

View file

@ -1,4 +1,4 @@
namespace Penumbra.Structs namespace Penumbra.Structs
{ {
public enum FileMode : uint public enum FileMode : uint
{ {

View file

@ -1,4 +1,4 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Penumbra.Structs namespace Penumbra.Structs
{ {

View file

@ -1,4 +1,4 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Penumbra.Structs namespace Penumbra.Structs
{ {

View file

@ -1,130 +1,130 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics; using System.Numerics;
using ImGuiNET; using ImGuiNET;
namespace Penumbra.UI namespace Penumbra.UI
{ {
public static partial class ImGuiCustom public static partial class ImGuiCustom
{ {
public static void BeginFramedGroup(string label) => BeginFramedGroupInternal(ref label, ZeroVector, false); public static void BeginFramedGroup(string label) => BeginFramedGroupInternal(ref label, ZeroVector, false);
public static void BeginFramedGroup(string label, Vector2 minSize) => BeginFramedGroupInternal(ref label, minSize, false); public static void BeginFramedGroup(string label, Vector2 minSize) => BeginFramedGroupInternal(ref label, minSize, false);
public static bool BeginFramedGroupEdit(ref string label) => BeginFramedGroupInternal(ref label, ZeroVector, true); public static bool BeginFramedGroupEdit(ref string label) => BeginFramedGroupInternal(ref label, ZeroVector, true);
public static bool BeginFramedGroupEdit(ref string label, Vector2 minSize) => BeginFramedGroupInternal(ref label, minSize, true); public static bool BeginFramedGroupEdit(ref string label, Vector2 minSize) => BeginFramedGroupInternal(ref label, minSize, true);
private static bool BeginFramedGroupInternal(ref string label, Vector2 minSize, bool edit) private static bool BeginFramedGroupInternal(ref string label, Vector2 minSize, bool edit)
{ {
var itemSpacing = ImGui.GetStyle().ItemSpacing; var itemSpacing = ImGui.GetStyle().ItemSpacing;
var frameHeight = ImGui.GetFrameHeight(); var frameHeight = ImGui.GetFrameHeight();
var halfFrameHeight = new Vector2(ImGui.GetFrameHeight() / 2, 0); var halfFrameHeight = new Vector2(ImGui.GetFrameHeight() / 2, 0);
ImGui.BeginGroup(); // First group ImGui.BeginGroup(); // First group
ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, ZeroVector); ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, ZeroVector);
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, ZeroVector); ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, ZeroVector);
ImGui.BeginGroup(); // Second group ImGui.BeginGroup(); // Second group
var effectiveSize = minSize; var effectiveSize = minSize;
if (effectiveSize.X < 0) if (effectiveSize.X < 0)
effectiveSize.X = ImGui.GetContentRegionAvail().X; effectiveSize.X = ImGui.GetContentRegionAvail().X;
// Ensure width. // Ensure width.
ImGui.Dummy(new(effectiveSize.X, 0)); ImGui.Dummy(new(effectiveSize.X, 0));
// Ensure left half boundary width/distance. // Ensure left half boundary width/distance.
ImGui.Dummy(halfFrameHeight); ImGui.Dummy(halfFrameHeight);
ImGui.SameLine(); ImGui.SameLine();
ImGui.BeginGroup(); // Third group. ImGui.BeginGroup(); // Third group.
// Ensure right half of boundary width/distance // Ensure right half of boundary width/distance
ImGui.Dummy(halfFrameHeight); ImGui.Dummy(halfFrameHeight);
// Label block // Label block
ImGui.SameLine(); ImGui.SameLine();
var ret = false; var ret = false;
if (edit) if (edit)
ret = ImGuiCustom.ResizingTextInput(ref label, 1024); ret = ImGuiCustom.ResizingTextInput(ref label, 1024);
else else
ImGui.TextUnformatted(label); ImGui.TextUnformatted(label);
var labelMin = ImGui.GetItemRectMin(); var labelMin = ImGui.GetItemRectMin();
var labelMax = ImGui.GetItemRectMax(); var labelMax = ImGui.GetItemRectMax();
ImGui.SameLine(); ImGui.SameLine();
// Ensure height and distance to label. // Ensure height and distance to label.
ImGui.Dummy(new Vector2(0, frameHeight + itemSpacing.Y)); ImGui.Dummy(new Vector2(0, frameHeight + itemSpacing.Y));
ImGui.BeginGroup(); // Fourth Group. ImGui.BeginGroup(); // Fourth Group.
ImGui.PopStyleVar(2); ImGui.PopStyleVar(2);
ImGui.SetWindowSize(new Vector2(ImGui.GetWindowSize().X - frameHeight, ImGui.GetWindowSize().Y)); ImGui.SetWindowSize(new Vector2(ImGui.GetWindowSize().X - frameHeight, ImGui.GetWindowSize().Y));
var itemWidth = ImGui.CalcItemWidth(); var itemWidth = ImGui.CalcItemWidth();
ImGui.PushItemWidth(Math.Max(0f, itemWidth - frameHeight)); ImGui.PushItemWidth(Math.Max(0f, itemWidth - frameHeight));
labelStack.Add((labelMin, labelMax)); labelStack.Add((labelMin, labelMax));
return ret; return ret;
} }
private static void DrawClippedRect(Vector2 clipMin, Vector2 clipMax, Vector2 drawMin, Vector2 drawMax, uint color, float thickness) private static void DrawClippedRect(Vector2 clipMin, Vector2 clipMax, Vector2 drawMin, Vector2 drawMax, uint color, float thickness)
{ {
ImGui.PushClipRect(clipMin, clipMax, true); ImGui.PushClipRect(clipMin, clipMax, true);
ImGui.GetWindowDrawList().AddRect(drawMin, drawMax, color, thickness); ImGui.GetWindowDrawList().AddRect(drawMin, drawMax, color, thickness);
ImGui.PopClipRect(); ImGui.PopClipRect();
} }
public static void EndFramedGroup() public static void EndFramedGroup()
{ {
uint borderColor = ImGui.ColorConvertFloat4ToU32(ImGui.GetStyle().Colors[(int)ImGuiCol.Border]); uint borderColor = ImGui.ColorConvertFloat4ToU32(ImGui.GetStyle().Colors[(int)ImGuiCol.Border]);
Vector2 itemSpacing = ImGui.GetStyle().ItemSpacing; Vector2 itemSpacing = ImGui.GetStyle().ItemSpacing;
float frameHeight = ImGui.GetFrameHeight(); float frameHeight = ImGui.GetFrameHeight();
Vector2 halfFrameHeight = new(ImGui.GetFrameHeight() / 2, 0); Vector2 halfFrameHeight = new(ImGui.GetFrameHeight() / 2, 0);
ImGui.PopItemWidth(); ImGui.PopItemWidth();
ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, ZeroVector); ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, ZeroVector);
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, ZeroVector); ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, ZeroVector);
ImGui.EndGroup(); // Close fourth group ImGui.EndGroup(); // Close fourth group
ImGui.EndGroup(); // Close third group ImGui.EndGroup(); // Close third group
ImGui.SameLine(); ImGui.SameLine();
// Ensure right distance. // Ensure right distance.
ImGui.Dummy(halfFrameHeight); ImGui.Dummy(halfFrameHeight);
// Ensure bottom distance // Ensure bottom distance
ImGui.Dummy(new Vector2(0, frameHeight/2 - itemSpacing.Y)); ImGui.Dummy(new Vector2(0, frameHeight/2 - itemSpacing.Y));
ImGui.EndGroup(); // Close second group ImGui.EndGroup(); // Close second group
var itemMin = ImGui.GetItemRectMin(); var itemMin = ImGui.GetItemRectMin();
var itemMax = ImGui.GetItemRectMax(); var itemMax = ImGui.GetItemRectMax();
var (currentLabelMin, currentLabelMax) = labelStack[labelStack.Count - 1]; var (currentLabelMin, currentLabelMax) = labelStack[labelStack.Count - 1];
labelStack.RemoveAt(labelStack.Count - 1); labelStack.RemoveAt(labelStack.Count - 1);
var halfFrame = new Vector2(frameHeight / 8, frameHeight / 2); var halfFrame = new Vector2(frameHeight / 8, frameHeight / 2);
currentLabelMin.X -= itemSpacing.X; currentLabelMin.X -= itemSpacing.X;
currentLabelMax.X += itemSpacing.X; currentLabelMax.X += itemSpacing.X;
var frameMin = itemMin + halfFrame; var frameMin = itemMin + halfFrame;
var frameMax = itemMax - new Vector2(halfFrame.X, 0); var frameMax = itemMax - new Vector2(halfFrame.X, 0);
// Left // Left
DrawClippedRect(new(-float.MaxValue , -float.MaxValue ), new(currentLabelMin.X, float.MaxValue ), frameMin, frameMax, borderColor, halfFrame.X); DrawClippedRect(new(-float.MaxValue , -float.MaxValue ), new(currentLabelMin.X, float.MaxValue ), frameMin, frameMax, borderColor, halfFrame.X);
// Right // Right
DrawClippedRect(new(currentLabelMax.X, -float.MaxValue ), new(float.MaxValue , float.MaxValue ), frameMin, frameMax, borderColor, halfFrame.X); DrawClippedRect(new(currentLabelMax.X, -float.MaxValue ), new(float.MaxValue , float.MaxValue ), frameMin, frameMax, borderColor, halfFrame.X);
// Top // Top
DrawClippedRect(new(currentLabelMin.X, -float.MaxValue ), new(currentLabelMax.X, currentLabelMin.Y), frameMin, frameMax, borderColor, halfFrame.X); DrawClippedRect(new(currentLabelMin.X, -float.MaxValue ), new(currentLabelMax.X, currentLabelMin.Y), frameMin, frameMax, borderColor, halfFrame.X);
// Bottom // Bottom
DrawClippedRect(new(currentLabelMin.X, currentLabelMax.Y), new(currentLabelMax.X, float.MaxValue ), frameMin, frameMax, borderColor, halfFrame.X); DrawClippedRect(new(currentLabelMin.X, currentLabelMax.Y), new(currentLabelMax.X, float.MaxValue ), frameMin, frameMax, borderColor, halfFrame.X);
ImGui.PopStyleVar(2); ImGui.PopStyleVar(2);
ImGui.SetWindowSize(new Vector2(ImGui.GetWindowSize().X + frameHeight, ImGui.GetWindowSize().Y)); ImGui.SetWindowSize(new Vector2(ImGui.GetWindowSize().X + frameHeight, ImGui.GetWindowSize().Y));
ImGui.Dummy(ZeroVector); ImGui.Dummy(ZeroVector);
ImGui.EndGroup(); // Close first group ImGui.EndGroup(); // Close first group
} }
private static readonly Vector2 ZeroVector = new(0, 0); private static readonly Vector2 ZeroVector = new(0, 0);
private static List<(Vector2, Vector2)> labelStack = new(); private static List<(Vector2, Vector2)> labelStack = new();
} }
} }

View file

@ -1,43 +1,43 @@
using ImGuiNET; using ImGuiNET;
namespace Penumbra.UI namespace Penumbra.UI
{ {
public static partial class ImGuiCustom public static partial class ImGuiCustom
{ {
public static bool RenameableCombo(string label, ref int currentItem, ref string newName, string[] items, int numItems) public static bool RenameableCombo(string label, ref int currentItem, ref string newName, string[] items, int numItems)
{ {
var ret = false; var ret = false;
newName = ""; newName = "";
var newOption = ""; var newOption = "";
if (ImGui.BeginCombo(label, (numItems > 0) ? items[currentItem] : newOption)) if (ImGui.BeginCombo(label, (numItems > 0) ? items[currentItem] : newOption))
{ {
for (var i = 0; i < numItems; ++i) for (var i = 0; i < numItems; ++i)
{ {
var isSelected = i == currentItem; var isSelected = i == currentItem;
ImGui.SetNextItemWidth(-1); ImGui.SetNextItemWidth(-1);
if (ImGui.InputText($"##{label}_{i}", ref items[i], 64, ImGuiInputTextFlags.EnterReturnsTrue)) if (ImGui.InputText($"##{label}_{i}", ref items[i], 64, ImGuiInputTextFlags.EnterReturnsTrue))
{ {
currentItem = i; currentItem = i;
newName = items[i]; newName = items[i];
ret = true; ret = true;
ImGui.CloseCurrentPopup(); ImGui.CloseCurrentPopup();
} }
if (isSelected) if (isSelected)
ImGui.SetItemDefaultFocus(); ImGui.SetItemDefaultFocus();
} }
ImGui.SetNextItemWidth(-1); ImGui.SetNextItemWidth(-1);
if (ImGui.InputText($"##{label}_new", ref newOption, 64, ImGuiInputTextFlags.EnterReturnsTrue)) if (ImGui.InputText($"##{label}_new", ref newOption, 64, ImGuiInputTextFlags.EnterReturnsTrue))
{ {
currentItem = numItems; currentItem = numItems;
newName = newOption; newName = newOption;
ret = true; ret = true;
ImGui.CloseCurrentPopup(); ImGui.CloseCurrentPopup();
} }
if (numItems == 0) if (numItems == 0)
ImGui.SetItemDefaultFocus(); ImGui.SetItemDefaultFocus();
ImGui.EndCombo(); ImGui.EndCombo();
} }
return ret; return ret;
} }
} }
} }

View file

@ -1,40 +1,40 @@
using System.Collections.Generic; using System.Collections.Generic;
using ImGuiNET; using ImGuiNET;
namespace Penumbra.UI namespace Penumbra.UI
{ {
public static partial class ImGuiCustom public static partial class ImGuiCustom
{ {
public static bool InputOrText(bool editable, string label, ref string text, uint maxLength) public static bool InputOrText(bool editable, string label, ref string text, uint maxLength)
{ {
if (editable) if (editable)
return ResizingTextInput(label, ref text, maxLength); return ResizingTextInput(label, ref text, maxLength);
ImGui.Text(text); ImGui.Text(text);
return false; return false;
} }
public static bool ResizingTextInput(string label, ref string input, uint maxLength) => ResizingTextInputIntern(label, ref input, maxLength).Item1; public static bool ResizingTextInput(string label, ref string input, uint maxLength) => ResizingTextInputIntern(label, ref input, maxLength).Item1;
public static bool ResizingTextInput(ref string input, uint maxLength) public static bool ResizingTextInput(ref string input, uint maxLength)
{ {
var (ret, id) = ResizingTextInputIntern($"##{input}", ref input, maxLength); var (ret, id) = ResizingTextInputIntern($"##{input}", ref input, maxLength);
if (ret) if (ret)
_textInputWidths.Remove(id); _textInputWidths.Remove(id);
return ret; return ret;
} }
private static (bool, uint) ResizingTextInputIntern(string label, ref string input, uint maxLength) private static (bool, uint) ResizingTextInputIntern(string label, ref string input, uint maxLength)
{ {
var id = ImGui.GetID(label); var id = ImGui.GetID(label);
if (!_textInputWidths.TryGetValue(id, out var width)) if (!_textInputWidths.TryGetValue(id, out var width))
width = ImGui.CalcTextSize(input).X + 10; width = ImGui.CalcTextSize(input).X + 10;
ImGui.SetNextItemWidth(width); ImGui.SetNextItemWidth(width);
var ret = ImGui.InputText(label, ref input, maxLength, ImGuiInputTextFlags.EnterReturnsTrue); var ret = ImGui.InputText(label, ref input, maxLength, ImGuiInputTextFlags.EnterReturnsTrue);
_textInputWidths[id] = ImGui.CalcTextSize(input).X + 10; _textInputWidths[id] = ImGui.CalcTextSize(input).X + 10;
return (ret, id); return (ret, id);
} }
private static readonly Dictionary<uint, float> _textInputWidths = new(); private static readonly Dictionary<uint, float> _textInputWidths = new();
} }
} }

View file

@ -1,26 +1,26 @@
using ImGuiNET; using ImGuiNET;
namespace Penumbra.UI namespace Penumbra.UI
{ {
public static partial class ImGuiCustom public static partial class ImGuiCustom
{ {
public static void VerticalDistance(float distance) public static void VerticalDistance(float distance)
{ {
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + distance); ImGui.SetCursorPosY(ImGui.GetCursorPosY() + distance);
} }
public static void RightJustifiedText(float pos, string text) public static void RightJustifiedText(float pos, string text)
{ {
ImGui.SetCursorPosX(pos - ImGui.CalcTextSize(text).X - 2 * ImGui.GetStyle().ItemSpacing.X); ImGui.SetCursorPosX(pos - ImGui.CalcTextSize(text).X - 2 * ImGui.GetStyle().ItemSpacing.X);
ImGui.Text(text); ImGui.Text(text);
} }
public static void RightJustifiedLabel(float pos, string text) public static void RightJustifiedLabel(float pos, string text)
{ {
ImGui.SetCursorPosX(pos - ImGui.CalcTextSize(text).X - ImGui.GetStyle().ItemSpacing.X / 2); ImGui.SetCursorPosX(pos - ImGui.CalcTextSize(text).X - ImGui.GetStyle().ItemSpacing.X / 2);
ImGui.Text(text); ImGui.Text(text);
ImGui.SameLine(pos); ImGui.SameLine(pos);
} }
} }
} }

View file

@ -2,40 +2,40 @@ using System.Numerics;
using ImGuiNET; using ImGuiNET;
namespace Penumbra.UI namespace Penumbra.UI
{ {
public partial class SettingsInterface public partial class SettingsInterface
{ {
private class LaunchButton private class LaunchButton
{ {
// magic numbers // magic numbers
private const int Padding = 50; private const int Padding = 50;
private const int Width = 200; private const int Width = 200;
private const int Height = 45; private const int Height = 45;
private const string MenuButtonsName = "Penumbra Menu Buttons"; private const string MenuButtonsName = "Penumbra Menu Buttons";
private const string MenuButtonLabel = "Manage Mods"; private const string MenuButtonLabel = "Manage Mods";
private static readonly Vector2 WindowSize = new(Width, Height); private static readonly Vector2 WindowSize = new(Width, Height);
private static readonly Vector2 WindowPosOffset = new(Padding + Width, Padding + Height); private static readonly Vector2 WindowPosOffset = new(Padding + Width, Padding + Height);
private readonly ImGuiWindowFlags ButtonFlags = ImGuiWindowFlags.AlwaysAutoResize private readonly ImGuiWindowFlags ButtonFlags = ImGuiWindowFlags.AlwaysAutoResize
| ImGuiWindowFlags.NoBackground | ImGuiWindowFlags.NoBackground
| ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoDecoration
| ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoMove
| ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollbar
| ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoResize
| ImGuiWindowFlags.NoSavedSettings; | ImGuiWindowFlags.NoSavedSettings;
private readonly SettingsInterface _base; private readonly SettingsInterface _base;
private readonly Dalamud.Game.ClientState.Condition _condition; private readonly Dalamud.Game.ClientState.Condition _condition;
public LaunchButton(SettingsInterface ui) public LaunchButton(SettingsInterface ui)
{ {
_base = ui; _base = ui;
_condition = ui._plugin.PluginInterface.ClientState.Condition; _condition = ui._plugin.PluginInterface.ClientState.Condition;
} }
public void Draw() public void Draw()
{ {
if( !_condition.Any() && !_base._menu.Visible ) if( !_condition.Any() && !_base._menu.Visible )
{ {
var ss = ImGui.GetIO().DisplaySize; var ss = ImGui.GetIO().DisplaySize;
@ -49,8 +49,8 @@ namespace Penumbra.UI
ImGui.End(); ImGui.End();
} }
} }
} }
} }
} }
} }

View file

@ -1,28 +1,28 @@
using ImGuiNET; using ImGuiNET;
namespace Penumbra.UI namespace Penumbra.UI
{ {
public partial class SettingsInterface public partial class SettingsInterface
{ {
private class MenuBar private class MenuBar
{ {
private const string MenuLabel = "Penumbra"; private const string MenuLabel = "Penumbra";
private const string MenuItemToggle = "Toggle UI"; private const string MenuItemToggle = "Toggle UI";
private const string SlashCommand = "/penumbra"; private const string SlashCommand = "/penumbra";
private const string MenuItemRediscover = "Rediscover Mods"; private const string MenuItemRediscover = "Rediscover Mods";
private const string MenuItemHide = "Hide Menu Bar"; private const string MenuItemHide = "Hide Menu Bar";
#if DEBUG #if DEBUG
private bool _showDebugBar = true; private bool _showDebugBar = true;
#else #else
private const bool _showDebugBar = false; private const bool _showDebugBar = false;
#endif #endif
private readonly SettingsInterface _base; private readonly SettingsInterface _base;
public MenuBar(SettingsInterface ui) => _base = ui; public MenuBar(SettingsInterface ui) => _base = ui;
public void Draw() public void Draw()
{ {
if( _showDebugBar && ImGui.BeginMainMenuBar() ) if( _showDebugBar && ImGui.BeginMainMenuBar() )
{ {
if( ImGui.BeginMenu( MenuLabel ) ) if( ImGui.BeginMenu( MenuLabel ) )
@ -31,18 +31,18 @@ namespace Penumbra.UI
_base.FlipVisibility(); _base.FlipVisibility();
if( ImGui.MenuItem( MenuItemRediscover ) ) if( ImGui.MenuItem( MenuItemRediscover ) )
_base.ReloadMods(); _base.ReloadMods();
#if DEBUG #if DEBUG
if ( ImGui.MenuItem( MenuItemHide) ) if ( ImGui.MenuItem( MenuItemHide) )
_showDebugBar = false; _showDebugBar = false;
#endif #endif
ImGui.EndMenu(); ImGui.EndMenu();
} }
ImGui.EndMainMenuBar(); ImGui.EndMainMenuBar();
} }
} }
} }
} }
} }

View file

@ -2,45 +2,45 @@ using System.IO;
using System.Numerics; using System.Numerics;
namespace Penumbra.UI namespace Penumbra.UI
{ {
public partial class SettingsInterface public partial class SettingsInterface
{ {
private const float DefaultVerticalSpace = 20f; private const float DefaultVerticalSpace = 20f;
private static readonly Vector2 AutoFillSize = new(-1, -1); private static readonly Vector2 AutoFillSize = new(-1, -1);
private static readonly Vector2 ZeroVector = new( 0, 0); private static readonly Vector2 ZeroVector = new( 0, 0);
private readonly Plugin _plugin; private readonly Plugin _plugin;
private readonly LaunchButton _launchButton; private readonly LaunchButton _launchButton;
private readonly MenuBar _menuBar; private readonly MenuBar _menuBar;
private readonly SettingsMenu _menu; private readonly SettingsMenu _menu;
public SettingsInterface( Plugin plugin ) public SettingsInterface( Plugin plugin )
{ {
_plugin = plugin; _plugin = plugin;
_launchButton = new(this); _launchButton = new(this);
_menuBar = new(this); _menuBar = new(this);
_menu = new(this); _menu = new(this);
} }
public void FlipVisibility() => _menu.Visible = !_menu.Visible; public void FlipVisibility() => _menu.Visible = !_menu.Visible;
public void Draw() public void Draw()
{ {
_menuBar.Draw(); _menuBar.Draw();
_launchButton.Draw(); _launchButton.Draw();
_menu.Draw(); _menu.Draw();
} }
private void ReloadMods() private void ReloadMods()
{ {
_menu._installedTab._selector.ClearSelection(); _menu._installedTab._selector.ClearSelection();
// create the directory if it doesn't exist // create the directory if it doesn't exist
Directory.CreateDirectory( _plugin.Configuration.CurrentCollection ); Directory.CreateDirectory( _plugin.Configuration.CurrentCollection );
_plugin.ModManager.DiscoverMods( _plugin.Configuration.CurrentCollection ); _plugin.ModManager.DiscoverMods( _plugin.Configuration.CurrentCollection );
_menu._effectiveTab.RebuildFileList(_plugin.Configuration.ShowAdvanced); _menu._effectiveTab.RebuildFileList(_plugin.Configuration.ShowAdvanced);
} }
} }
} }

View file

@ -2,45 +2,45 @@ using System.Numerics;
using ImGuiNET; using ImGuiNET;
namespace Penumbra.UI namespace Penumbra.UI
{ {
public partial class SettingsInterface public partial class SettingsInterface
{ {
private partial class SettingsMenu private partial class SettingsMenu
{ {
private const string PenumbraSettingsLabel = "PenumbraSettings"; private const string PenumbraSettingsLabel = "PenumbraSettings";
private static readonly Vector2 MinSettingsSize = new( 800, 450 ); private static readonly Vector2 MinSettingsSize = new( 800, 450 );
private static readonly Vector2 MaxSettingsSize = new( 69420, 42069 ); private static readonly Vector2 MaxSettingsSize = new( 69420, 42069 );
private readonly SettingsInterface _base; private readonly SettingsInterface _base;
public readonly TabSettings _settingsTab; public readonly TabSettings _settingsTab;
public readonly TabImport _importTab; public readonly TabImport _importTab;
public readonly TabBrowser _browserTab; public readonly TabBrowser _browserTab;
public readonly TabInstalled _installedTab; public readonly TabInstalled _installedTab;
public readonly TabEffective _effectiveTab; public readonly TabEffective _effectiveTab;
public SettingsMenu(SettingsInterface ui) public SettingsMenu(SettingsInterface ui)
{ {
_base = ui; _base = ui;
_settingsTab = new(_base); _settingsTab = new(_base);
_importTab = new(_base); _importTab = new(_base);
_browserTab = new(); _browserTab = new();
_installedTab = new(_base); _installedTab = new(_base);
_effectiveTab = new(_base); _effectiveTab = new(_base);
} }
#if DEBUG #if DEBUG
private const bool DefaultVisibility = true; private const bool DefaultVisibility = true;
#else #else
private const bool DefaultVisibility = false; private const bool DefaultVisibility = false;
#endif #endif
public bool Visible = DefaultVisibility; public bool Visible = DefaultVisibility;
public void Draw() public void Draw()
{ {
if( !Visible ) if( !Visible )
return; return;
ImGui.SetNextWindowSizeConstraints( MinSettingsSize, MaxSettingsSize ); ImGui.SetNextWindowSizeConstraints( MinSettingsSize, MaxSettingsSize );
#if DEBUG #if DEBUG
var ret = ImGui.Begin( _base._plugin.PluginDebugTitleStr, ref Visible ); var ret = ImGui.Begin( _base._plugin.PluginDebugTitleStr, ref Visible );
@ -49,12 +49,12 @@ namespace Penumbra.UI
#endif #endif
if( !ret ) if( !ret )
return; return;
ImGui.BeginTabBar( PenumbraSettingsLabel ); ImGui.BeginTabBar( PenumbraSettingsLabel );
_settingsTab.Draw(); _settingsTab.Draw();
_importTab.Draw(); _importTab.Draw();
if( !_importTab.IsImporting() ) if( !_importTab.IsImporting() )
{ {
_browserTab.Draw(); _browserTab.Draw();
@ -65,8 +65,8 @@ namespace Penumbra.UI
} }
ImGui.EndTabBar(); ImGui.EndTabBar();
ImGui.End(); ImGui.End();
} }
} }
} }
} }

View file

@ -1,12 +1,12 @@
using System.Diagnostics; using System.Diagnostics;
using ImGuiNET; using ImGuiNET;
namespace Penumbra.UI namespace Penumbra.UI
{ {
public partial class SettingsInterface public partial class SettingsInterface
{ {
private class TabBrowser private class TabBrowser
{ {
[Conditional( "DEBUG" )] [Conditional( "DEBUG" )]
public void Draw() public void Draw()
{ {
@ -16,7 +16,7 @@ namespace Penumbra.UI
ImGui.Text( "woah" ); ImGui.Text( "woah" );
ImGui.EndTabItem(); ImGui.EndTabItem();
} }
} }
} }
} }

View file

@ -1,50 +1,50 @@
using System.Linq; using System.Linq;
using ImGuiNET; using ImGuiNET;
using Penumbra.Mods; using Penumbra.Mods;
namespace Penumbra.UI namespace Penumbra.UI
{ {
public partial class SettingsInterface public partial class SettingsInterface
{ {
private class TabEffective private class TabEffective
{ {
private const string LabelTab = "Effective File List"; private const string LabelTab = "Effective File List";
private const float TextSizePadding = 5f; private const float TextSizePadding = 5f;
private ModManager _mods; private ModManager _mods;
private (string, string)[] _fileList = null; private (string, string)[] _fileList = null;
private float _maxGamePath = 0f; private float _maxGamePath = 0f;
public TabEffective(SettingsInterface ui) public TabEffective(SettingsInterface ui)
{ {
_mods = ui._plugin.ModManager; _mods = ui._plugin.ModManager;
RebuildFileList(ui._plugin.Configuration.ShowAdvanced); RebuildFileList(ui._plugin.Configuration.ShowAdvanced);
} }
public void RebuildFileList(bool advanced) public void RebuildFileList(bool advanced)
{ {
if (advanced) if (advanced)
{ {
_fileList = _mods.ResolvedFiles.Select( P => (P.Value.FullName, P.Key) ).ToArray(); _fileList = _mods.ResolvedFiles.Select( P => (P.Value.FullName, P.Key) ).ToArray();
_maxGamePath = ((_fileList.Length > 0) ? _fileList.Max( P => ImGui.CalcTextSize(P.Item2).X ) : 0f) + TextSizePadding; _maxGamePath = ((_fileList.Length > 0) ? _fileList.Max( P => ImGui.CalcTextSize(P.Item2).X ) : 0f) + TextSizePadding;
} }
else else
{ {
_fileList = null; _fileList = null;
_maxGamePath = 0f; _maxGamePath = 0f;
} }
} }
private void DrawFileLine((string, string) file) private void DrawFileLine((string, string) file)
{ {
ImGui.Selectable(file.Item2); ImGui.Selectable(file.Item2);
ImGui.SameLine(); ImGui.SameLine();
ImGui.SetCursorPosX(_maxGamePath); ImGui.SetCursorPosX(_maxGamePath);
ImGui.TextUnformatted(" <-- "); ImGui.TextUnformatted(" <-- ");
ImGui.SameLine(); ImGui.SameLine();
ImGui.Selectable(file.Item1); ImGui.Selectable(file.Item1);
} }
public void Draw() public void Draw()
{ {
var ret = ImGui.BeginTabItem( LabelTab ); var ret = ImGui.BeginTabItem( LabelTab );
@ -53,14 +53,14 @@ namespace Penumbra.UI
if( ImGui.ListBoxHeader( "##effective_files", AutoFillSize ) ) if( ImGui.ListBoxHeader( "##effective_files", AutoFillSize ) )
{ {
foreach( var file in _fileList ) foreach( var file in _fileList )
DrawFileLine(file); DrawFileLine(file);
ImGui.ListBoxFooter(); ImGui.ListBoxFooter();
} }
ImGui.EndTabItem(); ImGui.EndTabItem();
} }
} }
} }
} }

View file

@ -1,42 +1,42 @@
using ImGuiNET; using ImGuiNET;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
using System.IO; using System.IO;
using System; using System;
using Penumbra.Importer; using Penumbra.Importer;
using Dalamud.Plugin; using Dalamud.Plugin;
using System.Numerics; using System.Numerics;
namespace Penumbra.UI namespace Penumbra.UI
{ {
public partial class SettingsInterface public partial class SettingsInterface
{ {
private class TabImport private class TabImport
{ {
private const string LabelTab = "Import Mods"; private const string LabelTab = "Import Mods";
private const string LabelImportButton = "Import TexTools Modpacks"; private const string LabelImportButton = "Import TexTools Modpacks";
private const string FileTypeFilter = "TexTools TTMP Modpack (*.ttmp2)|*.ttmp*|All files (*.*)|*.*"; private const string FileTypeFilter = "TexTools TTMP Modpack (*.ttmp2)|*.ttmp*|All files (*.*)|*.*";
private const string LabelFileDialog = "Pick one or more modpacks."; private const string LabelFileDialog = "Pick one or more modpacks.";
private const string LabelFileImportRunning = "Import in progress..."; private const string LabelFileImportRunning = "Import in progress...";
private const string TooltipModpack1 = "Writing modpack to disk before extracting..."; private const string TooltipModpack1 = "Writing modpack to disk before extracting...";
private const string FailedImport = "One or more of your modpacks failed to import.\nPlease submit a bug report."; private const string FailedImport = "One or more of your modpacks failed to import.\nPlease submit a bug report.";
private const uint ColorRed = 0xFF0000C8; private const uint ColorRed = 0xFF0000C8;
private static readonly Vector2 ImportBarSize = new( -1, 0 ); private static readonly Vector2 ImportBarSize = new( -1, 0 );
private bool _isImportRunning = false; private bool _isImportRunning = false;
private bool _hasError = false; private bool _hasError = false;
private TexToolsImport _texToolsImport = null!; private TexToolsImport _texToolsImport = null!;
private readonly SettingsInterface _base; private readonly SettingsInterface _base;
public TabImport(SettingsInterface ui) => _base = ui; public TabImport(SettingsInterface ui) => _base = ui;
public bool IsImporting() => _isImportRunning; public bool IsImporting() => _isImportRunning;
private void RunImportTask() private void RunImportTask()
{ {
_isImportRunning = true; _isImportRunning = true;
Task.Run( async () => Task.Run( async () =>
{ {
var picker = new OpenFileDialog var picker = new OpenFileDialog
@ -51,7 +51,7 @@ namespace Penumbra.UI
if( result == DialogResult.OK ) if( result == DialogResult.OK )
{ {
_hasError = false; _hasError = false;
foreach( var fileName in picker.FileNames ) foreach( var fileName in picker.FileNames )
{ {
@ -73,19 +73,19 @@ namespace Penumbra.UI
_texToolsImport = null; _texToolsImport = null;
_base.ReloadMods(); _base.ReloadMods();
} }
_isImportRunning = false; _isImportRunning = false;
} ); } );
} }
private void DrawImportButton() private void DrawImportButton()
{ {
if( ImGui.Button( LabelImportButton ) ) if( ImGui.Button( LabelImportButton ) )
{ {
RunImportTask(); RunImportTask();
} }
} }
private void DrawImportProgress() private void DrawImportProgress()
{ {
ImGui.Button( LabelFileImportRunning ); ImGui.Button( LabelFileImportRunning );
@ -116,12 +116,12 @@ namespace Penumbra.UI
} }
private void DrawFailedImportMessage() private void DrawFailedImportMessage()
{ {
ImGui.PushStyleColor( ImGuiCol.Text, ColorRed ); ImGui.PushStyleColor( ImGuiCol.Text, ColorRed );
ImGui.Text( FailedImport ); ImGui.Text( FailedImport );
ImGui.PopStyleColor(); ImGui.PopStyleColor();
} }
public void Draw() public void Draw()
{ {
var ret = ImGui.BeginTabItem( LabelTab ); var ret = ImGui.BeginTabItem( LabelTab );
@ -131,13 +131,13 @@ namespace Penumbra.UI
if( !_isImportRunning ) if( !_isImportRunning )
DrawImportButton(); DrawImportButton();
else else
DrawImportProgress(); DrawImportProgress();
if (_hasError) if (_hasError)
DrawFailedImportMessage(); DrawFailedImportMessage();
ImGui.EndTabItem(); ImGui.EndTabItem();
} }
} }
} }
} }

View file

@ -1,53 +1,53 @@
using ImGuiNET; using ImGuiNET;
namespace Penumbra.UI namespace Penumbra.UI
{ {
public partial class SettingsInterface public partial class SettingsInterface
{ {
private partial class TabInstalled private partial class TabInstalled
{ {
private const string LabelTab = "Installed Mods"; private const string LabelTab = "Installed Mods";
private readonly SettingsInterface _base; private readonly SettingsInterface _base;
public readonly Selector _selector; public readonly Selector _selector;
public readonly ModPanel _modPanel; public readonly ModPanel _modPanel;
public TabInstalled(SettingsInterface ui) public TabInstalled(SettingsInterface ui)
{ {
_base = ui; _base = ui;
_selector = new(_base); _selector = new(_base);
_modPanel = new(_base, _selector); _modPanel = new(_base, _selector);
} }
private void DrawNoModsAvailable() private void DrawNoModsAvailable()
{ {
ImGui.Text( "You don't have any mods :(" ); ImGui.Text( "You don't have any mods :(" );
ImGuiCustom.VerticalDistance(20f); ImGuiCustom.VerticalDistance(20f);
ImGui.Text( "You'll need to install them first by creating a folder close to the root of your drive (preferably an SSD)." ); ImGui.Text( "You'll need to install them first by creating a folder close to the root of your drive (preferably an SSD)." );
ImGui.Text( "For example: D:/ffxiv/mods/" ); ImGui.Text( "For example: D:/ffxiv/mods/" );
ImGui.Text( "And pasting that path into the settings tab and clicking the 'Rediscover Mods' button." ); ImGui.Text( "And pasting that path into the settings tab and clicking the 'Rediscover Mods' button." );
ImGui.Text( "You can return to this tab once you've done that." ); ImGui.Text( "You can return to this tab once you've done that." );
} }
public void Draw() public void Draw()
{ {
var ret = ImGui.BeginTabItem( LabelTab ); var ret = ImGui.BeginTabItem( LabelTab );
if( !ret ) if( !ret )
return; return;
if (_base._plugin.ModManager.Mods != null) if (_base._plugin.ModManager.Mods != null)
{ {
_selector.Draw(); _selector.Draw();
ImGui.SameLine(); ImGui.SameLine();
_modPanel.Draw(); _modPanel.Draw();
} }
else else
DrawNoModsAvailable(); DrawNoModsAvailable();
ImGui.EndTabItem(); ImGui.EndTabItem();
return; return;
} }
} }
} }
} }

View file

@ -21,7 +21,7 @@ namespace Penumbra.UI
private const string ButtonRemoveFromGroup = "Remove from Group"; private const string ButtonRemoveFromGroup = "Remove from Group";
private const string TooltipGamePathText = "Click to copy to clipboard."; private const string TooltipGamePathText = "Click to copy to clipboard.";
private const string TooltipAboutEdit = "Use Ctrl+Enter for newlines."; private const string TooltipAboutEdit = "Use Ctrl+Enter for newlines.";
private const string TextDefaultGamePath = "default"; private const string TextDefaultGamePath = "default";
private const char GamePathsSeparator = ';'; private const char GamePathsSeparator = ';';
private static readonly string TooltipFilesTabEdit = $"{TooltipFilesTab}\nRed Files are replaced in another group or a different option in this group, but not contained in the current option."; private static readonly string TooltipFilesTabEdit = $"{TooltipFilesTab}\nRed Files are replaced in another group or a different option in this group, but not contained in the current option.";

View file

@ -1,149 +1,149 @@
using ImGuiNET; using ImGuiNET;
using Dalamud.Plugin; using Dalamud.Plugin;
using System; using System;
using System.Numerics; using System.Numerics;
using System.Diagnostics; using System.Diagnostics;
using Penumbra.Models; using Penumbra.Models;
namespace Penumbra.UI namespace Penumbra.UI
{ {
public partial class SettingsInterface public partial class SettingsInterface
{ {
private class ModPanel private class ModPanel
{ {
private const string LabelModPanel = "selectedModInfo"; private const string LabelModPanel = "selectedModInfo";
private const string LabelEditName = "##editName"; private const string LabelEditName = "##editName";
private const string LabelEditVersion = "##editVersion"; private const string LabelEditVersion = "##editVersion";
private const string LabelEditAuthor = "##editAuthor"; private const string LabelEditAuthor = "##editAuthor";
private const string LabelEditWebsite = "##editWebsite"; private const string LabelEditWebsite = "##editWebsite";
private const string ButtonOpenWebsite = "Open Website"; private const string ButtonOpenWebsite = "Open Website";
private const string LabelModEnabled = "Enabled"; private const string LabelModEnabled = "Enabled";
private const string LabelEditingEnabled = "Enable Editing"; private const string LabelEditingEnabled = "Enable Editing";
private const string ButtonOpenModFolder = "Open Mod Folder"; private const string ButtonOpenModFolder = "Open Mod Folder";
private const string TooltipOpenModFolder = "Open the directory containing this mod in your default file explorer."; private const string TooltipOpenModFolder = "Open the directory containing this mod in your default file explorer.";
private const string ButtonEditJson = "Edit JSON"; private const string ButtonEditJson = "Edit JSON";
private const string TooltipEditJson = "Open the JSON configuration file in your default application for .json."; private const string TooltipEditJson = "Open the JSON configuration file in your default application for .json.";
private const string ButtonReloadJson = "Reload JSON"; private const string ButtonReloadJson = "Reload JSON";
private const string TooltipReloadJson = "Reload the configuration of all mods."; private const string TooltipReloadJson = "Reload the configuration of all mods.";
private const string ButtonDeduplicate = "Deduplicate"; private const string ButtonDeduplicate = "Deduplicate";
private const string TooltipDeduplicate = "Try to find identical files and remove duplicate occurences to reduce the mods disk size. Introduces an invisible single-option Group \"Duplicates\"."; private const string TooltipDeduplicate = "Try to find identical files and remove duplicate occurences to reduce the mods disk size. Introduces an invisible single-option Group \"Duplicates\".";
private const float HeaderLineDistance = 10f; private const float HeaderLineDistance = 10f;
private static readonly Vector4 GreyColor = new( 1f, 1f, 1f, 0.66f ); private static readonly Vector4 GreyColor = new( 1f, 1f, 1f, 0.66f );
private readonly SettingsInterface _base; private readonly SettingsInterface _base;
private readonly Selector _selector; private readonly Selector _selector;
public readonly PluginDetails _details; public readonly PluginDetails _details;
private bool _editMode = false; private bool _editMode = false;
private string _currentWebsite; private string _currentWebsite;
private bool _validWebsite; private bool _validWebsite;
public ModPanel(SettingsInterface ui, Selector s) public ModPanel(SettingsInterface ui, Selector s)
{ {
_base = ui; _base = ui;
_selector = s; _selector = s;
_details = new(_base, _selector); _details = new(_base, _selector);
_currentWebsite = Meta?.Website; _currentWebsite = Meta?.Website;
} }
private ModInfo Mod { get{ return _selector.Mod(); } } private ModInfo Mod { get{ return _selector.Mod(); } }
private ModMeta Meta { get{ return Mod?.Mod.Meta; } } private ModMeta Meta { get{ return Mod?.Mod.Meta; } }
private void DrawName() private void DrawName()
{ {
var name = Meta.Name; var name = Meta.Name;
if (ImGuiCustom.InputOrText(_editMode, LabelEditName, ref name, 64) if (ImGuiCustom.InputOrText(_editMode, LabelEditName, ref name, 64)
&& name.Length > 0 && name != Meta.Name) && name.Length > 0 && name != Meta.Name)
{ {
Meta.Name = name; Meta.Name = name;
_selector.SaveCurrentMod(); _selector.SaveCurrentMod();
} }
} }
private void DrawVersion() private void DrawVersion()
{ {
if (_editMode) if (_editMode)
{ {
ImGui.BeginGroup(); ImGui.BeginGroup();
ImGui.Text("(Version "); ImGui.Text("(Version ");
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, ZeroVector); ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, ZeroVector);
ImGui.SameLine(); ImGui.SameLine();
var version = Meta.Version ?? ""; var version = Meta.Version ?? "";
if (ImGuiCustom.ResizingTextInput( LabelEditVersion, ref version, 16) if (ImGuiCustom.ResizingTextInput( LabelEditVersion, ref version, 16)
&& version != Meta.Version) && version != Meta.Version)
{ {
Meta.Version = version.Length > 0 ? version : null; Meta.Version = version.Length > 0 ? version : null;
_selector.SaveCurrentMod(); _selector.SaveCurrentMod();
} }
ImGui.SameLine(); ImGui.SameLine();
ImGui.Text(")"); ImGui.Text(")");
ImGui.PopStyleVar(); ImGui.PopStyleVar();
ImGui.EndGroup(); ImGui.EndGroup();
} }
else if ((Meta.Version?.Length ?? 0) > 0) else if ((Meta.Version?.Length ?? 0) > 0)
{ {
ImGui.Text( $"(Version {Meta.Version})" ); ImGui.Text( $"(Version {Meta.Version})" );
} }
} }
private void DrawAuthor() private void DrawAuthor()
{ {
ImGui.BeginGroup(); ImGui.BeginGroup();
ImGui.TextColored( GreyColor, "by" ); ImGui.TextColored( GreyColor, "by" );
ImGui.SameLine(); ImGui.SameLine();
var author = Meta.Author ?? ""; var author = Meta.Author ?? "";
if (ImGuiCustom.InputOrText(_editMode, LabelEditAuthor, ref author, 64) if (ImGuiCustom.InputOrText(_editMode, LabelEditAuthor, ref author, 64)
&& author != Meta.Author) && author != Meta.Author)
{ {
Meta.Author = author.Length > 0 ? author : null; Meta.Author = author.Length > 0 ? author : null;
_selector.SaveCurrentMod(); _selector.SaveCurrentMod();
} }
ImGui.EndGroup(); ImGui.EndGroup();
} }
private void DrawWebsite() private void DrawWebsite()
{ {
ImGui.BeginGroup(); ImGui.BeginGroup();
if (_editMode) if (_editMode)
{ {
ImGui.TextColored( GreyColor, "from" ); ImGui.TextColored( GreyColor, "from" );
ImGui.SameLine(); ImGui.SameLine();
var website = Meta.Website ?? ""; var website = Meta.Website ?? "";
if (ImGuiCustom.ResizingTextInput(LabelEditWebsite, ref website, 512) if (ImGuiCustom.ResizingTextInput(LabelEditWebsite, ref website, 512)
&& website != Meta.Website) && website != Meta.Website)
{ {
Meta.Website = website.Length > 0 ? website : null; Meta.Website = website.Length > 0 ? website : null;
_selector.SaveCurrentMod(); _selector.SaveCurrentMod();
} }
} }
else if (( Meta.Website?.Length ?? 0 ) > 0) else if (( Meta.Website?.Length ?? 0 ) > 0)
{ {
if (_currentWebsite != Meta.Website) if (_currentWebsite != Meta.Website)
{ {
_currentWebsite = Meta.Website; _currentWebsite = Meta.Website;
_validWebsite = Uri.TryCreate( Meta.Website, UriKind.Absolute, out var uriResult ) _validWebsite = Uri.TryCreate( Meta.Website, UriKind.Absolute, out var uriResult )
&& ( uriResult.Scheme == Uri.UriSchemeHttps || uriResult.Scheme == Uri.UriSchemeHttp ); && ( uriResult.Scheme == Uri.UriSchemeHttps || uriResult.Scheme == Uri.UriSchemeHttp );
} }
if( _validWebsite ) if( _validWebsite )
{ {
if( ImGui.SmallButton( ButtonOpenWebsite ) ) if( ImGui.SmallButton( ButtonOpenWebsite ) )
{ {
try try
{ {
var process = new ProcessStartInfo(Meta.Website) var process = new ProcessStartInfo(Meta.Website)
{ {
UseShellExecute = true UseShellExecute = true
}; };
Process.Start(process); Process.Start(process);
} }
catch(System.ComponentModel.Win32Exception) catch(System.ComponentModel.Win32Exception)
{ {
// Do nothing. // Do nothing.
} }
} }
if( ImGui.IsItemHovered() ) if( ImGui.IsItemHovered() )
@ -155,121 +155,121 @@ namespace Penumbra.UI
ImGui.SameLine(); ImGui.SameLine();
ImGui.Text( Meta.Website ); ImGui.Text( Meta.Website );
} }
} }
ImGui.EndGroup(); ImGui.EndGroup();
} }
private void DrawHeaderLine() private void DrawHeaderLine()
{ {
DrawName(); DrawName();
ImGui.SameLine();
DrawVersion();
ImGui.SameLine();
DrawAuthor();
ImGui.SameLine(); ImGui.SameLine();
DrawWebsite(); DrawVersion();
} ImGui.SameLine();
DrawAuthor();
private void DrawEnabledMark() ImGui.SameLine();
{ DrawWebsite();
}
private void DrawEnabledMark()
{
var enabled = Mod.Enabled; var enabled = Mod.Enabled;
if( ImGui.Checkbox( LabelModEnabled, ref enabled ) ) if( ImGui.Checkbox( LabelModEnabled, ref enabled ) )
{ {
Mod.Enabled = enabled; Mod.Enabled = enabled;
_base._plugin.ModManager.Mods.Save(); _base._plugin.ModManager.Mods.Save();
_base._plugin.ModManager.CalculateEffectiveFileList(); _base._plugin.ModManager.CalculateEffectiveFileList();
_base._menu._effectiveTab.RebuildFileList(_base._plugin.Configuration.ShowAdvanced); _base._menu._effectiveTab.RebuildFileList(_base._plugin.Configuration.ShowAdvanced);
} }
} }
private void DrawEditableMark() private void DrawEditableMark()
{ {
ImGui.Checkbox( LabelEditingEnabled, ref _editMode); ImGui.Checkbox( LabelEditingEnabled, ref _editMode);
} }
private void DrawOpenModFolderButton() private void DrawOpenModFolderButton()
{ {
if( ImGui.Button( ButtonOpenModFolder ) ) if( ImGui.Button( ButtonOpenModFolder ) )
{ {
Process.Start( Mod.Mod.ModBasePath.FullName ); Process.Start( Mod.Mod.ModBasePath.FullName );
} }
if( ImGui.IsItemHovered() ) if( ImGui.IsItemHovered() )
ImGui.SetTooltip( TooltipOpenModFolder ); ImGui.SetTooltip( TooltipOpenModFolder );
} }
private void DrawEditJsonButton() private void DrawEditJsonButton()
{ {
if( ImGui.Button( ButtonEditJson ) ) if( ImGui.Button( ButtonEditJson ) )
{ {
Process.Start( _selector.SaveCurrentMod() ); Process.Start( _selector.SaveCurrentMod() );
} }
if( ImGui.IsItemHovered() ) if( ImGui.IsItemHovered() )
ImGui.SetTooltip( TooltipEditJson ); ImGui.SetTooltip( TooltipEditJson );
} }
private void DrawReloadJsonButton() private void DrawReloadJsonButton()
{ {
if( ImGui.Button( ButtonReloadJson ) ) if( ImGui.Button( ButtonReloadJson ) )
{ {
_selector.ReloadCurrentMod(); _selector.ReloadCurrentMod();
} }
if( ImGui.IsItemHovered() ) if( ImGui.IsItemHovered() )
ImGui.SetTooltip( TooltipReloadJson ); ImGui.SetTooltip( TooltipReloadJson );
} }
private void DrawDeduplicateButton() private void DrawDeduplicateButton()
{ {
if( ImGui.Button( ButtonDeduplicate ) ) if( ImGui.Button( ButtonDeduplicate ) )
{ {
new Deduplicator(Mod.Mod.ModBasePath, Meta).Run(); new Deduplicator(Mod.Mod.ModBasePath, Meta).Run();
_selector.SaveCurrentMod(); _selector.SaveCurrentMod();
Mod.Mod.RefreshModFiles(); Mod.Mod.RefreshModFiles();
_base._plugin.ModManager.CalculateEffectiveFileList(); _base._plugin.ModManager.CalculateEffectiveFileList();
_base._menu._effectiveTab.RebuildFileList(_base._plugin.Configuration.ShowAdvanced); _base._menu._effectiveTab.RebuildFileList(_base._plugin.Configuration.ShowAdvanced);
} }
if( ImGui.IsItemHovered() ) if( ImGui.IsItemHovered() )
ImGui.SetTooltip( TooltipDeduplicate ); ImGui.SetTooltip( TooltipDeduplicate );
} }
private void DrawEditLine() private void DrawEditLine()
{ {
DrawOpenModFolderButton(); DrawOpenModFolderButton();
ImGui.SameLine(); ImGui.SameLine();
DrawEditJsonButton(); DrawEditJsonButton();
ImGui.SameLine(); ImGui.SameLine();
DrawReloadJsonButton(); DrawReloadJsonButton();
ImGui.SameLine(); ImGui.SameLine();
DrawDeduplicateButton(); DrawDeduplicateButton();
} }
public void Draw() public void Draw()
{ {
if( Mod != null ) if( Mod != null )
{ {
try try
{ {
var ret = ImGui.BeginChild( LabelModPanel, AutoFillSize, true ); var ret = ImGui.BeginChild( LabelModPanel, AutoFillSize, true );
if (!ret) if (!ret)
return; return;
DrawHeaderLine(); DrawHeaderLine();
// Next line with fixed distance. // Next line with fixed distance.
ImGuiCustom.VerticalDistance(HeaderLineDistance); ImGuiCustom.VerticalDistance(HeaderLineDistance);
DrawEnabledMark(); DrawEnabledMark();
if (_base._plugin.Configuration.ShowAdvanced) if (_base._plugin.Configuration.ShowAdvanced)
{ {
ImGui.SameLine(); ImGui.SameLine();
DrawEditableMark(); DrawEditableMark();
} }
// Next line, if editable. // Next line, if editable.
if (_editMode) if (_editMode)
DrawEditLine(); DrawEditLine();
_details.Draw(_editMode); _details.Draw(_editMode);
ImGui.EndChild(); ImGui.EndChild();
} }
catch( Exception ex ) catch( Exception ex )
@ -277,7 +277,7 @@ namespace Penumbra.UI
PluginLog.LogError( ex, "fuck" ); PluginLog.LogError( ex, "fuck" );
} }
} }
} }
} }
} }
} }

View file

@ -1,53 +1,53 @@
using System.Numerics; using System.Numerics;
using System.Linq; using System.Linq;
using System.IO; using System.IO;
using Newtonsoft.Json; using Newtonsoft.Json;
using ImGuiNET; using ImGuiNET;
using Penumbra.Mods; using Penumbra.Mods;
using Penumbra.Models; using Penumbra.Models;
using Dalamud.Interface; using Dalamud.Interface;
namespace Penumbra.UI namespace Penumbra.UI
{ {
public partial class SettingsInterface public partial class SettingsInterface
{ {
private class Selector private class Selector
{ {
private const string LabelSelectorList = "##availableModList"; private const string LabelSelectorList = "##availableModList";
private const string TooltipMoveDown = "Move the selected mod down in priority"; private const string TooltipMoveDown = "Move the selected mod down in priority";
private const string TooltipMoveUp = "Move the selected mod up in priority"; private const string TooltipMoveUp = "Move the selected mod up in priority";
private const string TooltipDelete = "Delete the selected mod"; private const string TooltipDelete = "Delete the selected mod";
private const string TooltipAdd = "Add an empty mod"; private const string TooltipAdd = "Add an empty mod";
private const string DialogDeleteMod = "PenumbraDeleteMod"; private const string DialogDeleteMod = "PenumbraDeleteMod";
private const string ButtonYesDelete = "Yes, delete it"; private const string ButtonYesDelete = "Yes, delete it";
private const string ButtonNoDelete = "No, keep it"; private const string ButtonNoDelete = "No, keep it";
private const float SelectorPanelWidth = 240f; private const float SelectorPanelWidth = 240f;
private const uint DisabledModColor = 0xFF666666; private const uint DisabledModColor = 0xFF666666;
private const uint ConflictingModColor = 0xFFAAAAFF; private const uint ConflictingModColor = 0xFFAAAAFF;
private static readonly Vector2 SelectorButtonSizes = new(60, 0); private static readonly Vector2 SelectorButtonSizes = new(60, 0);
private static readonly string ArrowUpString = FontAwesomeIcon.ArrowUp.ToIconString(); private static readonly string ArrowUpString = FontAwesomeIcon.ArrowUp.ToIconString();
private static readonly string ArrowDownString = FontAwesomeIcon.ArrowDown.ToIconString(); private static readonly string ArrowDownString = FontAwesomeIcon.ArrowDown.ToIconString();
private readonly SettingsInterface _base; private readonly SettingsInterface _base;
private ModCollection Mods{ get{ return _base._plugin.ModManager.Mods; } } private ModCollection Mods{ get{ return _base._plugin.ModManager.Mods; } }
private ModInfo _mod = null; private ModInfo _mod = null;
private int _index = 0; private int _index = 0;
private int? _deleteIndex = null; private int? _deleteIndex = null;
public Selector(SettingsInterface ui) public Selector(SettingsInterface ui)
{ {
_base = ui; _base = ui;
} }
private void DrawPriorityChangeButton(string iconString, bool up, int unavailableWhen) private void DrawPriorityChangeButton(string iconString, bool up, int unavailableWhen)
{ {
ImGui.PushFont( UiBuilder.IconFont ); ImGui.PushFont( UiBuilder.IconFont );
if( _index != unavailableWhen ) if( _index != unavailableWhen )
{ {
if( ImGui.Button( iconString, SelectorButtonSizes ) ) if( ImGui.Button( iconString, SelectorButtonSizes ) )
{ {
SetSelection(_index); SetSelection(_index);
_base._plugin.ModManager.ChangeModPriority( _mod, up ); _base._plugin.ModManager.ChangeModPriority( _mod, up );
_index += up ? 1 : -1; _index += up ? 1 : -1;
@ -60,18 +60,18 @@ namespace Penumbra.UI
ImGui.PopStyleVar(); ImGui.PopStyleVar();
} }
ImGui.PopFont(); ImGui.PopFont();
if( ImGui.IsItemHovered() ) if( ImGui.IsItemHovered() )
{ {
ImGui.SetTooltip( ImGui.SetTooltip(
_base._plugin.Configuration.InvertModListOrder ^ up ? TooltipMoveDown : TooltipMoveUp _base._plugin.Configuration.InvertModListOrder ^ up ? TooltipMoveDown : TooltipMoveUp
); );
} }
} }
private void DrawModTrashButton() private void DrawModTrashButton()
{ {
ImGui.PushFont( UiBuilder.IconFont ); ImGui.PushFont( UiBuilder.IconFont );
if( ImGui.Button( FontAwesomeIcon.Trash.ToIconString(), SelectorButtonSizes ) ) if( ImGui.Button( FontAwesomeIcon.Trash.ToIconString(), SelectorButtonSizes ) )
@ -79,48 +79,48 @@ namespace Penumbra.UI
_deleteIndex = _index; _deleteIndex = _index;
} }
ImGui.PopFont(); ImGui.PopFont();
if( ImGui.IsItemHovered() ) if( ImGui.IsItemHovered() )
ImGui.SetTooltip( TooltipDelete ); ImGui.SetTooltip( TooltipDelete );
} }
private void DrawModAddButton() private void DrawModAddButton()
{ {
ImGui.PushFont( UiBuilder.IconFont ); ImGui.PushFont( UiBuilder.IconFont );
if( ImGui.Button( FontAwesomeIcon.Plus.ToIconString(), SelectorButtonSizes ) ) if( ImGui.Button( FontAwesomeIcon.Plus.ToIconString(), SelectorButtonSizes ) )
{ {
// Do nothing. YEAH. #TODO. // Do nothing. YEAH. #TODO.
} }
ImGui.PopFont(); ImGui.PopFont();
if( ImGui.IsItemHovered() ) if( ImGui.IsItemHovered() )
ImGui.SetTooltip( TooltipAdd ); ImGui.SetTooltip( TooltipAdd );
} }
private void DrawModsSelectorButtons() private void DrawModsSelectorButtons()
{ {
// Selector controls // Selector controls
ImGui.PushStyleVar( ImGuiStyleVar.WindowPadding, ZeroVector ); ImGui.PushStyleVar( ImGuiStyleVar.WindowPadding, ZeroVector );
ImGui.PushStyleVar( ImGuiStyleVar.FrameRounding, 0 ); ImGui.PushStyleVar( ImGuiStyleVar.FrameRounding, 0 );
DrawPriorityChangeButton(ArrowUpString, false, 0); DrawPriorityChangeButton(ArrowUpString, false, 0);
ImGui.SameLine(); ImGui.SameLine();
DrawPriorityChangeButton(ArrowDownString, true, Mods?.ModSettings.Count - 1 ?? 0); DrawPriorityChangeButton(ArrowDownString, true, Mods?.ModSettings.Count - 1 ?? 0);
ImGui.SameLine(); ImGui.SameLine();
DrawModTrashButton(); DrawModTrashButton();
ImGui.SameLine(); ImGui.SameLine();
DrawModAddButton(); DrawModAddButton();
ImGui.PopStyleVar( 3 ); ImGui.PopStyleVar( 3 );
} }
void DrawDeleteModal() void DrawDeleteModal()
{ {
if( _deleteIndex == null ) if( _deleteIndex == null )
return; return;
ImGui.OpenPopup( DialogDeleteMod ); ImGui.OpenPopup( DialogDeleteMod );
@ -131,7 +131,7 @@ namespace Penumbra.UI
if( _mod?.Mod == null ) if( _mod?.Mod == null )
{ {
ImGui.CloseCurrentPopup(); ImGui.CloseCurrentPopup();
ImGui.EndPopup(); ImGui.EndPopup();
return; return;
} }
@ -143,7 +143,7 @@ namespace Penumbra.UI
{ {
ImGui.CloseCurrentPopup(); ImGui.CloseCurrentPopup();
_base._plugin.ModManager.DeleteMod( _mod.Mod ); _base._plugin.ModManager.DeleteMod( _mod.Mod );
ClearSelection(); ClearSelection();
_base.ReloadMods(); _base.ReloadMods();
} }
@ -156,14 +156,14 @@ namespace Penumbra.UI
} }
ImGui.EndPopup(); ImGui.EndPopup();
} }
public void Draw() public void Draw()
{ {
if (Mods == null) if (Mods == null)
return; return;
// Selector pane // Selector pane
ImGui.BeginGroup(); ImGui.BeginGroup();
ImGui.PushStyleVar( ImGuiStyleVar.ItemSpacing, ZeroVector ); ImGui.PushStyleVar( ImGuiStyleVar.ItemSpacing, ZeroVector );
@ -203,37 +203,37 @@ namespace Penumbra.UI
SetSelection(modIndex, settings); SetSelection(modIndex, settings);
} }
ImGui.EndChild(); ImGui.EndChild();
DrawModsSelectorButtons(); DrawModsSelectorButtons();
ImGui.EndGroup(); ImGui.EndGroup();
DrawDeleteModal(); DrawDeleteModal();
} }
public ModInfo Mod() => _mod; public ModInfo Mod() => _mod;
private void SetSelection(int idx, ModInfo info) private void SetSelection(int idx, ModInfo info)
{ {
_mod = info; _mod = info;
if (idx != _index) if (idx != _index)
_base._menu._installedTab._modPanel._details.ResetState(); _base._menu._installedTab._modPanel._details.ResetState();
_index = idx; _index = idx;
_deleteIndex = null; _deleteIndex = null;
} }
public void SetSelection(int idx) public void SetSelection(int idx)
{ {
if (idx >= (Mods?.ModSettings?.Count ?? 0)) if (idx >= (Mods?.ModSettings?.Count ?? 0))
idx = -1; idx = -1;
if (idx < 0) if (idx < 0)
SetSelection(0, null); SetSelection(0, null);
else else
SetSelection(idx, Mods.ModSettings[idx]); SetSelection(idx, Mods.ModSettings[idx]);
} }
public void ClearSelection() => SetSelection(-1); public void ClearSelection() => SetSelection(-1);
public void SelectModByName( string name ) public void SelectModByName( string name )
{ {
for( var modIndex = 0; modIndex < Mods.ModSettings.Count; modIndex++ ) for( var modIndex = 0; modIndex < Mods.ModSettings.Count; modIndex++ )
@ -242,39 +242,39 @@ namespace Penumbra.UI
if( mod.Mod.Meta.Name != name ) if( mod.Mod.Meta.Name != name )
continue; continue;
SetSelection(modIndex, mod); SetSelection(modIndex, mod);
return; return;
} }
} }
private string GetCurrentModMetaFile() private string GetCurrentModMetaFile()
{ {
if( _mod == null ) if( _mod == null )
return ""; return "";
return Path.Combine( _mod.Mod.ModBasePath.FullName, "meta.json" ); return Path.Combine( _mod.Mod.ModBasePath.FullName, "meta.json" );
} }
public void ReloadCurrentMod() public void ReloadCurrentMod()
{ {
var metaPath = GetCurrentModMetaFile(); var metaPath = GetCurrentModMetaFile();
if (metaPath.Length > 0 && File.Exists(metaPath)) if (metaPath.Length > 0 && File.Exists(metaPath))
{ {
_mod.Mod.Meta = ModMeta.LoadFromFile(metaPath) ?? _mod.Mod.Meta; _mod.Mod.Meta = ModMeta.LoadFromFile(metaPath) ?? _mod.Mod.Meta;
_base._menu._installedTab._modPanel._details.ResetState(); _base._menu._installedTab._modPanel._details.ResetState();
} }
_mod.Mod.RefreshModFiles(); _mod.Mod.RefreshModFiles();
_base._plugin.ModManager.CalculateEffectiveFileList(); _base._plugin.ModManager.CalculateEffectiveFileList();
} }
public string SaveCurrentMod() public string SaveCurrentMod()
{ {
var metaPath = GetCurrentModMetaFile(); var metaPath = GetCurrentModMetaFile();
if (metaPath.Length > 0) if (metaPath.Length > 0)
File.WriteAllText( metaPath, JsonConvert.SerializeObject( _mod.Mod.Meta, Formatting.Indented ) ); File.WriteAllText( metaPath, JsonConvert.SerializeObject( _mod.Mod.Meta, Formatting.Indented ) );
_base._menu._installedTab._modPanel._details.ResetState(); _base._menu._installedTab._modPanel._details.ResetState();
return metaPath; return metaPath;
} }
} }
} }
} }

View file

@ -1,113 +1,113 @@
using System.Diagnostics; using System.Diagnostics;
using ImGuiNET; using ImGuiNET;
namespace Penumbra.UI namespace Penumbra.UI
{ {
public partial class SettingsInterface public partial class SettingsInterface
{ {
private class TabSettings private class TabSettings
{ {
private const string LabelTab = "Settings"; private const string LabelTab = "Settings";
private const string LabelRootFolder = "Root Folder"; private const string LabelRootFolder = "Root Folder";
private const string LabelRediscoverButton = "Rediscover Mods"; private const string LabelRediscoverButton = "Rediscover Mods";
private const string LabelOpenFolder = "Open Mods Folder"; private const string LabelOpenFolder = "Open Mods Folder";
private const string LabelEnabled = "Enable Mods"; private const string LabelEnabled = "Enable Mods";
private const string LabelInvertModOrder = "Invert mod load order (mods are loaded bottom up)"; private const string LabelInvertModOrder = "Invert mod load order (mods are loaded bottom up)";
private const string LabelShowAdvanced = "Show Advanced Settings"; private const string LabelShowAdvanced = "Show Advanced Settings";
private const string LabelLogLoadedFiles = "Log all loaded files"; private const string LabelLogLoadedFiles = "Log all loaded files";
private const string LabelDisableNotifications = "Disable filesystem change notifications"; private const string LabelDisableNotifications = "Disable filesystem change notifications";
private const string LabelEnableHttpApi = "Enable HTTP API"; private const string LabelEnableHttpApi = "Enable HTTP API";
private const string LabelReloadResource = "Reload Player Resource"; private const string LabelReloadResource = "Reload Player Resource";
private readonly SettingsInterface _base; private readonly SettingsInterface _base;
private readonly Configuration _config; private readonly Configuration _config;
private bool _configChanged; private bool _configChanged;
public TabSettings(SettingsInterface ui) public TabSettings(SettingsInterface ui)
{ {
_base = ui; _base = ui;
_config = _base._plugin.Configuration; _config = _base._plugin.Configuration;
_configChanged = false; _configChanged = false;
} }
private void DrawRootFolder() private void DrawRootFolder()
{ {
var basePath = _config.CurrentCollection; var basePath = _config.CurrentCollection;
if( ImGui.InputText( LabelRootFolder, ref basePath, 255 ) && _config.CurrentCollection != basePath ) if( ImGui.InputText( LabelRootFolder, ref basePath, 255 ) && _config.CurrentCollection != basePath )
{ {
_config.CurrentCollection = basePath; _config.CurrentCollection = basePath;
_configChanged = true; _configChanged = true;
} }
} }
private void DrawRediscoverButton() private void DrawRediscoverButton()
{ {
if( ImGui.Button( LabelRediscoverButton ) ) if( ImGui.Button( LabelRediscoverButton ) )
{ {
_base.ReloadMods(); _base.ReloadMods();
_base._menu._installedTab._selector.ClearSelection(); _base._menu._installedTab._selector.ClearSelection();
} }
} }
private void DrawOpenModsButton() private void DrawOpenModsButton()
{ {
if( ImGui.Button( LabelOpenFolder ) ) if( ImGui.Button( LabelOpenFolder ) )
{ {
Process.Start( _config.CurrentCollection ); Process.Start( _config.CurrentCollection );
} }
} }
private void DrawEnabledBox() private void DrawEnabledBox()
{ {
var enabled = _config.IsEnabled; var enabled = _config.IsEnabled;
if( ImGui.Checkbox( LabelEnabled, ref enabled ) ) if( ImGui.Checkbox( LabelEnabled, ref enabled ) )
{ {
_config.IsEnabled = enabled; _config.IsEnabled = enabled;
_configChanged = true; _configChanged = true;
RefreshActors.RedrawAll(_base._plugin.PluginInterface.ClientState.Actors); RefreshActors.RedrawAll(_base._plugin.PluginInterface.ClientState.Actors);
} }
} }
private void DrawInvertModOrderBox() private void DrawInvertModOrderBox()
{ {
var invertOrder = _config.InvertModListOrder; var invertOrder = _config.InvertModListOrder;
if( ImGui.Checkbox( LabelInvertModOrder, ref invertOrder ) ) if( ImGui.Checkbox( LabelInvertModOrder, ref invertOrder ) )
{ {
_config.InvertModListOrder = invertOrder; _config.InvertModListOrder = invertOrder;
_base.ReloadMods(); _base.ReloadMods();
_configChanged = true; _configChanged = true;
} }
} }
private void DrawShowAdvancedBox() private void DrawShowAdvancedBox()
{ {
var showAdvanced = _config.ShowAdvanced; var showAdvanced = _config.ShowAdvanced;
if( ImGui.Checkbox( LabelShowAdvanced, ref showAdvanced ) ) if( ImGui.Checkbox( LabelShowAdvanced, ref showAdvanced ) )
{ {
_config.ShowAdvanced = showAdvanced; _config.ShowAdvanced = showAdvanced;
_configChanged = true; _configChanged = true;
_base._menu._effectiveTab.RebuildFileList(showAdvanced); _base._menu._effectiveTab.RebuildFileList(showAdvanced);
} }
} }
private void DrawLogLoadedFilesBox() private void DrawLogLoadedFilesBox()
{ {
if( _base._plugin.ResourceLoader != null ) if( _base._plugin.ResourceLoader != null )
ImGui.Checkbox( LabelLogLoadedFiles, ref _base._plugin.ResourceLoader.LogAllFiles ); ImGui.Checkbox( LabelLogLoadedFiles, ref _base._plugin.ResourceLoader.LogAllFiles );
} }
private void DrawDisableNotificationsBox() private void DrawDisableNotificationsBox()
{ {
var fswatch = _config.DisableFileSystemNotifications; var fswatch = _config.DisableFileSystemNotifications;
if( ImGui.Checkbox( LabelDisableNotifications, ref fswatch ) ) if( ImGui.Checkbox( LabelDisableNotifications, ref fswatch ) )
{ {
_config.DisableFileSystemNotifications = fswatch; _config.DisableFileSystemNotifications = fswatch;
_configChanged = true; _configChanged = true;
} }
} }
private void DrawEnableHttpApiBox() private void DrawEnableHttpApiBox()
{ {
var http = _config.EnableHttpApi; var http = _config.EnableHttpApi;
if( ImGui.Checkbox( LabelEnableHttpApi, ref http ) ) if( ImGui.Checkbox( LabelEnableHttpApi, ref http ) )
{ {
@ -118,57 +118,57 @@ namespace Penumbra.UI
_config.EnableHttpApi = http; _config.EnableHttpApi = http;
_configChanged = true; _configChanged = true;
} }
} }
private void DrawReloadResourceButton() private void DrawReloadResourceButton()
{ {
if( ImGui.Button( LabelReloadResource ) ) if( ImGui.Button( LabelReloadResource ) )
{ {
_base._plugin.GameUtils.ReloadPlayerResources(); _base._plugin.GameUtils.ReloadPlayerResources();
} }
} }
private void DrawAdvancedSettings() private void DrawAdvancedSettings()
{ {
DrawLogLoadedFilesBox(); DrawLogLoadedFilesBox();
DrawDisableNotificationsBox(); DrawDisableNotificationsBox();
DrawEnableHttpApiBox(); DrawEnableHttpApiBox();
DrawReloadResourceButton(); DrawReloadResourceButton();
} }
public void Draw() public void Draw()
{ {
var ret = ImGui.BeginTabItem( LabelTab ); var ret = ImGui.BeginTabItem( LabelTab );
if( !ret ) if( !ret )
return; return;
DrawRootFolder(); DrawRootFolder();
DrawRediscoverButton(); DrawRediscoverButton();
ImGui.SameLine(); ImGui.SameLine();
DrawOpenModsButton(); DrawOpenModsButton();
ImGuiCustom.VerticalDistance(DefaultVerticalSpace); ImGuiCustom.VerticalDistance(DefaultVerticalSpace);
DrawEnabledBox(); DrawEnabledBox();
ImGuiCustom.VerticalDistance(DefaultVerticalSpace); ImGuiCustom.VerticalDistance(DefaultVerticalSpace);
DrawInvertModOrderBox(); DrawInvertModOrderBox();
ImGuiCustom.VerticalDistance(DefaultVerticalSpace); ImGuiCustom.VerticalDistance(DefaultVerticalSpace);
DrawShowAdvancedBox(); DrawShowAdvancedBox();
if( _config.ShowAdvanced ) if( _config.ShowAdvanced )
DrawAdvancedSettings(); DrawAdvancedSettings();
if( _configChanged ) if( _configChanged )
{ {
_config.Save(); _config.Save();
_configChanged = false; _configChanged = false;
} }
ImGui.EndTabItem(); ImGui.EndTabItem();
} }
} }
} }
} }

View file

@ -1,4 +1,4 @@
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace Penumbra.Util namespace Penumbra.Util

View file

@ -1,55 +1,55 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
public class SingleOrArrayConverter<T> : JsonConverter public class SingleOrArrayConverter<T> : JsonConverter
{ {
public override bool CanConvert( Type objectType ) public override bool CanConvert( Type objectType )
{ {
return (objectType == typeof(HashSet<T>)); return (objectType == typeof(HashSet<T>));
} }
public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer ) public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer )
{ {
var token = JToken.Load(reader); var token = JToken.Load(reader);
if (token.Type == JTokenType.Array) if (token.Type == JTokenType.Array)
{ {
return token.ToObject<HashSet<T>>(); return token.ToObject<HashSet<T>>();
} }
return new HashSet<T>{ token.ToObject<T>() }; return new HashSet<T>{ token.ToObject<T>() };
} }
public override bool CanWrite => false; public override bool CanWrite => false;
public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer ) public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer )
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }
public class DictSingleOrArrayConverter<T,U> : JsonConverter public class DictSingleOrArrayConverter<T,U> : JsonConverter
{ {
public override bool CanConvert( Type objectType ) public override bool CanConvert( Type objectType )
{ {
return (objectType == typeof(Dictionary<T, HashSet<U>>)); return (objectType == typeof(Dictionary<T, HashSet<U>>));
} }
public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer ) public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer )
{ {
var token = JToken.Load(reader); var token = JToken.Load(reader);
if (token.Type == JTokenType.Array) if (token.Type == JTokenType.Array)
{ {
return token.ToObject<HashSet<T>>(); return token.ToObject<HashSet<T>>();
} }
return new HashSet<T>{ token.ToObject<T>() }; return new HashSet<T>{ token.ToObject<T>() };
} }
public override bool CanWrite => false; public override bool CanWrite => false;
public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer ) public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer )
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }

View file

@ -1,18 +1,18 @@
using System.IO; using System.IO;
namespace Penumbra namespace Penumbra
{ {
public static class StringPathExtensions public static class StringPathExtensions
{ {
private static char[] _invalid = Path.GetInvalidFileNameChars(); private static char[] _invalid = Path.GetInvalidFileNameChars();
public static string ReplaceInvalidPathSymbols( this string s, string replacement = "_" ) public static string ReplaceInvalidPathSymbols( this string s, string replacement = "_" )
{ {
return string.Join( replacement, s.Split( _invalid ) ); return string.Join( replacement, s.Split( _invalid ) );
} }
public static string RemoveInvalidPathSymbols( this string s ) public static string RemoveInvalidPathSymbols( this string s )
{ {
return string.Concat( s.Split( _invalid ) ); return string.Concat( s.Split( _invalid ) );
} }
} }
} }