mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2026-02-24 05:31:50 +01:00
Merge branch 'luna' into luna-advdye-extensions
# Conflicts: # Glamourer/Designs/DesignEditor.cs # Glamourer/Gui/Tabs/SettingsTab/SettingsTab.cs # Glamourer/State/StateEditor.cs
This commit is contained in:
commit
93ac50cbdc
171 changed files with 3709 additions and 3038 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit 51b3c72e91816af0002dd543d64944e777b246ba
|
||||
Subproject commit 941dc7e1da694127a4405f4888ae162133131268
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 18
|
||||
VisualStudioVersion = 18.3.11415.281 d18.3
|
||||
VisualStudioVersion = 18.3.11415.281
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{383AEE76-D423-431C-893A-7AB3DEA13630}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
|
|
@ -20,8 +20,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Penumbra.GameData", "Penumb
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Penumbra.String", "Penumbra.String\Penumbra.String.csproj", "{AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OtterGui", "OtterGui\OtterGui.csproj", "{EF233CE2-F243-449E-BE05-72B9D110E419}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Glamourer.Api", "Glamourer.Api\Glamourer.Api.csproj", "{9B46691B-FAB2-4CC3-9B89-C8B91A590F47}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Luna", "Luna\Luna\Luna.csproj", "{DEA936D7-1386-55A1-7451-E0C240F56E9D}"
|
||||
|
|
@ -68,14 +66,6 @@ Global
|
|||
{AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}.Release|Any CPU.Build.0 = Release|x64
|
||||
{AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}.Release|x64.ActiveCfg = Release|x64
|
||||
{AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}.Release|x64.Build.0 = Release|x64
|
||||
{EF233CE2-F243-449E-BE05-72B9D110E419}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{EF233CE2-F243-449E-BE05-72B9D110E419}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{EF233CE2-F243-449E-BE05-72B9D110E419}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{EF233CE2-F243-449E-BE05-72B9D110E419}.Debug|x64.Build.0 = Debug|x64
|
||||
{EF233CE2-F243-449E-BE05-72B9D110E419}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{EF233CE2-F243-449E-BE05-72B9D110E419}.Release|Any CPU.Build.0 = Release|x64
|
||||
{EF233CE2-F243-449E-BE05-72B9D110E419}.Release|x64.ActiveCfg = Release|x64
|
||||
{EF233CE2-F243-449E-BE05-72B9D110E419}.Release|x64.Build.0 = Release|x64
|
||||
{9B46691B-FAB2-4CC3-9B89-C8B91A590F47}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{9B46691B-FAB2-4CC3-9B89-C8B91A590F47}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{9B46691B-FAB2-4CC3-9B89-C8B91A590F47}.Debug|x64.ActiveCfg = Debug|x64
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
using Glamourer.Designs;
|
||||
using Glamourer.State;
|
||||
using Luna;
|
||||
using OtterGui.Extensions;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Interop;
|
||||
|
|
@ -73,26 +72,26 @@ public class ApiHelpers(ActorObjectManager objects, StateManager stateManager, A
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
internal static void Lock(ActorState state, uint key, ApplyFlag flags)
|
||||
{
|
||||
if ((flags & ApplyFlag.Lock) != 0 && key != 0)
|
||||
if ((flags & ApplyFlag.Lock) is not 0 && key is not 0)
|
||||
state.Lock(key);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
internal IEnumerable<ActorState> FindStates(string objectName)
|
||||
{
|
||||
if (objectName.Length == 0 || !ByteString.FromString(objectName, out var byteString))
|
||||
if (objectName.Length is 0 || !ByteString.FromString(objectName, out var byteString))
|
||||
return [];
|
||||
|
||||
return stateManager.Values.Where(state => state.Identifier.Type is IdentifierType.Player && state.Identifier.PlayerName == byteString)
|
||||
.Concat(ArrayExtensions.SelectWhere(objects
|
||||
.Where(kvp => kvp.Key is { IsValid: true, Type: IdentifierType.Player } && kvp.Key.PlayerName == byteString), kvp =>
|
||||
{
|
||||
if (stateManager.ContainsKey(kvp.Key))
|
||||
return (false, null);
|
||||
.Concat(objects
|
||||
.Where(kvp => kvp.Key is { IsValid: true, Type: IdentifierType.Player } && kvp.Key.PlayerName == byteString).SelectWhere(kvp =>
|
||||
{
|
||||
if (stateManager.ContainsKey(kvp.Key))
|
||||
return (false, null);
|
||||
|
||||
var ret = stateManager.GetOrCreate(kvp.Key, kvp.Value.Objects[0], out var state);
|
||||
return (ret, state);
|
||||
}));
|
||||
var ret = stateManager.GetOrCreate(kvp.Key, kvp.Value.Objects[0], out var state);
|
||||
return (ret, state);
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
using Glamourer.Api.Enums;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.State;
|
||||
using ImSharp;
|
||||
using Luna;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
|
|
@ -12,21 +11,19 @@ public class DesignsApi(
|
|||
ApiHelpers helpers,
|
||||
DesignManager designs,
|
||||
StateManager stateManager,
|
||||
DesignFileSystem fileSystem,
|
||||
DesignColors color,
|
||||
DesignConverter converter)
|
||||
: IGlamourerApiDesigns, IApiService
|
||||
{
|
||||
public Dictionary<Guid, string> GetDesignList()
|
||||
=> designs.Designs.ToDictionary(d => d.Identifier, d => d.Name.Text);
|
||||
=> designs.Designs.ToDictionary(d => d.Identifier, d => d.Name);
|
||||
|
||||
public Dictionary<Guid, (string DisplayName, string FullPath, uint DisplayColor, bool ShownInQdb)> GetDesignListExtended()
|
||||
=> fileSystem.ToDictionary(kvp => kvp.Key.Identifier,
|
||||
kvp => (kvp.Key.Name.Text, kvp.Value.FullName(), color.GetColor(kvp.Key).Color, kvp.Key.QuickDesign));
|
||||
=> designs.Designs.ToDictionary(d => d.Identifier, d => (d.DisplayName, d.Path.CurrentPath, color.GetColor(d).Color, d.QuickDesign));
|
||||
|
||||
public (string DisplayName, string FullPath, uint DisplayColor, bool ShowInQdb) GetExtendedDesignData(Guid designId)
|
||||
=> designs.Designs.ByIdentifier(designId) is { } d
|
||||
? (d.Name.Text, fileSystem.TryGetValue(d, out var leaf) ? leaf.FullName() : d.Name.Text, color.GetColor(d).Color, d.QuickDesign)
|
||||
? (d.Name, d.Path.CurrentPath, color.GetColor(d).Color, d.QuickDesign)
|
||||
: (string.Empty, string.Empty, 0, false);
|
||||
|
||||
public GlamourerApiEc ApplyDesign(Guid designId, int objectIndex, uint key, ApplyFlag flags)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Glamourer.Api.Api;
|
||||
using Glamourer.Config;
|
||||
using Luna;
|
||||
|
||||
namespace Glamourer.Api;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
using Glamourer.Api.Enums;
|
||||
using Glamourer.Automation;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Designs.History;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.State;
|
||||
using Luna;
|
||||
|
|
@ -143,10 +142,10 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
|
|||
|
||||
public GlamourerApiEc ReapplyStateName(string playerName, uint key, ApplyFlag flags)
|
||||
{
|
||||
var args = ApiHelpers.Args("Name", playerName, "Key", key, "Flags", flags);
|
||||
var args = ApiHelpers.Args("Name", playerName, "Key", key, "Flags", flags);
|
||||
var states = _helpers.FindExistingStates(playerName);
|
||||
|
||||
var any = false;
|
||||
var any = false;
|
||||
var anyReapplied = false;
|
||||
foreach (var state in states)
|
||||
{
|
||||
|
|
@ -154,7 +153,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
|
|||
if (!state.CanUnlock(key))
|
||||
continue;
|
||||
|
||||
anyReapplied = true;
|
||||
anyReapplied = true;
|
||||
anyReapplied |= Reapply(state, key, flags) is GlamourerApiEc.Success;
|
||||
}
|
||||
|
||||
|
|
@ -227,13 +226,14 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
|
|||
public GlamourerApiEc CanUnlock(int objectIndex, uint key, out bool isLocked, out bool canUnlock)
|
||||
{
|
||||
var args = ApiHelpers.Args("Index", objectIndex, "Key", key);
|
||||
isLocked = false;
|
||||
isLocked = false;
|
||||
canUnlock = true;
|
||||
if (_helpers.FindExistingState(objectIndex, out var state) is not GlamourerApiEc.Success)
|
||||
return ApiHelpers.Return(GlamourerApiEc.ActorNotFound, args);
|
||||
if (state is null)
|
||||
return ApiHelpers.Return(GlamourerApiEc.Success, args);
|
||||
isLocked = state.IsLocked;
|
||||
return ApiHelpers.Return(GlamourerApiEc.Success, args);
|
||||
|
||||
isLocked = state.IsLocked;
|
||||
canUnlock = state.CanUnlock(key);
|
||||
return ApiHelpers.Return(GlamourerApiEc.Success, args);
|
||||
}
|
||||
|
|
@ -423,29 +423,31 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
|
|||
};
|
||||
}
|
||||
|
||||
private void OnAutoRedrawChange(bool autoReload)
|
||||
private void OnAutoRedrawChange(in bool autoReload)
|
||||
=> AutoReloadGearChanged?.Invoke(autoReload);
|
||||
|
||||
private void OnStateChanged(StateChangeType type, StateSource _2, ActorState _3, ActorData actors, ITransaction? _5)
|
||||
private void OnStateChanged(in StateChanged.Arguments arguments)
|
||||
{
|
||||
Glamourer.Log.Excessive($"[OnStateChanged] State Changed with Type {type} [Affecting {actors.ToLazyString("nothing")}.]");
|
||||
if (StateChanged != null)
|
||||
foreach (var actor in actors.Objects)
|
||||
Glamourer.Log.Excessive(
|
||||
$"[OnStateChanged] State Changed with Type {arguments.Type} [Affecting {arguments.Actors.ToLazyString("nothing")}.]");
|
||||
if (StateChanged is not null)
|
||||
foreach (var actor in arguments.Actors.Objects)
|
||||
StateChanged.Invoke(actor.Address);
|
||||
|
||||
if (StateChangedWithType != null)
|
||||
foreach (var actor in actors.Objects)
|
||||
StateChangedWithType.Invoke(actor.Address, type);
|
||||
if (StateChangedWithType is not null)
|
||||
foreach (var actor in arguments.Actors.Objects)
|
||||
StateChangedWithType.Invoke(actor.Address, arguments.Type);
|
||||
}
|
||||
|
||||
private void OnStateFinalized(StateFinalizationType type, ActorData actors)
|
||||
private void OnStateFinalized(in StateFinalized.Arguments arguments)
|
||||
{
|
||||
Glamourer.Log.Verbose($"[OnStateUpdated] State Updated with Type {type}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||
if (StateFinalized != null)
|
||||
foreach (var actor in actors.Objects)
|
||||
StateFinalized.Invoke(actor.Address, type);
|
||||
Glamourer.Log.Verbose(
|
||||
$"[OnStateUpdated] State Updated with Type {arguments.Type}. [Affecting {arguments.Actors.ToLazyString("nothing")}.]");
|
||||
if (StateFinalized is not null)
|
||||
foreach (var actor in arguments.Actors.Objects)
|
||||
StateFinalized.Invoke(actor.Address, arguments.Type);
|
||||
}
|
||||
|
||||
private void OnGPoseChange(bool gPose)
|
||||
private void OnGPoseChange(in bool gPose)
|
||||
=> GPoseChanged?.Invoke(gPose);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
|
||||
using Glamourer.Config;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Designs.Links;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.Interop.Material;
|
||||
using Glamourer.State;
|
||||
using Luna;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.GameData.DataContainers;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
|
@ -14,7 +16,7 @@ using Penumbra.GameData.Structs;
|
|||
|
||||
namespace Glamourer.Automation;
|
||||
|
||||
public sealed class AutoDesignApplier : IDisposable
|
||||
public sealed class AutoDesignApplier : IDisposable, IRequiredService
|
||||
{
|
||||
private readonly Configuration _config;
|
||||
private readonly AutoDesignManager _manager;
|
||||
|
|
@ -71,38 +73,38 @@ public sealed class AutoDesignApplier : IDisposable
|
|||
_jobs.JobChanged -= OnJobChange;
|
||||
}
|
||||
|
||||
private void OnWeaponLoading(Actor actor, EquipSlot slot, ref CharacterWeapon weapon)
|
||||
private void OnWeaponLoading(in WeaponLoading.Arguments arguments)
|
||||
{
|
||||
if (!_jobChangeState.HasState || !_config.EnableAutoDesigns)
|
||||
return;
|
||||
|
||||
var id = actor.GetIdentifier(_actors);
|
||||
var id = arguments.Actor.GetIdentifier(_actors);
|
||||
if (id == _jobChangeState.Identifier)
|
||||
{
|
||||
var state = _jobChangeState.State!;
|
||||
var current = state.BaseData.Item(slot);
|
||||
switch (slot)
|
||||
var current = state.BaseData.Item(arguments.Slot);
|
||||
switch (arguments.Slot)
|
||||
{
|
||||
case EquipSlot.MainHand:
|
||||
{
|
||||
if (_jobChangeState.TryGetValue(current.Type, actor.Job, false, out var data))
|
||||
if (_jobChangeState.TryGetValue(current.Type, arguments.Actor.Job, false, out var data))
|
||||
{
|
||||
Glamourer.Log.Verbose(
|
||||
$"Changing Mainhand from {state.ModelData.Weapon(EquipSlot.MainHand)} | {state.BaseData.Weapon(EquipSlot.MainHand)} to {data.Item1} for 0x{actor.Address:X}.");
|
||||
$"Changing Mainhand from {state.ModelData.Weapon(EquipSlot.MainHand)} | {state.BaseData.Weapon(EquipSlot.MainHand)} to {data.Item1} for 0x{arguments.Actor.Address:X}.");
|
||||
_state.ChangeItem(state, EquipSlot.MainHand, data.Item1, new ApplySettings(Source: data.Item2));
|
||||
weapon = state.ModelData.Weapon(EquipSlot.MainHand);
|
||||
arguments.Weapon = state.ModelData.Weapon(EquipSlot.MainHand);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case EquipSlot.OffHand when current.Type == state.BaseData.MainhandType.Offhand():
|
||||
{
|
||||
if (_jobChangeState.TryGetValue(current.Type, actor.Job, false, out var data))
|
||||
if (_jobChangeState.TryGetValue(current.Type, arguments.Actor.Job, false, out var data))
|
||||
{
|
||||
Glamourer.Log.Verbose(
|
||||
$"Changing Offhand from {state.ModelData.Weapon(EquipSlot.OffHand)} | {state.BaseData.Weapon(EquipSlot.OffHand)} to {data.Item1} for 0x{actor.Address:X}.");
|
||||
$"Changing Offhand from {state.ModelData.Weapon(EquipSlot.OffHand)} | {state.BaseData.Weapon(EquipSlot.OffHand)} to {data.Item1} for 0x{arguments.Actor.Address:X}.");
|
||||
_state.ChangeItem(state, EquipSlot.OffHand, data.Item1, new ApplySettings(Source: data.Item2));
|
||||
weapon = state.ModelData.Weapon(EquipSlot.OffHand);
|
||||
arguments.Weapon = state.ModelData.Weapon(EquipSlot.OffHand);
|
||||
}
|
||||
|
||||
_jobChangeState.Reset();
|
||||
|
|
@ -116,22 +118,22 @@ public sealed class AutoDesignApplier : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
private void OnAutomationChange(AutomationChanged.Type type, AutoDesignSet? set, object? bonusData)
|
||||
private void OnAutomationChange(in AutomationChanged.Arguments arguments)
|
||||
{
|
||||
if (!_config.EnableAutoDesigns || set == null)
|
||||
if (!_config.EnableAutoDesigns)
|
||||
return;
|
||||
|
||||
switch (type)
|
||||
switch (arguments.Type)
|
||||
{
|
||||
case AutomationChanged.Type.ToggleSet when !set.Enabled:
|
||||
case AutomationChanged.Type.DeletedDesign when set.Enabled:
|
||||
// The automation set was disabled or deleted, no other for those identifiers can be enabled, remove existing Fixed Locks.
|
||||
RemoveOld(set.Identifiers);
|
||||
// The automation set was disabled or deleted, no other for those identifiers can be enabled, remove existing Fixed Locks.
|
||||
case AutomationChanged.Type.ToggleSet when arguments.Set.Enabled:
|
||||
case AutomationChanged.Type.DeletedDesign when arguments.Set.Enabled:
|
||||
RemoveOld(arguments.Set.Identifiers);
|
||||
break;
|
||||
case AutomationChanged.Type.ChangeIdentifier when set.Enabled:
|
||||
case AutomationChanged.Type.ChangeIdentifier
|
||||
when arguments.As<AutomationChanged.ChangeIdentifierArguments>().Set is { Enabled: true } set:
|
||||
// Remove fixed state from the old identifiers assigned and the old enabled set, if any.
|
||||
var (oldIds, _, _) = ((ActorIdentifier[], ActorIdentifier, AutoDesignSet?))bonusData!;
|
||||
RemoveOld(oldIds);
|
||||
RemoveOld(arguments.As<AutomationChanged.ChangeIdentifierArguments>().OldIdentifiers);
|
||||
ApplyNew(set); // Does not need to disable oldSet because same identifiers.
|
||||
break;
|
||||
case AutomationChanged.Type.ToggleSet: // Does not need to disable old states because same identifiers.
|
||||
|
|
@ -142,7 +144,7 @@ public sealed class AutoDesignApplier : IDisposable
|
|||
case AutomationChanged.Type.ChangedConditions:
|
||||
case AutomationChanged.Type.ChangedType:
|
||||
case AutomationChanged.Type.ChangedData:
|
||||
ApplyNew(set);
|
||||
ApplyNew(arguments.Set);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -302,7 +304,7 @@ public sealed class AutoDesignApplier : IDisposable
|
|||
mergedDesign.ResetTemporarySettings = true;
|
||||
}
|
||||
|
||||
_state.ApplyDesign(state, mergedDesign, new ApplySettings(0, StateSource.Fixed, respectManual, fromJobChange, false, false, false));
|
||||
_state.ApplyDesign(state, mergedDesign, new ApplySettings(0, StateSource.Fixed, respectManual, fromJobChange));
|
||||
forcedRedraw = mergedDesign.ForcedRedraw;
|
||||
}
|
||||
|
||||
|
|
@ -344,7 +346,7 @@ public sealed class AutoDesignApplier : IDisposable
|
|||
|
||||
internal static int NewGearsetId = -1;
|
||||
|
||||
private void OnEquippedGearset(string name, int id, int prior, byte _, byte job)
|
||||
private void OnEquippedGearset(in EquippedGearset.Arguments arguments)
|
||||
{
|
||||
if (!_config.EnableAutoDesigns)
|
||||
return;
|
||||
|
|
@ -356,9 +358,9 @@ public sealed class AutoDesignApplier : IDisposable
|
|||
if (!GetPlayerSet(player, out var set) || !_state.TryGetValue(player, out var state))
|
||||
return;
|
||||
|
||||
var respectManual = prior == id;
|
||||
NewGearsetId = id;
|
||||
Reduce(data.Objects[0], state, set, respectManual, job != state.LastJob, prior == id, out var forcedRedraw);
|
||||
var respectManual = arguments.PriorId == arguments.Id;
|
||||
NewGearsetId = arguments.Id;
|
||||
Reduce(data.Objects[0], state, set, respectManual, arguments.JobId != state.LastJob, arguments.PriorId == arguments.Id, out var forcedRedraw);
|
||||
NewGearsetId = -1;
|
||||
foreach (var actor in data.Objects)
|
||||
_state.ReapplyState(actor, forcedRedraw, StateSource.Fixed);
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
using Dalamud.Game.ClientState.Objects.Enums;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Designs.History;
|
||||
using Glamourer.Designs.Special;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.Services;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using OtterGui.Extensions;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
|
@ -16,7 +14,7 @@ using Luna;
|
|||
|
||||
namespace Glamourer.Automation;
|
||||
|
||||
public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDisposable
|
||||
public sealed class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDisposable, IService
|
||||
{
|
||||
public const int CurrentVersion = 1;
|
||||
|
||||
|
|
@ -77,7 +75,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
|||
_data.Add(newSet);
|
||||
Save();
|
||||
Glamourer.Log.Debug($"Created new design set for {newSet.Identifiers[0].Incognito(null)}.");
|
||||
_event.Invoke(AutomationChanged.Type.AddedSet, newSet, (_data.Count - 1, name));
|
||||
_event.Invoke(new AutomationChanged.AddedSetArguments(newSet, _data.Count - 1, name));
|
||||
}
|
||||
|
||||
public void DuplicateDesignSet(AutoDesignSet set)
|
||||
|
|
@ -102,7 +100,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
|||
Save();
|
||||
Glamourer.Log.Debug(
|
||||
$"Duplicated new design set for {newSet.Identifiers[0].Incognito(null)} with {newSet.Designs.Count} auto designs from existing set.");
|
||||
_event.Invoke(AutomationChanged.Type.AddedSet, newSet, (_data.Count - 1, name));
|
||||
_event.Invoke(new AutomationChanged.AddedSetArguments(newSet, _data.Count - 1, name));
|
||||
}
|
||||
|
||||
public void DeleteDesignSet(int whichSet)
|
||||
|
|
@ -121,12 +119,12 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
|||
_data.RemoveAt(whichSet);
|
||||
Save();
|
||||
Glamourer.Log.Debug($"Deleted design set {whichSet + 1}.");
|
||||
_event.Invoke(AutomationChanged.Type.DeletedSet, set, whichSet);
|
||||
_event.Invoke(new AutomationChanged.DeletedSetArguments(set, whichSet));
|
||||
}
|
||||
|
||||
public void Rename(int whichSet, string newName)
|
||||
{
|
||||
if (whichSet >= _data.Count || whichSet < 0 || newName.Length == 0)
|
||||
if (whichSet >= _data.Count || whichSet < 0 || newName.Length is 0)
|
||||
return;
|
||||
|
||||
var set = _data[whichSet];
|
||||
|
|
@ -137,7 +135,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
|||
set.Name = newName;
|
||||
Save();
|
||||
Glamourer.Log.Debug($"Renamed design set {whichSet + 1} from {old} to {newName}.");
|
||||
_event.Invoke(AutomationChanged.Type.RenamedSet, set, (old, newName));
|
||||
_event.Invoke(new AutomationChanged.RenamedSetArguments(set, old, newName));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -148,7 +146,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
|||
|
||||
Save();
|
||||
Glamourer.Log.Debug($"Moved design set {whichSet + 1} to position {toWhichSet + 1}.");
|
||||
_event.Invoke(AutomationChanged.Type.MovedSet, _data[toWhichSet], (whichSet, toWhichSet));
|
||||
_event.Invoke(new AutomationChanged.MovedSetArguments(_data[toWhichSet], whichSet, toWhichSet));
|
||||
}
|
||||
|
||||
public void ChangeIdentifier(int whichSet, ActorIdentifier to)
|
||||
|
|
@ -180,7 +178,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
|||
|
||||
Save();
|
||||
Glamourer.Log.Debug($"Changed Identifier of design set {whichSet + 1} from {old[0].Incognito(null)} to {to.Incognito(null)}.");
|
||||
_event.Invoke(AutomationChanged.Type.ChangeIdentifier, set, (old, to, oldEnabled));
|
||||
_event.Invoke(new AutomationChanged.ChangeIdentifierArguments(set, old, to, oldEnabled));
|
||||
}
|
||||
|
||||
public void SetState(int whichSet, bool value)
|
||||
|
|
@ -214,7 +212,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
|||
|
||||
Save();
|
||||
Glamourer.Log.Debug($"Changed enabled state of design set {whichSet + 1} to {value}.");
|
||||
_event.Invoke(AutomationChanged.Type.ToggleSet, set, oldEnabled);
|
||||
_event.Invoke(new AutomationChanged.ToggleSetArguments(set, oldEnabled));
|
||||
}
|
||||
|
||||
public void ChangeBaseState(int whichSet, AutoDesignSet.Base newBase)
|
||||
|
|
@ -230,7 +228,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
|||
set.BaseState = newBase;
|
||||
Save();
|
||||
Glamourer.Log.Debug($"Changed base state of set {whichSet + 1} from {old} to {newBase}.");
|
||||
_event.Invoke(AutomationChanged.Type.ChangedBase, set, (old, newBase));
|
||||
_event.Invoke(new AutomationChanged.ChangedBaseArguments(set, old, newBase));
|
||||
}
|
||||
|
||||
public void ChangeResetSettings(int whichSet, bool newValue)
|
||||
|
|
@ -246,12 +244,12 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
|||
set.ResetTemporarySettings = newValue;
|
||||
Save();
|
||||
Glamourer.Log.Debug($"Changed resetting of temporary settings of set {whichSet + 1} from {old} to {newValue}.");
|
||||
_event.Invoke(AutomationChanged.Type.ChangedTemporarySettingsReset, set, newValue);
|
||||
_event.Invoke(new AutomationChanged.ChangedTemporarySettingsResetArguments(set, newValue));
|
||||
}
|
||||
|
||||
public void AddDesign(AutoDesignSet set, IDesignStandIn design)
|
||||
{
|
||||
var newDesign = new AutoDesign()
|
||||
var newDesign = new AutoDesign
|
||||
{
|
||||
Design = design,
|
||||
Type = ApplicationType.All,
|
||||
|
|
@ -261,7 +259,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
|||
Save();
|
||||
Glamourer.Log.Debug(
|
||||
$"Added new associated design {design.ResolveName(true)} as design {set.Designs.Count} to design set.");
|
||||
_event.Invoke(AutomationChanged.Type.AddedDesign, set, set.Designs.Count - 1);
|
||||
_event.Invoke(new AutomationChanged.AddedDesignArguments(set, set.Designs.Count - 1));
|
||||
}
|
||||
|
||||
/// <remarks> Only used to move between sets. </remarks>
|
||||
|
|
@ -275,8 +273,8 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
|||
from.Designs.RemoveAt(idx);
|
||||
Save();
|
||||
Glamourer.Log.Debug($"Moved design {idx} from design set {from.Name} to design set {to.Name}.");
|
||||
_event.Invoke(AutomationChanged.Type.AddedDesign, to, to.Designs.Count - 1);
|
||||
_event.Invoke(AutomationChanged.Type.DeletedDesign, from, idx);
|
||||
_event.Invoke(new AutomationChanged.AddedDesignArguments(to, to.Designs.Count - 1));
|
||||
_event.Invoke(new AutomationChanged.DeletedDesignArguments(from, idx));
|
||||
}
|
||||
|
||||
public void DeleteDesign(AutoDesignSet set, int which)
|
||||
|
|
@ -287,7 +285,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
|||
set.Designs.RemoveAt(which);
|
||||
Save();
|
||||
Glamourer.Log.Debug($"Removed associated design {which + 1} from design set.");
|
||||
_event.Invoke(AutomationChanged.Type.DeletedDesign, set, which);
|
||||
_event.Invoke(new AutomationChanged.DeletedDesignArguments(set, which));
|
||||
}
|
||||
|
||||
public void MoveDesign(AutoDesignSet set, int from, int to)
|
||||
|
|
@ -297,7 +295,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
|||
|
||||
Save();
|
||||
Glamourer.Log.Debug($"Moved design {from + 1} to {to + 1} in design set.");
|
||||
_event.Invoke(AutomationChanged.Type.MovedDesign, set, (from, to));
|
||||
_event.Invoke(new AutomationChanged.MovedDesignArguments(set, from, to));
|
||||
}
|
||||
|
||||
public void ChangeDesign(AutoDesignSet set, int which, IDesignStandIn newDesign)
|
||||
|
|
@ -314,7 +312,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
|||
Save();
|
||||
Glamourer.Log.Debug(
|
||||
$"Changed linked design from {old.ResolveName(true)} to {newDesign.ResolveName(true)} for associated design {which + 1} in design set.");
|
||||
_event.Invoke(AutomationChanged.Type.ChangedDesign, set, (which, old, newDesign));
|
||||
_event.Invoke(new AutomationChanged.ChangedDesignArguments(set, which, old, newDesign));
|
||||
}
|
||||
|
||||
public void ChangeJobCondition(AutoDesignSet set, int which, JobGroup jobs)
|
||||
|
|
@ -331,7 +329,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
|||
design.Jobs = jobs;
|
||||
Save();
|
||||
Glamourer.Log.Debug($"Changed job condition from {old.Id} to {jobs.Id} for associated design {which + 1} in design set.");
|
||||
_event.Invoke(AutomationChanged.Type.ChangedConditions, set, (which, old, jobs));
|
||||
_event.Invoke(new AutomationChanged.ChangedConditionsArguments(set, which, old, jobs));
|
||||
}
|
||||
|
||||
public void ChangeGearsetCondition(AutoDesignSet set, int which, short index)
|
||||
|
|
@ -347,7 +345,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
|||
design.GearsetIndex = index;
|
||||
Save();
|
||||
Glamourer.Log.Debug($"Changed gearset condition from {old} to {index} for associated design {which + 1} in design set.");
|
||||
_event.Invoke(AutomationChanged.Type.ChangedConditions, set, (which, old, index));
|
||||
_event.Invoke(new AutomationChanged.ChangedConditionsArguments(set, which, default, default));
|
||||
}
|
||||
|
||||
public void ChangeApplicationType(AutoDesignSet set, int which, ApplicationType applicationType)
|
||||
|
|
@ -364,7 +362,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
|||
design.Type = applicationType;
|
||||
Save();
|
||||
Glamourer.Log.Debug($"Changed application type from {old} to {applicationType} for associated design {which + 1} in design set.");
|
||||
_event.Invoke(AutomationChanged.Type.ChangedType, set, (which, old, applicationType));
|
||||
_event.Invoke(new AutomationChanged.ChangedTypeArguments(set, which, old, applicationType));
|
||||
}
|
||||
|
||||
public void ChangeData(AutoDesignSet set, int which, object data)
|
||||
|
|
@ -378,10 +376,10 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
|||
|
||||
Save();
|
||||
Glamourer.Log.Debug($"Changed additional design data for associated design {which + 1} in design set.");
|
||||
_event.Invoke(AutomationChanged.Type.ChangedData, set, (which, data));
|
||||
_event.Invoke(new AutomationChanged.ChangedDataArguments(set, which, data));
|
||||
}
|
||||
|
||||
public string ToFilename(FilenameService fileNames)
|
||||
public string ToFilePath(FilenameService fileNames)
|
||||
=> fileNames.AutomationFile;
|
||||
|
||||
public void Save(StreamWriter writer)
|
||||
|
|
@ -397,7 +395,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
|||
foreach (var set in _data)
|
||||
array.Add(set.Serialize());
|
||||
|
||||
return new JObject()
|
||||
return new JObject
|
||||
{
|
||||
["Version"] = CurrentVersion,
|
||||
["Data"] = array,
|
||||
|
|
@ -424,9 +422,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
|||
Glamourer.Messager.NotificationMessage("Failure to load automated designs: No valid version available.",
|
||||
NotificationType.Error);
|
||||
break;
|
||||
case 1:
|
||||
LoadV1(obj["Data"]);
|
||||
break;
|
||||
case 1: LoadV1(obj["Data"]); break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -638,17 +634,17 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
|||
}
|
||||
}
|
||||
|
||||
private void OnDesignChange(DesignChanged.Type type, Design design, ITransaction? _)
|
||||
private void OnDesignChange(in DesignChanged.Arguments arguments)
|
||||
{
|
||||
if (type is not DesignChanged.Type.Deleted)
|
||||
if (arguments.Type is not DesignChanged.Type.Deleted)
|
||||
return;
|
||||
|
||||
foreach (var (set, idx) in this.WithIndex())
|
||||
foreach (var (idx, set) in this.Index())
|
||||
{
|
||||
var deleted = 0;
|
||||
for (var i = 0; i < set.Designs.Count; ++i)
|
||||
{
|
||||
if (set.Designs[i].Design != design)
|
||||
if (set.Designs[i].Design != arguments.Design)
|
||||
continue;
|
||||
|
||||
DeleteDesign(set, i--);
|
||||
|
|
@ -657,7 +653,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
|||
|
||||
if (deleted > 0)
|
||||
Glamourer.Log.Information(
|
||||
$"Removed {deleted} automated designs from automated design set {idx} due to deletion of {design.Incognito}.");
|
||||
$"Removed {deleted} automated designs from automated design set {idx} due to deletion of {arguments.Design.Incognito}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ using Penumbra.String;
|
|||
|
||||
namespace Glamourer.Automation;
|
||||
|
||||
public class FixedDesignMigrator(JobService jobs)
|
||||
public sealed class FixedDesignMigrator(JobService jobs) : IRequiredService
|
||||
{
|
||||
private List<(string Name, List<(string, JobGroup, bool)> Data)>? _migratedData;
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ public class FixedDesignMigrator(JobService jobs)
|
|||
var set = autoManager[^1];
|
||||
foreach (var design in data.AsEnumerable().Reverse())
|
||||
{
|
||||
if (!designFileSystem.Find(design.Item1, out var child) || child is not DesignFileSystem.Leaf leaf)
|
||||
if (!designFileSystem.Find(design.Item1, out var child) || child is not IFileSystemData<Design> leaf)
|
||||
{
|
||||
Glamourer.Messager.NotificationMessage($"Could not find design with path {design.Item1}, skipped fixed design.",
|
||||
NotificationType.Warning);
|
||||
|
|
|
|||
|
|
@ -6,53 +6,23 @@ using Glamourer.Gui;
|
|||
using Glamourer.Gui.Tabs.DesignTab;
|
||||
using Glamourer.Services;
|
||||
using ImSharp;
|
||||
using Newtonsoft.Json;
|
||||
using OtterGui.Filesystem;
|
||||
using Luna;
|
||||
using Luna.Generators;
|
||||
using Newtonsoft.Json;
|
||||
using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs;
|
||||
|
||||
namespace Glamourer;
|
||||
namespace Glamourer.Config;
|
||||
|
||||
[TooltipEnum]
|
||||
public enum HeightDisplayType
|
||||
public sealed partial class Configuration : IPluginConfiguration, ISavable, IService
|
||||
{
|
||||
[Tooltip("Do Not Display")]
|
||||
None,
|
||||
public const int CurrentVersion = 9;
|
||||
|
||||
[Tooltip("Centimetres (000.0 cm)")]
|
||||
Centimetre,
|
||||
|
||||
[Tooltip("Metres (0.00 m)")]
|
||||
Metre,
|
||||
|
||||
[Tooltip("Inches (00.0 in)")]
|
||||
Wrong,
|
||||
|
||||
[Tooltip("Feet (0'00'')")]
|
||||
WrongFoot,
|
||||
|
||||
[Tooltip("Corgis (0.0 Corgis)")]
|
||||
Corgi,
|
||||
|
||||
[Tooltip("Olympic-size swimming Pools (0.000 Pools)")]
|
||||
OlympicPool,
|
||||
}
|
||||
|
||||
public class DefaultDesignSettings
|
||||
{
|
||||
public bool AlwaysForceRedrawing = false;
|
||||
public bool ResetAdvancedDyes = false;
|
||||
public bool ShowQuickDesignBar = true;
|
||||
public bool ResetTemporarySettings = false;
|
||||
public bool Locked = false;
|
||||
}
|
||||
|
||||
public class Configuration : IPluginConfiguration, ISavable
|
||||
{
|
||||
[JsonIgnore]
|
||||
public readonly EphemeralConfig Ephemeral;
|
||||
|
||||
[JsonIgnore]
|
||||
public readonly UiConfig Ui;
|
||||
|
||||
public bool AttachToPcp { get; set; } = true;
|
||||
public bool UseRestrictedGearProtection { get; set; } = false;
|
||||
public bool OpenFoldersByDefault { get; set; } = false;
|
||||
|
|
@ -91,8 +61,11 @@ public class Configuration : IPluginConfiguration, ISavable
|
|||
|
||||
public DefaultDesignSettings DefaultDesignSettings { get; set; } = new();
|
||||
|
||||
public HeightDisplayType HeightDisplayType { get; set; } = HeightDisplayType.Centimetre;
|
||||
public RenameField ShowRename { get; set; } = RenameField.BothDataPrio;
|
||||
public HeightDisplayType HeightDisplayType { get; set; } = HeightDisplayType.Centimetre;
|
||||
|
||||
[ConfigProperty(EventName = "OnRenameChanged")]
|
||||
private RenameField _showRename = RenameField.BothDataPrio;
|
||||
|
||||
public ModifiableHotkey ToggleQuickDesignBar { get; set; } = new(VirtualKey.NO_KEY);
|
||||
public DoubleModifier DeleteDesignModifier { get; set; } = new(ModifierHotkey.Control, ModifierHotkey.Shift);
|
||||
public DoubleModifier IncognitoModifier { get; set; } = new(ModifierHotkey.Control);
|
||||
|
|
@ -103,7 +76,7 @@ public class Configuration : IPluginConfiguration, ISavable
|
|||
|
||||
[JsonConverter(typeof(SortModeConverter))]
|
||||
[JsonProperty(Order = int.MaxValue)]
|
||||
public ISortMode<Design> SortMode { get; set; } = ISortMode<Design>.FoldersFirst;
|
||||
public ISortMode SortMode { get; set; } = ISortMode.FoldersFirst;
|
||||
|
||||
public List<(string Code, bool Enabled)> Codes { get; set; } = [];
|
||||
|
||||
|
|
@ -113,7 +86,7 @@ public class Configuration : IPluginConfiguration, ISavable
|
|||
public bool DebugMode { get; set; } = false;
|
||||
#endif
|
||||
|
||||
public int Version { get; set; } = Constants.CurrentVersion;
|
||||
public int Version { get; set; } = CurrentVersion;
|
||||
|
||||
public Dictionary<ColorId, uint> Colors { get; private set; }
|
||||
= ColorId.Values.ToDictionary(c => c, c => c.Data().DefaultColor);
|
||||
|
|
@ -121,10 +94,11 @@ public class Configuration : IPluginConfiguration, ISavable
|
|||
[JsonIgnore]
|
||||
private readonly SaveService _saveService;
|
||||
|
||||
public Configuration(SaveService saveService, ConfigMigrationService migrator, EphemeralConfig ephemeral)
|
||||
public Configuration(SaveService saveService, ConfigMigrationService migrator, EphemeralConfig ephemeral, UiConfig ui)
|
||||
{
|
||||
_saveService = saveService;
|
||||
Ephemeral = ephemeral;
|
||||
Ui = ui;
|
||||
Load(migrator);
|
||||
}
|
||||
|
||||
|
|
@ -133,13 +107,13 @@ public class Configuration : IPluginConfiguration, ISavable
|
|||
|
||||
private void Load(ConfigMigrationService migrator)
|
||||
{
|
||||
if (!File.Exists(_saveService.FileNames.ConfigFile))
|
||||
if (!File.Exists(_saveService.FileNames.ConfigurationFile))
|
||||
return;
|
||||
|
||||
if (File.Exists(_saveService.FileNames.ConfigFile))
|
||||
if (File.Exists(_saveService.FileNames.ConfigurationFile))
|
||||
try
|
||||
{
|
||||
var text = File.ReadAllText(_saveService.FileNames.ConfigFile);
|
||||
var text = File.ReadAllText(_saveService.FileNames.ConfigurationFile);
|
||||
JsonConvert.PopulateObject(text, this, new JsonSerializerSettings
|
||||
{
|
||||
Error = HandleDeserializationError,
|
||||
|
|
@ -163,8 +137,8 @@ public class Configuration : IPluginConfiguration, ISavable
|
|||
}
|
||||
}
|
||||
|
||||
public string ToFilename(FilenameService fileNames)
|
||||
=> fileNames.ConfigFile;
|
||||
public string ToFilePath(FilenameService fileNames)
|
||||
=> fileNames.ConfigurationFile;
|
||||
|
||||
public void Save(StreamWriter writer)
|
||||
{
|
||||
|
|
@ -174,45 +148,22 @@ public class Configuration : IPluginConfiguration, ISavable
|
|||
serializer.Serialize(jWriter, this);
|
||||
}
|
||||
|
||||
public static class Constants
|
||||
{
|
||||
public const int CurrentVersion = 8;
|
||||
|
||||
public static readonly ISortMode<Design>[] ValidSortModes =
|
||||
[
|
||||
ISortMode<Design>.FoldersFirst,
|
||||
ISortMode<Design>.Lexicographical,
|
||||
new DesignFileSystem.CreationDate(),
|
||||
new DesignFileSystem.InverseCreationDate(),
|
||||
new DesignFileSystem.UpdateDate(),
|
||||
new DesignFileSystem.InverseUpdateDate(),
|
||||
ISortMode<Design>.InverseFoldersFirst,
|
||||
ISortMode<Design>.InverseLexicographical,
|
||||
ISortMode<Design>.FoldersLast,
|
||||
ISortMode<Design>.InverseFoldersLast,
|
||||
ISortMode<Design>.InternalOrder,
|
||||
ISortMode<Design>.InverseInternalOrder,
|
||||
];
|
||||
}
|
||||
|
||||
/// <summary> Convert SortMode Types to their name. </summary>
|
||||
private class SortModeConverter : JsonConverter<ISortMode<Design>>
|
||||
private class SortModeConverter : JsonConverter<ISortMode>
|
||||
{
|
||||
public override void WriteJson(JsonWriter writer, ISortMode<Design>? value, JsonSerializer serializer)
|
||||
public override void WriteJson(JsonWriter writer, ISortMode? value, JsonSerializer serializer)
|
||||
{
|
||||
value ??= ISortMode<Design>.FoldersFirst;
|
||||
value ??= ISortMode.FoldersFirst;
|
||||
serializer.Serialize(writer, value.GetType().Name);
|
||||
}
|
||||
|
||||
public override ISortMode<Design> ReadJson(JsonReader reader, Type objectType, ISortMode<Design>? existingValue,
|
||||
bool hasExistingValue,
|
||||
public override ISortMode ReadJson(JsonReader reader, Type objectType, ISortMode? existingValue, bool hasExistingValue,
|
||||
JsonSerializer serializer)
|
||||
{
|
||||
var name = serializer.Deserialize<string>(reader);
|
||||
if (name == null || !Constants.ValidSortModes.FindFirst(s => s.GetType().Name == name, out var mode))
|
||||
return existingValue ?? ISortMode<Design>.FoldersFirst;
|
||||
if (serializer.Deserialize<string>(reader) is { } name)
|
||||
return ISortMode.Valid.GetValueOrDefault(name, existingValue ?? ISortMode.FoldersFirst);
|
||||
|
||||
return mode;
|
||||
return existingValue ?? ISortMode.FoldersFirst;
|
||||
}
|
||||
}
|
||||
}
|
||||
10
Glamourer/Config/DefaultDesignSettings.cs
Normal file
10
Glamourer/Config/DefaultDesignSettings.cs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
namespace Glamourer.Config;
|
||||
|
||||
public class DefaultDesignSettings
|
||||
{
|
||||
public bool AlwaysForceRedrawing = false;
|
||||
public bool ResetAdvancedDyes = false;
|
||||
public bool ShowQuickDesignBar = true;
|
||||
public bool ResetTemporarySettings = false;
|
||||
public bool Locked = false;
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
using Luna.Generators;
|
||||
using ImSharp;
|
||||
using ImSharp;
|
||||
using Luna.Generators;
|
||||
|
||||
namespace Glamourer;
|
||||
namespace Glamourer.Config;
|
||||
|
||||
[Flags]
|
||||
[NamedEnum(Utf16: false)]
|
||||
|
|
@ -40,7 +40,7 @@ public enum DesignPanelFlag : uint
|
|||
|
||||
public static partial class DesignPanelFlagExtensions
|
||||
{
|
||||
private static readonly StringU8 Expand = new("Expand"u8);
|
||||
private static readonly StringU8 Expand = new("Expand"u8);
|
||||
|
||||
public static Im.HeaderDisposable Header(this DesignPanelFlag flag, Configuration config)
|
||||
{
|
||||
|
|
@ -55,7 +55,6 @@ public static partial class DesignPanelFlagExtensions
|
|||
Action<DesignPanelFlag> setterExpand)
|
||||
{
|
||||
var checkBoxWidth = Math.Max(Im.Style.FrameHeight, Expand.CalculateSize().X);
|
||||
var test = DesignPanelFlag.AdvancedCustomizations.ToNameU8();
|
||||
var textWidth = AdvancedCustomizations_Name__GenU8.CalculateSize().X;
|
||||
var tableSize = 2 * (textWidth + 2 * checkBoxWidth)
|
||||
+ 10 * Im.Style.CellPadding.X
|
||||
|
|
@ -6,28 +6,23 @@ using Luna.Generators;
|
|||
using Newtonsoft.Json;
|
||||
using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs;
|
||||
|
||||
namespace Glamourer;
|
||||
namespace Glamourer.Config;
|
||||
|
||||
public partial class EphemeralConfig : ISavable
|
||||
public partial class EphemeralConfig : ISavable, IService
|
||||
{
|
||||
public int Version { get; set; } = Configuration.Constants.CurrentVersion;
|
||||
public int Version { get; set; } = Configuration.CurrentVersion;
|
||||
|
||||
[ConfigProperty]
|
||||
private bool _incognitoMode;
|
||||
|
||||
public bool UnlockDetailMode { get; set; } = true;
|
||||
public bool ShowDesignQuickBar { get; set; } = false;
|
||||
public bool LockDesignQuickBar { get; set; } = false;
|
||||
public bool LockMainWindow { get; set; } = false;
|
||||
public bool ShowDesignQuickBar { get; set; }
|
||||
public bool LockDesignQuickBar { get; set; }
|
||||
public bool LockMainWindow { get; set; }
|
||||
public MainTabType SelectedMainTab { get; set; } = MainTabType.Settings;
|
||||
public Guid SelectedDesign { get; set; } = Guid.Empty;
|
||||
public Guid SelectedQuickDesign { get; set; } = Guid.Empty;
|
||||
public int LastSeenVersion { get; set; } = GlamourerChangelog.LastChangelogVersion;
|
||||
|
||||
public float CurrentDesignSelectorWidth { get; set; } = 200f;
|
||||
public float DesignSelectorMinimumScale { get; set; } = 0.1f;
|
||||
public float DesignSelectorMaximumScale { get; set; } = 0.5f;
|
||||
|
||||
|
||||
[JsonIgnore]
|
||||
private readonly SaveService _saveService;
|
||||
|
|
@ -69,7 +64,7 @@ public partial class EphemeralConfig : ISavable
|
|||
}
|
||||
}
|
||||
|
||||
public string ToFilename(FilenameService fileNames)
|
||||
public string ToFilePath(FilenameService fileNames)
|
||||
=> fileNames.EphemeralConfigFile;
|
||||
|
||||
public void Save(StreamWriter writer)
|
||||
28
Glamourer/Config/HeightDisplayType.cs
Normal file
28
Glamourer/Config/HeightDisplayType.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
using Luna.Generators;
|
||||
|
||||
namespace Glamourer.Config;
|
||||
|
||||
[TooltipEnum]
|
||||
public enum HeightDisplayType
|
||||
{
|
||||
[Tooltip("Do Not Display")]
|
||||
None,
|
||||
|
||||
[Tooltip("Centimetres (000.0 cm)")]
|
||||
Centimetre,
|
||||
|
||||
[Tooltip("Metres (0.00 m)")]
|
||||
Metre,
|
||||
|
||||
[Tooltip("Inches (00.0 in)")]
|
||||
Wrong,
|
||||
|
||||
[Tooltip("Feet (0'00'')")]
|
||||
WrongFoot,
|
||||
|
||||
[Tooltip("Corgis (0.0 Corgis)")]
|
||||
Corgi,
|
||||
|
||||
[Tooltip("Olympic-size swimming Pools (0.000 Pools)")]
|
||||
OlympicPool,
|
||||
}
|
||||
84
Glamourer/Config/IgnoredMods.cs
Normal file
84
Glamourer/Config/IgnoredMods.cs
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
using Glamourer.Services;
|
||||
using Luna;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Glamourer.Config;
|
||||
|
||||
public sealed class IgnoredMods : ConfigurationFile<FilenameService>, IReadOnlySet<string>
|
||||
{
|
||||
public override int CurrentVersion
|
||||
=> 1;
|
||||
|
||||
private readonly HashSet<string> _ignoredMods = [];
|
||||
|
||||
public IgnoredMods(SaveService saveService, MessageService messageService)
|
||||
: base(saveService, messageService)
|
||||
{
|
||||
Load();
|
||||
}
|
||||
|
||||
protected override void AddData(JsonTextWriter j)
|
||||
{
|
||||
j.WritePropertyName("IgnoredMods");
|
||||
j.WriteStartArray();
|
||||
foreach (var mod in _ignoredMods)
|
||||
j.WriteValue(mod);
|
||||
j.WriteEndArray();
|
||||
}
|
||||
|
||||
protected override void LoadData(JObject j)
|
||||
{
|
||||
_ignoredMods.Clear();
|
||||
if (j["IgnoredMods"] is not JArray arr)
|
||||
return;
|
||||
|
||||
foreach (var value in arr.Values<string>().OfType<string>())
|
||||
_ignoredMods.Add(value);
|
||||
}
|
||||
|
||||
public override string ToFilePath(FilenameService fileNames)
|
||||
=> fileNames.IgnoredModsFile;
|
||||
|
||||
public IEnumerator<string> GetEnumerator()
|
||||
=> _ignoredMods.GetEnumerator();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
=> GetEnumerator();
|
||||
|
||||
public int Count
|
||||
=> _ignoredMods.Count;
|
||||
|
||||
public void Add(string mod)
|
||||
{
|
||||
if (_ignoredMods.Add(mod))
|
||||
Save();
|
||||
}
|
||||
|
||||
public void Remove(string mod)
|
||||
{
|
||||
if (_ignoredMods.Remove(mod))
|
||||
Save();
|
||||
}
|
||||
|
||||
public bool Contains(string item)
|
||||
=> _ignoredMods.Contains(item);
|
||||
|
||||
public bool IsProperSubsetOf(IEnumerable<string> other)
|
||||
=> _ignoredMods.IsProperSubsetOf(other);
|
||||
|
||||
public bool IsProperSupersetOf(IEnumerable<string> other)
|
||||
=> _ignoredMods.IsProperSupersetOf(other);
|
||||
|
||||
public bool IsSubsetOf(IEnumerable<string> other)
|
||||
=> _ignoredMods.IsSubsetOf(other);
|
||||
|
||||
public bool IsSupersetOf(IEnumerable<string> other)
|
||||
=> _ignoredMods.IsSupersetOf(other);
|
||||
|
||||
public bool Overlaps(IEnumerable<string> other)
|
||||
=> _ignoredMods.Overlaps(other);
|
||||
|
||||
public bool SetEquals(IEnumerable<string> other)
|
||||
=> _ignoredMods.SetEquals(other);
|
||||
}
|
||||
50
Glamourer/Config/UiConfig.cs
Normal file
50
Glamourer/Config/UiConfig.cs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
using Glamourer.Services;
|
||||
using Luna;
|
||||
using Luna.Generators;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Glamourer.Config;
|
||||
|
||||
public sealed partial class UiConfig : ConfigurationFile<FilenameService>
|
||||
{
|
||||
public UiConfig(SaveService saveService, MessageService messageService)
|
||||
: base(saveService, messageService)
|
||||
{
|
||||
Load();
|
||||
}
|
||||
|
||||
[ConfigProperty]
|
||||
private TwoPanelWidth _actorsTabScale = new(250, ScalingMode.Absolute);
|
||||
|
||||
[ConfigProperty]
|
||||
private TwoPanelWidth _designsTabScale = new(0.3f, ScalingMode.Percentage);
|
||||
|
||||
[ConfigProperty]
|
||||
private TwoPanelWidth _automationTabScale = new(0.3f, ScalingMode.Percentage);
|
||||
|
||||
[ConfigProperty]
|
||||
private TwoPanelWidth _npcTabScale = new(250, ScalingMode.Absolute);
|
||||
|
||||
public override int CurrentVersion
|
||||
=> 1;
|
||||
|
||||
protected override void AddData(JsonTextWriter j)
|
||||
{
|
||||
ActorsTabScale.WriteJson(j, "ActorsTab");
|
||||
DesignsTabScale.WriteJson(j, "DesignsTab");
|
||||
AutomationTabScale.WriteJson(j, "AutomationTab");
|
||||
NpcTabScale.WriteJson(j, "NpcTab");
|
||||
}
|
||||
|
||||
protected override void LoadData(JObject j)
|
||||
{
|
||||
_actorsTabScale = TwoPanelWidth.ReadJson(j, "ActorsTab", new TwoPanelWidth(250, ScalingMode.Absolute));
|
||||
_designsTabScale = TwoPanelWidth.ReadJson(j, "DesignsTab", new TwoPanelWidth(0.3f, ScalingMode.Percentage));
|
||||
_automationTabScale = TwoPanelWidth.ReadJson(j, "AutomationTab", new TwoPanelWidth(0.3f, ScalingMode.Percentage));
|
||||
_npcTabScale = TwoPanelWidth.ReadJson(j, "NpcTab", new TwoPanelWidth(250, ScalingMode.Absolute));
|
||||
}
|
||||
|
||||
public override string ToFilePath(FilenameService fileNames)
|
||||
=> fileNames.UiConfiguration;
|
||||
}
|
||||
|
|
@ -7,15 +7,13 @@ using Glamourer.Services;
|
|||
using Glamourer.State;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using OtterGui.Classes;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Luna;
|
||||
using Notification = Luna.Notification;
|
||||
using SaveType = OtterGui.Classes.SaveType;
|
||||
|
||||
namespace Glamourer.Designs;
|
||||
|
||||
public sealed class Design : DesignBase, ISavable, IDesignStandIn
|
||||
public sealed class Design : DesignBase, ISavable, IDesignStandIn, IFileSystemValue<Design>
|
||||
{
|
||||
#region Data
|
||||
|
||||
|
|
@ -45,9 +43,10 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn
|
|||
public new const int FileVersion = 2;
|
||||
|
||||
public Guid Identifier { get; internal init; }
|
||||
public IFileSystemData<Design>? Node { get; set; }
|
||||
public DateTimeOffset CreationDate { get; internal init; }
|
||||
public DateTimeOffset LastEdit { get; internal set; }
|
||||
public LowerString Name { get; internal set; } = LowerString.Empty;
|
||||
public string Name { get; internal set; } = string.Empty;
|
||||
public string Description { get; internal set; } = string.Empty;
|
||||
public string[] Tags { get; internal set; } = [];
|
||||
public int Index { get; internal set; }
|
||||
|
|
@ -58,6 +57,7 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn
|
|||
public string Color { get; internal set; } = string.Empty;
|
||||
public SortedList<Mod, ModSettings> AssociatedMods { get; private set; } = [];
|
||||
public LinkContainer Links { get; private set; } = [];
|
||||
public DataPath Path { get; } = new();
|
||||
|
||||
public string Incognito
|
||||
=> Identifier.ToString()[..8];
|
||||
|
|
@ -70,7 +70,7 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn
|
|||
#region IDesignStandIn
|
||||
|
||||
public string ResolveName(bool incognito)
|
||||
=> incognito ? Incognito : Name.Text;
|
||||
=> incognito ? Incognito : Name;
|
||||
|
||||
public string SerializeName()
|
||||
=> Identifier.ToString();
|
||||
|
|
@ -108,7 +108,7 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn
|
|||
["Identifier"] = Identifier,
|
||||
["CreationDate"] = CreationDate,
|
||||
["LastEdit"] = LastEdit,
|
||||
["Name"] = Name.Text,
|
||||
["Name"] = Name,
|
||||
["Description"] = Description,
|
||||
["ForcedRedraw"] = ForcedRedraw,
|
||||
["ResetAdvancedDyes"] = ResetAdvancedDyes,
|
||||
|
|
@ -125,6 +125,12 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn
|
|||
["Mods"] = SerializeMods(),
|
||||
["Links"] = Links.Serialize(),
|
||||
};
|
||||
if (Path.Folder.Length > 0)
|
||||
ret["FileSystemFolder"] = Path.Folder;
|
||||
if (Path.SortName is not null)
|
||||
ret["SortOrderName"] = Path.SortName;
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -193,7 +199,7 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn
|
|||
var hasNegativeGloss = false;
|
||||
var hasNonPositiveGloss = false;
|
||||
var specularLarger = 0;
|
||||
foreach (var (key, value) in materialData.GetValues(MaterialValueIndex.Min(), MaterialValueIndex.Max()))
|
||||
foreach (var (_, value) in materialData.GetValues(MaterialValueIndex.Min(), MaterialValueIndex.Max()))
|
||||
{
|
||||
hasNegativeGloss |= value.Value.GlossStrength < 0;
|
||||
hasNonPositiveGloss |= value.Value.GlossStrength <= 0;
|
||||
|
|
@ -244,7 +250,7 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn
|
|||
{
|
||||
CreationDate = creationDate,
|
||||
Identifier = json["Identifier"]?.ToObject<Guid>() ?? throw new ArgumentNullException("Identifier"),
|
||||
Name = new LowerString(json["Name"]?.ToObject<string>() ?? throw new ArgumentNullException("Name")),
|
||||
Name = json["Name"]?.ToObject<string>() ?? throw new ArgumentNullException("Name"),
|
||||
Description = json["Description"]?.ToObject<string>() ?? string.Empty,
|
||||
Tags = ParseTags(json),
|
||||
LastEdit = json["LastEdit"]?.ToObject<DateTimeOffset>() ?? creationDate,
|
||||
|
|
@ -252,6 +258,9 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn
|
|||
};
|
||||
if (design.LastEdit < creationDate)
|
||||
design.LastEdit = creationDate;
|
||||
design.Path.Folder = json["FileSystemFolder"]?.Value<string>() ?? string.Empty;
|
||||
design.Path.SortName = json["SortOrderName"]?.Value<string>()?.FixName();
|
||||
|
||||
design.SetWriteProtected(json["WriteProtected"]?.ToObject<bool>() ?? false);
|
||||
LoadCustomize(customizations, json["Customize"], design, design.Name, true, false);
|
||||
LoadEquip(items, json["Equipment"], design, design.Name, true);
|
||||
|
|
@ -329,21 +338,25 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn
|
|||
|
||||
#region ISavable
|
||||
|
||||
public string ToFilename(FilenameService fileNames)
|
||||
public string ToFilePath(FilenameService fileNames)
|
||||
=> fileNames.DesignFile(this);
|
||||
|
||||
public void Save(StreamWriter writer)
|
||||
{
|
||||
using var j = new JsonTextWriter(writer)
|
||||
{
|
||||
Formatting = Formatting.Indented,
|
||||
};
|
||||
using var j = new JsonTextWriter(writer);
|
||||
j.Formatting = Formatting.Indented;
|
||||
var obj = JsonSerialize();
|
||||
obj.WriteTo(j);
|
||||
}
|
||||
|
||||
public string LogName(string fileName)
|
||||
=> Path.GetFileNameWithoutExtension(fileName);
|
||||
=> System.IO.Path.GetFileNameWithoutExtension(fileName);
|
||||
|
||||
#endregion
|
||||
|
||||
string IFileSystemValue.Identifier
|
||||
=> Identifier.ToString();
|
||||
|
||||
public string DisplayName
|
||||
=> Name;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
using Glamourer.Api.Enums;
|
||||
using Glamourer.Services;
|
||||
using OtterGui;
|
||||
using OtterGui.Extensions;
|
||||
using Penumbra.GameData.DataContainers;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
|
@ -107,7 +105,7 @@ public class DesignBase64Migration
|
|||
}
|
||||
|
||||
data.Customize = *(CustomizeArray*)(ptr + 4);
|
||||
foreach (var (slot, idx) in EquipSlotExtensions.EqdpSlots.WithIndex())
|
||||
foreach (var (idx, slot) in EquipSlotExtensions.EqdpSlots.Index())
|
||||
{
|
||||
var mdl = eq[idx];
|
||||
var item = items.Identify(slot, mdl.Set, mdl.Variant);
|
||||
|
|
@ -121,7 +119,7 @@ public class DesignBase64Migration
|
|||
data.SetStain(slot, mdl.Stain);
|
||||
}
|
||||
|
||||
var main = cur[0].Skeleton.Id == 0
|
||||
var main = cur[0].Skeleton.Id is 0
|
||||
? items.DefaultSword
|
||||
: items.Identify(EquipSlot.MainHand, cur[0].Skeleton, cur[0].Weapon, cur[0].Variant);
|
||||
if (!main.Valid)
|
||||
|
|
|
|||
|
|
@ -8,114 +8,7 @@ using Newtonsoft.Json.Linq;
|
|||
|
||||
namespace Glamourer.Designs;
|
||||
|
||||
public class DesignColorUi(DesignColors colors, Configuration config)
|
||||
{
|
||||
private string _newName = string.Empty;
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
using var table = Im.Table.Begin("designColors"u8, 3, TableFlags.RowBackground);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
var changeString = string.Empty;
|
||||
Rgba32? changeValue = null;
|
||||
|
||||
table.SetupColumn("##Delete"u8, TableColumnFlags.WidthFixed, Im.Style.FrameHeight);
|
||||
table.SetupColumn("##Select"u8, TableColumnFlags.WidthFixed, Im.Style.FrameHeight);
|
||||
table.SetupColumn("Color Name"u8, TableColumnFlags.WidthStretch);
|
||||
|
||||
table.HeaderRow();
|
||||
|
||||
table.NextColumn();
|
||||
if (ImEx.Icon.Button(LunaStyle.RefreshIcon, "Revert the color used for missing design colors to its default."u8,
|
||||
colors.MissingColor == DesignColors.MissingColorDefault))
|
||||
{
|
||||
changeString = DesignColors.MissingColorName;
|
||||
changeValue = DesignColors.MissingColorDefault;
|
||||
}
|
||||
|
||||
table.NextColumn();
|
||||
if (DrawColorButton(DesignColors.MissingColorNameU8, colors.MissingColor, out var newColor))
|
||||
{
|
||||
changeString = DesignColors.MissingColorName;
|
||||
changeValue = newColor;
|
||||
}
|
||||
|
||||
table.NextColumn();
|
||||
Im.Cursor.X += Im.Style.FramePadding.X;
|
||||
Im.Text(DesignColors.MissingColorNameU8);
|
||||
Im.Tooltip.OnHover("This color is used when the color specified in a design is not available."u8);
|
||||
|
||||
var disabled = !config.DeleteDesignModifier.IsActive();
|
||||
foreach (var (idx, (name, color)) in colors.Index())
|
||||
{
|
||||
using var id = Im.Id.Push(idx);
|
||||
table.NextColumn();
|
||||
|
||||
if (ImEx.Icon.Button(LunaStyle.DeleteIcon, "Delete this color. This does not remove it from designs using it."u8, disabled))
|
||||
{
|
||||
changeString = name;
|
||||
changeValue = null;
|
||||
}
|
||||
|
||||
if (disabled)
|
||||
Im.Tooltip.OnHover($"\nHold {config.DeleteDesignModifier} to delete.");
|
||||
|
||||
table.NextColumn();
|
||||
if (DrawColorButton(name, color, out newColor))
|
||||
{
|
||||
changeString = name;
|
||||
changeValue = newColor;
|
||||
}
|
||||
|
||||
table.NextColumn();
|
||||
Im.Cursor.X += Im.Style.FramePadding.X;
|
||||
Im.Text(name);
|
||||
}
|
||||
|
||||
table.NextColumn();
|
||||
(var tt, disabled) = _newName.Length == 0
|
||||
? ("Specify a name for a new color first.", true)
|
||||
: _newName is DesignColors.MissingColorName or DesignColors.AutomaticName
|
||||
? ($"You can not use the name {DesignColors.MissingColorName} or {DesignColors.AutomaticName}, choose a different one.", true)
|
||||
: colors.ContainsKey(_newName)
|
||||
? ($"The color {_newName} already exists, please choose a different name.", true)
|
||||
: ($"Add a new color {_newName} to your list.", false);
|
||||
if (ImEx.Icon.Button(LunaStyle.AddObjectIcon, tt, disabled))
|
||||
{
|
||||
changeString = _newName;
|
||||
changeValue = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
table.NextColumn();
|
||||
table.NextColumn();
|
||||
Im.Item.SetNextWidth(Im.ContentRegion.Available.X);
|
||||
if (Im.Input.Text("##newDesignColor"u8, ref _newName, "New Color Name..."u8, InputTextFlags.EnterReturnsTrue))
|
||||
{
|
||||
changeString = _newName;
|
||||
changeValue = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
if (changeString.Length > 0)
|
||||
{
|
||||
if (!changeValue.HasValue)
|
||||
colors.DeleteColor(changeString);
|
||||
else
|
||||
colors.SetColor(changeString, changeValue.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool DrawColorButton(Utf8StringHandler<LabelStringHandlerBuffer> tooltip, Rgba32 color, out Rgba32 newColor)
|
||||
{
|
||||
var ret = Im.Color.Editor(tooltip, ref color, ColorEditorFlags.AlphaPreviewHalf | ColorEditorFlags.NoInputs);
|
||||
Im.Tooltip.OnHover(ref tooltip);
|
||||
newColor = color;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public class DesignColors : ISavable, IReadOnlyDictionary<string, Rgba32>
|
||||
public sealed class DesignColors : ISavable, IReadOnlyDictionary<string, Rgba32>, IService
|
||||
{
|
||||
public const string AutomaticName = "Automatic";
|
||||
public static readonly StringU8 AutomaticNameU8 = new("Automatic"u8);
|
||||
|
|
@ -183,7 +76,7 @@ public class DesignColors : ISavable, IReadOnlyDictionary<string, Rgba32>
|
|||
SaveAndInvoke();
|
||||
}
|
||||
|
||||
public string ToFilename(FilenameService fileNames)
|
||||
public string ToFilePath(FilenameService fileNames)
|
||||
=> fileNames.DesignColorFile;
|
||||
|
||||
public void Save(StreamWriter writer)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using Glamourer.Interop.Material;
|
|||
using Glamourer.Services;
|
||||
using Glamourer.State;
|
||||
using Glamourer.Utility;
|
||||
using Luna;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Penumbra.GameData.DataContainers;
|
||||
|
|
@ -12,13 +13,13 @@ using Penumbra.GameData.Structs;
|
|||
|
||||
namespace Glamourer.Designs;
|
||||
|
||||
public class DesignConverter(
|
||||
public sealed class DesignConverter(
|
||||
SaveService saveService,
|
||||
ItemManager _items,
|
||||
DesignManager _designs,
|
||||
CustomizeService _customize,
|
||||
HumanModelList _humans,
|
||||
DesignLinkLoader _linkLoader)
|
||||
ItemManager items,
|
||||
DesignManager designs,
|
||||
CustomizeService customizeService,
|
||||
HumanModelList humans,
|
||||
DesignLinkLoader linkLoader) : IService
|
||||
{
|
||||
public const byte Version = 6;
|
||||
|
||||
|
|
@ -54,9 +55,9 @@ public class DesignConverter(
|
|||
|
||||
public DesignBase Convert(in DesignData data, in StateMaterialManager materials, in ApplicationRules rules)
|
||||
{
|
||||
var design = _designs.CreateTemporary();
|
||||
var design = designs.CreateTemporary();
|
||||
rules.Apply(design);
|
||||
design.SetDesignData(_customize, data);
|
||||
design.SetDesignData(customizeService, data);
|
||||
if (rules.Materials)
|
||||
ComputeMaterials(design.GetMaterialDataRef(), materials, rules.Equip);
|
||||
return design;
|
||||
|
|
@ -70,8 +71,8 @@ public class DesignConverter(
|
|||
try
|
||||
{
|
||||
var ret = jObject["Identifier"] != null
|
||||
? Design.LoadDesign(saveService, _customize, _items, _linkLoader, jObject)
|
||||
: DesignBase.LoadDesignBase(_customize, _items, jObject);
|
||||
? Design.LoadDesign(saveService, customizeService, items, linkLoader, jObject)
|
||||
: DesignBase.LoadDesignBase(customizeService, items, jObject);
|
||||
|
||||
if (!customize)
|
||||
ret.Application.RemoveCustomize();
|
||||
|
|
@ -101,14 +102,14 @@ public class DesignConverter(
|
|||
case (byte)'{':
|
||||
var jObj1 = JObject.Parse(Encoding.UTF8.GetString(bytes));
|
||||
ret = jObj1["Identifier"] != null
|
||||
? Design.LoadDesign(saveService, _customize, _items, _linkLoader, jObj1)
|
||||
: DesignBase.LoadDesignBase(_customize, _items, jObj1);
|
||||
? Design.LoadDesign(saveService, customizeService, items, linkLoader, jObj1)
|
||||
: DesignBase.LoadDesignBase(customizeService, items, jObj1);
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
ret = _designs.CreateTemporary();
|
||||
ret.MigrateBase64(_customize, _items, _humans, base64);
|
||||
ret = designs.CreateTemporary();
|
||||
ret.MigrateBase64(customizeService, items, humans, base64);
|
||||
break;
|
||||
case 3:
|
||||
{
|
||||
|
|
@ -116,8 +117,8 @@ public class DesignConverter(
|
|||
var jObj2 = JObject.Parse(decompressed);
|
||||
Debug.Assert(version == 3);
|
||||
ret = jObj2["Identifier"] != null
|
||||
? Design.LoadDesign(saveService, _customize, _items, _linkLoader, jObj2)
|
||||
: DesignBase.LoadDesignBase(_customize, _items, jObj2);
|
||||
? Design.LoadDesign(saveService, customizeService, items, linkLoader, jObj2)
|
||||
: DesignBase.LoadDesignBase(customizeService, items, jObj2);
|
||||
break;
|
||||
}
|
||||
case 5:
|
||||
|
|
@ -127,8 +128,8 @@ public class DesignConverter(
|
|||
var jObj2 = JObject.Parse(decompressed);
|
||||
Debug.Assert(version == 5);
|
||||
ret = jObj2["Identifier"] != null
|
||||
? Design.LoadDesign(saveService, _customize, _items, _linkLoader, jObj2)
|
||||
: DesignBase.LoadDesignBase(_customize, _items, jObj2);
|
||||
? Design.LoadDesign(saveService, customizeService, items, linkLoader, jObj2)
|
||||
: DesignBase.LoadDesignBase(customizeService, items, jObj2);
|
||||
break;
|
||||
}
|
||||
case 6:
|
||||
|
|
@ -137,8 +138,8 @@ public class DesignConverter(
|
|||
var jObj2 = JObject.Parse(decompressed);
|
||||
Debug.Assert(version == 6);
|
||||
ret = jObj2["Identifier"] != null
|
||||
? Design.LoadDesign(saveService, _customize, _items, _linkLoader, jObj2)
|
||||
: DesignBase.LoadDesignBase(_customize, _items, jObj2);
|
||||
? Design.LoadDesign(saveService, customizeService, items, linkLoader, jObj2)
|
||||
: DesignBase.LoadDesignBase(customizeService, items, jObj2);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -177,7 +178,7 @@ public class DesignConverter(
|
|||
{
|
||||
var index = (int)slot.ToIndex();
|
||||
var armor = armors[index];
|
||||
var item = _items.Identify(slot, armor.Set, armor.Variant);
|
||||
var item = items.Identify(slot, armor.Set, armor.Variant);
|
||||
if (!item.Valid)
|
||||
{
|
||||
if (!skipWarnings)
|
||||
|
|
@ -188,20 +189,20 @@ public class DesignConverter(
|
|||
yield return (slot, item, armor.Stains);
|
||||
}
|
||||
|
||||
var mh = _items.Identify(EquipSlot.MainHand, mainhand.Skeleton, mainhand.Weapon, mainhand.Variant);
|
||||
var mh = items.Identify(EquipSlot.MainHand, mainhand.Skeleton, mainhand.Weapon, mainhand.Variant);
|
||||
if (!skipWarnings && !mh.Valid)
|
||||
{
|
||||
Glamourer.Log.Warning($"Appearance data {mainhand} for mainhand weapon invalid, item could not be identified.");
|
||||
mh = _items.DefaultSword;
|
||||
mh = items.DefaultSword;
|
||||
}
|
||||
|
||||
yield return (EquipSlot.MainHand, mh, mainhand.Stains);
|
||||
|
||||
var oh = _items.Identify(EquipSlot.OffHand, offhand.Skeleton, offhand.Weapon, offhand.Variant, mh.Type);
|
||||
var oh = items.Identify(EquipSlot.OffHand, offhand.Skeleton, offhand.Weapon, offhand.Variant, mh.Type);
|
||||
if (!skipWarnings && !oh.Valid)
|
||||
{
|
||||
Glamourer.Log.Warning($"Appearance data {offhand} for offhand weapon invalid, item could not be identified.");
|
||||
oh = _items.GetDefaultOffhand(mh);
|
||||
oh = items.GetDefaultOffhand(mh);
|
||||
if (!oh.Valid)
|
||||
oh = ItemManager.NothingItem(FullEquipType.Shield);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
using Glamourer.GameData;
|
||||
using Glamourer.Services;
|
||||
using OtterGui.Classes;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.String.Functions;
|
||||
|
|
@ -47,8 +46,8 @@ public unsafe struct DesignData
|
|||
{ }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)]
|
||||
public readonly bool ContainsName(LowerString name)
|
||||
=> ItemNames.Any(name.IsContained);
|
||||
public readonly bool ContainsName(string name)
|
||||
=> ItemNames.Any(i => i.Contains(name, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
public readonly StainIds Stain(EquipSlot slot)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using Glamourer.Config;
|
||||
using Glamourer.Designs.History;
|
||||
using Glamourer.Designs.Links;
|
||||
using Glamourer.Events;
|
||||
|
|
@ -5,7 +6,6 @@ using Glamourer.GameData;
|
|||
using Glamourer.Interop.Material;
|
||||
using Glamourer.Services;
|
||||
using ImSharp;
|
||||
using Luna;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
|
|
@ -75,7 +75,7 @@ public class DesignEditor(
|
|||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
Glamourer.Log.Debug($"Changed customize {idx.ToName()} in design {design.Identifier} from {oldValue.Value} to {value.Value}.");
|
||||
SaveService.QueueSave(design);
|
||||
DesignChanged.Invoke(DesignChanged.Type.Customize, design, new CustomizeTransaction(idx, oldValue, value));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Customize, design, new CustomizeTransaction(idx, oldValue, value)));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
@ -91,7 +91,7 @@ public class DesignEditor(
|
|||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
Glamourer.Log.Debug($"Changed entire customize with resulting flags {applied} and {changed}.");
|
||||
SaveService.QueueSave(design);
|
||||
DesignChanged.Invoke(DesignChanged.Type.EntireCustomize, design, new EntireCustomizeTransaction(changed, oldCustomize, newCustomize));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.EntireCustomize, design, new EntireCustomizeTransaction(changed, oldCustomize, newCustomize)));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
@ -106,7 +106,7 @@ public class DesignEditor(
|
|||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
Glamourer.Log.Debug($"Set customize parameter {flag} in design {design.Identifier} from {old} to {@new}.");
|
||||
SaveService.QueueSave(design);
|
||||
DesignChanged.Invoke(DesignChanged.Type.Parameter, design, new ParameterTransaction(flag, old, @new));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Parameter, design, new ParameterTransaction(flag, old, @new)));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
@ -130,9 +130,9 @@ public class DesignEditor(
|
|||
SaveService.QueueSave(design);
|
||||
Glamourer.Log.Debug(
|
||||
$"Set {EquipSlot.MainHand.ToName()} weapon in design {design.Identifier} from {currentMain.Name} ({currentMain.ItemId}) to {item.Name} ({item.ItemId}).");
|
||||
DesignChanged.Invoke(DesignChanged.Type.Weapon, design,
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Weapon, design,
|
||||
new WeaponTransaction(currentMain, currentOff, currentGauntlets, item, newOff ?? currentOff,
|
||||
newGauntlets ?? currentGauntlets));
|
||||
newGauntlets ?? currentGauntlets)));
|
||||
return;
|
||||
}
|
||||
case EquipSlot.OffHand:
|
||||
|
|
@ -150,8 +150,8 @@ public class DesignEditor(
|
|||
SaveService.QueueSave(design);
|
||||
Glamourer.Log.Debug(
|
||||
$"Set {EquipSlot.OffHand.ToName()} weapon in design {design.Identifier} from {currentOff.Name} ({currentOff.ItemId}) to {item.Name} ({item.ItemId}).");
|
||||
DesignChanged.Invoke(DesignChanged.Type.Weapon, design,
|
||||
new WeaponTransaction(currentMain, currentOff, currentGauntlets, currentMain, item, currentGauntlets));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Weapon, design,
|
||||
new WeaponTransaction(currentMain, currentOff, currentGauntlets, currentMain, item, currentGauntlets)));
|
||||
return;
|
||||
}
|
||||
default:
|
||||
|
|
@ -167,7 +167,7 @@ public class DesignEditor(
|
|||
Glamourer.Log.Debug(
|
||||
$"Set {slot.ToName()} equipment piece in design {design.Identifier} from {old.Name} ({old.ItemId}) to {item.Name} ({item.ItemId}).");
|
||||
SaveService.QueueSave(design);
|
||||
DesignChanged.Invoke(DesignChanged.Type.Equip, design, new EquipTransaction(slot, old, item));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Equip, design, new EquipTransaction(slot, old, item)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -187,7 +187,7 @@ public class DesignEditor(
|
|||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.QueueSave(design);
|
||||
Glamourer.Log.Debug($"Set {slot} bonus item to {item}.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.BonusItem, design, new BonusItemTransaction(slot, oldItem, item));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.BonusItem, design, new BonusItemTransaction(slot, oldItem, item)));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
@ -204,7 +204,7 @@ public class DesignEditor(
|
|||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.QueueSave(design);
|
||||
Glamourer.Log.Debug($"Set stain of {slot} equipment piece to {stains}.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.Stains, design, new StainTransaction(slot, oldStain, stains));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Stains, design, new StainTransaction(slot, oldStain, stains)));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
@ -227,7 +227,7 @@ public class DesignEditor(
|
|||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.QueueSave(design);
|
||||
Glamourer.Log.Debug($"Set crest visibility of {slot} equipment piece to {crest}.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.Crest, design, new CrestTransaction(slot, oldCrest, crest));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Crest, design, new CrestTransaction(slot, oldCrest, crest)));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
@ -240,7 +240,7 @@ public class DesignEditor(
|
|||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.QueueSave(design);
|
||||
Glamourer.Log.Debug($"Set value of {metaIndex} to {value}.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.Other, design, new MetaTransaction(metaIndex, !value, value));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Other, design, new MetaTransaction(metaIndex, !value, value)));
|
||||
}
|
||||
|
||||
public void ChangeMaterialRevert(Design design, MaterialValueIndex index, bool revert)
|
||||
|
|
@ -253,7 +253,7 @@ public class DesignEditor(
|
|||
Glamourer.Log.Debug($"Changed advanced dye value for {index} to {(revert ? "Revert." : "no longer Revert.")}");
|
||||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.QueueSave(design);
|
||||
DesignChanged.Invoke(DesignChanged.Type.MaterialRevert, design, new MaterialRevertTransaction(index, !revert, revert));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.MaterialRevert, design, new MaterialRevertTransaction(index, !revert, revert)));
|
||||
}
|
||||
|
||||
public void ChangeMaterialMode(Design design, MaterialValueIndex index, ColorRow.Mode mode)
|
||||
|
|
@ -267,7 +267,7 @@ public class DesignEditor(
|
|||
Glamourer.Log.Debug($"Changed advanced dye value for {index} from {oldMode} to {mode} mode.");
|
||||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.QueueSave(design);
|
||||
DesignChanged.Invoke(DesignChanged.Type.MaterialMode, design, new MaterialModeTransaction(index, oldMode, mode));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.MaterialMode, design, new MaterialModeTransaction(index, oldMode, mode)));
|
||||
}
|
||||
|
||||
public void ChangeMaterialValue(Design design, MaterialValueIndex index, ColorRow? row, ColorRow.Mode? mode = null)
|
||||
|
|
@ -307,8 +307,7 @@ public class DesignEditor(
|
|||
|
||||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.DelaySave(design);
|
||||
DesignChanged.Invoke(DesignChanged.Type.Material, design,
|
||||
new MaterialTransaction(index, oldValue.Value, row, mode.HasValue ? oldValue.Mode : null, mode));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Material, design, new MaterialTransaction(index, oldValue.Value, row, mode.HasValue ? oldValue.Mode : null, mode)));
|
||||
}
|
||||
|
||||
public void ChangeApplyMaterialValue(Design design, MaterialValueIndex index, bool value)
|
||||
|
|
@ -321,7 +320,7 @@ public class DesignEditor(
|
|||
Glamourer.Log.Debug($"Changed application of advanced dye for {index} to {value}.");
|
||||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.QueueSave(design);
|
||||
DesignChanged.Invoke(DesignChanged.Type.ApplyMaterial, design, new ApplicationTransaction(index, !value, value));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ApplyMaterial, design, new ApplicationTransaction(index, !value, value)));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,197 +1,75 @@
|
|||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Glamourer.Designs.History;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Services;
|
||||
using Luna;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Glamourer.Designs;
|
||||
|
||||
public sealed class DesignFileSystem : OtterGui.Filesystem.FileSystem<Design>, IDisposable, ISavable
|
||||
public sealed class DesignFileSystem : BaseFileSystem, IDisposable, IRequiredService
|
||||
{
|
||||
private readonly DesignChanged _designChanged;
|
||||
private readonly DesignFileSystemSaver _saver;
|
||||
private readonly DesignChanged _designChanged;
|
||||
private readonly TabSelected _tabSelected;
|
||||
|
||||
private readonly SaveService _saveService;
|
||||
private readonly DesignManager _designManager;
|
||||
|
||||
public DesignFileSystem(DesignManager designManager, SaveService saveService, DesignChanged designChanged)
|
||||
public DesignFileSystem(Logger log, SaveService saveService, DesignStorage designs, DesignChanged designChanged, TabSelected tabSelected)
|
||||
: base("DesignFileSystem", log, true)
|
||||
{
|
||||
_designManager = designManager;
|
||||
_saveService = saveService;
|
||||
_designChanged = designChanged;
|
||||
_designChanged.Subscribe(OnDesignChange, DesignChanged.Priority.DesignFileSystem);
|
||||
Changed += OnChange;
|
||||
Reload();
|
||||
_tabSelected = tabSelected;
|
||||
_saver = new DesignFileSystemSaver(log, this, saveService, designs);
|
||||
|
||||
_saver.Load();
|
||||
_designChanged.Subscribe(OnDesignChanged, DesignChanged.Priority.DesignFileSystem);
|
||||
_tabSelected.Subscribe(OnTabSelected, TabSelected.Priority.DesignSelector);
|
||||
}
|
||||
|
||||
private void Reload()
|
||||
private void OnTabSelected(in TabSelected.Arguments arguments)
|
||||
{
|
||||
if (Load(new FileInfo(_saveService.FileNames.DesignFileSystem), _designManager.Designs, DesignToIdentifier, DesignToName))
|
||||
_saveService.ImmediateSave(this);
|
||||
if (arguments.Design?.Node is { } node)
|
||||
Selection.Select(node);
|
||||
}
|
||||
|
||||
Glamourer.Log.Debug("Reloaded design filesystem.");
|
||||
private void OnDesignChanged(in DesignChanged.Arguments arguments)
|
||||
{
|
||||
switch (arguments.Type)
|
||||
{
|
||||
case DesignChanged.Type.ReloadedAll: _saver.Load(); break;
|
||||
case DesignChanged.Type.Created:
|
||||
var parent = Root;
|
||||
if (arguments.Design.Path.Folder.Length > 0)
|
||||
try
|
||||
{
|
||||
parent = FindOrCreateAllFolders(arguments.Design.Path.Folder);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Messager.NotificationMessage(ex,
|
||||
$"Could not move design to {arguments.Design.Path} because the folder could not be created.",
|
||||
NotificationType.Error);
|
||||
}
|
||||
|
||||
var (data, _) = CreateDuplicateDataNode(parent, arguments.Design.Path.SortName ?? arguments.Design.Name, arguments.Design);
|
||||
Selection.Select(data);
|
||||
break;
|
||||
case DesignChanged.Type.Deleted:
|
||||
if (arguments.Design.Node is { } node)
|
||||
{
|
||||
if (node.Selected)
|
||||
Selection.UnselectAll();
|
||||
Delete(node);
|
||||
}
|
||||
|
||||
break;
|
||||
case DesignChanged.Type.Renamed when arguments.Design.Path.SortName is null:
|
||||
RenameWithDuplicates(arguments.Design.Node!, arguments.Design.Path.GetIntendedName(arguments.Design.Name));
|
||||
break;
|
||||
// TODO: Maybe add path changes?
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_designChanged.Unsubscribe(OnDesignChange);
|
||||
}
|
||||
|
||||
public struct CreationDate : OtterGui.Filesystem.ISortMode<Design>
|
||||
{
|
||||
public ReadOnlySpan<byte> Name
|
||||
=> "Creation Date (Older First)"u8;
|
||||
|
||||
public ReadOnlySpan<byte> Description
|
||||
=> "In each folder, sort all subfolders lexicographically, then sort all leaves using their creation date."u8;
|
||||
|
||||
public IEnumerable<IPath> GetChildren(Folder f)
|
||||
=> f.GetSubFolders().Cast<IPath>().Concat(f.GetLeaves().OrderBy(l => l.Value.CreationDate));
|
||||
}
|
||||
|
||||
public struct UpdateDate : OtterGui.Filesystem.ISortMode<Design>
|
||||
{
|
||||
public ReadOnlySpan<byte> Name
|
||||
=> "Update Date (Older First)"u8;
|
||||
|
||||
public ReadOnlySpan<byte> Description
|
||||
=> "In each folder, sort all subfolders lexicographically, then sort all leaves using their last update date."u8;
|
||||
|
||||
public IEnumerable<IPath> GetChildren(Folder f)
|
||||
=> f.GetSubFolders().Cast<IPath>().Concat(f.GetLeaves().OrderBy(l => l.Value.LastEdit));
|
||||
}
|
||||
|
||||
public struct InverseCreationDate : OtterGui.Filesystem.ISortMode<Design>
|
||||
{
|
||||
public ReadOnlySpan<byte> Name
|
||||
=> "Creation Date (Newer First)"u8;
|
||||
|
||||
public ReadOnlySpan<byte> Description
|
||||
=> "In each folder, sort all subfolders lexicographically, then sort all leaves using their inverse creation date."u8;
|
||||
|
||||
public IEnumerable<IPath> GetChildren(Folder f)
|
||||
=> f.GetSubFolders().Cast<IPath>().Concat(f.GetLeaves().OrderByDescending(l => l.Value.CreationDate));
|
||||
}
|
||||
|
||||
public struct InverseUpdateDate : OtterGui.Filesystem.ISortMode<Design>
|
||||
{
|
||||
public ReadOnlySpan<byte> Name
|
||||
=> "Update Date (Newer First)"u8;
|
||||
|
||||
public ReadOnlySpan<byte> Description
|
||||
=> "In each folder, sort all subfolders lexicographically, then sort all leaves using their inverse last update date."u8;
|
||||
|
||||
public IEnumerable<IPath> GetChildren(Folder f)
|
||||
=> f.GetSubFolders().Cast<IPath>().Concat(f.GetLeaves().OrderByDescending(l => l.Value.LastEdit));
|
||||
}
|
||||
|
||||
private void OnChange(OtterGui.Filesystem.FileSystemChangeType type, IPath _1, IPath? _2, IPath? _3)
|
||||
{
|
||||
if (type != OtterGui.Filesystem.FileSystemChangeType.Reload)
|
||||
_saveService.QueueSave(this);
|
||||
}
|
||||
|
||||
private void OnDesignChange(DesignChanged.Type type, Design design, ITransaction? data)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case DesignChanged.Type.Created:
|
||||
var parent = Root;
|
||||
if ((data as CreationTransaction?)?.Path is { } path)
|
||||
try
|
||||
{
|
||||
parent = FindOrCreateAllFolders(path);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Messager.NotificationMessage(ex, $"Could not move design to {path} because the folder could not be created.",
|
||||
NotificationType.Error);
|
||||
}
|
||||
|
||||
CreateDuplicateLeaf(parent, design.Name.Text, design);
|
||||
|
||||
return;
|
||||
case DesignChanged.Type.Deleted:
|
||||
if (TryGetValue(design, out var leaf1))
|
||||
Delete(leaf1);
|
||||
return;
|
||||
case DesignChanged.Type.ReloadedAll:
|
||||
Reload();
|
||||
return;
|
||||
case DesignChanged.Type.Renamed when (data as RenameTransaction?)?.Old is { } oldName:
|
||||
if (!TryGetValue(design, out var leaf2))
|
||||
return;
|
||||
|
||||
var old = oldName.FixName();
|
||||
if (old == leaf2.Name || leaf2.Name.IsDuplicateName(out var baseName, out _) && baseName == old)
|
||||
RenameWithDuplicates(leaf2, design.Name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Used for saving and loading.
|
||||
private static string DesignToIdentifier(Design design)
|
||||
=> design.Identifier.ToString();
|
||||
|
||||
private static string DesignToName(Design design)
|
||||
=> design.Name.Text.FixName();
|
||||
|
||||
private static bool DesignHasDefaultPath(Design design, string fullPath)
|
||||
{
|
||||
var regex = new Regex($@"^{Regex.Escape(DesignToName(design))}( \(\d+\))?$");
|
||||
return regex.IsMatch(fullPath);
|
||||
}
|
||||
|
||||
private static (string, bool) SaveDesign(Design design, string fullPath)
|
||||
// Only save pairs with non-default paths.
|
||||
=> DesignHasDefaultPath(design, fullPath)
|
||||
? (string.Empty, false)
|
||||
: (DesignToIdentifier(design), true);
|
||||
|
||||
internal static void MigrateOldPaths(SaveService saveService, Dictionary<string, string> oldPaths)
|
||||
{
|
||||
if (oldPaths.Count == 0)
|
||||
return;
|
||||
|
||||
var file = saveService.FileNames.DesignFileSystem;
|
||||
try
|
||||
{
|
||||
JObject jObject;
|
||||
if (File.Exists(file))
|
||||
{
|
||||
var text = File.ReadAllText(file);
|
||||
jObject = JObject.Parse(text);
|
||||
var dict = jObject["Data"]?.ToObject<Dictionary<string, string>>();
|
||||
if (dict != null)
|
||||
foreach (var (key, value) in dict)
|
||||
oldPaths.TryAdd(key, value);
|
||||
|
||||
jObject["Data"] = JToken.FromObject(oldPaths);
|
||||
}
|
||||
else
|
||||
{
|
||||
jObject = new JObject
|
||||
{
|
||||
["Data"] = JToken.FromObject(oldPaths),
|
||||
["EmptyFolders"] = JToken.FromObject(Array.Empty<string>()),
|
||||
};
|
||||
}
|
||||
|
||||
var data = jObject.ToString(Formatting.Indented);
|
||||
File.WriteAllText(file, data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Log.Error($"Could not migrate old folder paths to new version:\n{ex}");
|
||||
}
|
||||
}
|
||||
|
||||
public string ToFilename(FilenameService fileNames)
|
||||
=> fileNames.DesignFileSystem;
|
||||
|
||||
public void Save(StreamWriter writer)
|
||||
{
|
||||
SaveToFile(writer, SaveDesign, true);
|
||||
_tabSelected.Unsubscribe(OnTabSelected);
|
||||
_designChanged.Unsubscribe(OnDesignChanged);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
57
Glamourer/Designs/DesignFileSystemSaver.cs
Normal file
57
Glamourer/Designs/DesignFileSystemSaver.cs
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
using Glamourer.Services;
|
||||
using Luna;
|
||||
|
||||
namespace Glamourer.Designs;
|
||||
|
||||
public sealed class DesignFileSystemSaver(Logger log, BaseFileSystem fileSystem, SaveService saveService, DesignStorage designs)
|
||||
: FileSystemSaver<SaveService, FilenameService>(log, fileSystem, saveService)
|
||||
{
|
||||
protected override void SaveDataValue(IFileSystemValue value)
|
||||
{
|
||||
if (value is Design design)
|
||||
SaveService.QueueSave(design);
|
||||
}
|
||||
|
||||
protected override string LockedFile(FilenameService provider)
|
||||
=> provider.FileSystemLockedNodes;
|
||||
|
||||
protected override string ExpandedFile(FilenameService provider)
|
||||
=> provider.FileSystemExpandedFolders;
|
||||
|
||||
protected override string EmptyFoldersFile(FilenameService provider)
|
||||
=> provider.FileSystemEmptyFolders;
|
||||
|
||||
protected override string SelectionFile(FilenameService provider)
|
||||
=> provider.FileSystemSelectedNodes;
|
||||
|
||||
protected override string MigrationFile(FilenameService provider)
|
||||
=> provider.MigrationDesignFileSystem;
|
||||
|
||||
protected override bool GetValueFromIdentifier(ReadOnlySpan<char> identifier, [NotNullWhen(true)] out IFileSystemValue? value)
|
||||
{
|
||||
if (!Guid.TryParse(identifier, out var guid))
|
||||
{
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = designs.FirstOrDefault(d => d.Identifier == guid);
|
||||
return value is not null;
|
||||
}
|
||||
|
||||
protected override void CreateDataNodes()
|
||||
{
|
||||
foreach (var design in designs)
|
||||
{
|
||||
try
|
||||
{
|
||||
var folder = design.Path.Folder.Length is 0 ? FileSystem.Root : FileSystem.FindOrCreateAllFolders(design.Path.Folder);
|
||||
FileSystem.CreateDuplicateDataNode(folder, design.Path.SortName ?? design.Name, design);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error($"Could not create folder structure for design {design.Name} at path {design.Path.Folder}: {ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
using Dalamud.Utility;
|
||||
using Glamourer.Config;
|
||||
using Glamourer.Designs.History;
|
||||
using Glamourer.Designs.Links;
|
||||
using Glamourer.Events;
|
||||
|
|
@ -6,7 +7,7 @@ using Glamourer.GameData;
|
|||
using Glamourer.Interop.Material;
|
||||
using Glamourer.Interop.Penumbra;
|
||||
using Glamourer.Services;
|
||||
using OtterGui.Extensions;
|
||||
using Luna;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Penumbra.GameData.DataContainers;
|
||||
|
|
@ -15,7 +16,7 @@ using Penumbra.GameData.Enums;
|
|||
|
||||
namespace Glamourer.Designs;
|
||||
|
||||
public sealed class DesignManager : DesignEditor
|
||||
public sealed class DesignManager : DesignEditor, IService
|
||||
{
|
||||
public readonly DesignStorage Designs;
|
||||
private readonly HumanModelList _humans;
|
||||
|
|
@ -88,7 +89,7 @@ public sealed class DesignManager : DesignEditor
|
|||
|
||||
Glamourer.Log.Information(
|
||||
$"Loaded {Designs.Count} designs in {stopwatch.ElapsedMilliseconds} ms.{(skipped > 0 ? $" Skipped loading {skipped} designs due to errors." : string.Empty)}");
|
||||
DesignChanged.Invoke(DesignChanged.Type.ReloadedAll, null!, null);
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ReloadedAll, null!));
|
||||
}
|
||||
|
||||
/// <summary> Create a new temporary design without adding it to the manager. </summary>
|
||||
|
|
@ -115,7 +116,7 @@ public sealed class DesignManager : DesignEditor
|
|||
Designs.Add(design);
|
||||
Glamourer.Log.Debug($"Added new design {design.Identifier}.");
|
||||
SaveService.ImmediateSave(design);
|
||||
DesignChanged.Invoke(DesignChanged.Type.Created, design, new CreationTransaction(actualName, path));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Created, design, new CreationTransaction(actualName, path)));
|
||||
return design;
|
||||
}
|
||||
|
||||
|
|
@ -140,7 +141,7 @@ public sealed class DesignManager : DesignEditor
|
|||
Designs.Add(design);
|
||||
Glamourer.Log.Debug($"Added new design {design.Identifier} by cloning Temporary Design.");
|
||||
SaveService.ImmediateSave(design);
|
||||
DesignChanged.Invoke(DesignChanged.Type.Created, design, new CreationTransaction(actualName, path));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Created, design, new CreationTransaction(actualName, path)));
|
||||
return design;
|
||||
}
|
||||
|
||||
|
|
@ -161,7 +162,7 @@ public sealed class DesignManager : DesignEditor
|
|||
Glamourer.Log.Debug(
|
||||
$"Added new design {design.Identifier} by cloning {clone.Identifier.ToString()}.");
|
||||
SaveService.ImmediateSave(design);
|
||||
DesignChanged.Invoke(DesignChanged.Type.Created, design, new CreationTransaction(actualName, path));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Created, design, new CreationTransaction(actualName, path)));
|
||||
return design;
|
||||
}
|
||||
|
||||
|
|
@ -172,7 +173,7 @@ public sealed class DesignManager : DesignEditor
|
|||
--d.Index;
|
||||
Designs.RemoveAt(design.Index);
|
||||
SaveService.ImmediateDelete(design);
|
||||
DesignChanged.Invoke(DesignChanged.Type.Deleted, design, null);
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Deleted, design));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
@ -182,7 +183,7 @@ public sealed class DesignManager : DesignEditor
|
|||
/// <summary> Rename a design. </summary>
|
||||
public void Rename(Design design, string newName)
|
||||
{
|
||||
var oldName = design.Name.Text;
|
||||
var oldName = design.Name;
|
||||
if (oldName == newName)
|
||||
return;
|
||||
|
||||
|
|
@ -190,7 +191,7 @@ public sealed class DesignManager : DesignEditor
|
|||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.QueueSave(design);
|
||||
Glamourer.Log.Debug($"Renamed design {design.Identifier}.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.Renamed, design, new RenameTransaction(oldName, newName));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Renamed, design, new RenameTransaction(oldName, newName)));
|
||||
}
|
||||
|
||||
/// <summary> Change the description of a design. </summary>
|
||||
|
|
@ -204,7 +205,7 @@ public sealed class DesignManager : DesignEditor
|
|||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.QueueSave(design);
|
||||
Glamourer.Log.Debug($"Changed description of design {design.Identifier}.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.ChangedDescription, design, new DescriptionTransaction(oldDescription, description));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ChangedDescription, design, new DescriptionTransaction(oldDescription, description)));
|
||||
}
|
||||
|
||||
/// <summary> Change the associated color of a design. </summary>
|
||||
|
|
@ -218,7 +219,7 @@ public sealed class DesignManager : DesignEditor
|
|||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.QueueSave(design);
|
||||
Glamourer.Log.Debug($"Changed color of design {design.Identifier}.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.ChangedColor, design, new DesignColorTransaction(oldColor, newColor));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ChangedColor, design, new DesignColorTransaction(oldColor, newColor)));
|
||||
}
|
||||
|
||||
/// <summary> Add a new tag to a design. The tags remain sorted. </summary>
|
||||
|
|
@ -232,7 +233,7 @@ public sealed class DesignManager : DesignEditor
|
|||
var idx = design.Tags.AsEnumerable().IndexOf(tag);
|
||||
SaveService.QueueSave(design);
|
||||
Glamourer.Log.Debug($"Added tag {tag} at {idx} to design {design.Identifier}.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.AddedTag, design, new TagAddedTransaction(tag, idx));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.AddedTag, design, new TagAddedTransaction(tag, idx)));
|
||||
}
|
||||
|
||||
/// <summary> Remove a tag from a design by its index. </summary>
|
||||
|
|
@ -246,7 +247,7 @@ public sealed class DesignManager : DesignEditor
|
|||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.QueueSave(design);
|
||||
Glamourer.Log.Debug($"Removed tag {oldTag} at {tagIdx} from design {design.Identifier}.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.RemovedTag, design, new TagRemovedTransaction(oldTag, tagIdx));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.RemovedTag, design, new TagRemovedTransaction(oldTag, tagIdx)));
|
||||
}
|
||||
|
||||
/// <summary> Rename a tag from a design by its index. The tags stay sorted.</summary>
|
||||
|
|
@ -261,8 +262,8 @@ public sealed class DesignManager : DesignEditor
|
|||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.QueueSave(design);
|
||||
Glamourer.Log.Debug($"Renamed tag {oldTag} at {tagIdx} to {newTag} in design {design.Identifier} and reordered tags.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.ChangedTag, design,
|
||||
new TagChangedTransaction(oldTag, newTag, tagIdx, design.Tags.AsEnumerable().IndexOf(newTag)));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ChangedTag, design,
|
||||
new TagChangedTransaction(oldTag, newTag, tagIdx, design.Tags.AsEnumerable().IndexOf(newTag))));
|
||||
}
|
||||
|
||||
/// <summary> Add an associated mod to a design. </summary>
|
||||
|
|
@ -274,7 +275,7 @@ public sealed class DesignManager : DesignEditor
|
|||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.QueueSave(design);
|
||||
Glamourer.Log.Debug($"Added associated mod {mod.DirectoryName} to design {design.Identifier}.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.AddedMod, design, new ModAddedTransaction(mod, settings));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.AddedMod, design, new ModAddedTransaction(mod, settings)));
|
||||
}
|
||||
|
||||
/// <summary> Remove an associated mod from a design. </summary>
|
||||
|
|
@ -286,7 +287,7 @@ public sealed class DesignManager : DesignEditor
|
|||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.QueueSave(design);
|
||||
Glamourer.Log.Debug($"Removed associated mod {mod.DirectoryName} from design {design.Identifier}.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.RemovedMod, design, new ModRemovedTransaction(mod, settings));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.RemovedMod, design, new ModRemovedTransaction(mod, settings)));
|
||||
}
|
||||
|
||||
/// <summary> Add or update an associated mod to a design. </summary>
|
||||
|
|
@ -299,12 +300,12 @@ public sealed class DesignManager : DesignEditor
|
|||
if (hasOldSettings)
|
||||
{
|
||||
Glamourer.Log.Debug($"Updated associated mod {mod.DirectoryName} from design {design.Identifier}.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.UpdatedMod, design, new ModUpdatedTransaction(mod, oldSettings, settings));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.UpdatedMod, design, new ModUpdatedTransaction(mod, oldSettings, settings)));
|
||||
}
|
||||
else
|
||||
{
|
||||
Glamourer.Log.Debug($"Added associated mod {mod.DirectoryName} from design {design.Identifier}.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.AddedMod, design, new ModAddedTransaction(mod, settings));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.AddedMod, design, new ModAddedTransaction(mod, settings)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -316,7 +317,7 @@ public sealed class DesignManager : DesignEditor
|
|||
|
||||
SaveService.QueueSave(design);
|
||||
Glamourer.Log.Debug($"Set design {design.Identifier} to {(value ? "no longer be " : string.Empty)} write-protected.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.WriteProtection, design, null);
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.WriteProtection, design));
|
||||
}
|
||||
|
||||
/// <summary> Set the quick design bar display status of a design. </summary>
|
||||
|
|
@ -329,7 +330,7 @@ public sealed class DesignManager : DesignEditor
|
|||
SaveService.QueueSave(design);
|
||||
Glamourer.Log.Debug(
|
||||
$"Set design {design.Identifier} to {(!value ? "no longer be " : string.Empty)} displayed in the quick design bar.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.QuickDesignBar, design, null);
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.QuickDesignBar, design));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
@ -344,7 +345,7 @@ public sealed class DesignManager : DesignEditor
|
|||
design.ForcedRedraw = forcedRedraw;
|
||||
SaveService.QueueSave(design);
|
||||
Glamourer.Log.Debug($"Set {design.Identifier} to {(forcedRedraw ? string.Empty : "not")} force redraws.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.ForceRedraw, design, null);
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ForceRedraw, design));
|
||||
}
|
||||
|
||||
public void ChangeResetAdvancedDyes(Design design, bool resetAdvancedDyes)
|
||||
|
|
@ -355,7 +356,7 @@ public sealed class DesignManager : DesignEditor
|
|||
design.ResetAdvancedDyes = resetAdvancedDyes;
|
||||
SaveService.QueueSave(design);
|
||||
Glamourer.Log.Debug($"Set {design.Identifier} to {(resetAdvancedDyes ? string.Empty : "not")} reset advanced dyes.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.ResetAdvancedDyes, design, null);
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ResetAdvancedDyes, design));
|
||||
}
|
||||
|
||||
public void ChangeResetTemporarySettings(Design design, bool resetTemporarySettings)
|
||||
|
|
@ -366,7 +367,7 @@ public sealed class DesignManager : DesignEditor
|
|||
design.ResetTemporarySettings = resetTemporarySettings;
|
||||
SaveService.QueueSave(design);
|
||||
Glamourer.Log.Debug($"Set {design.Identifier} to {(resetTemporarySettings ? string.Empty : "not")} reset temporary settings.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.ResetTemporarySettings, design, null);
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ResetTemporarySettings, design));
|
||||
}
|
||||
|
||||
/// <summary> Change whether to apply a specific customize value. </summary>
|
||||
|
|
@ -378,7 +379,7 @@ public sealed class DesignManager : DesignEditor
|
|||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.QueueSave(design);
|
||||
Glamourer.Log.Debug($"Set applying of customization {idx.ToName()} to {value}.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.ApplyCustomize, design, new ApplicationTransaction(idx, !value, value));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ApplyCustomize, design, new ApplicationTransaction(idx, !value, value)));
|
||||
}
|
||||
|
||||
/// <summary> Change whether to apply a specific equipment piece. </summary>
|
||||
|
|
@ -390,7 +391,7 @@ public sealed class DesignManager : DesignEditor
|
|||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.QueueSave(design);
|
||||
Glamourer.Log.Debug($"Set applying of {slot} equipment piece to {value}.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.ApplyEquip, design, new ApplicationTransaction((slot, false), !value, value));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ApplyEquip, design, new ApplicationTransaction((slot, false), !value, value)));
|
||||
}
|
||||
|
||||
/// <summary> Change whether to apply a specific equipment piece. </summary>
|
||||
|
|
@ -402,7 +403,7 @@ public sealed class DesignManager : DesignEditor
|
|||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.QueueSave(design);
|
||||
Glamourer.Log.Debug($"Set applying of {slot} bonus item to {value}.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.ApplyBonusItem, design, new ApplicationTransaction(slot, !value, value));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ApplyBonusItem, design, new ApplicationTransaction(slot, !value, value)));
|
||||
}
|
||||
|
||||
/// <summary> Change whether to apply a specific stain. </summary>
|
||||
|
|
@ -414,7 +415,7 @@ public sealed class DesignManager : DesignEditor
|
|||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.QueueSave(design);
|
||||
Glamourer.Log.Debug($"Set applying of stain of {slot} equipment piece to {value}.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.ApplyStain, design, new ApplicationTransaction((slot, true), !value, value));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ApplyStain, design, new ApplicationTransaction((slot, true), !value, value)));
|
||||
}
|
||||
|
||||
/// <summary> Change whether to apply a specific crest visibility. </summary>
|
||||
|
|
@ -426,7 +427,7 @@ public sealed class DesignManager : DesignEditor
|
|||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.QueueSave(design);
|
||||
Glamourer.Log.Debug($"Set applying of crest visibility of {slot} equipment piece to {value}.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.ApplyCrest, design, new ApplicationTransaction(slot, !value, value));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ApplyCrest, design, new ApplicationTransaction(slot, !value, value)));
|
||||
}
|
||||
|
||||
/// <summary> Change the application value of one of the meta flags. </summary>
|
||||
|
|
@ -438,7 +439,7 @@ public sealed class DesignManager : DesignEditor
|
|||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.QueueSave(design);
|
||||
Glamourer.Log.Debug($"Set applying of {metaIndex} to {value}.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.Other, design, new ApplicationTransaction(metaIndex, !value, value));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Other, design, new ApplicationTransaction(metaIndex, !value, value)));
|
||||
}
|
||||
|
||||
/// <summary> Change the application value of a customize parameter. </summary>
|
||||
|
|
@ -450,7 +451,7 @@ public sealed class DesignManager : DesignEditor
|
|||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.QueueSave(design);
|
||||
Glamourer.Log.Debug($"Set applying of parameter {flag} to {value}.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.ApplyParameter, design, new ApplicationTransaction(flag, !value, value));
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ApplyParameter, design, new ApplicationTransaction(flag, !value, value)));
|
||||
}
|
||||
|
||||
/// <summary> Change multiple application values at once. </summary>
|
||||
|
|
@ -511,7 +512,6 @@ public sealed class DesignManager : DesignEditor
|
|||
{
|
||||
var text = File.ReadAllText(SaveService.FileNames.MigrationDesignFile);
|
||||
var dict = JsonConvert.DeserializeObject<Dictionary<string, string>>(text) ?? new Dictionary<string, string>();
|
||||
var migratedFileSystemPaths = new Dictionary<string, string>(dict.Count);
|
||||
foreach (var (name, base64) in dict)
|
||||
{
|
||||
try
|
||||
|
|
@ -528,7 +528,6 @@ public sealed class DesignManager : DesignEditor
|
|||
if (!oldDesigns.Any(d => d.Name == design.Name && d.CreationDate == design.CreationDate))
|
||||
{
|
||||
Add(design, $"Migrated old design to {design.Identifier}.");
|
||||
migratedFileSystemPaths.Add(design.Identifier.ToString(), name);
|
||||
++successes;
|
||||
}
|
||||
else
|
||||
|
|
@ -545,7 +544,6 @@ public sealed class DesignManager : DesignEditor
|
|||
}
|
||||
}
|
||||
|
||||
DesignFileSystem.MigrateOldPaths(SaveService, migratedFileSystemPaths);
|
||||
Glamourer.Log.Information(
|
||||
$"Successfully migrated {successes} old designs. Skipped {skips} already migrated designs. Failed to migrate {errors} designs.");
|
||||
}
|
||||
|
|
@ -632,7 +630,7 @@ public sealed class DesignManager : DesignEditor
|
|||
if (!message.IsNullOrEmpty())
|
||||
Glamourer.Log.Debug(message);
|
||||
SaveService.ImmediateSave(design);
|
||||
DesignChanged.Invoke(DesignChanged.Type.Created, design, null);
|
||||
DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Created, design));
|
||||
}
|
||||
|
||||
/// <summary> Split a given string into its folder path and its name, if <paramref name="handlePath"/> is true. </summary>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
using Glamourer.Api.Enums;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.State;
|
||||
using Luna;
|
||||
using Penumbra.GameData.Interop;
|
||||
|
||||
namespace Glamourer.Designs.History;
|
||||
|
||||
|
|
@ -171,21 +169,21 @@ public class EditorHistory : IDisposable, IService
|
|||
}
|
||||
|
||||
|
||||
private void OnStateChanged(StateChangeType type, StateSource source, ActorState state, ActorData actors, ITransaction? data)
|
||||
private void OnStateChanged(in StateChanged.Arguments arguments)
|
||||
{
|
||||
if (_undoMode || source is not StateSource.Manual)
|
||||
if (_undoMode || arguments.Source is not StateSource.Manual)
|
||||
return;
|
||||
|
||||
if (data is not null)
|
||||
AddStateTransaction(state, data);
|
||||
if (arguments.Transaction is not null)
|
||||
AddStateTransaction(arguments.State, arguments.Transaction);
|
||||
}
|
||||
|
||||
private void OnDesignChanged(DesignChanged.Type type, Design design, ITransaction? data)
|
||||
private void OnDesignChanged(in DesignChanged.Arguments arguments)
|
||||
{
|
||||
if (_undoMode)
|
||||
return;
|
||||
|
||||
if (data is not null)
|
||||
AddDesignTransaction(design, data);
|
||||
if (arguments.Transaction is not null)
|
||||
AddDesignTransaction(arguments.Design, arguments.Transaction);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ public interface ITransaction
|
|||
public void Revert(IDesignEditor editor, object data);
|
||||
}
|
||||
|
||||
public readonly record struct CustomizeTransaction(CustomizeIndex Slot, CustomizeValue Old, CustomizeValue New)
|
||||
public record CustomizeTransaction(CustomizeIndex Slot, CustomizeValue Old, CustomizeValue New)
|
||||
: ITransaction
|
||||
{
|
||||
public ITransaction? Merge(ITransaction older)
|
||||
|
|
@ -20,7 +20,7 @@ public readonly record struct CustomizeTransaction(CustomizeIndex Slot, Customiz
|
|||
=> editor.ChangeCustomize(data, Slot, Old, ApplySettings.Manual);
|
||||
}
|
||||
|
||||
public readonly record struct EntireCustomizeTransaction(CustomizeFlag Apply, CustomizeArray Old, CustomizeArray New)
|
||||
public record EntireCustomizeTransaction(CustomizeFlag Apply, CustomizeArray Old, CustomizeArray New)
|
||||
: ITransaction
|
||||
{
|
||||
public ITransaction? Merge(ITransaction older)
|
||||
|
|
@ -30,7 +30,7 @@ public readonly record struct EntireCustomizeTransaction(CustomizeFlag Apply, Cu
|
|||
=> editor.ChangeEntireCustomize(data, Old, Apply, ApplySettings.Manual);
|
||||
}
|
||||
|
||||
public readonly record struct EquipTransaction(EquipSlot Slot, EquipItem Old, EquipItem New)
|
||||
public record EquipTransaction(EquipSlot Slot, EquipItem Old, EquipItem New)
|
||||
: ITransaction
|
||||
{
|
||||
public ITransaction? Merge(ITransaction older)
|
||||
|
|
@ -40,7 +40,7 @@ public readonly record struct EquipTransaction(EquipSlot Slot, EquipItem Old, Eq
|
|||
=> editor.ChangeItem(data, Slot, Old, ApplySettings.Manual);
|
||||
}
|
||||
|
||||
public readonly record struct BonusItemTransaction(BonusItemFlag Slot, EquipItem Old, EquipItem New)
|
||||
public record BonusItemTransaction(BonusItemFlag Slot, EquipItem Old, EquipItem New)
|
||||
: ITransaction
|
||||
{
|
||||
public ITransaction? Merge(ITransaction older)
|
||||
|
|
@ -50,7 +50,7 @@ public readonly record struct BonusItemTransaction(BonusItemFlag Slot, EquipItem
|
|||
=> editor.ChangeBonusItem(data, Slot, Old, ApplySettings.Manual);
|
||||
}
|
||||
|
||||
public readonly record struct WeaponTransaction(
|
||||
public record WeaponTransaction(
|
||||
EquipItem OldMain,
|
||||
EquipItem OldOff,
|
||||
EquipItem OldGauntlets,
|
||||
|
|
@ -72,7 +72,7 @@ public readonly record struct WeaponTransaction(
|
|||
}
|
||||
}
|
||||
|
||||
public readonly record struct StainTransaction(EquipSlot Slot, StainIds Old, StainIds New)
|
||||
public record StainTransaction(EquipSlot Slot, StainIds Old, StainIds New)
|
||||
: ITransaction
|
||||
{
|
||||
public ITransaction? Merge(ITransaction older)
|
||||
|
|
@ -82,7 +82,7 @@ public readonly record struct StainTransaction(EquipSlot Slot, StainIds Old, Sta
|
|||
=> editor.ChangeStains(data, Slot, Old, ApplySettings.Manual);
|
||||
}
|
||||
|
||||
public readonly record struct CrestTransaction(CrestFlag Slot, bool Old, bool New)
|
||||
public record CrestTransaction(CrestFlag Slot, bool Old, bool New)
|
||||
: ITransaction
|
||||
{
|
||||
public ITransaction? Merge(ITransaction older)
|
||||
|
|
@ -92,7 +92,7 @@ public readonly record struct CrestTransaction(CrestFlag Slot, bool Old, bool Ne
|
|||
=> editor.ChangeCrest(data, Slot, Old, ApplySettings.Manual);
|
||||
}
|
||||
|
||||
public readonly record struct ParameterTransaction(CustomizeParameterFlag Slot, CustomizeParameterValue Old, CustomizeParameterValue New)
|
||||
public record ParameterTransaction(CustomizeParameterFlag Slot, CustomizeParameterValue Old, CustomizeParameterValue New)
|
||||
: ITransaction
|
||||
{
|
||||
public ITransaction? Merge(ITransaction older)
|
||||
|
|
@ -102,7 +102,7 @@ public readonly record struct ParameterTransaction(CustomizeParameterFlag Slot,
|
|||
=> editor.ChangeCustomizeParameter(data, Slot, Old, ApplySettings.Manual);
|
||||
}
|
||||
|
||||
public readonly record struct MetaTransaction(MetaIndex Slot, bool Old, bool New)
|
||||
public record MetaTransaction(MetaIndex Slot, bool Old, bool New)
|
||||
: ITransaction
|
||||
{
|
||||
public ITransaction? Merge(ITransaction older)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
using Glamourer.Automation;
|
||||
using Glamourer.Designs.History;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Services;
|
||||
using Luna;
|
||||
|
|
@ -32,7 +31,7 @@ public sealed class DesignLinkManager : IService, IDisposable
|
|||
parent.LastEdit = DateTimeOffset.UtcNow;
|
||||
_saveService.QueueSave(parent);
|
||||
Glamourer.Log.Debug($"Moved link from {orderFrom} {idxFrom} to {idxTo} {orderTo}.");
|
||||
_event.Invoke(DesignChanged.Type.ChangedLink, parent, null);
|
||||
_event.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ChangedLink, parent));
|
||||
}
|
||||
|
||||
public void AddDesignLink(Design parent, Design child, LinkOrder order)
|
||||
|
|
@ -43,7 +42,7 @@ public sealed class DesignLinkManager : IService, IDisposable
|
|||
parent.LastEdit = DateTimeOffset.UtcNow;
|
||||
_saveService.QueueSave(parent);
|
||||
Glamourer.Log.Debug($"Added new {order} link to {child.Identifier} for {parent.Identifier}.");
|
||||
_event.Invoke(DesignChanged.Type.ChangedLink, parent, null);
|
||||
_event.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ChangedLink, parent));
|
||||
}
|
||||
|
||||
public void RemoveDesignLink(Design parent, int idx, LinkOrder order)
|
||||
|
|
@ -54,7 +53,7 @@ public sealed class DesignLinkManager : IService, IDisposable
|
|||
parent.LastEdit = DateTimeOffset.UtcNow;
|
||||
_saveService.QueueSave(parent);
|
||||
Glamourer.Log.Debug($"Removed the {order} link at {idx} for {parent.Identifier}.");
|
||||
_event.Invoke(DesignChanged.Type.ChangedLink, parent, null);
|
||||
_event.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ChangedLink, parent));
|
||||
}
|
||||
|
||||
public void ChangeApplicationType(Design parent, int idx, LinkOrder order, ApplicationType applicationType)
|
||||
|
|
@ -64,22 +63,23 @@ public sealed class DesignLinkManager : IService, IDisposable
|
|||
return;
|
||||
|
||||
_saveService.QueueSave(parent);
|
||||
Glamourer.Log.Debug($"Changed link application type from {old} to {applicationType} for design link {order} {idx + 1} in design {parent.Identifier}.");
|
||||
_event.Invoke(DesignChanged.Type.ChangedLink, parent, null);
|
||||
Glamourer.Log.Debug(
|
||||
$"Changed link application type from {old} to {applicationType} for design link {order} {idx + 1} in design {parent.Identifier}.");
|
||||
_event.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ChangedLink, parent));
|
||||
}
|
||||
|
||||
private void OnDesignChanged(DesignChanged.Type type, Design deletedDesign, ITransaction? _)
|
||||
private void OnDesignChanged(in DesignChanged.Arguments arguments)
|
||||
{
|
||||
if (type is not DesignChanged.Type.Deleted)
|
||||
if (arguments.Type is not DesignChanged.Type.Deleted)
|
||||
return;
|
||||
|
||||
foreach (var design in _storage)
|
||||
{
|
||||
if (!design.Links.Remove(deletedDesign))
|
||||
if (!design.Links.Remove(arguments.Design))
|
||||
continue;
|
||||
|
||||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
Glamourer.Log.Debug($"Removed {deletedDesign.Identifier} from {design.Identifier} links due to deletion.");
|
||||
Glamourer.Log.Debug($"Removed {arguments.Design.Identifier} from {design.Identifier} links due to deletion.");
|
||||
_saveService.QueueSave(design);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Glamourer.Api.Enums;
|
||||
using Glamourer.Automation;
|
||||
using Glamourer.Config;
|
||||
using Glamourer.GameData;
|
||||
using Glamourer.Interop.Material;
|
||||
using Glamourer.Services;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
using Glamourer.Automation;
|
||||
using Luna;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using OtterGui.Filesystem;
|
||||
|
||||
namespace Glamourer.Designs.Links;
|
||||
|
||||
|
|
|
|||
99
Glamourer/Designs/SortMode.cs
Normal file
99
Glamourer/Designs/SortMode.cs
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
using System.Collections.Frozen;
|
||||
using Luna;
|
||||
|
||||
namespace Glamourer.Designs;
|
||||
|
||||
public readonly struct CreationDate : ISortMode
|
||||
{
|
||||
public static readonly CreationDate Instance = new();
|
||||
|
||||
public ReadOnlySpan<byte> Name
|
||||
=> "Creation Date (Older First)"u8;
|
||||
|
||||
public ReadOnlySpan<byte> Description
|
||||
=> "In each folder, sort all subfolders lexicographically, then sort all leaves using their creation date."u8;
|
||||
|
||||
public IEnumerable<IFileSystemNode> GetChildren(IFileSystemFolder f)
|
||||
=> f.GetSubFolders().Cast<IFileSystemNode>().Concat(f.GetLeaves().OfType<IFileSystemData<Design>>().OrderBy(l => l.Value.CreationDate));
|
||||
}
|
||||
|
||||
public readonly struct UpdateDate : ISortMode
|
||||
{
|
||||
public static readonly UpdateDate Instance = new();
|
||||
|
||||
public ReadOnlySpan<byte> Name
|
||||
=> "Update Date (Older First)"u8;
|
||||
|
||||
public ReadOnlySpan<byte> Description
|
||||
=> "In each folder, sort all subfolders lexicographically, then sort all leaves using their last update date."u8;
|
||||
|
||||
public IEnumerable<IFileSystemNode> GetChildren(IFileSystemFolder f)
|
||||
=> f.GetSubFolders().Cast<IFileSystemNode>().Concat(f.GetLeaves().OfType<IFileSystemData<Design>>().OrderBy(l => l.Value.LastEdit));
|
||||
}
|
||||
|
||||
public readonly struct InverseCreationDate : ISortMode
|
||||
{
|
||||
public static readonly InverseCreationDate Instance = new();
|
||||
|
||||
public ReadOnlySpan<byte> Name
|
||||
=> "Creation Date (Newer First)"u8;
|
||||
|
||||
public ReadOnlySpan<byte> Description
|
||||
=> "In each folder, sort all subfolders lexicographically, then sort all leaves using their inverse creation date."u8;
|
||||
|
||||
public IEnumerable<IFileSystemNode> GetChildren(IFileSystemFolder f)
|
||||
=> f.GetSubFolders().Cast<IFileSystemNode>()
|
||||
.Concat(f.GetLeaves().OfType<IFileSystemData<Design>>().OrderByDescending(l => l.Value.CreationDate));
|
||||
}
|
||||
|
||||
public readonly struct InverseUpdateDate : ISortMode
|
||||
{
|
||||
public static readonly InverseUpdateDate Instance = new();
|
||||
|
||||
public ReadOnlySpan<byte> Name
|
||||
=> "Update Date (Newer First)"u8;
|
||||
|
||||
public ReadOnlySpan<byte> Description
|
||||
=> "In each folder, sort all subfolders lexicographically, then sort all leaves using their inverse last update date."u8;
|
||||
|
||||
public IEnumerable<IFileSystemNode> GetChildren(IFileSystemFolder f)
|
||||
=> f.GetSubFolders().Cast<IFileSystemNode>()
|
||||
.Concat(f.GetLeaves().OfType<IFileSystemData<Design>>().OrderByDescending(l => l.Value.LastEdit));
|
||||
}
|
||||
|
||||
public static class SortModeExtensions
|
||||
{
|
||||
private static readonly FrozenDictionary<string, ISortMode> ValidSortModes = new Dictionary<string, ISortMode>
|
||||
{
|
||||
[nameof(ISortMode.FoldersFirst)] = ISortMode.FoldersFirst,
|
||||
[nameof(ISortMode.Lexicographical)] = ISortMode.Lexicographical,
|
||||
[nameof(CreationDate)] = ISortMode.CreationDate,
|
||||
[nameof(InverseCreationDate)] = ISortMode.InverseCreationDate,
|
||||
[nameof(UpdateDate)] = ISortMode.UpdateDate,
|
||||
[nameof(InverseUpdateDate)] = ISortMode.InverseUpdateDate,
|
||||
[nameof(ISortMode.InverseFoldersFirst)] = ISortMode.InverseFoldersFirst,
|
||||
[nameof(ISortMode.InverseLexicographical)] = ISortMode.InverseLexicographical,
|
||||
[nameof(ISortMode.FoldersLast)] = ISortMode.FoldersLast,
|
||||
[nameof(ISortMode.InverseFoldersLast)] = ISortMode.InverseFoldersLast,
|
||||
[nameof(ISortMode.InternalOrder)] = ISortMode.InternalOrder,
|
||||
[nameof(ISortMode.InverseInternalOrder)] = ISortMode.InverseInternalOrder,
|
||||
}.ToFrozenDictionary();
|
||||
|
||||
extension(ISortMode)
|
||||
{
|
||||
public static ISortMode CreationDate
|
||||
=> CreationDate.Instance;
|
||||
|
||||
public static ISortMode InverseCreationDate
|
||||
=> InverseCreationDate.Instance;
|
||||
|
||||
public static ISortMode UpdateDate
|
||||
=> UpdateDate.Instance;
|
||||
|
||||
public static ISortMode InverseUpdateDate
|
||||
=> InverseUpdateDate.Instance;
|
||||
|
||||
public static IReadOnlyDictionary<string, ISortMode> Valid
|
||||
=> ValidSortModes;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
|
||||
using Glamourer.Config;
|
||||
using Luna;
|
||||
|
||||
namespace Glamourer.Designs.Special;
|
||||
|
||||
public class RandomDesignGenerator(DesignStorage designs, DesignFileSystem fileSystem, Configuration config) : IService
|
||||
public class RandomDesignGenerator(DesignStorage designs, Configuration config) : IService
|
||||
{
|
||||
private readonly Random _rng = new();
|
||||
private readonly WeakReference<Design> _lastDesign = new(null!, false);
|
||||
|
|
@ -34,7 +34,7 @@ public class RandomDesignGenerator(DesignStorage designs, DesignFileSystem fileS
|
|||
=> Design(designs);
|
||||
|
||||
public Design? Design(IDesignPredicate predicate)
|
||||
=> Design(predicate.Get(designs, fileSystem).ToList());
|
||||
=> Design(predicate.Get(designs).ToList());
|
||||
|
||||
public Design? Design(IReadOnlyList<IDesignPredicate> predicates)
|
||||
{
|
||||
|
|
@ -42,7 +42,7 @@ public class RandomDesignGenerator(DesignStorage designs, DesignFileSystem fileS
|
|||
{
|
||||
0 => Design(),
|
||||
1 => Design(predicates[0]),
|
||||
_ => Design(IDesignPredicate.Get(predicates, designs, fileSystem).ToList()),
|
||||
_ => Design(IDesignPredicate.Get(predicates, designs).ToList()),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,61 +1,59 @@
|
|||
using OtterGui.Classes;
|
||||
|
||||
namespace Glamourer.Designs.Special;
|
||||
namespace Glamourer.Designs.Special;
|
||||
|
||||
public interface IDesignPredicate
|
||||
{
|
||||
public bool Invoke(Design design, string lowerName, string identifier, string lowerPath);
|
||||
bool Invoke(Design design, string name, string identifier, string path);
|
||||
|
||||
public bool Invoke((Design Design, string LowerName, string Identifier, string LowerPath) args)
|
||||
=> Invoke(args.Design, args.LowerName, args.Identifier, args.LowerPath);
|
||||
bool Invoke((Design Design, string Name, string Identifier, string Path) args)
|
||||
=> Invoke(args.Design, args.Name, args.Identifier, args.Path);
|
||||
|
||||
public IEnumerable<Design> Get(IEnumerable<Design> designs, DesignFileSystem fileSystem)
|
||||
=> designs.Select(d => Transform(d, fileSystem))
|
||||
IEnumerable<Design> Get(IEnumerable<Design> designs)
|
||||
=> designs.Select(Transform)
|
||||
.Where(Invoke)
|
||||
.Select(t => t.Design);
|
||||
|
||||
public static IEnumerable<Design> Get(IReadOnlyList<IDesignPredicate> predicates, IEnumerable<Design> designs, DesignFileSystem fileSystem)
|
||||
static IEnumerable<Design> Get(IReadOnlyList<IDesignPredicate> predicates, IEnumerable<Design> designs)
|
||||
=> predicates.Count > 0
|
||||
? designs.Select(d => Transform(d, fileSystem))
|
||||
? designs.Select(Transform)
|
||||
.Where(t => predicates.Any(p => p.Invoke(t)))
|
||||
.Select(t => t.Design)
|
||||
: designs;
|
||||
|
||||
private static (Design Design, string LowerName, string Identifier, string LowerPath) Transform(Design d, DesignFileSystem fs)
|
||||
=> (d, d.Name.Lower, d.Identifier.ToString(), fs.TryGetValue(d, out var l) ? l.FullName().ToLowerInvariant() : string.Empty);
|
||||
private static (Design Design, string Name, string Identifier, string Path) Transform(Design d)
|
||||
=> (d, d.Name, d.Identifier.ToString(), d.Path.CurrentPath);
|
||||
}
|
||||
|
||||
public static class RandomPredicate
|
||||
{
|
||||
public readonly struct StartsWith(string value) : IDesignPredicate
|
||||
{
|
||||
public LowerString Value { get; } = value;
|
||||
public string Value { get; } = value;
|
||||
|
||||
public bool Invoke(Design design, string lowerName, string identifier, string lowerPath)
|
||||
=> lowerPath.StartsWith(Value.Lower);
|
||||
public bool Invoke(Design design, string name, string identifier, string path)
|
||||
=> path.StartsWith(Value, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
public override string ToString()
|
||||
=> $"/{Value.Text}";
|
||||
=> $"/{Value}";
|
||||
}
|
||||
|
||||
public readonly struct Contains(string value) : IDesignPredicate
|
||||
{
|
||||
public LowerString Value { get; } = value;
|
||||
public string Value { get; } = value;
|
||||
|
||||
public bool Invoke(Design design, string lowerName, string identifier, string lowerPath)
|
||||
public bool Invoke(Design design, string name, string identifier, string path)
|
||||
{
|
||||
if (lowerName.Contains(Value.Lower))
|
||||
if (name.Contains(Value, StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
if (identifier.Contains(Value.Lower))
|
||||
if (identifier.Contains(Value, StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
if (lowerPath.Contains(Value.Lower))
|
||||
if (path.Contains(Value, StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
=> Value.Text;
|
||||
=> Value;
|
||||
}
|
||||
|
||||
public readonly struct Exact(Exact.Type type, string value) : IDesignPredicate
|
||||
|
|
@ -69,25 +67,25 @@ public static class RandomPredicate
|
|||
Color,
|
||||
}
|
||||
|
||||
public Type Which { get; } = type;
|
||||
public LowerString Value { get; } = value;
|
||||
public Type Which { get; } = type;
|
||||
public string Value { get; } = value;
|
||||
|
||||
public bool Invoke(Design design, string lowerName, string identifier, string lowerPath)
|
||||
public bool Invoke(Design design, string name, string identifier, string path)
|
||||
=> Which switch
|
||||
{
|
||||
Type.Name => lowerName == Value.Lower,
|
||||
Type.Path => lowerPath == Value.Lower,
|
||||
Type.Identifier => identifier == Value.Lower,
|
||||
Type.Name => string.Equals(name, Value, StringComparison.OrdinalIgnoreCase),
|
||||
Type.Path => string.Equals(path, Value, StringComparison.OrdinalIgnoreCase),
|
||||
Type.Identifier => string.Equals(identifier, Value, StringComparison.OrdinalIgnoreCase),
|
||||
Type.Tag => IsContained(Value, design.Tags),
|
||||
Type.Color => design.Color == Value,
|
||||
Type.Color => string.Equals(design.Color, Value, StringComparison.OrdinalIgnoreCase),
|
||||
_ => false,
|
||||
};
|
||||
|
||||
private static bool IsContained(LowerString value, IEnumerable<string> data)
|
||||
=> data.Any(t => t == value);
|
||||
private static bool IsContained(string value, IEnumerable<string> data)
|
||||
=> data.Any(t => string.Equals(t, value, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
public override string ToString()
|
||||
=> $"\"{Which switch { Type.Name => 'n', Type.Identifier => 'i', Type.Path => 'p', Type.Tag => 't', Type.Color => 'c', _ => '?' }}?{Value.Text}\"";
|
||||
=> $"\"{Which switch { Type.Name => 'n', Type.Identifier => 'i', Type.Path => 'p', Type.Tag => 't', Type.Color => 'c', _ => '?' }}?{Value}\"";
|
||||
}
|
||||
|
||||
public static IDesignPredicate CreateSinglePredicate(string restriction)
|
||||
|
|
@ -125,7 +123,7 @@ public static class RandomPredicate
|
|||
|
||||
public static List<IDesignPredicate> GeneratePredicates(string restrictions)
|
||||
{
|
||||
if (restrictions.Length == 0)
|
||||
if (restrictions.Length is 0)
|
||||
return [];
|
||||
|
||||
List<IDesignPredicate> predicates = new(1);
|
||||
|
|
@ -153,9 +151,9 @@ public static class RandomPredicate
|
|||
|
||||
public static string GeneratePredicateString(IReadOnlyCollection<IDesignPredicate> predicates)
|
||||
{
|
||||
if (predicates.Count == 0)
|
||||
if (predicates.Count is 0)
|
||||
return string.Empty;
|
||||
if (predicates.Count == 1)
|
||||
if (predicates.Count is 1)
|
||||
return predicates.First()!.ToString()!;
|
||||
|
||||
return $"{{{string.Join("; ", predicates)}}}";
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
using OtterGui.Classes;
|
||||
using Luna;
|
||||
|
||||
namespace Glamourer.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when the auto-reload gear setting is changed in glamourer configuration.
|
||||
/// </summary>
|
||||
public sealed class AutoRedrawChanged()
|
||||
: EventWrapper<bool, AutoRedrawChanged.Priority>(nameof(AutoRedrawChanged))
|
||||
public sealed class AutoRedrawChanged(Logger log)
|
||||
: EventBase<bool, AutoRedrawChanged.Priority>(nameof(AutoRedrawChanged), log)
|
||||
{
|
||||
public enum Priority
|
||||
{
|
||||
/// <seealso cref="Api.StateApi.OnGPoseChange"/>
|
||||
StateApi = int.MinValue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,64 +1,60 @@
|
|||
using Glamourer.Automation;
|
||||
using OtterGui.Classes;
|
||||
using Glamourer.Designs;
|
||||
using Luna;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when an automated design is changed in any way.
|
||||
/// <list type="number">
|
||||
/// <item>Parameter is the type of the change </item>
|
||||
/// <item>Parameter is the added or changed design set or null on deletion. </item>
|
||||
/// <item>Parameter is additional data depending on the type of change. </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public sealed class AutomationChanged()
|
||||
: EventWrapper<AutomationChanged.Type, AutoDesignSet?, object?, AutomationChanged.Priority>(nameof(AutomationChanged))
|
||||
/// <summary> Triggered when an automated design is changed in any way. </summary>
|
||||
public sealed class AutomationChanged(Logger log)
|
||||
: EventBase<AutomationChanged.Arguments, AutomationChanged.Priority>(nameof(AutomationChanged), log)
|
||||
{
|
||||
public enum Type
|
||||
{
|
||||
/// <summary> Add a new set. Names and identifiers do not have to be unique. It is not enabled by default. Additional data is the index it gets added at and the name [(int, string)]. </summary>
|
||||
/// <summary> Add a new set. Names and identifiers do not have to be unique. It is not enabled by default. </summary>
|
||||
AddedSet,
|
||||
|
||||
/// <summary> Delete a given set. Additional data is the index it got removed from [int].</summary>
|
||||
/// <summary> Delete a given set. </summary>
|
||||
DeletedSet,
|
||||
|
||||
/// <summary> Rename a given set. Names do not have to be unique. Additional data is the old name and the new name [(string, string)]. </summary>
|
||||
/// <summary> Rename a given set. Names do not have to be unique. </summary>
|
||||
RenamedSet,
|
||||
|
||||
/// <summary> Move a given set to a different position. Additional data is the old index of the set and the new index of the set [(int, int)]. </summary>
|
||||
/// <summary> Move a given set to a different position. </summary>
|
||||
MovedSet,
|
||||
|
||||
/// <summary> Change the identifier a given set is associated with to another one. Additional data is the old identifier and the new one, and a potentially disabled other design set. [(ActorIdentifier[], ActorIdentifier, AutoDesignSet?)]. </summary>
|
||||
/// <summary> Change the identifier a given set is associated with to another one. </summary>
|
||||
ChangeIdentifier,
|
||||
|
||||
/// <summary> Toggle the enabled state of a given set. Additional data is the thus disabled other set, if any [AutoDesignSet?]. </summary>
|
||||
/// <summary> Toggle the enabled state of a given set. </summary>
|
||||
ToggleSet,
|
||||
|
||||
/// <summary> Change the used base state of a given set. Additional data is prior and new base. [(AutoDesignSet.Base, AutoDesignSet.Base)]. </summary>
|
||||
/// <summary> Change the used base state of a given set. </summary>
|
||||
ChangedBase,
|
||||
|
||||
/// <summary> Change the resetting of temporary settings for a given set. Additional data is the new value. </summary>
|
||||
/// <summary> Change the resetting of temporary settings for a given set. </summary>
|
||||
ChangedTemporarySettingsReset,
|
||||
|
||||
/// <summary> Add a new associated design to a given set. Additional data is the index it got added at [int]. </summary>
|
||||
/// <summary> Add a new associated design to a given set. </summary>
|
||||
AddedDesign,
|
||||
|
||||
/// <summary> Remove a given associated design from a given set. Additional data is the index it got removed from [int]. </summary>
|
||||
/// <summary> Remove a given associated design from a given set. </summary>
|
||||
DeletedDesign,
|
||||
|
||||
/// <summary> Move a given associated design in the list of a given set. Additional data is the index that got moved and the index it got moved to [(int, int)]. </summary>
|
||||
/// <summary> Move a given associated design in the list of a given set. </summary>
|
||||
MovedDesign,
|
||||
|
||||
/// <summary> Change the linked design in an associated design for a given set. Additional data is the index of the changed associated design, the old linked design and the new linked design [(int, IDesignStandIn, IDesignStandIn)]. </summary>
|
||||
/// <summary> Change the linked design in an associated design for a given set. </summary>
|
||||
ChangedDesign,
|
||||
|
||||
/// <summary> Change the job condition in an associated design for a given set. Additional data is the index of the changed associated design, the old job group and the new job group [(int, JobGroup, JobGroup)]. </summary>
|
||||
/// <summary> Change the job condition in an associated design for a given set. </summary>
|
||||
ChangedConditions,
|
||||
|
||||
/// <summary> Change the application type in an associated design for a given set. Additional data is the index of the changed associated design, the old type and the new type. [(int, AutoDesign.Type, AutoDesign.Type)]. </summary>
|
||||
/// <summary> Change the application type in an associated design for a given set. </summary>
|
||||
ChangedType,
|
||||
|
||||
/// <summary> Change the additional data for a specific design type. Additional data is the index of the changed associated design and the new data. [(int, object)] </summary>
|
||||
/// <summary> Change the additional data for a specific design type. </summary>
|
||||
ChangedData,
|
||||
}
|
||||
|
||||
|
|
@ -76,4 +72,92 @@ public sealed class AutomationChanged()
|
|||
/// <seealso cref="Gui.Tabs.AutomationTab.RandomRestrictionDrawer.OnAutomationChange"/>
|
||||
RandomRestrictionDrawer = -1,
|
||||
}
|
||||
|
||||
/// <param name="Type"> The type of change. </param>
|
||||
public record Arguments(Type Type, AutoDesignSet Set)
|
||||
{
|
||||
public T As<T>() where T : Arguments
|
||||
=> (T)this;
|
||||
}
|
||||
|
||||
/// <param name="Set"> The added set. </param>
|
||||
/// <param name="Index"> The index the set was added at. </param>
|
||||
/// <param name="Name"> The name of the added set. </param>
|
||||
public sealed record AddedSetArguments(AutoDesignSet Set, int Index, string Name) : Arguments(Type.AddedSet, Set);
|
||||
|
||||
/// <param name="Set"> The deleted set. </param>
|
||||
/// <param name="Index"> The index the set was deleted from. </param>
|
||||
public sealed record DeletedSetArguments(AutoDesignSet Set, int Index) : Arguments(Type.DeletedSet, Set);
|
||||
|
||||
/// <param name="Set"> The renamed set. </param>
|
||||
/// <param name="OldName"> The old name of the set. </param>
|
||||
/// <param name="NewName"> The new name of the set. </param>
|
||||
public sealed record RenamedSetArguments(AutoDesignSet Set, string OldName, string NewName) : Arguments(Type.RenamedSet, Set);
|
||||
|
||||
/// <param name="Set"> The moved set. </param>
|
||||
/// <param name="OldIndex"> The index the set was moved from. </param>
|
||||
/// <param name="NewIndex"> The index the set was moved to. </param>
|
||||
public sealed record MovedSetArguments(AutoDesignSet Set, int OldIndex, int NewIndex) : Arguments(Type.MovedSet, Set);
|
||||
|
||||
/// <param name="Set"> The set that got its associated identifiers changed. </param>
|
||||
/// <param name="OldIdentifiers"> The prior identifiers that got removed. </param>
|
||||
/// <param name="NewIdentifier"> The new identifiers associated with the set. </param>
|
||||
/// <param name="DisabledSet"> A set that was associated with the new identifiers before and got disabled through this change, or null. </param>
|
||||
public sealed record ChangeIdentifierArguments(
|
||||
AutoDesignSet Set,
|
||||
ActorIdentifier[] OldIdentifiers,
|
||||
ActorIdentifier NewIdentifier,
|
||||
AutoDesignSet? DisabledSet) : Arguments(Type.ChangeIdentifier, Set);
|
||||
|
||||
/// <param name="Set"> The set that got toggled on or off. </param>
|
||||
/// <param name="DisabledSet"> A set with the same association that got disabled due to this change, or null. </param>
|
||||
public sealed record ToggleSetArguments(AutoDesignSet Set, AutoDesignSet? DisabledSet) : Arguments(Type.ToggleSet, Set);
|
||||
|
||||
/// <param name="Set"> The set that changed its base state. </param>
|
||||
/// <param name="OldBase"> The old base state of the set. </param>
|
||||
/// <param name="NewBase"> The new base state of the set. </param>
|
||||
public sealed record ChangedBaseArguments(AutoDesignSet Set, AutoDesignSet.Base OldBase, AutoDesignSet.Base NewBase) : Arguments(Type.ChangedBase, Set);
|
||||
|
||||
/// <param name="Set"> The set that changed whether it resets all temporary settings. </param>
|
||||
/// <param name="NewValue"> The new state of resetting temporary settings. </param>
|
||||
public sealed record ChangedTemporarySettingsResetArguments(AutoDesignSet Set, bool NewValue) : Arguments(Type.ChangedTemporarySettingsReset, Set);
|
||||
|
||||
/// <param name="Set"> The set that added a new design. </param>
|
||||
/// <param name="Index"> The index the new design was added in the set. </param>
|
||||
public sealed record AddedDesignArguments(AutoDesignSet Set, int Index) : Arguments(Type.AddedDesign, Set);
|
||||
|
||||
/// <param name="Set"> The set that removed a design. </param>
|
||||
/// <param name="Index"> The index the design was removed from. </param>
|
||||
public sealed record DeletedDesignArguments(AutoDesignSet Set, int Index) : Arguments(Type.DeletedDesign, Set);
|
||||
|
||||
/// <param name="Set"> The set that moved a design. </param>
|
||||
/// <param name="OldIndex"> The index the design was moved from in the set. </param>
|
||||
/// <param name="NewIndex"> The index the design was moved from to the set. </param>
|
||||
public sealed record MovedDesignArguments(AutoDesignSet Set, int OldIndex, int NewIndex) : Arguments(Type.MovedDesign, Set);
|
||||
|
||||
/// <param name="Set"> The set that changed a design. </param>
|
||||
/// <param name="DesignIndex"> The index of the changed design. </param>
|
||||
/// <param name="OldDesign"> The design previously assigned to the set. </param>
|
||||
/// <param name="NewDesign"> The design the old one was changed to. </param>
|
||||
public sealed record ChangedDesignArguments(AutoDesignSet Set, int DesignIndex, IDesignStandIn OldDesign, IDesignStandIn NewDesign)
|
||||
: Arguments(Type.ChangedDesign, Set);
|
||||
|
||||
/// <param name="Set"> The set that changed a job group condition. </param>
|
||||
/// <param name="DesignIndex"> The index of the changed design. </param>
|
||||
/// <param name="OldGroup"> The prior job condition for the design. </param>
|
||||
/// <param name="NewGroup"> The new job condition for the design. </param>
|
||||
public sealed record ChangedConditionsArguments(AutoDesignSet Set, int DesignIndex, JobGroup OldGroup, JobGroup NewGroup)
|
||||
: Arguments(Type.ChangedConditions, Set);
|
||||
|
||||
/// <param name="Set"> The set that changed its application type. </param>
|
||||
/// <param name="DesignIndex"> The index of the changed design. </param>
|
||||
/// <param name="OldType"> The old application flags. </param>
|
||||
/// <param name="NewType"> The new application flags. </param>
|
||||
public sealed record ChangedTypeArguments(AutoDesignSet Set, int DesignIndex, ApplicationType OldType, ApplicationType NewType)
|
||||
: Arguments(Type.ChangedType, Set);
|
||||
|
||||
/// <param name="Set"> The set that got a design data changed. </param>
|
||||
/// <param name="DesignIndex"> The index of the changed design. </param>
|
||||
/// <param name="NewData"> The new additional data for the changed design. </param>
|
||||
public sealed record ChangedDataArguments(AutoDesignSet Set, int DesignIndex, object NewData) : Arguments(Type.ChangedData, Set);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,32 @@
|
|||
using OtterGui.Classes;
|
||||
using Luna;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Interop;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when a model flags a bonus slot for an update.
|
||||
/// <list type="number">
|
||||
/// <item>Parameter is the model with a flagged slot. </item>
|
||||
/// <item>Parameter is the bonus slot changed. </item>
|
||||
/// <item>Parameter is the model values to change the bonus piece to. </item>
|
||||
/// <item>Parameter is the return value the function should return, if it is ulong.MaxValue, the original will be called and returned. </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public sealed class BonusSlotUpdating()
|
||||
: EventWrapperRef34<Model, BonusItemFlag, CharacterArmor, ulong, BonusSlotUpdating.Priority>(nameof(BonusSlotUpdating))
|
||||
/// <summary> Triggered when a model flags a bonus slot for an update. </summary>
|
||||
public sealed class BonusSlotUpdating(Logger log)
|
||||
: EventBase<BonusSlotUpdating.Arguments, BonusSlotUpdating.Priority>(nameof(BonusSlotUpdating), log)
|
||||
{
|
||||
public enum Priority
|
||||
{
|
||||
/// <seealso cref="State.StateListener.OnBonusSlotUpdating"/>
|
||||
StateListener = 0,
|
||||
}
|
||||
|
||||
public ref struct Arguments(Model model, BonusItemFlag flag, ref CharacterArmor armor, ref ulong returnValue)
|
||||
{
|
||||
/// <summary> The draw object with an updated bonus slot. </summary>
|
||||
public readonly Model Model = model;
|
||||
|
||||
/// <summary> The updated slot. </summary>
|
||||
public readonly BonusItemFlag Slot = flag;
|
||||
|
||||
/// <summary> The model data for the new bonus slot. This can be changed. </summary>
|
||||
public ref CharacterArmor Armor = ref armor;
|
||||
|
||||
/// <summary> The return value of the event. If this is <see cref="ulong.MaxValue"/>, the original function will be called and its return value used, otherwise this value will be returned. </summary>
|
||||
public ref ulong ReturnValue = ref returnValue;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,14 @@
|
|||
using Glamourer.Designs;
|
||||
using Glamourer.Designs.History;
|
||||
using Glamourer.Gui;
|
||||
using OtterGui.Classes;
|
||||
using Glamourer.Gui.Tabs.DesignTab;
|
||||
using Luna;
|
||||
|
||||
namespace Glamourer.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when a Design is edited in any way.
|
||||
/// <list type="number">
|
||||
/// <item>Parameter is the type of the change </item>
|
||||
/// <item>Parameter is the changed Design. </item>
|
||||
/// <item>Parameter is any additional data depending on the type of change. </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public sealed class DesignChanged()
|
||||
: EventWrapper<DesignChanged.Type, Design, ITransaction?, DesignChanged.Priority>(nameof(DesignChanged))
|
||||
/// <summary> Triggered when a Design is edited in any way. </summary>
|
||||
public sealed class DesignChanged(Logger log)
|
||||
: EventBase<DesignChanged.Arguments, DesignChanged.Priority>(nameof(DesignChanged), log)
|
||||
{
|
||||
public enum Type
|
||||
{
|
||||
|
|
@ -138,10 +132,13 @@ public sealed class DesignChanged()
|
|||
/// <seealso cref="Automation.AutoDesignManager.OnDesignChange"/>
|
||||
AutoDesignManager = 1,
|
||||
|
||||
/// <seealso cref="DesignFileSystem.OnDesignChange"/>
|
||||
/// <seealso cref="DesignFileSystem.OnDesignChanged"/>
|
||||
DesignFileSystem = 0,
|
||||
|
||||
/// <seealso cref="Gui.Tabs.DesignTab.DesignFileSystemSelector.OnDesignChange"/>
|
||||
/// <seealso cref="DesignHeader.OnDesignChanged"/>
|
||||
DesignHeader = 0,
|
||||
|
||||
/// <seealso cref="DesignFileSystemDrawer.OnDesignChanged"/>
|
||||
DesignFileSystemSelector = -1,
|
||||
|
||||
/// <seealso cref="DesignComboBase.OnDesignChanged"/>
|
||||
|
|
@ -150,4 +147,10 @@ public sealed class DesignChanged()
|
|||
/// <seealso cref="EditorHistory.OnDesignChanged" />
|
||||
EditorHistory = -1000,
|
||||
}
|
||||
|
||||
/// <summary> Arguments for the DesignChanged event. </summary>
|
||||
/// <param name="Type"> The type of changed. </param>
|
||||
/// <param name="Design"> The changed design. </param>
|
||||
/// <param name="Transaction"> A transaction with further data corresponding to the change. </param>
|
||||
public readonly record struct Arguments(Type Type, Design Design, ITransaction? Transaction = null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,32 @@
|
|||
using OtterGui.Classes;
|
||||
using Luna;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Interop;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when a model flags an equipment slot for an update.
|
||||
/// <list type="number">
|
||||
/// <item>Parameter is the model with a flagged slot. </item>
|
||||
/// <item>Parameter is the equipment slot changed. </item>
|
||||
/// <item>Parameter is the model values to change the equipment piece to. </item>
|
||||
/// <item>Parameter is the return value the function should return, if it is ulong.MaxValue, the original will be called and returned. </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public sealed class EquipSlotUpdating()
|
||||
: EventWrapperRef34<Model, EquipSlot, CharacterArmor, ulong, EquipSlotUpdating.Priority>(nameof(EquipSlotUpdating))
|
||||
/// <summary> Triggered when a model flags an equipment slot for an update. </summary>
|
||||
public sealed class EquipSlotUpdating(Logger log)
|
||||
: EventBase<EquipSlotUpdating.Arguments, EquipSlotUpdating.Priority>(nameof(EquipSlotUpdating), log)
|
||||
{
|
||||
public enum Priority
|
||||
{
|
||||
/// <seealso cref="State.StateListener.OnEquipSlotUpdating"/>
|
||||
StateListener = 0,
|
||||
}
|
||||
|
||||
public ref struct Arguments(Model model, EquipSlot slot, ref CharacterArmor armor, ref ulong returnValue)
|
||||
{
|
||||
/// <summary> The draw object with an updated equipment slot. </summary>
|
||||
public readonly Model Model = model;
|
||||
|
||||
/// <summary> The updated slot. </summary>
|
||||
public readonly EquipSlot Slot = slot;
|
||||
|
||||
/// <summary> The model data for the new equipment slot. This can be changed. </summary>
|
||||
public ref CharacterArmor Armor = ref armor;
|
||||
|
||||
/// <summary> The return value of the event. If this is <see cref="ulong.MaxValue"/>, the original function will be called and its return value used, otherwise this value will be returned. </summary>
|
||||
public ref ulong ReturnValue = ref returnValue;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +1,23 @@
|
|||
using OtterGui.Classes;
|
||||
using Luna;
|
||||
using Penumbra.String;
|
||||
|
||||
namespace Glamourer.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when the player equips a gear set.
|
||||
/// <list type="number">
|
||||
/// <item>Parameter is the name of the gear set. </item>
|
||||
/// <item>Parameter is the id of the gear set. </item>
|
||||
/// <item>Parameter is the id of the prior gear set. </item>
|
||||
/// <item>Parameter is the id of the associated glamour. </item>
|
||||
/// <item>Parameter is the job id of the associated job. </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public sealed class EquippedGearset()
|
||||
: EventWrapper<string, int, int, byte, byte, EquippedGearset.Priority>(nameof(EquippedGearset))
|
||||
/// <summary> Triggered when the player equips a gear set. </summary>
|
||||
public sealed class EquippedGearset(Logger log)
|
||||
: EventBase<EquippedGearset.Arguments, EquippedGearset.Priority>(nameof(EquippedGearset), log)
|
||||
{
|
||||
public enum Priority
|
||||
{
|
||||
/// <seealso cref="Automation.AutoDesignApplier.OnEquippedGearset"/>
|
||||
AutoDesignApplier = 0,
|
||||
}
|
||||
|
||||
/// <summary> Arguments for the EquippedGearset event. </summary>
|
||||
/// <param name="Name"> The name of the equipped gear set. </param>
|
||||
/// <param name="Id"> The ID of the equipped gear set.</param>
|
||||
/// <param name="PriorId"> The ID of the gear set previously equipped.</param>
|
||||
/// <param name="GlamourId"> The ID of the associated glamour plate. </param>
|
||||
/// <param name="JobId"> The job ID of the associated job. </param>
|
||||
public readonly record struct Arguments(ByteString Name, int Id, int PriorId, int GlamourId, byte JobId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
using Dalamud.Plugin.Services;
|
||||
using OtterGui.Classes;
|
||||
using Luna;
|
||||
|
||||
namespace Glamourer.Events;
|
||||
|
||||
public sealed class GPoseService : EventWrapper<bool, GPoseService.Priority>
|
||||
public sealed class GPoseService : EventBase<bool, GPoseService.Priority>
|
||||
{
|
||||
private readonly IFramework _framework;
|
||||
private readonly IClientState _state;
|
||||
|
|
@ -19,8 +19,8 @@ public sealed class GPoseService : EventWrapper<bool, GPoseService.Priority>
|
|||
|
||||
public bool InGPose { get; private set; }
|
||||
|
||||
public GPoseService(IFramework framework, IClientState state)
|
||||
: base(nameof(GPoseService))
|
||||
public GPoseService(IFramework framework, IClientState state, Logger log)
|
||||
: base(nameof(GPoseService), log)
|
||||
{
|
||||
_framework = framework;
|
||||
_state = state;
|
||||
|
|
|
|||
|
|
@ -1,21 +1,23 @@
|
|||
using OtterGui.Classes;
|
||||
using Luna;
|
||||
using Penumbra.GameData.Interop;
|
||||
|
||||
namespace Glamourer.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Triggers when the equipped gearset finished all LoadEquipment, LoadWeapon, and LoadCrest calls. (All Non-MetaData)
|
||||
/// This defines an endpoint for when the gameState is updated.
|
||||
/// <list type="number">
|
||||
/// <item>The model draw object associated with the finished load (Also fired by other players on render) </item>
|
||||
/// </list>
|
||||
/// Triggers when the equipped gearset finished all LoadEquipment, LoadWeapon, and LoadCrest calls. (All Non-MetaData)
|
||||
/// This defines an endpoint for when the gameState is updated.
|
||||
/// </summary>
|
||||
public sealed class GearsetDataLoaded()
|
||||
: EventWrapper<Actor, Model, GearsetDataLoaded.Priority>(nameof(GearsetDataLoaded))
|
||||
public sealed class GearsetDataLoaded(Logger log)
|
||||
: EventBase<GearsetDataLoaded.Arguments, GearsetDataLoaded.Priority>(nameof(GearsetDataLoaded), log)
|
||||
{
|
||||
public enum Priority
|
||||
{
|
||||
/// <seealso cref="State.StateListener.OnGearsetDataLoaded"/>
|
||||
StateListener = 0,
|
||||
}
|
||||
|
||||
/// <summary> Arguments for the GearsetDataLoaded event. </summary>
|
||||
/// <param name="Actor"> The actor that loaded a gear set. </param>
|
||||
/// <param name="Model"> The draw object that finished the load. </param>
|
||||
public readonly record struct Arguments(Actor Actor, Model Model);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,24 @@
|
|||
using OtterGui.Classes;
|
||||
using Luna;
|
||||
using Penumbra.GameData.Interop;
|
||||
|
||||
namespace Glamourer.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when the visibility of head gear is changed.
|
||||
/// <list type="number">
|
||||
/// <item>Parameter is the actor with changed head gear visibility. </item>
|
||||
/// <item>Parameter is the new state. </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public sealed class HeadGearVisibilityChanged()
|
||||
: EventWrapperRef2<Actor, bool, HeadGearVisibilityChanged.Priority>(nameof(HeadGearVisibilityChanged))
|
||||
/// <summary> Triggered when the visibility of head gear is changed. </summary>
|
||||
public sealed class HeadGearVisibilityChanged(Logger log)
|
||||
: EventBase<HeadGearVisibilityChanged.Arguments, HeadGearVisibilityChanged.Priority>(nameof(HeadGearVisibilityChanged), log)
|
||||
{
|
||||
public enum Priority
|
||||
{
|
||||
/// <seealso cref="State.StateListener.OnHeadGearVisibilityChange"/>
|
||||
StateListener = 0,
|
||||
}
|
||||
|
||||
public ref struct Arguments(Actor actor, ref bool visible)
|
||||
{
|
||||
/// <summary> The actor whose head gear visibility changed. </summary>
|
||||
public readonly Actor Actor = actor;
|
||||
|
||||
/// <summary> The new visibility state. </summary>
|
||||
public ref bool Visible = ref visible;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using OtterGui.Classes;
|
||||
using Luna;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
|
|
@ -10,12 +10,14 @@ namespace Glamourer.Events;
|
|||
/// <item>Parameter is an array of slots updated and corresponding item ids and stains. </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public sealed class MovedEquipment()
|
||||
: EventWrapper<(EquipSlot, uint, StainIds)[], MovedEquipment.Priority>(nameof(MovedEquipment))
|
||||
public sealed class MovedEquipment(Logger log)
|
||||
: EventBase<MovedEquipment.Arguments, MovedEquipment.Priority>(nameof(MovedEquipment), log)
|
||||
{
|
||||
public enum Priority
|
||||
{
|
||||
/// <seealso cref="State.StateListener.OnMovedEquipment"/>
|
||||
StateListener = 0,
|
||||
}
|
||||
|
||||
public readonly record struct Arguments(params (EquipSlot, uint, StainIds)[] Items);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,10 @@
|
|||
using OtterGui.Classes;
|
||||
using Luna;
|
||||
|
||||
namespace Glamourer.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when a new item or customization is unlocked.
|
||||
/// <list type="number">
|
||||
/// <item>Parameter is the type of the unlocked object </item>
|
||||
/// <item>Parameter is the id of the unlocked object. </item>
|
||||
/// <item>Parameter is the timestamp of the unlock. </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public sealed class ObjectUnlocked()
|
||||
: EventWrapper<ObjectUnlocked.Type, uint, DateTimeOffset, ObjectUnlocked.Priority>(nameof(ObjectUnlocked))
|
||||
/// <summary> Triggered when a new item or customization is unlocked. </summary>
|
||||
public sealed class ObjectUnlocked(Logger log)
|
||||
: EventBase<ObjectUnlocked.Arguments, ObjectUnlocked.Priority>(nameof(ObjectUnlocked), log), IRequiredService
|
||||
{
|
||||
public enum Type
|
||||
{
|
||||
|
|
@ -21,8 +14,15 @@ public sealed class ObjectUnlocked()
|
|||
|
||||
public enum Priority
|
||||
{
|
||||
/// <seealso cref="Gui.Tabs.UnlocksTab.UnlockTable.OnObjectUnlock"/>
|
||||
/// <seealso cref="Gui.Tabs.UnlocksTab.UnlockTable.Cache.OnItemUnlock"/>
|
||||
/// <remarks> Currently used as a hack to make the unlock table dirty in it. If anything else starts using this, rework. </remarks>
|
||||
UnlockTable = 0,
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Arguments for the ObjectUnlocked event. </summary>
|
||||
/// <param name="Type"> The type of the unlocked object. </param>
|
||||
/// <param name="Id"> The ID of the unlocked object. </param>
|
||||
/// <param name="Timestamp"> The timestamp of the unlock event.</param>
|
||||
public readonly record struct Arguments(Type Type, uint Id, DateTimeOffset Timestamp);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
using OtterGui.Classes;
|
||||
using Luna;
|
||||
|
||||
namespace Glamourer.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when Penumbra is reloaded.
|
||||
/// </summary>
|
||||
public sealed class PenumbraReloaded()
|
||||
: EventWrapper<PenumbraReloaded.Priority>(nameof(PenumbraReloaded))
|
||||
public sealed class PenumbraReloaded(Logger log)
|
||||
: EventBase<PenumbraReloaded.Priority>(nameof(PenumbraReloaded), log)
|
||||
{
|
||||
public enum Priority
|
||||
{
|
||||
/// <seealso cref="Interop.ChangeCustomizeService.Restore"/>
|
||||
/// <seealso cref="global::Glamourer.Interop.ChangeCustomizeService.Restore"/>
|
||||
ChangeCustomizeService = 0,
|
||||
|
||||
/// <seealso cref="Interop.VisorService.Restore"/>
|
||||
/// <seealso cref="global::Glamourer.Interop.VisorService.Restore"/>
|
||||
VisorService = 0,
|
||||
|
||||
/// <seealso cref="Interop.VieraEarService.Restore"/>
|
||||
/// <seealso cref="global::Glamourer.Interop.VieraEarService.Restore"/>
|
||||
VieraEarService = 0,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,33 +1,37 @@
|
|||
using Glamourer.Api.Enums;
|
||||
using Glamourer.Designs.History;
|
||||
using Glamourer.Interop.Structs;
|
||||
using Glamourer.State;
|
||||
using OtterGui.Classes;
|
||||
using Penumbra.GameData.Interop;
|
||||
using Luna;
|
||||
|
||||
namespace Glamourer.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when a Design is edited in any way.
|
||||
/// <list type="number">
|
||||
/// <item>Parameter is the type of the change </item>
|
||||
/// <item>Parameter is the changed saved state. </item>
|
||||
/// <item>Parameter is the existing actors using this saved state. </item>
|
||||
/// <item>Parameter is any additional data depending on the type of change. </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public sealed class StateChanged()
|
||||
: EventWrapper<StateChangeType, StateSource, ActorState, ActorData, ITransaction?, StateChanged.Priority>(nameof(StateChanged))
|
||||
/// <summary> Triggered when a state changes in any way. </summary>
|
||||
public sealed class StateChanged(Logger log)
|
||||
: EventBase<StateChanged.Arguments, StateChanged.Priority>(nameof(StateChanged), log)
|
||||
{
|
||||
public enum Priority
|
||||
{
|
||||
/// <seealso cref="Api.StateApi.OnStateChanged" />
|
||||
GlamourerIpc = int.MinValue,
|
||||
|
||||
/// <seealso cref="Interop.Penumbra.PenumbraAutoRedraw.OnStateChanged" />
|
||||
/// <seealso cref="global::Glamourer.Interop.Penumbra.PenumbraAutoRedraw.OnStateChanged" />
|
||||
PenumbraAutoRedraw = 0,
|
||||
|
||||
/// <seealso cref="EditorHistory.OnStateChanged" />
|
||||
EditorHistory = -1000,
|
||||
}
|
||||
|
||||
/// <summary> Arguments for the StateChanged event. </summary>
|
||||
/// <param name="Type"> The type of change that occured. </param>
|
||||
/// <param name="Source"> The source of the change. </param>
|
||||
/// <param name="State"> The changed state. </param>
|
||||
/// <param name="Actors"> Any currently affected actors. </param>
|
||||
/// <param name="Transaction"> Additional data depending on the type of change. </param>
|
||||
public readonly record struct Arguments(
|
||||
StateChangeType Type,
|
||||
StateSource Source,
|
||||
ActorState State,
|
||||
ActorData Actors,
|
||||
ITransaction? Transaction = null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,22 @@
|
|||
using Glamourer.Api;
|
||||
using Glamourer.Api.Enums;
|
||||
using Glamourer.Interop.Structs;
|
||||
using OtterGui.Classes;
|
||||
using Luna;
|
||||
using Penumbra.GameData.Interop;
|
||||
|
||||
namespace Glamourer.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when a set of grouped changes finishes being applied to a Glamourer state.
|
||||
/// <list type="number">
|
||||
/// <item>Parameter is the operation that finished updating the saved state. </item>
|
||||
/// <item>Parameter is the existing actors using this saved state. </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public sealed class StateFinalized()
|
||||
: EventWrapper<StateFinalizationType, ActorData, StateFinalized.Priority>(nameof(StateFinalized))
|
||||
/// <summary> Triggered when a set of grouped changes finishes being applied to a Glamourer state. </summary>
|
||||
public sealed class StateFinalized(Logger log)
|
||||
: EventBase<StateFinalized.Arguments, StateFinalized.Priority>(nameof(StateFinalized), log)
|
||||
{
|
||||
public enum Priority
|
||||
{
|
||||
/// <seealso cref="StateApi.OnStateFinalized"/>
|
||||
StateApi = int.MinValue,
|
||||
}
|
||||
|
||||
/// <summary> Arguments for the StateFinalized event. </summary>
|
||||
/// <param name="Type"> The operation that finished updating the saved state. </param>
|
||||
/// <param name="Actors"> The existing actors using this saved state at the moment. </param>
|
||||
public readonly record struct Arguments(StateFinalizationType Type, ActorData Actors);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,24 @@
|
|||
using Glamourer.Designs;
|
||||
using Glamourer.Gui;
|
||||
using OtterGui.Classes;
|
||||
using Luna;
|
||||
|
||||
namespace Glamourer.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when an automated design is changed in any way.
|
||||
/// <list type="number">
|
||||
/// <item>Parameter is the tab to select. </item>
|
||||
/// <item>Parameter is the design to select if the tab is the designs tab. </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public sealed class TabSelected()
|
||||
: EventWrapper<MainTabType, Design?, TabSelected.Priority>(nameof(TabSelected))
|
||||
/// <summary> Triggered to select a tab or design. </summary>
|
||||
public sealed class TabSelected(Logger log)
|
||||
: EventBase<TabSelected.Arguments, TabSelected.Priority>(nameof(TabSelected), log)
|
||||
{
|
||||
public enum Priority
|
||||
{
|
||||
/// <seealso cref="Gui.Tabs.DesignTab.DesignFileSystemSelector.OnTabSelected"/>
|
||||
/// <seealso cref="DesignFileSystem.OnTabSelected"/>
|
||||
DesignSelector = 0,
|
||||
|
||||
/// <seealso cref="Gui.MainWindow.OnTabSelected"/>
|
||||
/// <seealso cref="Gui.MainTabBar.OnEvent"/>
|
||||
MainWindow = 1,
|
||||
}
|
||||
|
||||
/// <summary> Arguments for the TabSelected event. </summary>
|
||||
/// <param name="Type"> The tab to be selected. </param>
|
||||
/// <param name="Design"> The design to be selected in the Designs tab. </param>
|
||||
public readonly record struct Arguments(MainTabType Type, Design? Design = null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,24 @@
|
|||
using OtterGui.Classes;
|
||||
using Luna;
|
||||
using Penumbra.GameData.Interop;
|
||||
|
||||
namespace Glamourer.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when the state of viera ear visibility for any draw object is changed.
|
||||
/// <list type="number">
|
||||
/// <item>Parameter is the model with a changed viera ear visibility state. </item>
|
||||
/// <item>Parameter is the new state. </item>
|
||||
/// <item>Parameter is whether to call the original function. </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public sealed class VieraEarStateChanged()
|
||||
: EventWrapperRef2<Actor, bool, VieraEarStateChanged.Priority>(nameof(VieraEarStateChanged))
|
||||
/// <summary> Triggered when the state of viera ear visibility for any draw object is changed. </summary>
|
||||
public sealed class VieraEarStateChanged(Logger log)
|
||||
: EventBase<VieraEarStateChanged.Arguments, VieraEarStateChanged.Priority>(nameof(VieraEarStateChanged), log)
|
||||
{
|
||||
public enum Priority
|
||||
{
|
||||
/// <seealso cref="State.StateListener.OnVieraEarChange"/>
|
||||
StateListener = 0,
|
||||
}
|
||||
|
||||
public ref struct Arguments(Actor actor, ref bool state)
|
||||
{
|
||||
/// <summary> The actor with a changed viera ear visibility state. </summary>
|
||||
public readonly Actor Actor = actor;
|
||||
|
||||
/// <summary> The new ear visibility state. </summary>
|
||||
public ref bool State = ref state;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,27 @@
|
|||
using OtterGui.Classes;
|
||||
using Luna;
|
||||
using Penumbra.GameData.Interop;
|
||||
|
||||
namespace Glamourer.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when the state of a visor for any draw object is changed.
|
||||
/// <list type="number">
|
||||
/// <item>Parameter is the model with a changed visor state. </item>
|
||||
/// <item>Parameter is the new state. </item>
|
||||
/// <item>Parameter is whether to call the original function. </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public sealed class VisorStateChanged()
|
||||
: EventWrapperRef3<Model, bool, bool, VisorStateChanged.Priority>(nameof(VisorStateChanged))
|
||||
/// <summary> Triggered when the state of a visor for any draw object is changed. </summary>
|
||||
public sealed class VisorStateChanged(Logger log)
|
||||
: EventBase<VisorStateChanged.Arguments, VisorStateChanged.Priority>(nameof(VisorStateChanged), log)
|
||||
{
|
||||
public enum Priority
|
||||
{
|
||||
/// <seealso cref="State.StateListener.OnVisorChange"/>
|
||||
StateListener = 0,
|
||||
}
|
||||
}
|
||||
|
||||
public ref struct Arguments(Model model, bool visorState, ref bool value)
|
||||
{
|
||||
/// <summary> The model with a changed visor state. </summary>
|
||||
public readonly Model Model = model;
|
||||
|
||||
/// <summary> The game's visor state. </summary>
|
||||
public readonly bool NewVisorState = visorState;
|
||||
|
||||
/// <summary> The actual new value </summary>
|
||||
public ref bool Value = ref value;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,13 @@
|
|||
using OtterGui.Classes;
|
||||
using Luna;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Interop;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when a model flags an equipment slot for an update.
|
||||
/// <list type="number">
|
||||
/// <item>Parameter is the actor that has its weapons changed. </item>
|
||||
/// <item>Parameter is the equipment slot changed (Mainhand or Offhand). </item>
|
||||
/// <item>Parameter is the model values to change the weapon to. </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public sealed class WeaponLoading()
|
||||
: EventWrapperRef3<Actor, EquipSlot, CharacterWeapon, WeaponLoading.Priority>(nameof(WeaponLoading))
|
||||
/// <summary> Triggered when a model flags an equipment slot for an update. </summary>
|
||||
public sealed class WeaponLoading(Logger log)
|
||||
: EventBase<WeaponLoading.Arguments, WeaponLoading.Priority>(nameof(WeaponLoading), log)
|
||||
{
|
||||
public enum Priority
|
||||
{
|
||||
|
|
@ -24,4 +17,16 @@ public sealed class WeaponLoading()
|
|||
/// <seealso cref="Automation.AutoDesignApplier.OnWeaponLoading"/>
|
||||
AutoDesignApplier = -1,
|
||||
}
|
||||
|
||||
public ref struct Arguments(Actor actor, EquipSlot slot, ref CharacterWeapon weapon)
|
||||
{
|
||||
/// <summary> The actor that has its weapons changed. </summary>
|
||||
public readonly Actor Actor = actor;
|
||||
|
||||
/// <summary> The changed equipment slot (either Mainhand or Offhand). </summary>
|
||||
public readonly EquipSlot Slot = slot;
|
||||
|
||||
/// <summary> The model data for the new weapon. </summary>
|
||||
public ref CharacterWeapon Weapon = ref weapon;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,24 @@
|
|||
using OtterGui.Classes;
|
||||
using Luna;
|
||||
using Penumbra.GameData.Interop;
|
||||
|
||||
namespace Glamourer.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when the visibility of weapons is changed.
|
||||
/// <list type="number">
|
||||
/// <item>Parameter is the actor with changed weapon visibility. </item>
|
||||
/// <item>Parameter is the new state. </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public sealed class WeaponVisibilityChanged() : EventWrapperRef2<Actor, bool, WeaponVisibilityChanged.Priority>(nameof(WeaponVisibilityChanged))
|
||||
/// <summary> Triggered when the visibility of weapons is changed. </summary>
|
||||
public sealed class WeaponVisibilityChanged(Logger log)
|
||||
: EventBase<WeaponVisibilityChanged.Arguments, WeaponVisibilityChanged.Priority>(nameof(WeaponVisibilityChanged), log)
|
||||
{
|
||||
public enum Priority
|
||||
{
|
||||
/// <seealso cref="State.StateListener.OnWeaponVisibilityChange"/>
|
||||
StateListener = 0,
|
||||
}
|
||||
|
||||
public ref struct Arguments(Actor actor, ref bool value)
|
||||
{
|
||||
/// <summary> The actor with changed weapon visibility. </summary>
|
||||
public readonly Actor Actor = actor;
|
||||
|
||||
/// <summary> The new state. </summary>
|
||||
public ref bool Value = ref value;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
using ImSharp;
|
||||
using OtterGui.Extensions;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Race = Penumbra.GameData.Enums.Race;
|
||||
|
|
@ -139,10 +138,10 @@ public class CustomizeSet
|
|||
_ => Invalid(out custom),
|
||||
};
|
||||
|
||||
int Get(IEnumerable<CustomizeData> list, CustomizeValue v, out CustomizeData? output)
|
||||
static int Get(IEnumerable<CustomizeData> list, CustomizeValue v, out CustomizeData? output)
|
||||
{
|
||||
var (val, idx) = list.Cast<CustomizeData?>().WithIndex().FirstOrDefault(p => p.Value!.Value.Value == v);
|
||||
if (val == null)
|
||||
var (idx, val) = list.Cast<CustomizeData?>().Index().FirstOrDefault(p => p.Item!.Value.Value == v);
|
||||
if (val is null)
|
||||
{
|
||||
output = null;
|
||||
return -1;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using Dalamud.Plugin;
|
||||
using Glamourer.Api;
|
||||
using Glamourer.Automation;
|
||||
using Glamourer.Config;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Gui;
|
||||
using Glamourer.Interop;
|
||||
|
|
@ -9,7 +10,7 @@ using Glamourer.State;
|
|||
using ImSharp;
|
||||
using Luna;
|
||||
using Penumbra.GameData.Interop;
|
||||
using Logger = OtterGui.Log.Logger;
|
||||
using Vortice.Direct3D11.Debug;
|
||||
|
||||
namespace Glamourer;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Dalamud.NET.Sdk/14.0.1">
|
||||
<Project Sdk="Dalamud.NET.Sdk/14.0.2">
|
||||
<PropertyGroup>
|
||||
<RootNamespace>Glamourer</RootNamespace>
|
||||
<AssemblyName>Glamourer</AssemblyName>
|
||||
|
|
@ -22,10 +22,14 @@
|
|||
<EmbeddedResource Include="LegacyTattoo.raw" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Use_DalamudPackager>false</Use_DalamudPackager>
|
||||
<Use_Dalamud_ImGui>false</Use_Dalamud_ImGui>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Glamourer.Api\Glamourer.Api.csproj" />
|
||||
<ProjectReference Include="..\Luna\Luna\Luna.csproj" />
|
||||
<ProjectReference Include="..\OtterGui\OtterGui.csproj" />
|
||||
<ProjectReference Include="..\Penumbra.Api\Penumbra.Api.csproj" />
|
||||
<ProjectReference Include="..\Penumbra.String\Penumbra.String.csproj" />
|
||||
<ProjectReference Include="..\Penumbra.GameData\Penumbra.GameData.csproj" />
|
||||
|
|
|
|||
2
Glamourer/Glamourer.csproj.DotSettings
Normal file
2
Glamourer/Glamourer.csproj.DotSettings
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=gui_005Ctabs_005Cdesigntab_005Cselector/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
using Glamourer.Config;
|
||||
using ImSharp;
|
||||
|
||||
namespace Glamourer.Gui;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Text.Unicode;
|
||||
using Glamourer.Config;
|
||||
using ImSharp;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using Dalamud.Interface.Textures;
|
||||
using Dalamud.Interface.Textures.TextureWraps;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Glamourer.Config;
|
||||
using Glamourer.GameData;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.Unlocks;
|
||||
|
|
@ -11,13 +12,13 @@ using Penumbra.GameData.Structs;
|
|||
|
||||
namespace Glamourer.Gui.Customization;
|
||||
|
||||
public partial class CustomizationDrawer(
|
||||
public sealed partial class CustomizationDrawer(
|
||||
ITextureProvider textures,
|
||||
CustomizeService service,
|
||||
Configuration config,
|
||||
FavoriteManager favorites,
|
||||
HeightService heightService)
|
||||
: IDisposable
|
||||
: IDisposable, IUiService
|
||||
{
|
||||
private readonly Vector4 _redTint = new(0.6f, 0.3f, 0.3f, 1f);
|
||||
private readonly IDalamudTextureWrap? _legacyTattoo = GetLegacyTattooIcon(textures);
|
||||
|
|
@ -81,7 +82,7 @@ public partial class CustomizationDrawer(
|
|||
private CustomizeValue _currentByte = CustomizeValue.Zero;
|
||||
private bool _currentApply;
|
||||
private int _currentCount;
|
||||
private StringU8 _currentOption = StringU8.Empty;
|
||||
private StringU8 _currentOption = StringU8.Empty;
|
||||
|
||||
// Prepare a new customization option.
|
||||
private Im.IdDisposable SetId(CustomizeIndex index)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Glamourer.Designs;
|
||||
using Glamourer.Config;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.GameData;
|
||||
using Glamourer.Interop.PalettePlus;
|
||||
using Glamourer.State;
|
||||
|
|
@ -125,7 +126,7 @@ public class CustomizeParameterDrawer(Configuration config, PaletteImport import
|
|||
DrawColorFormatOptions(withApply);
|
||||
var value = config.ShowColorConfig;
|
||||
Im.Line.Same();
|
||||
if (Im.Checkbox("Show Config"u8, ref value))
|
||||
if (Im.Checkbox("Show Configuration"u8, ref value))
|
||||
{
|
||||
config.ShowColorConfig = value;
|
||||
config.Save();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
using Glamourer.Automation;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Designs.History;
|
||||
using Glamourer.Designs.Special;
|
||||
using Glamourer.Events;
|
||||
using ImSharp;
|
||||
|
|
@ -9,7 +8,7 @@ using Luna;
|
|||
namespace Glamourer.Gui;
|
||||
|
||||
public abstract class DesignComboBase(
|
||||
EphemeralConfig config,
|
||||
Config.EphemeralConfig config,
|
||||
DesignManager designs,
|
||||
DesignChanged designChanged,
|
||||
DesignColors designColors,
|
||||
|
|
@ -17,18 +16,18 @@ public abstract class DesignComboBase(
|
|||
DesignFileSystem designFileSystem)
|
||||
: FilterComboBase<DesignComboBase.CacheItem>(new DesignFilter(), ConfigData.Default with { ComputeWidth = true })
|
||||
{
|
||||
protected readonly EphemeralConfig Config = config;
|
||||
protected readonly DesignChanged DesignChanged = designChanged;
|
||||
protected readonly DesignColors DesignColors = designColors;
|
||||
protected readonly DesignFileSystem DesignFileSystem = designFileSystem;
|
||||
protected readonly TabSelected TabSelected = tabSelected;
|
||||
protected readonly DesignManager Designs = designs;
|
||||
protected IDesignStandIn? CurrentDesign;
|
||||
protected readonly Config.EphemeralConfig Config = config;
|
||||
protected readonly DesignChanged DesignChanged = designChanged;
|
||||
protected readonly DesignColors DesignColors = designColors;
|
||||
protected readonly DesignFileSystem DesignFileSystem = designFileSystem;
|
||||
protected readonly TabSelected TabSelected = tabSelected;
|
||||
protected readonly DesignManager Designs = designs;
|
||||
protected IDesignStandIn? CurrentDesign;
|
||||
|
||||
protected CacheItem CreateItem(IDesignStandIn design)
|
||||
{
|
||||
var color = design is Design d1 ? DesignColors.GetColor(d1).ToVector() : ColorId.NormalDesign.Value().ToVector();
|
||||
var path = design is Design d2 && DesignFileSystem.TryGetValue(d2, out var leaf) ? leaf.FullName() : string.Empty;
|
||||
var path = design is Design d2 ? d2.Node!.FullPath : string.Empty;
|
||||
var name = design.ResolveName(false);
|
||||
if (path == name)
|
||||
path = string.Empty;
|
||||
|
|
@ -54,7 +53,7 @@ public abstract class DesignComboBase(
|
|||
if (CurrentDesign is Design design)
|
||||
{
|
||||
if (Im.Item.RightClicked() && Im.Io.KeyControl)
|
||||
TabSelected.Invoke(MainTabType.Designs, design);
|
||||
TabSelected.Invoke(new TabSelected.Arguments(MainTabType.Designs, design));
|
||||
Im.Tooltip.OnHover("Control + Right-Click to move to design."u8);
|
||||
}
|
||||
else
|
||||
|
|
@ -81,7 +80,7 @@ public abstract class DesignComboBase(
|
|||
Im.Text("Currently resolving to "u8);
|
||||
using var color = ImGuiColor.Text.Push(DesignColors.GetColor(linkedDesign));
|
||||
Im.Line.NoSpacing();
|
||||
Im.Text(linkedDesign.Name.Text);
|
||||
Im.Text(linkedDesign.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -118,7 +117,10 @@ public abstract class DesignComboBase(
|
|||
|
||||
protected override void ComputeWidth()
|
||||
=> ComboWidth = UnfilteredItems.Max(d
|
||||
=> d.Name.Utf8.CalculateSize(false).X + d.FullPath.Utf8.CalculateSize(false).X + 2 * Im.Style.ItemSpacing.X + Im.Style.ScrollbarSize);
|
||||
=> d.Name.Utf8.CalculateSize(false).X
|
||||
+ d.FullPath.Utf8.CalculateSize(false).X
|
||||
+ 2 * Im.Style.ItemSpacing.X
|
||||
+ Im.Style.ScrollbarSize);
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
|
|
@ -130,9 +132,9 @@ public abstract class DesignComboBase(
|
|||
private void OnDesignColorChanged()
|
||||
=> Dirty |= IManagedCache.DirtyFlags.Custom;
|
||||
|
||||
private void OnDesignChanged(DesignChanged.Type type, Design? _1, ITransaction? _2 = null)
|
||||
private void OnDesignChanged(in DesignChanged.Arguments arguments)
|
||||
{
|
||||
if (type switch
|
||||
if (arguments.Type switch
|
||||
{
|
||||
DesignChanged.Type.Created => true,
|
||||
DesignChanged.Type.Renamed => true,
|
||||
|
|
@ -203,7 +205,7 @@ public sealed class QuickDesignCombo : DesignComboBase, IDisposable, IUiService
|
|||
}
|
||||
|
||||
|
||||
public QuickDesignCombo(EphemeralConfig config, DesignChanged designChanged, DesignColors designColors, TabSelected tabSelected,
|
||||
public QuickDesignCombo(Config.EphemeralConfig config, DesignChanged designChanged, DesignColors designColors, TabSelected tabSelected,
|
||||
DesignFileSystem designFileSystem, DesignManager designs)
|
||||
: base(config, designs, designChanged, designColors, tabSelected, designFileSystem)
|
||||
{
|
||||
|
|
@ -212,18 +214,18 @@ public sealed class QuickDesignCombo : DesignComboBase, IDisposable, IUiService
|
|||
DesignChanged.Subscribe(OnDesignChanged, DesignChanged.Priority.DesignCombo);
|
||||
}
|
||||
|
||||
private void OnDesignChanged(DesignChanged.Type type, Design changedDesign, ITransaction? _)
|
||||
private void OnDesignChanged(in DesignChanged.Arguments arguments)
|
||||
{
|
||||
switch (type)
|
||||
switch (arguments.Type)
|
||||
{
|
||||
case DesignChanged.Type.Created:
|
||||
// If the quick design bar has no selection, select the new design if it supports the bar.
|
||||
if (QuickDesign is null && changedDesign.QuickDesign)
|
||||
QuickDesign = changedDesign;
|
||||
if (QuickDesign is null && arguments.Design.QuickDesign)
|
||||
QuickDesign = arguments.Design;
|
||||
break;
|
||||
case DesignChanged.Type.Deleted:
|
||||
// If the deleted design was selected, select the first design that supports the bar, if any.
|
||||
if (QuickDesign == changedDesign)
|
||||
if (QuickDesign == arguments.Design)
|
||||
QuickDesign = Designs.Designs.FirstOrDefault(d => d.QuickDesign);
|
||||
break;
|
||||
case DesignChanged.Type.ReloadedAll:
|
||||
|
|
@ -232,10 +234,10 @@ public sealed class QuickDesignCombo : DesignComboBase, IDisposable, IUiService
|
|||
break;
|
||||
case DesignChanged.Type.QuickDesignBar:
|
||||
// If the quick design support of a design was changed, select the new design if the bar has no selection and the design now supports it,
|
||||
if (QuickDesign is null && changedDesign.QuickDesign)
|
||||
QuickDesign = changedDesign;
|
||||
if (QuickDesign is null && arguments.Design.QuickDesign)
|
||||
QuickDesign = arguments.Design;
|
||||
// or select the first design that supports the bar, if any, if the support was removed from the currently selected design.
|
||||
else if (QuickDesign == changedDesign && !changedDesign.QuickDesign)
|
||||
else if (QuickDesign == arguments.Design && !arguments.Design.QuickDesign)
|
||||
QuickDesign = Designs.Designs.FirstOrDefault(d => d.QuickDesign);
|
||||
break;
|
||||
}
|
||||
|
|
@ -264,7 +266,7 @@ public sealed class LinkDesignCombo : DesignComboBase, IUiService, IDisposable
|
|||
{
|
||||
public Design? NewSelection { get; private set; }
|
||||
|
||||
public LinkDesignCombo(EphemeralConfig config, DesignChanged designChanged, DesignColors designColors, TabSelected tabSelected,
|
||||
public LinkDesignCombo(Config.EphemeralConfig config, DesignChanged designChanged, DesignColors designColors, TabSelected tabSelected,
|
||||
DesignFileSystem designFileSystem, DesignManager designs)
|
||||
: base(config, designs, designChanged, designColors, tabSelected, designFileSystem)
|
||||
{
|
||||
|
|
@ -287,15 +289,16 @@ public sealed class LinkDesignCombo : DesignComboBase, IUiService, IDisposable
|
|||
public void Dispose()
|
||||
=> DesignChanged.Unsubscribe(OnDesignChanged);
|
||||
|
||||
private void OnDesignChanged(DesignChanged.Type type, Design design, ITransaction? _)
|
||||
private void OnDesignChanged(in DesignChanged.Arguments arguments)
|
||||
{
|
||||
if (type is DesignChanged.Type.Deleted && design == NewSelection || type is DesignChanged.Type.ReloadedAll)
|
||||
if (arguments.Type is DesignChanged.Type.Deleted && arguments.Design == NewSelection
|
||||
|| arguments.Type is DesignChanged.Type.ReloadedAll)
|
||||
NewSelection = null;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class RandomDesignCombo(
|
||||
EphemeralConfig config,
|
||||
Config.EphemeralConfig config,
|
||||
DesignManager designs,
|
||||
DesignChanged designChanged,
|
||||
DesignColors designColors,
|
||||
|
|
@ -307,21 +310,17 @@ public sealed class RandomDesignCombo(
|
|||
{
|
||||
return exact.Which switch
|
||||
{
|
||||
RandomPredicate.Exact.Type.Name => Designs.Designs.FirstOrDefault(d => d.Name == exact.Value),
|
||||
RandomPredicate.Exact.Type.Path => DesignFileSystem.Find(exact.Value.Text, out var c) && c is DesignFileSystem.Leaf l
|
||||
? l.Value
|
||||
: null,
|
||||
RandomPredicate.Exact.Type.Identifier => Designs.Designs.ByIdentifier(Guid.TryParse(exact.Value.Text, out var g)
|
||||
? g
|
||||
: Guid.Empty),
|
||||
_ => null,
|
||||
RandomPredicate.Exact.Type.Name => Designs.Designs.FirstOrDefault(d => d.Name == exact.Value),
|
||||
RandomPredicate.Exact.Type.Path => Designs.Designs.FirstOrDefault(d => d.Node!.FullPath == exact.Value),
|
||||
RandomPredicate.Exact.Type.Identifier => Designs.Designs.ByIdentifier(Guid.TryParse(exact.Value, out var g) ? g : Guid.Empty),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
public bool Draw(RandomPredicate.Exact exact, [NotNullWhen(true)] out Design? newDesign, float width)
|
||||
{
|
||||
var design = GetDesign(exact);
|
||||
if (Draw(StringU8.Empty, design?.ResolveName(Config.IncognitoMode) ?? $"Not Found [{exact.Value.Text}]", StringU8.Empty, width,
|
||||
if (Draw(StringU8.Empty, design?.ResolveName(Config.IncognitoMode) ?? $"Not Found [{exact.Value}]", StringU8.Empty, width,
|
||||
out var newItem)
|
||||
&& newItem.Design is Design d)
|
||||
{
|
||||
|
|
@ -346,7 +345,7 @@ public sealed class SpecialDesignCombo : DesignComboBase, IUiService
|
|||
private readonly CacheItem _revert;
|
||||
private readonly CacheItem _quick;
|
||||
|
||||
public SpecialDesignCombo(EphemeralConfig config,
|
||||
public SpecialDesignCombo(Config.EphemeralConfig config,
|
||||
DesignManager designs,
|
||||
DesignChanged designChanged,
|
||||
DesignColors designColors,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
using Dalamud.Interface;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Glamourer.Automation;
|
||||
using Glamourer.Config;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Interop.Penumbra;
|
||||
using Glamourer.State;
|
||||
|
|
@ -24,6 +25,7 @@ public enum QdbButtons
|
|||
ReapplyAutomation = 0x40,
|
||||
ResetSettings = 0x80,
|
||||
RevertAdvancedCustomization = 0x100,
|
||||
ToggleMainWindow = 0x200,
|
||||
}
|
||||
|
||||
public sealed class DesignQuickBar : Window, IDisposable
|
||||
|
|
@ -45,6 +47,8 @@ public sealed class DesignQuickBar : Window, IDisposable
|
|||
private int _numButtons;
|
||||
private readonly StringBuilder _tooltipBuilder = new(512);
|
||||
|
||||
public event Action? ToggleMainWindow;
|
||||
|
||||
public DesignQuickBar(Configuration config, QuickDesignCombo designCombo, StateManager stateManager, IKeyState keyState,
|
||||
ActorObjectManager objects, AutoDesignApplier autoDesignApplier, PenumbraService penumbra)
|
||||
: base("Glamourer Quick Bar", WindowFlags.NoDecoration | WindowFlags.NoDocking)
|
||||
|
|
@ -77,38 +81,41 @@ public sealed class DesignQuickBar : Window, IDisposable
|
|||
public override void PreDraw()
|
||||
{
|
||||
Flags = GetFlags;
|
||||
UpdateWidth();
|
||||
|
||||
_style.Push(ImStyleDouble.WindowPadding, new Vector2(Im.Style.GlobalScale * 4))
|
||||
.Push(ImStyleSingle.WindowBorderThickness, 0);
|
||||
_style.Push(ImGuiColor.WindowBackground, ColorId.QuickDesignBg.Value())
|
||||
.Push(ImGuiColor.Button, ColorId.QuickDesignButton.Value())
|
||||
.Push(ImGuiColor.FrameBackground, ColorId.QuickDesignFrame.Value());
|
||||
|
||||
UpdateWidth();
|
||||
}
|
||||
|
||||
public override void PostDraw()
|
||||
=> _style.Dispose();
|
||||
|
||||
public void DrawAtEnd(float yPos)
|
||||
public void DrawAtEnd(float yPos, bool mainWindow)
|
||||
{
|
||||
var width = UpdateWidth();
|
||||
var numButtons = CalculateButtonCount(mainWindow);
|
||||
var width = CalculateWidth(numButtons, mainWindow);
|
||||
Im.Cursor.Position = new Vector2(Im.ContentRegion.Maximum.X - width, yPos - Im.Style.GlobalScale);
|
||||
Draw();
|
||||
Draw(Im.ContentRegion.Available.X, numButtons, mainWindow);
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
=> Draw(Im.ContentRegion.Available.X);
|
||||
=> Draw(Im.ContentRegion.Available.X, _numButtons, false);
|
||||
|
||||
private void Draw(float width)
|
||||
private void Draw(float width, int numButtons, bool mainWindow)
|
||||
{
|
||||
using var group = Im.Group();
|
||||
var spacing = Im.Style.ItemInnerSpacing;
|
||||
using var style = ImStyleDouble.ItemSpacing.Push(spacing);
|
||||
var buttonSize = new Vector2(Im.Style.FrameHeight);
|
||||
PrepareButtons();
|
||||
DrawToggleMainWindowButton(buttonSize, mainWindow);
|
||||
if (_config.QdbButtons.HasFlag(QdbButtons.ApplyDesign))
|
||||
{
|
||||
var comboSize = width - _numButtons * (buttonSize.X + spacing.X);
|
||||
var comboSize = width - numButtons * (buttonSize.X + spacing.X);
|
||||
_designCombo.Draw(StringU8.Empty, comboSize);
|
||||
Im.Line.Same();
|
||||
DrawApplyButton(buttonSize);
|
||||
|
|
@ -485,6 +492,16 @@ public sealed class DesignQuickBar : Window, IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
private void DrawToggleMainWindowButton(Vector2 buttonSize, bool mainWindow)
|
||||
{
|
||||
if (mainWindow || !_config.QdbButtons.HasFlag(QdbButtons.ToggleMainWindow))
|
||||
return;
|
||||
|
||||
if (ImEx.Icon.Button(FontAwesomeIcon.TheaterMasks.Icon(), "Toggle Glamourer's main window."u8, ToggleMainWindow is null, buttonSize))
|
||||
ToggleMainWindow?.Invoke();
|
||||
Im.Line.Same();
|
||||
}
|
||||
|
||||
private (bool, ActorIdentifier, ActorData, ActorState?) ResolveTarget(AwesomeIcon icon, Vector2 buttonSize, int available)
|
||||
{
|
||||
var enumerator = _tooltipBuilder.GetChunks();
|
||||
|
|
@ -516,44 +533,51 @@ public sealed class DesignQuickBar : Window, IDisposable
|
|||
return _keyState[key.Hotkey] && key.Modifiers.IsActive();
|
||||
}
|
||||
|
||||
private float UpdateWidth()
|
||||
private int CalculateButtonCount(bool mainWindow)
|
||||
{
|
||||
_numButtons = 0;
|
||||
var numButtons = 0;
|
||||
if (_config.QdbButtons.HasFlag(QdbButtons.RevertAll))
|
||||
++_numButtons;
|
||||
++numButtons;
|
||||
if (_config.EnableAutoDesigns)
|
||||
{
|
||||
if (_config.QdbButtons.HasFlag(QdbButtons.RevertAutomation))
|
||||
++_numButtons;
|
||||
++numButtons;
|
||||
if (_config.QdbButtons.HasFlag(QdbButtons.ReapplyAutomation))
|
||||
++_numButtons;
|
||||
++numButtons;
|
||||
}
|
||||
|
||||
if (_config.QdbButtons.HasFlag(QdbButtons.RevertAdvancedCustomization))
|
||||
++_numButtons;
|
||||
++numButtons;
|
||||
if (_config.QdbButtons.HasFlag(QdbButtons.RevertAdvancedDyes))
|
||||
++_numButtons;
|
||||
++numButtons;
|
||||
if (_config.QdbButtons.HasFlag(QdbButtons.RevertCustomize))
|
||||
++_numButtons;
|
||||
++numButtons;
|
||||
if (_config.QdbButtons.HasFlag(QdbButtons.RevertEquip))
|
||||
++_numButtons;
|
||||
++numButtons;
|
||||
if (_config.UseTemporarySettings && _config.QdbButtons.HasFlag(QdbButtons.ResetSettings))
|
||||
++_numButtons;
|
||||
++numButtons;
|
||||
if (_config.QdbButtons.HasFlag(QdbButtons.ApplyDesign))
|
||||
{
|
||||
++_numButtons;
|
||||
Size = new Vector2((7 + _numButtons) * Im.Style.FrameHeight + _numButtons * Im.Style.ItemInnerSpacing.X,
|
||||
Im.Style.FrameHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
Size = new Vector2(
|
||||
_numButtons * Im.Style.FrameHeight
|
||||
+ (_numButtons - 1) * Im.Style.ItemInnerSpacing.X
|
||||
+ Im.Style.WindowPadding.X * 2,
|
||||
Im.Style.FrameHeight);
|
||||
}
|
||||
++numButtons;
|
||||
if (!mainWindow && _config.QdbButtons.HasFlag(QdbButtons.ToggleMainWindow))
|
||||
++numButtons;
|
||||
|
||||
return Size.Value.X;
|
||||
return numButtons;
|
||||
}
|
||||
|
||||
private float CalculateWidth(int numButtons, bool mainWindow)
|
||||
{
|
||||
var content = _config.QdbButtons.HasFlag(QdbButtons.ApplyDesign)
|
||||
? (7 + numButtons) * Im.Style.FrameHeight + numButtons * Im.Style.ItemInnerSpacing.X
|
||||
: numButtons * Im.Style.FrameHeight + (numButtons - 1) * Im.Style.ItemInnerSpacing.X;
|
||||
var padding = mainWindow ? 0 : Im.Style.WindowPadding.X * 2;
|
||||
|
||||
return content + padding;
|
||||
}
|
||||
|
||||
private void UpdateWidth()
|
||||
{
|
||||
_numButtons = CalculateButtonCount(false);
|
||||
var width = CalculateWidth(_numButtons, false);
|
||||
Size = new Vector2(width, Im.Style.FrameHeight);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Dalamud.Plugin.Services;
|
||||
using Glamourer.Config;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Gui.Materials;
|
||||
using Glamourer.Services;
|
||||
|
|
@ -11,7 +12,7 @@ using Penumbra.GameData.Structs;
|
|||
|
||||
namespace Glamourer.Gui.Equipment;
|
||||
|
||||
public class EquipmentDrawer
|
||||
public sealed class EquipmentDrawer : IUiService
|
||||
{
|
||||
private const float DefaultWidth = 280;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
using Dalamud.Game.ClientState.Conditions;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Glamourer.Config;
|
||||
using ImSharp;
|
||||
using Luna;
|
||||
|
||||
namespace Glamourer.Gui;
|
||||
|
||||
public class GenericPopupWindow : Luna.Window
|
||||
public sealed class GenericPopupWindow : Window
|
||||
{
|
||||
private readonly Configuration _config;
|
||||
private readonly ICondition _condition;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
using Glamourer.Config;
|
||||
using ImSharp;
|
||||
using Luna;
|
||||
|
||||
namespace Glamourer.Gui;
|
||||
|
||||
public class GlamourerChangelog
|
||||
public sealed class GlamourerChangelog : IUiService
|
||||
{
|
||||
public const int LastChangelogVersion = 0;
|
||||
private readonly Configuration _config;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Windowing;
|
||||
using Glamourer.Config;
|
||||
using Glamourer.Gui.Tabs.UnlocksTab;
|
||||
using Luna;
|
||||
using WindowSystem = Dalamud.Interface.Windowing.WindowSystem;
|
||||
|
||||
namespace Glamourer.Gui;
|
||||
|
||||
public class GlamourerWindowSystem : IDisposable
|
||||
public sealed class GlamourerWindowSystem : IDisposable, IUiService
|
||||
{
|
||||
private readonly WindowSystem _windowSystem = new("Glamourer");
|
||||
private readonly IUiBuilder _uiBuilder;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using Glamourer.Designs;
|
||||
using Glamourer.Config;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Gui.Tabs;
|
||||
using Glamourer.Gui.Tabs.ActorTab;
|
||||
|
|
@ -30,8 +30,8 @@ public sealed class MainTabBar : TabBar<MainTabType>
|
|||
NextTab = _config.SelectedMainTab;
|
||||
}
|
||||
|
||||
private void OnEvent(MainTabType tab, Design? _)
|
||||
=> NextTab = tab;
|
||||
private void OnEvent(in TabSelected.Arguments arguments)
|
||||
=> NextTab = arguments.Type;
|
||||
|
||||
private void OnTabSelected(in MainTabType arguments)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Dalamud.Plugin;
|
||||
using Glamourer.Config;
|
||||
using Glamourer.Interop.Penumbra;
|
||||
using ImSharp;
|
||||
using Luna;
|
||||
|
|
@ -21,7 +22,7 @@ public sealed class MainWindow : Window, IDisposable
|
|||
pi.UiBuilder.DisableGposeUiHide = true;
|
||||
SizeConstraints = new WindowSizeConstraints
|
||||
{
|
||||
MinimumSize = new Vector2(700, 675),
|
||||
MinimumSize = new Vector2(700, 675),
|
||||
MaximumSize = new Vector2(3840, 2160),
|
||||
};
|
||||
_mainTabBar = mainTabBar;
|
||||
|
|
@ -32,6 +33,7 @@ public sealed class MainWindow : Window, IDisposable
|
|||
IsOpen = _config.OpenWindowAtStart;
|
||||
|
||||
_penumbra.DrawSettingsSection += _mainTabBar.Settings.DrawPenumbraIntegrationSettings;
|
||||
_quickBar.ToggleMainWindow += Toggle;
|
||||
}
|
||||
|
||||
public void OpenSettings()
|
||||
|
|
@ -49,7 +51,10 @@ public sealed class MainWindow : Window, IDisposable
|
|||
}
|
||||
|
||||
public void Dispose()
|
||||
=> _penumbra.DrawSettingsSection -= _mainTabBar.Settings.DrawPenumbraIntegrationSettings;
|
||||
{
|
||||
_penumbra.DrawSettingsSection -= _mainTabBar.Settings.DrawPenumbraIntegrationSettings;
|
||||
_quickBar.ToggleMainWindow -= Toggle;
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
|
|
@ -74,7 +79,7 @@ public sealed class MainWindow : Window, IDisposable
|
|||
{
|
||||
_mainTabBar.Draw();
|
||||
if (_config.ShowQuickBarInTabs)
|
||||
_quickBar.DrawAtEnd(yPos);
|
||||
_quickBar.DrawAtEnd(yPos, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Render;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
|
||||
using Glamourer.Config;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Interop.Material;
|
||||
using Glamourer.State;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Glamourer.Designs;
|
||||
using Glamourer.Config;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Interop.Material;
|
||||
using ImSharp;
|
||||
using Luna;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using Dalamud.Interface.ImGuiNotification;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||
using Glamourer.Automation;
|
||||
using Glamourer.Config;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Designs.History;
|
||||
using Glamourer.Gui.Customization;
|
||||
|
|
@ -19,18 +20,18 @@ namespace Glamourer.Gui.Tabs.ActorTab;
|
|||
|
||||
public sealed class ActorPanel : IPanel
|
||||
{
|
||||
private readonly ActorSelection _selection;
|
||||
private readonly StateManager _stateManager;
|
||||
private readonly CustomizationDrawer _customizationDrawer;
|
||||
private readonly EquipmentDrawer _equipmentDrawer;
|
||||
private readonly AutoDesignApplier _autoDesignApplier;
|
||||
private readonly Configuration _config;
|
||||
private readonly DesignConverter _converter;
|
||||
private readonly ActorObjectManager _objects;
|
||||
private readonly ImportService _importService;
|
||||
private readonly DictModelChara _modelChara;
|
||||
private readonly CustomizeParameterDrawer _parameterDrawer;
|
||||
private readonly AdvancedDyePopup _advancedDyes;
|
||||
private readonly ActorSelection _selection;
|
||||
private readonly StateManager _stateManager;
|
||||
private readonly CustomizationDrawer _customizationDrawer;
|
||||
private readonly EquipmentDrawer _equipmentDrawer;
|
||||
private readonly AutoDesignApplier _autoDesignApplier;
|
||||
private readonly Configuration _config;
|
||||
private readonly DesignConverter _converter;
|
||||
private readonly ActorObjectManager _objects;
|
||||
private readonly ImportService _importService;
|
||||
private readonly DictModelChara _modelChara;
|
||||
private readonly CustomizeParameterDrawer _parameterDrawer;
|
||||
private readonly AdvancedDyePopup _advancedDyes;
|
||||
|
||||
public ActorPanel(StateManager stateManager,
|
||||
CustomizationDrawer customizationDrawer,
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ public readonly struct ActorCacheItem(ActorIdentifier identifier, ActorData data
|
|||
public readonly StringU8 IncognitoText = new(identifier.Incognito(data.Label));
|
||||
}
|
||||
|
||||
public sealed class ActorSelector(ActorSelection selection, ActorObjectManager objects, ActorFilter filter, PenumbraService penumbra, EphemeralConfig config) : IPanel
|
||||
public sealed class ActorSelector(ActorSelection selection, ActorObjectManager objects, ActorFilter filter, PenumbraService penumbra, Config.EphemeralConfig config) : IPanel
|
||||
{
|
||||
public ReadOnlySpan<byte> Id
|
||||
=> "ActorSelector"u8;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using ImSharp;
|
||||
using Glamourer.Config;
|
||||
using ImSharp;
|
||||
using Luna;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.ActorTab;
|
||||
|
|
@ -6,11 +7,13 @@ namespace Glamourer.Gui.Tabs.ActorTab;
|
|||
public sealed class ActorTab : TwoPanelLayout, ITab<MainTabType>
|
||||
{
|
||||
private readonly ActorSelection _selection;
|
||||
private readonly UiConfig _uiConfig;
|
||||
|
||||
public ActorTab(ActorSelector selector, ActorPanel panel, ActorFilter filter, SelectPlayerButton selectPlayer,
|
||||
SelectTargetButton selectTarget, ActorsHeader header, ActorSelection selection)
|
||||
SelectTargetButton selectTarget, ActorsHeader header, ActorSelection selection, UiConfig uiConfig)
|
||||
{
|
||||
_selection = selection;
|
||||
_uiConfig = uiConfig;
|
||||
LeftPanel = selector;
|
||||
LeftHeader = new FilterHeader<ActorCacheItem>(filter, new StringU8("Filter..."u8));
|
||||
var footer = new ButtonFooter();
|
||||
|
|
@ -29,9 +32,18 @@ public sealed class ActorTab : TwoPanelLayout, ITab<MainTabType>
|
|||
public void DrawContent()
|
||||
{
|
||||
_selection.Update();
|
||||
Draw(TwoPanelWidth.IndeterminateRelative);
|
||||
Draw(_uiConfig.ActorsTabScale);
|
||||
}
|
||||
|
||||
protected override void SetWidth(float width, ScalingMode mode)
|
||||
=> _uiConfig.ActorsTabScale = new TwoPanelWidth(width, mode);
|
||||
|
||||
protected override float MinimumWidth
|
||||
=> LeftHeader.MinimumWidth;
|
||||
|
||||
protected override float MaximumWidth
|
||||
=> Im.Window.Width - 500 * Im.Style.GlobalScale;
|
||||
|
||||
public MainTabType Identifier
|
||||
=> MainTabType.Actors;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@ namespace Glamourer.Gui.Tabs.ActorTab;
|
|||
|
||||
public sealed class ActorsHeader : SplitButtonHeader
|
||||
{
|
||||
private readonly ActorSelection _selection;
|
||||
private readonly EphemeralConfig _config;
|
||||
private readonly ActorSelection _selection;
|
||||
private readonly Config.EphemeralConfig _config;
|
||||
|
||||
public ActorsHeader(SetFromClipboardButton setFromClipboard, ExportToClipboardButton exportToClipboard, SaveAsDesignButton save,
|
||||
UndoButton undo, LockedButton locked, IncognitoButton incognito, ActorSelection selection, EphemeralConfig config)
|
||||
UndoButton undo, LockedButton locked, IncognitoButton incognito, ActorSelection selection, Config.EphemeralConfig config)
|
||||
{
|
||||
_selection = selection;
|
||||
_config = config;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Glamourer.Automation;
|
||||
using Glamourer.Config;
|
||||
using ImSharp;
|
||||
using Luna;
|
||||
using Penumbra.GameData.Actors;
|
||||
|
|
|
|||
|
|
@ -1,13 +1,28 @@
|
|||
using ImSharp;
|
||||
using Glamourer.Config;
|
||||
using ImSharp;
|
||||
using Luna;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.AutomationTab;
|
||||
|
||||
public sealed class AutomationHeader(Configuration config, AutomationSelection selection) : IHeader
|
||||
public sealed class AutomationHeader : SplitButtonHeader
|
||||
{
|
||||
public bool Collapsed
|
||||
=> false;
|
||||
private readonly Configuration _config;
|
||||
private readonly AutomationSelection _selection;
|
||||
|
||||
public void Draw(Vector2 size)
|
||||
=> ImEx.TextFramed(config.Ephemeral.IncognitoMode ? selection.Incognito : selection.Name, size with { Y = Im.Style.FrameHeight });
|
||||
public AutomationHeader(Configuration config, AutomationSelection selection, IncognitoButton incognito)
|
||||
{
|
||||
_config = config;
|
||||
_selection = selection;
|
||||
RightButtons.AddButton(incognito, 100);
|
||||
}
|
||||
|
||||
public override void Draw(Vector2 size)
|
||||
{
|
||||
var color = ColorId.HeaderButtons.Value();
|
||||
using var _ = ImGuiColor.Text.Push(color).Push(ImGuiColor.Border, color);
|
||||
base.Draw(size with { Y = Im.Style.FrameHeight });
|
||||
}
|
||||
|
||||
public override ReadOnlySpan<byte> Text
|
||||
=> _config.Ephemeral.IncognitoMode ? _selection.Incognito : _selection.Name;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ public sealed class AutomationSelection : IUiService, IDisposable
|
|||
|
||||
public int DraggedDesignIndex = -1;
|
||||
|
||||
public AutoDesignSet? Set { get; private set; }
|
||||
public AutoDesignSet? Set { get; private set; }
|
||||
public int Index { get; private set; } = -1;
|
||||
public StringU8 Name { get; private set; } = NoSelection;
|
||||
public StringU8 Incognito { get; private set; } = NoSelection;
|
||||
|
|
@ -29,16 +29,16 @@ public sealed class AutomationSelection : IUiService, IDisposable
|
|||
_automationChanged.Unsubscribe(OnAutomationChanged);
|
||||
}
|
||||
|
||||
private void OnAutomationChanged(AutomationChanged.Type type, AutoDesignSet? set, object? data)
|
||||
private void OnAutomationChanged(in AutomationChanged.Arguments arguments)
|
||||
{
|
||||
if (set != Set)
|
||||
if (arguments.Set != Set)
|
||||
return;
|
||||
|
||||
switch (type)
|
||||
switch (arguments.Type)
|
||||
{
|
||||
case AutomationChanged.Type.DeletedSet: Update(null); break;
|
||||
case AutomationChanged.Type.RenamedSet: Name = new StringU8(set!.Name); break;
|
||||
case AutomationChanged.Type.MovedSet: Index = (((int, int))data!).Item2; break;
|
||||
case AutomationChanged.Type.RenamedSet: Name = new StringU8(arguments.Set!.Name); break;
|
||||
case AutomationChanged.Type.MovedSet: Index = arguments.As<AutomationChanged.MovedSetArguments>().NewIndex; break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
using ImSharp;
|
||||
using Glamourer.Config;
|
||||
using ImSharp;
|
||||
using Luna;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.AutomationTab;
|
||||
|
||||
public class AutomationTab : TwoPanelLayout, ITab<MainTabType>
|
||||
public sealed class AutomationTab : TwoPanelLayout, ITab<MainTabType>
|
||||
{
|
||||
private readonly Configuration _config;
|
||||
|
||||
|
|
@ -30,7 +31,11 @@ public class AutomationTab : TwoPanelLayout, ITab<MainTabType>
|
|||
=> MainTabType.Automation;
|
||||
|
||||
public void DrawContent()
|
||||
{
|
||||
Draw(TwoPanelWidth.IndeterminateRelative);
|
||||
}
|
||||
=> Draw(_config.Ui.AutomationTabScale);
|
||||
|
||||
protected override float MinimumWidth
|
||||
=> LeftFooter.MinimumWidth;
|
||||
|
||||
protected override float MaximumWidth
|
||||
=> Im.Window.Width - 500 * Im.Style.GlobalScale;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Dalamud.Game.ClientState.Objects.Enums;
|
||||
using ImSharp;
|
||||
using Luna;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.GameData.DataContainers;
|
||||
using Penumbra.GameData.Gui;
|
||||
|
|
@ -7,13 +8,13 @@ using Penumbra.String;
|
|||
|
||||
namespace Glamourer.Gui.Tabs.AutomationTab;
|
||||
|
||||
public class IdentifierDrawer(
|
||||
public sealed class IdentifierDrawer(
|
||||
ActorManager actors,
|
||||
DictWorld dictWorld,
|
||||
DictModelChara dictModelChara,
|
||||
DictBNpcNames bNpcNames,
|
||||
DictBNpc bNpc,
|
||||
HumanModelList humans)
|
||||
HumanModelList humans) : IUiService
|
||||
{
|
||||
private readonly WorldCombo _worldCombo = new(dictWorld);
|
||||
private readonly HumanNpcCombo _humanNpcCombo = new(bNpcNames, dictModelChara, humans, bNpc);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Dalamud.Interface;
|
||||
using Glamourer.Automation;
|
||||
using Glamourer.Config;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Designs.Special;
|
||||
using Glamourer.Events;
|
||||
|
|
@ -19,21 +20,19 @@ public sealed class RandomRestrictionDrawer : IService, IDisposable
|
|||
private readonly RandomDesignCombo _randomDesignCombo;
|
||||
private readonly AutomationSelection _selection;
|
||||
private readonly DesignStorage _designs;
|
||||
private readonly DesignFileSystem _designFileSystem;
|
||||
|
||||
private string _newText = string.Empty;
|
||||
private string? _newDefinition;
|
||||
private Design? _newDesign;
|
||||
|
||||
public RandomRestrictionDrawer(AutomationChanged automationChanged, Configuration config, AutoDesignManager autoDesignManager,
|
||||
RandomDesignCombo randomDesignCombo, AutomationSelection selection, DesignFileSystem designFileSystem, DesignStorage designs)
|
||||
RandomDesignCombo randomDesignCombo, AutomationSelection selection, DesignStorage designs)
|
||||
{
|
||||
_automationChanged = automationChanged;
|
||||
_config = config;
|
||||
_autoDesignManager = autoDesignManager;
|
||||
_randomDesignCombo = randomDesignCombo;
|
||||
_selection = selection;
|
||||
_designFileSystem = designFileSystem;
|
||||
_designs = designs;
|
||||
_automationChanged.Subscribe(OnAutomationChange, AutomationChanged.Priority.RandomRestrictionDrawer);
|
||||
}
|
||||
|
|
@ -168,7 +167,7 @@ public sealed class RandomRestrictionDrawer : IService, IDisposable
|
|||
{
|
||||
ImEx.TextFrameAligned("that contain"u8);
|
||||
table.NextColumn();
|
||||
var data = contains.Value.Text;
|
||||
var data = contains.Value;
|
||||
Im.Item.SetNextWidthFull();
|
||||
if (Im.Input.Text("##match"u8, ref data, "Name, Path, or Identifier Contains..."u8))
|
||||
{
|
||||
|
|
@ -185,7 +184,7 @@ public sealed class RandomRestrictionDrawer : IService, IDisposable
|
|||
{
|
||||
ImEx.TextFrameAligned("whose path starts with"u8);
|
||||
table.NextColumn();
|
||||
var data = startsWith.Value.Text;
|
||||
var data = startsWith.Value;
|
||||
Im.Item.SetNextWidthFull();
|
||||
if (Im.Input.Text("##startsWith"u8, ref data, "Path Starts With..."u8))
|
||||
{
|
||||
|
|
@ -203,7 +202,7 @@ public sealed class RandomRestrictionDrawer : IService, IDisposable
|
|||
ImEx.TextFrameAligned("that contain the tag"u8);
|
||||
table.NextColumn();
|
||||
Im.Item.SetNextWidthFull();
|
||||
var data = exact.Value.Text;
|
||||
var data = exact.Value;
|
||||
if (Im.Input.Text("##color"u8, ref data, "Contained tag..."u8))
|
||||
{
|
||||
if (data.Length is 0)
|
||||
|
|
@ -220,7 +219,7 @@ public sealed class RandomRestrictionDrawer : IService, IDisposable
|
|||
ImEx.TextFrameAligned("that are set to the color"u8);
|
||||
table.NextColumn();
|
||||
Im.Item.SetNextWidthFull();
|
||||
var data = exact.Value.Text;
|
||||
var data = exact.Value;
|
||||
if (Im.Input.Text("##color"u8, ref data, "Assigned Color is..."u8))
|
||||
{
|
||||
if (data.Length is 0)
|
||||
|
|
@ -264,23 +263,23 @@ public sealed class RandomRestrictionDrawer : IService, IDisposable
|
|||
if (!Im.Item.Hovered())
|
||||
return;
|
||||
|
||||
var designs = predicate.Get(_designs, _designFileSystem);
|
||||
var designs = predicate.Get(_designs);
|
||||
LookupTooltip(designs);
|
||||
}
|
||||
|
||||
private void LookupTooltip(IEnumerable<Design> designs)
|
||||
private static void LookupTooltip(IEnumerable<Design> designs)
|
||||
{
|
||||
using var _ = Im.Tooltip.Begin();
|
||||
using var enumerator = designs.GetEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
Im.Text("Matches the following designs:"u8);
|
||||
var name = _designFileSystem.TryGetValue(enumerator.Current, out var l) ? l.FullName() : enumerator.Current.Name.Text;
|
||||
var name = enumerator.Current.Path.CurrentPath;
|
||||
Im.Separator();
|
||||
Im.BulletText(name);
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
name = _designFileSystem.TryGetValue(enumerator.Current, out l) ? l.FullName() : enumerator.Current.Name.Text;
|
||||
name = enumerator.Current.Path.CurrentPath;
|
||||
Im.BulletText(name);
|
||||
}
|
||||
|
||||
|
|
@ -375,7 +374,7 @@ public sealed class RandomRestrictionDrawer : IService, IDisposable
|
|||
|
||||
private void DrawTotalPreview(IReadOnlyList<IDesignPredicate> list)
|
||||
{
|
||||
var designs = IDesignPredicate.Get(list, _designs, _designFileSystem).ToList();
|
||||
var designs = IDesignPredicate.Get(list, _designs).ToList();
|
||||
Im.Button(designs.Count > 0
|
||||
? $"All Restrictions Combined Match {designs.Count} Designs"
|
||||
: "None of the Restrictions Matches Any Designs"u8, Im.ContentRegion.Available with { Y = 0 });
|
||||
|
|
@ -413,26 +412,27 @@ public sealed class RandomRestrictionDrawer : IService, IDisposable
|
|||
DrawManualInput(list);
|
||||
}
|
||||
|
||||
private void OnAutomationChange(AutomationChanged.Type type, AutoDesignSet? set, object? data)
|
||||
private void OnAutomationChange(in AutomationChanged.Arguments arguments)
|
||||
{
|
||||
if (set != _set || _set is null)
|
||||
if (arguments.Set != _set || _set is null)
|
||||
return;
|
||||
|
||||
switch (type)
|
||||
switch (arguments.Type)
|
||||
{
|
||||
case AutomationChanged.Type.DeletedSet:
|
||||
case AutomationChanged.Type.DeletedDesign when data is int index && _designIndex == index:
|
||||
case AutomationChanged.Type.DeletedDesign when arguments.As<AutomationChanged.DeletedDesignArguments>().Index == _designIndex:
|
||||
Close();
|
||||
break;
|
||||
case AutomationChanged.Type.MovedDesign when data is (int from, int to):
|
||||
if (_designIndex == from)
|
||||
_designIndex = to;
|
||||
else if (_designIndex < from && _designIndex > to)
|
||||
case AutomationChanged.Type.MovedDesign:
|
||||
var data = arguments.As<AutomationChanged.MovedDesignArguments>();
|
||||
if (_designIndex == data.OldIndex)
|
||||
_designIndex = data.NewIndex;
|
||||
else if (_designIndex < data.OldIndex && _designIndex > data.NewIndex)
|
||||
_designIndex++;
|
||||
else if (_designIndex > to && _designIndex < from)
|
||||
else if (_designIndex > data.NewIndex && _designIndex < data.OldIndex)
|
||||
_designIndex--;
|
||||
break;
|
||||
case AutomationChanged.Type.ChangedDesign when data is (int index, IDesignStandIn _, IDesignStandIn _) && index == _designIndex:
|
||||
case AutomationChanged.Type.ChangedDesign when arguments.As<AutomationChanged.ChangedDesignArguments>().DesignIndex == _designIndex:
|
||||
Close();
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using Glamourer.Designs.Special;
|
|||
using Glamourer.Interop;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.Unlocks;
|
||||
using Glamourer.Config;
|
||||
using ImSharp;
|
||||
using Luna;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
|
@ -11,7 +12,7 @@ using Penumbra.GameData.Structs;
|
|||
|
||||
namespace Glamourer.Gui.Tabs.AutomationTab;
|
||||
|
||||
public class SetPanel(
|
||||
public sealed class SetPanel(
|
||||
AutoDesignManager manager,
|
||||
JobService jobs,
|
||||
ItemUnlockManager itemUnlocks,
|
||||
|
|
@ -33,8 +34,7 @@ public class SetPanel(
|
|||
|
||||
public void Draw()
|
||||
{
|
||||
using var child = Im.Child.Begin("##Panel"u8, Im.ContentRegion.Available, true);
|
||||
if (!child || selection.Index < 0)
|
||||
if (selection.Index < 0)
|
||||
return;
|
||||
|
||||
using (Im.Group())
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Glamourer.Automation;
|
||||
using Glamourer.Config;
|
||||
using Glamourer.Events;
|
||||
using ImSharp;
|
||||
using Luna;
|
||||
|
|
@ -54,7 +55,8 @@ public sealed class SetSelector(
|
|||
var identifier = config.Ephemeral.IncognitoMode ? item.IdentifierIncognito : item.IdentifierString;
|
||||
var textSize = identifier.CalculateSize();
|
||||
var textColor = item.Set.Identifiers.Any(objects.ContainsKey) ? cache.AutomationAvailable : cache.AutomationUnavailable;
|
||||
Im.Cursor.Position = new Vector2(Im.ContentRegion.Available.X - textSize.X - Im.Style.FramePadding.X, Im.Cursor.Y - Im.Style.TextHeightWithSpacing);
|
||||
Im.Cursor.Position = new Vector2(Im.ContentRegion.Available.X - textSize.X - Im.Style.FramePadding.X,
|
||||
Im.Cursor.Y - Im.Style.TextHeightWithSpacing);
|
||||
Im.Text(identifier, textColor);
|
||||
}
|
||||
|
||||
|
|
@ -123,9 +125,9 @@ public sealed class SetSelector(
|
|||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private void OnAutomationChanged(AutomationChanged.Type type, AutoDesignSet? set, object? data)
|
||||
private void OnAutomationChanged(in AutomationChanged.Arguments arguments)
|
||||
{
|
||||
switch (type)
|
||||
switch (arguments.Type)
|
||||
{
|
||||
case AutomationChanged.Type.DeletedSet:
|
||||
case AutomationChanged.Type.AddedSet:
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using ImSharp;
|
||||
using Glamourer.Config;
|
||||
using ImSharp;
|
||||
using Luna;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.DebugTab;
|
||||
|
|
|
|||
|
|
@ -1,19 +1,18 @@
|
|||
using Glamourer.Gui.Tabs.DebugTab.IpcTester;
|
||||
using ImSharp;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using OtterGui.Raii;
|
||||
using Penumbra.GameData.Gui.Debug;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.DebugTab;
|
||||
|
||||
public class DebugTabHeader(string label, params IGameDataDrawer[] subTrees)
|
||||
public class DebugTabHeader(ReadOnlySpan<byte> label, params IGameDataDrawer[] subTrees)
|
||||
{
|
||||
public string Label { get; } = label;
|
||||
public StringU8 Label { get; } = new(label);
|
||||
public IReadOnlyList<IGameDataDrawer> SubTrees { get; } = subTrees;
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
using var h = ImRaii.CollapsingHeader(Label);
|
||||
using var h = Im.Tree.HeaderId(Label);
|
||||
if (!h)
|
||||
return;
|
||||
|
||||
|
|
@ -32,7 +31,7 @@ public class DebugTabHeader(string label, params IGameDataDrawer[] subTrees)
|
|||
public static DebugTabHeader CreateInterop(IServiceProvider provider)
|
||||
=> new
|
||||
(
|
||||
"Interop",
|
||||
"Interop"u8,
|
||||
provider.GetRequiredService<ModelEvaluationPanel>(),
|
||||
provider.GetRequiredService<ObjectManagerPanel>(),
|
||||
provider.GetRequiredService<PenumbraPanel>(),
|
||||
|
|
@ -46,7 +45,7 @@ public class DebugTabHeader(string label, params IGameDataDrawer[] subTrees)
|
|||
public static DebugTabHeader CreateGameData(IServiceProvider provider)
|
||||
=> new
|
||||
(
|
||||
"Game Data",
|
||||
"Game Data"u8,
|
||||
provider.GetRequiredService<DataServiceDiagnosticsDrawer>(),
|
||||
provider.GetRequiredService<IdentificationDrawer>(),
|
||||
provider.GetRequiredService<RestrictedGearDrawer>(),
|
||||
|
|
@ -62,7 +61,7 @@ public class DebugTabHeader(string label, params IGameDataDrawer[] subTrees)
|
|||
public static DebugTabHeader CreateDesigns(IServiceProvider provider)
|
||||
=> new
|
||||
(
|
||||
"Designs",
|
||||
"Designs"u8,
|
||||
provider.GetRequiredService<DesignManagerPanel>(),
|
||||
provider.GetRequiredService<DesignConverterPanel>(),
|
||||
provider.GetRequiredService<DesignTesterPanel>(),
|
||||
|
|
@ -72,7 +71,7 @@ public class DebugTabHeader(string label, params IGameDataDrawer[] subTrees)
|
|||
public static DebugTabHeader CreateState(IServiceProvider provider)
|
||||
=> new
|
||||
(
|
||||
"State",
|
||||
"State"u8,
|
||||
provider.GetRequiredService<ActiveStatePanel>(),
|
||||
provider.GetRequiredService<RetainedStatePanel>(),
|
||||
provider.GetRequiredService<FunPanel>()
|
||||
|
|
@ -81,7 +80,7 @@ public class DebugTabHeader(string label, params IGameDataDrawer[] subTrees)
|
|||
public static DebugTabHeader CreateUnlocks(IServiceProvider provider)
|
||||
=> new
|
||||
(
|
||||
"Unlocks",
|
||||
"Unlocks"u8,
|
||||
provider.GetRequiredService<CustomizationUnlockPanel>(),
|
||||
provider.GetRequiredService<ItemUnlockPanel>(),
|
||||
provider.GetRequiredService<UnlockableItemsPanel>(),
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ public sealed class DesignConverterPanel(DesignConverter designConverter) : IGam
|
|||
Im.Text("JSON Parsing Successful!"u8);
|
||||
|
||||
if (_tmpDesign is not null)
|
||||
DesignManagerPanel.DrawDesign(_tmpDesign, null);
|
||||
DesignManagerPanel.DrawDesign(_tmpDesign);
|
||||
|
||||
if (_clipboardProblem.Length > 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -20,11 +20,11 @@ public sealed class DesignManagerPanel(DesignManager designManager, DesignFileSy
|
|||
foreach (var (idx, design) in designManager.Designs.Index())
|
||||
{
|
||||
using var id = Im.Id.Push(idx);
|
||||
using var t = Im.Tree.Node(design.Name.Text);
|
||||
using var t = Im.Tree.Node(design.Name);
|
||||
if (!t)
|
||||
continue;
|
||||
|
||||
DrawDesign(design, designFileSystem);
|
||||
DrawDesign(design);
|
||||
var base64 = DesignBase64Migration.CreateOldBase64(design.DesignData, design.Application.Equip, design.Application.Customize,
|
||||
design.Application.Meta,
|
||||
design.WriteProtected());
|
||||
|
|
@ -50,18 +50,18 @@ public sealed class DesignManagerPanel(DesignManager designManager, DesignFileSy
|
|||
var designs = designManager.Designs.Where(d => d.Tags.Contains("_DebugTest")).ToArray();
|
||||
foreach (var design in designs)
|
||||
designManager.Delete(design);
|
||||
if (designFileSystem.Find("Test Designs", out var path) && path is DesignFileSystem.Folder { TotalChildren: 0 })
|
||||
if (designFileSystem.Find("Test Designs", out var path) && path is IFileSystemFolder { TotalDescendants: 0 })
|
||||
designFileSystem.Delete(path);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawDesign(DesignBase design, DesignFileSystem? fileSystem)
|
||||
public static void DrawDesign(DesignBase design)
|
||||
{
|
||||
using var table = Im.Table.Begin("##equip"u8, 8, TableFlags.RowBackground | TableFlags.SizingFixedFit);
|
||||
if (design is Design d)
|
||||
{
|
||||
table.DrawColumn("Name"u8);
|
||||
table.DrawColumn(d.Name.Text);
|
||||
table.DrawColumn(d.Name);
|
||||
table.DrawColumn($"({d.Index})");
|
||||
table.DrawColumn("Description (Hover)"u8);
|
||||
Im.Tooltip.OnHover(d.Description);
|
||||
|
|
@ -70,8 +70,7 @@ public sealed class DesignManagerPanel(DesignManager designManager, DesignFileSy
|
|||
table.DrawDataPair("Identifier"u8, d.Identifier);
|
||||
table.NextRow();
|
||||
table.DrawColumn("Design File System Path"u8);
|
||||
if (fileSystem is not null)
|
||||
table.DrawColumn(fileSystem.TryGetValue(d, out var leaf) ? leaf.FullName() : "No Path Known"u8);
|
||||
table.DrawColumn(d.Path.CurrentPath);
|
||||
table.NextRow();
|
||||
|
||||
table.DrawDataPair("Creation"u8, d.CreationDate);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Glamourer.State;
|
||||
using Glamourer.Config;
|
||||
using Glamourer.State;
|
||||
using ImSharp;
|
||||
using Penumbra.GameData.Gui.Debug;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
using Glamourer.State;
|
||||
using OtterGui.Raii;
|
||||
using ImSharp;
|
||||
using Penumbra.GameData.Gui.Debug;
|
||||
using Penumbra.GameData.Interop;
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ public sealed class RetainedStatePanel(StateManager stateManager, ActorObjectMan
|
|||
{
|
||||
foreach (var (identifier, state) in stateManager.Where(kvp => !objectManager.ContainsKey(kvp.Key)))
|
||||
{
|
||||
using var t = ImRaii.TreeNode(identifier.ToString());
|
||||
using var t = Im.Tree.Node($"{identifier}");
|
||||
if (t)
|
||||
ActiveStatePanel.DrawState(stateManager, ActorData.Invalid, state);
|
||||
}
|
||||
|
|
|
|||
55
Glamourer/Gui/Tabs/DesignTab/ApplyCharacterButton.cs
Normal file
55
Glamourer/Gui/Tabs/DesignTab/ApplyCharacterButton.cs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.State;
|
||||
using ImSharp;
|
||||
using Luna;
|
||||
using Penumbra.GameData.Interop;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.DesignTab;
|
||||
|
||||
public sealed class ApplyCharacterButton(
|
||||
DesignFileSystem fileSystem,
|
||||
DesignManager manager,
|
||||
ActorObjectManager objects,
|
||||
StateManager stateManager,
|
||||
DesignConverter converter) : BaseIconButton<AwesomeIcon>
|
||||
{
|
||||
private static readonly AwesomeIcon UserIcon = FontAwesomeIcon.UserEdit;
|
||||
|
||||
public override bool IsVisible
|
||||
=> fileSystem.Selection.Selection is not null && objects.Player.Valid;
|
||||
|
||||
public override AwesomeIcon Icon
|
||||
=> UserIcon;
|
||||
|
||||
public override bool Enabled
|
||||
=> !((Design)fileSystem.Selection.Selection!.Value).WriteProtected();
|
||||
|
||||
public override bool HasTooltip
|
||||
=> true;
|
||||
|
||||
public override void DrawTooltip()
|
||||
=> Im.Text("Overwrite this design with your character's current state."u8);
|
||||
|
||||
public override void OnClick()
|
||||
{
|
||||
var selection = (Design)fileSystem.Selection.Selection!.Value;
|
||||
try
|
||||
{
|
||||
var (player, actor) = objects.PlayerData;
|
||||
if (!player.IsValid || !actor.Valid || !stateManager.GetOrCreate(player, actor.Objects[0], out var state))
|
||||
throw new Exception("No player state available.");
|
||||
|
||||
var design = converter.Convert(state, ApplicationRules.FromModifiers(state))
|
||||
?? throw new Exception("The clipboard did not contain valid data.");
|
||||
selection.GetMaterialDataRef().Clear();
|
||||
manager.ApplyDesign(selection, design);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Messager.NotificationMessage(ex, $"Could not apply player state to {selection.Name}.",
|
||||
$"Could not apply player state to design {selection.Identifier}", NotificationType.Error, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +1,28 @@
|
|||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Glamourer.Config;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Gui.Tabs.SettingsTab;
|
||||
using Glamourer.Services;
|
||||
using ImSharp;
|
||||
using Luna;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.DesignTab;
|
||||
|
||||
public class DesignDetailTab
|
||||
public sealed class DesignDetailTab : IUiService
|
||||
{
|
||||
private readonly SaveService _saveService;
|
||||
private readonly Configuration _config;
|
||||
private readonly DesignFileSystemSelector _selector;
|
||||
private readonly DesignFileSystem _fileSystem;
|
||||
private readonly DesignManager _manager;
|
||||
private readonly DesignColors _colors;
|
||||
private readonly DesignColorCombo _colorCombo;
|
||||
private readonly SaveService _saveService;
|
||||
private readonly Configuration _config;
|
||||
private readonly DesignFileSystem _fileSystem;
|
||||
private readonly DesignManager _manager;
|
||||
private readonly DesignColors _colors;
|
||||
private readonly DesignColorCombo _colorCombo;
|
||||
|
||||
private bool _editDescriptionMode;
|
||||
|
||||
public DesignDetailTab(SaveService saveService, DesignFileSystemSelector selector, DesignManager manager, DesignFileSystem fileSystem,
|
||||
public DesignDetailTab(SaveService saveService, DesignManager manager, DesignFileSystem fileSystem,
|
||||
DesignColors colors, Configuration config)
|
||||
{
|
||||
_saveService = saveService;
|
||||
_selector = selector;
|
||||
_manager = manager;
|
||||
_fileSystem = fileSystem;
|
||||
_colors = colors;
|
||||
|
|
@ -41,6 +41,8 @@ public class DesignDetailTab
|
|||
Im.Line.New();
|
||||
}
|
||||
|
||||
private Design Selected
|
||||
=> (Design) _fileSystem.Selection.Selection!.Value;
|
||||
|
||||
private void DrawDesignInfoTable()
|
||||
{
|
||||
|
|
@ -56,13 +58,13 @@ public class DesignDetailTab
|
|||
table.NextColumn();
|
||||
var width = Im.ContentRegion.Available with { Y = 0 };
|
||||
Im.Item.SetNextWidth(width.X);
|
||||
if (ImEx.InputOnDeactivation.Text("##Name"u8, _selector.Selected!.Name.Text, out string newName))
|
||||
_manager.Rename(_selector.Selected!, newName);
|
||||
if (ImEx.InputOnDeactivation.Text("##Name"u8, Selected.Name, out string newName))
|
||||
_manager.Rename(Selected, newName);
|
||||
|
||||
var identifier = _selector.Selected!.Identifier.ToString();
|
||||
var identifier = Selected.Identifier.ToString();
|
||||
table.DrawFrameColumn("Unique Identifier"u8);
|
||||
table.NextColumn();
|
||||
var fileName = _saveService.FileNames.DesignFile(_selector.Selected!);
|
||||
var fileName = _saveService.FileNames.DesignFile(Selected);
|
||||
using (Im.Font.PushMono())
|
||||
{
|
||||
if (Im.Button(identifier, width))
|
||||
|
|
@ -86,10 +88,10 @@ public class DesignDetailTab
|
|||
table.DrawFrameColumn("Full Selector Path"u8);
|
||||
table.NextColumn();
|
||||
Im.Item.SetNextWidth(width.X);
|
||||
if (ImEx.InputOnDeactivation.Text("##Path"u8, _selector.SelectedLeaf!.FullName(), out string newPath))
|
||||
if (ImEx.InputOnDeactivation.Text("##Path"u8, Selected.Path.CurrentPath, out string newPath))
|
||||
try
|
||||
{
|
||||
_fileSystem.RenameAndMove(_selector.SelectedLeaf, newPath);
|
||||
_fileSystem.RenameAndMove(Selected.Node!, newPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -98,56 +100,56 @@ public class DesignDetailTab
|
|||
|
||||
table.DrawFrameColumn("Quick Design Bar"u8);
|
||||
table.NextColumn();
|
||||
if (Im.RadioButton("Display##qdb"u8, _selector.Selected.QuickDesign))
|
||||
_manager.SetQuickDesign(_selector.Selected!, true);
|
||||
if (Im.RadioButton("Display##qdb"u8, Selected.QuickDesign))
|
||||
_manager.SetQuickDesign(Selected, true);
|
||||
var hovered = Im.Item.Hovered();
|
||||
Im.Line.SameInner();
|
||||
if (Im.RadioButton("Hide##qdb"u8, !_selector.Selected.QuickDesign))
|
||||
_manager.SetQuickDesign(_selector.Selected!, false);
|
||||
if (Im.RadioButton("Hide##qdb"u8, !Selected.QuickDesign))
|
||||
_manager.SetQuickDesign(Selected, false);
|
||||
if (hovered || Im.Item.Hovered())
|
||||
Im.Tooltip.Set("Display or hide this design in your quick design bar."u8);
|
||||
|
||||
var forceRedraw = _selector.Selected!.ForcedRedraw;
|
||||
var forceRedraw = Selected.ForcedRedraw;
|
||||
table.DrawFrameColumn("Force Redrawing"u8);
|
||||
table.NextColumn();
|
||||
if (Im.Checkbox("##ForceRedraw"u8, ref forceRedraw))
|
||||
_manager.ChangeForcedRedraw(_selector.Selected!, forceRedraw);
|
||||
_manager.ChangeForcedRedraw(Selected, forceRedraw);
|
||||
Im.Tooltip.OnHover("Set this design to always force a redraw when it is applied through any means."u8);
|
||||
|
||||
var resetAdvancedDyes = _selector.Selected!.ResetAdvancedDyes;
|
||||
var resetAdvancedDyes = Selected.ResetAdvancedDyes;
|
||||
table.DrawFrameColumn("Reset Advanced Dyes"u8);
|
||||
table.NextColumn();
|
||||
if (Im.Checkbox("##ResetAdvancedDyes"u8, ref resetAdvancedDyes))
|
||||
_manager.ChangeResetAdvancedDyes(_selector.Selected!, resetAdvancedDyes);
|
||||
_manager.ChangeResetAdvancedDyes(Selected, resetAdvancedDyes);
|
||||
Im.Tooltip.OnHover("Set this design to reset any previously applied advanced dyes when it is applied through any means."u8);
|
||||
|
||||
var resetTemporarySettings = _selector.Selected!.ResetTemporarySettings;
|
||||
var resetTemporarySettings = Selected.ResetTemporarySettings;
|
||||
table.DrawFrameColumn("Reset Temporary Settings"u8);
|
||||
table.NextColumn();
|
||||
if (Im.Checkbox("##ResetTemporarySettings"u8, ref resetTemporarySettings))
|
||||
_manager.ChangeResetTemporarySettings(_selector.Selected!, resetTemporarySettings);
|
||||
_manager.ChangeResetTemporarySettings(Selected, resetTemporarySettings);
|
||||
Im.Tooltip.OnHover(
|
||||
"Set this design to reset any temporary settings previously applied to the associated collection when it is applied through any means."u8);
|
||||
|
||||
table.DrawFrameColumn("Color"u8);
|
||||
table.NextColumn();
|
||||
if (_colorCombo.Draw("##colorCombo"u8, _selector.Selected!.Color.Length is 0 ? DesignColors.AutomaticName : _selector.Selected!.Color,
|
||||
if (_colorCombo.Draw("##colorCombo"u8, Selected.Color.Length is 0 ? DesignColors.AutomaticName : Selected.Color,
|
||||
"Associate a color with this design.\n"u8
|
||||
+ "Right-Click to revert to automatic coloring.\n"u8
|
||||
+ "Hold Control and scroll the mousewheel to scroll."u8,
|
||||
width.X - Im.Style.ItemSpacing.X - Im.Style.FrameHeight, out var newColorName))
|
||||
_manager.ChangeColor(_selector.Selected!, newColorName == DesignColors.AutomaticName ? string.Empty : newColorName);
|
||||
_manager.ChangeColor(Selected, newColorName == DesignColors.AutomaticName ? string.Empty : newColorName);
|
||||
|
||||
if (Im.Item.RightClicked())
|
||||
_manager.ChangeColor(_selector.Selected!, string.Empty);
|
||||
_manager.ChangeColor(Selected, string.Empty);
|
||||
|
||||
if (_colors.TryGetValue(_selector.Selected!.Color, out var currentColor))
|
||||
if (_colors.TryGetValue(Selected.Color, out var currentColor))
|
||||
{
|
||||
Im.Line.Same();
|
||||
if (DesignColorUi.DrawColorButton($"Color associated with {_selector.Selected!.Color}", currentColor, out var newColor))
|
||||
_colors.SetColor(_selector.Selected!.Color, newColor);
|
||||
if (DesignColorUi.DrawColorButton($"Color associated with {Selected.Color}", currentColor, out var newColor))
|
||||
_colors.SetColor(Selected.Color, newColor);
|
||||
}
|
||||
else if (_selector.Selected!.Color.Length != 0)
|
||||
else if (Selected.Color.Length is not 0)
|
||||
{
|
||||
Im.Line.Same();
|
||||
ImEx.Icon.Draw(LunaStyle.WarningIcon, _colors.MissingColor);
|
||||
|
|
@ -156,11 +158,11 @@ public class DesignDetailTab
|
|||
|
||||
table.DrawFrameColumn("Creation Date"u8);
|
||||
table.NextColumn();
|
||||
ImEx.TextFramed($"{_selector.Selected!.CreationDate.LocalDateTime:F}", width, 0);
|
||||
ImEx.TextFramed($"{Selected.CreationDate.LocalDateTime:F}", width, 0);
|
||||
|
||||
table.DrawFrameColumn("Last Update Date"u8);
|
||||
table.NextColumn();
|
||||
ImEx.TextFramed($"{_selector.Selected!.LastEdit.LocalDateTime:F}", width, 0);
|
||||
ImEx.TextFramed($"{Selected.LastEdit.LocalDateTime:F}", width, 0);
|
||||
|
||||
table.DrawFrameColumn("Tags"u8);
|
||||
table.NextColumn();
|
||||
|
|
@ -169,26 +171,26 @@ public class DesignDetailTab
|
|||
|
||||
private void DrawTags()
|
||||
{
|
||||
var idx = TagButtons.Draw(StringU8.Empty, StringU8.Empty, _selector.Selected!.Tags, out var editedTag);
|
||||
var idx = TagButtons.Draw(StringU8.Empty, StringU8.Empty, Selected.Tags, out var editedTag);
|
||||
if (idx < 0)
|
||||
return;
|
||||
|
||||
if (idx < _selector.Selected!.Tags.Length)
|
||||
if (idx < Selected.Tags.Length)
|
||||
{
|
||||
if (editedTag.Length is 0)
|
||||
_manager.RemoveTag(_selector.Selected!, idx);
|
||||
_manager.RemoveTag(Selected, idx);
|
||||
else
|
||||
_manager.RenameTag(_selector.Selected!, idx, editedTag);
|
||||
_manager.RenameTag(Selected, idx, editedTag);
|
||||
}
|
||||
else
|
||||
{
|
||||
_manager.AddTag(_selector.Selected!, editedTag);
|
||||
_manager.AddTag(Selected, editedTag);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawDescription()
|
||||
{
|
||||
var desc = _selector.Selected!.Description;
|
||||
var desc = Selected.Description;
|
||||
var size = Im.ContentRegion.Available with { Y = 12 * Im.Style.TextHeightWithSpacing };
|
||||
if (!_editDescriptionMode)
|
||||
{
|
||||
|
|
@ -204,7 +206,7 @@ public class DesignDetailTab
|
|||
else
|
||||
{
|
||||
if (ImEx.InputOnDeactivation.MultiLine("##desc"u8, desc, out string newDescription, size))
|
||||
_manager.ChangeDescription(_selector.Selected!, newDescription);
|
||||
_manager.ChangeDescription(Selected, newDescription);
|
||||
|
||||
if (Im.Button("Stop Editing"u8))
|
||||
_editDescriptionMode = false;
|
||||
|
|
|
|||
|
|
@ -1,402 +0,0 @@
|
|||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Designs.History;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Services;
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using ImSharp;
|
||||
using OtterGui;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Filesystem;
|
||||
using OtterGui.FileSystem.Selector;
|
||||
using OtterGui.Log;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Text;
|
||||
using Luna;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.DesignTab;
|
||||
|
||||
public sealed class DesignFileSystemSelector : FileSystemSelector<Design, DesignFileSystemSelector.DesignState>
|
||||
{
|
||||
private readonly DesignManager _designManager;
|
||||
private readonly DesignChanged _event;
|
||||
private readonly Configuration _config;
|
||||
private readonly DesignConverter _converter;
|
||||
private readonly TabSelected _selectionEvent;
|
||||
private readonly DesignColors _designColors;
|
||||
private readonly DesignApplier _designApplier;
|
||||
|
||||
private string? _clipboardText;
|
||||
private Design? _cloneDesign;
|
||||
private string _newName = string.Empty;
|
||||
|
||||
public new DesignFileSystem.Leaf? SelectedLeaf
|
||||
=> base.SelectedLeaf;
|
||||
|
||||
public record struct DesignState(Rgba32 Color)
|
||||
{ }
|
||||
|
||||
protected override float CurrentWidth
|
||||
=> _config.Ephemeral.CurrentDesignSelectorWidth * Im.Style.GlobalScale;
|
||||
|
||||
protected override float MinimumAbsoluteRemainder
|
||||
=> 470 * Im.Style.GlobalScale;
|
||||
|
||||
protected override float MinimumScaling
|
||||
=> _config.Ephemeral.DesignSelectorMinimumScale;
|
||||
|
||||
protected override float MaximumScaling
|
||||
=> _config.Ephemeral.DesignSelectorMaximumScale;
|
||||
|
||||
protected override void SetSize(Vector2 size)
|
||||
{
|
||||
base.SetSize(size);
|
||||
var adaptedSize = MathF.Round(size.X / Im.Style.GlobalScale);
|
||||
if (adaptedSize == _config.Ephemeral.CurrentDesignSelectorWidth)
|
||||
return;
|
||||
|
||||
_config.Ephemeral.CurrentDesignSelectorWidth = adaptedSize;
|
||||
_config.Ephemeral.Save();
|
||||
}
|
||||
|
||||
public DesignFileSystemSelector(DesignManager designManager, DesignFileSystem fileSystem, IKeyState keyState, DesignChanged @event,
|
||||
Configuration config, DesignConverter converter, TabSelected selectionEvent, OtterGui.Log.Logger log, DesignColors designColors,
|
||||
DesignApplier designApplier)
|
||||
: base(fileSystem, keyState, log, allowMultipleSelection: true)
|
||||
{
|
||||
_designManager = designManager;
|
||||
_event = @event;
|
||||
_config = config;
|
||||
_converter = converter;
|
||||
_selectionEvent = selectionEvent;
|
||||
_designColors = designColors;
|
||||
_designApplier = designApplier;
|
||||
_event.Subscribe(OnDesignChange, DesignChanged.Priority.DesignFileSystemSelector);
|
||||
_selectionEvent.Subscribe(OnTabSelected, TabSelected.Priority.DesignSelector);
|
||||
_designColors.ColorChanged += SetFilterDirty;
|
||||
|
||||
AddButton(NewDesignButton, 0);
|
||||
AddButton(ImportDesignButton, 10);
|
||||
AddButton(CloneDesignButton, 20);
|
||||
AddButton(DeleteButton, 1000);
|
||||
UnsubscribeRightClickLeaf(RenameLeaf);
|
||||
SetRenameSearchPath(_config.ShowRename);
|
||||
SetFilterTooltip();
|
||||
|
||||
if (_config.Ephemeral.SelectedDesign == Guid.Empty)
|
||||
return;
|
||||
|
||||
var design = designManager.Designs.ByIdentifier(_config.Ephemeral.SelectedDesign);
|
||||
if (design != null)
|
||||
SelectByValue(design);
|
||||
}
|
||||
|
||||
public void SetRenameSearchPath(RenameField value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case RenameField.RenameSearchPath:
|
||||
SubscribeRightClickLeaf(RenameLeafDesign, 1000);
|
||||
UnsubscribeRightClickLeaf(RenameDesign);
|
||||
break;
|
||||
case RenameField.RenameData:
|
||||
UnsubscribeRightClickLeaf(RenameLeafDesign);
|
||||
SubscribeRightClickLeaf(RenameDesign, 1000);
|
||||
break;
|
||||
case RenameField.BothSearchPathPrio:
|
||||
UnsubscribeRightClickLeaf(RenameLeafDesign);
|
||||
UnsubscribeRightClickLeaf(RenameDesign);
|
||||
SubscribeRightClickLeaf(RenameLeafDesign, 1001);
|
||||
SubscribeRightClickLeaf(RenameDesign, 1000);
|
||||
break;
|
||||
case RenameField.BothDataPrio:
|
||||
UnsubscribeRightClickLeaf(RenameLeafDesign);
|
||||
UnsubscribeRightClickLeaf(RenameDesign);
|
||||
SubscribeRightClickLeaf(RenameLeafDesign, 1000);
|
||||
SubscribeRightClickLeaf(RenameDesign, 1001);
|
||||
break;
|
||||
default:
|
||||
UnsubscribeRightClickLeaf(RenameLeafDesign);
|
||||
UnsubscribeRightClickLeaf(RenameDesign);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void RenameLeafDesign(DesignFileSystem.Leaf leaf)
|
||||
{
|
||||
Im.Separator();
|
||||
RenameLeaf(leaf);
|
||||
}
|
||||
|
||||
private void RenameDesign(DesignFileSystem.Leaf leaf)
|
||||
{
|
||||
Im.Separator();
|
||||
var currentName = leaf.Value.Name.Text;
|
||||
if (ImGui.IsWindowAppearing())
|
||||
ImGui.SetKeyboardFocusHere(0);
|
||||
ImGui.TextUnformatted("Rename Design:");
|
||||
if (Im.Input.Text("##RenameDesign"u8, ref currentName, StringU8.Empty, InputTextFlags.EnterReturnsTrue))
|
||||
{
|
||||
_designManager.Rename(leaf.Value, currentName);
|
||||
ImGui.CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGuiUtil.HoverTooltip("Enter a new name here to rename the changed design.");
|
||||
}
|
||||
|
||||
protected override void Select(FileSystem<Design>.Leaf? leaf, bool clear, in DesignState storage = default)
|
||||
{
|
||||
base.Select(leaf, clear, storage);
|
||||
var id = SelectedLeaf?.Value.Identifier ?? Guid.Empty;
|
||||
if (id != _config.Ephemeral.SelectedDesign)
|
||||
{
|
||||
_config.Ephemeral.SelectedDesign = id;
|
||||
_config.Ephemeral.Save();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void DrawPopups()
|
||||
{
|
||||
DrawNewDesignPopup();
|
||||
}
|
||||
|
||||
protected override void DrawLeafName(FileSystem<Design>.Leaf leaf, in DesignState state, bool selected)
|
||||
{
|
||||
var flag = selected ? ImGuiTreeNodeFlags.Selected | LeafFlags : LeafFlags;
|
||||
var name = _config.Ephemeral.IncognitoMode ? leaf.Value.Incognito : leaf.Value.Name.Text;
|
||||
using var color = ImGuiColor.Text.Push(state.Color);
|
||||
using var _ = ImUtf8.TreeNode(name, flag);
|
||||
if (_config.AllowDoubleClickToApply && ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(ImGuiMouseButton.Left))
|
||||
_designApplier.ApplyToPlayer(leaf.Value);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
_event.Unsubscribe(OnDesignChange);
|
||||
_selectionEvent.Unsubscribe(OnTabSelected);
|
||||
_designColors.ColorChanged -= SetFilterDirty;
|
||||
}
|
||||
|
||||
public override ISortMode<Design> SortMode
|
||||
=> _config.SortMode;
|
||||
|
||||
protected override uint ExpandedFolderColor
|
||||
=> ColorId.FolderExpanded.Value().Color;
|
||||
|
||||
protected override uint CollapsedFolderColor
|
||||
=> ColorId.FolderCollapsed.Value().Color;
|
||||
|
||||
protected override uint FolderLineColor
|
||||
=> ColorId.FolderLine.Value().Color;
|
||||
|
||||
protected override bool FoldersDefaultOpen
|
||||
=> _config.OpenFoldersByDefault;
|
||||
|
||||
private void OnDesignChange(DesignChanged.Type type, Design design, ITransaction? _)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case DesignChanged.Type.ReloadedAll:
|
||||
case DesignChanged.Type.Renamed:
|
||||
case DesignChanged.Type.AddedTag:
|
||||
case DesignChanged.Type.ChangedTag:
|
||||
case DesignChanged.Type.RemovedTag:
|
||||
case DesignChanged.Type.AddedMod:
|
||||
case DesignChanged.Type.RemovedMod:
|
||||
case DesignChanged.Type.Created:
|
||||
case DesignChanged.Type.Deleted:
|
||||
case DesignChanged.Type.ApplyCustomize:
|
||||
case DesignChanged.Type.ApplyEquip:
|
||||
case DesignChanged.Type.ApplyStain:
|
||||
case DesignChanged.Type.ApplyCrest:
|
||||
case DesignChanged.Type.Customize:
|
||||
case DesignChanged.Type.Equip:
|
||||
case DesignChanged.Type.ChangedColor:
|
||||
SetFilterDirty();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void NewDesignButton(Vector2 size)
|
||||
{
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), size, "Create a new design with default configuration.", false,
|
||||
true))
|
||||
{
|
||||
_cloneDesign = null;
|
||||
_clipboardText = null;
|
||||
ImGui.OpenPopup("##NewDesign");
|
||||
}
|
||||
}
|
||||
|
||||
private void ImportDesignButton(Vector2 size)
|
||||
{
|
||||
if (!ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.FileImport.ToIconString(), size, "Try to import a design from your clipboard.", false,
|
||||
true))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
_cloneDesign = null;
|
||||
_clipboardText = ImGui.GetClipboardText();
|
||||
ImGui.OpenPopup("##NewDesign");
|
||||
}
|
||||
catch
|
||||
{
|
||||
Glamourer.Messager.NotificationMessage("Could not import data from clipboard.", NotificationType.Error, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void CloneDesignButton(Vector2 size)
|
||||
{
|
||||
var tt = SelectedLeaf == null
|
||||
? "No design selected."
|
||||
: "Clone the currently selected design to a duplicate";
|
||||
if (!ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Clone.ToIconString(), size, tt, SelectedLeaf == null, true))
|
||||
return;
|
||||
|
||||
_clipboardText = null;
|
||||
_cloneDesign = Selected!;
|
||||
ImGui.OpenPopup("##NewDesign");
|
||||
}
|
||||
|
||||
private void DeleteButton(Vector2 size)
|
||||
=> DeleteSelectionButton(size,
|
||||
new OtterGui.Classes.DoubleModifier(new OtterGui.Classes.ModifierHotkey(_config.DeleteDesignModifier.Modifier1),
|
||||
new OtterGui.Classes.ModifierHotkey(_config.DeleteDesignModifier.Modifier2)), "design", "designs", _designManager.Delete);
|
||||
|
||||
private void DrawNewDesignPopup()
|
||||
{
|
||||
if (!ImGuiUtil.OpenNameField("##NewDesign", ref _newName))
|
||||
return;
|
||||
|
||||
if (_clipboardText != null)
|
||||
{
|
||||
var design = _converter.FromBase64(_clipboardText, true, true, out _);
|
||||
if (design is Design d)
|
||||
_designManager.CreateClone(d, _newName, true);
|
||||
else if (design != null)
|
||||
_designManager.CreateClone(design, _newName, true);
|
||||
else
|
||||
Glamourer.Messager.NotificationMessage("Could not create a design, clipboard did not contain valid design data.",
|
||||
NotificationType.Error, false);
|
||||
_clipboardText = null;
|
||||
}
|
||||
else if (_cloneDesign != null)
|
||||
{
|
||||
_designManager.CreateClone(_cloneDesign, _newName, true);
|
||||
_cloneDesign = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
_designManager.CreateEmpty(_newName, true);
|
||||
}
|
||||
|
||||
_newName = string.Empty;
|
||||
}
|
||||
|
||||
private void OnTabSelected(MainTabType type, Design? design)
|
||||
{
|
||||
if (type == MainTabType.Designs && design != null)
|
||||
SelectByValue(design);
|
||||
}
|
||||
|
||||
#region Filters
|
||||
|
||||
private const StringComparison IgnoreCase = StringComparison.OrdinalIgnoreCase;
|
||||
private LowerString _designFilter = LowerString.Empty;
|
||||
private int _filterType = -1;
|
||||
|
||||
private void SetFilterTooltip()
|
||||
{
|
||||
FilterTooltip = "Filter designs for those where their full paths or names contain the given substring.\n"
|
||||
+ "Enter m:[string] to filter for designs with with a mod association containing the string.\n"
|
||||
+ "Enter t:[string] to filter for designs set to specific tags.\n"
|
||||
+ "Enter c:[string] to filter for designs set to specific colors.\n"
|
||||
+ "Enter i:[string] to filter for designs containing specific items.\n"
|
||||
+ "Enter n:[string] to filter only for design names and no paths.\n\n"
|
||||
+ "Use None as a placeholder value that only matches empty lists or names.";
|
||||
}
|
||||
|
||||
/// <summary> Appropriately identify and set the string filter and its type. </summary>
|
||||
protected override bool ChangeFilter(string filterValue)
|
||||
{
|
||||
(_designFilter, _filterType) = filterValue.Length switch
|
||||
{
|
||||
0 => (LowerString.Empty, -1),
|
||||
> 1 when filterValue[1] == ':' =>
|
||||
filterValue[0] switch
|
||||
{
|
||||
'n' => filterValue.Length == 2 ? (LowerString.Empty, -1) : (new LowerString(filterValue[2..]), 1),
|
||||
'N' => filterValue.Length == 2 ? (LowerString.Empty, -1) : (new LowerString(filterValue[2..]), 1),
|
||||
'm' => filterValue.Length == 2 ? (LowerString.Empty, -1) : ParseFilter(filterValue, 2),
|
||||
'M' => filterValue.Length == 2 ? (LowerString.Empty, -1) : ParseFilter(filterValue, 2),
|
||||
't' => filterValue.Length == 2 ? (LowerString.Empty, -1) : ParseFilter(filterValue, 3),
|
||||
'T' => filterValue.Length == 2 ? (LowerString.Empty, -1) : ParseFilter(filterValue, 3),
|
||||
'i' => filterValue.Length == 2 ? (LowerString.Empty, -1) : (new LowerString(filterValue[2..]), 4),
|
||||
'I' => filterValue.Length == 2 ? (LowerString.Empty, -1) : (new LowerString(filterValue[2..]), 4),
|
||||
'c' => filterValue.Length == 2 ? (LowerString.Empty, -1) : (new LowerString(filterValue[2..]), 5),
|
||||
'C' => filterValue.Length == 2 ? (LowerString.Empty, -1) : (new LowerString(filterValue[2..]), 5),
|
||||
_ => (new LowerString(filterValue), 0),
|
||||
},
|
||||
_ => (new LowerString(filterValue), 0),
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private const int EmptyOffset = 128;
|
||||
|
||||
private static (LowerString, int) ParseFilter(string value, int id)
|
||||
{
|
||||
value = value[2..];
|
||||
var lower = new LowerString(value);
|
||||
return (lower, lower.Lower is "none" ? id + EmptyOffset : id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The overwritten filter method also computes the state.
|
||||
/// Folders have default state and are filtered out on the direct string instead of the other options.
|
||||
/// If any filter is set, they should be hidden by default unless their children are visible,
|
||||
/// or they contain the path search string.
|
||||
/// </summary>
|
||||
protected override bool ApplyFiltersAndState(FileSystem<Design>.IPath path, out DesignState state)
|
||||
{
|
||||
if (path is DesignFileSystem.Folder f)
|
||||
{
|
||||
state = default;
|
||||
return FilterValue.Length > 0 && !f.FullName().Contains(FilterValue, IgnoreCase);
|
||||
}
|
||||
|
||||
return ApplyFiltersAndState((DesignFileSystem.Leaf)path, out state);
|
||||
}
|
||||
|
||||
/// <summary> Apply the string filters. </summary>
|
||||
private bool ApplyStringFilters(DesignFileSystem.Leaf leaf, Design design)
|
||||
{
|
||||
return _filterType switch
|
||||
{
|
||||
-1 => false,
|
||||
0 => !(_designFilter.IsContained(leaf.FullName()) || design.Name.Contains(_designFilter)),
|
||||
1 => !design.Name.Contains(_designFilter),
|
||||
2 => !design.AssociatedMods.Any(kvp => _designFilter.IsContained(kvp.Key.Name)),
|
||||
3 => !design.Tags.Any(_designFilter.IsContained),
|
||||
4 => !design.DesignData.ContainsName(_designFilter),
|
||||
5 => !_designFilter.IsContained(design.Color.Length == 0 ? DesignColors.AutomaticName : design.Color),
|
||||
2 + EmptyOffset => design.AssociatedMods.Count > 0,
|
||||
3 + EmptyOffset => design.Tags.Length > 0,
|
||||
_ => false, // Should never happen
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary> Combined wrapper for handling all filters and setting state. </summary>
|
||||
private bool ApplyFiltersAndState(DesignFileSystem.Leaf leaf, out DesignState state)
|
||||
{
|
||||
state = new DesignState(_designColors.GetColor(leaf.Value));
|
||||
return ApplyStringFilters(leaf, leaf.Value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
106
Glamourer/Gui/Tabs/DesignTab/DesignHeader.cs
Normal file
106
Glamourer/Gui/Tabs/DesignTab/DesignHeader.cs
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
using Glamourer.Config;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Designs.History;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.State;
|
||||
using ImSharp;
|
||||
using Luna;
|
||||
using Penumbra.GameData.Interop;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.DesignTab;
|
||||
|
||||
public sealed class DesignHeader : SplitButtonHeader, IDisposable
|
||||
{
|
||||
private readonly DesignFileSystem _fileSystem;
|
||||
private readonly DesignChanged _designChanged;
|
||||
private readonly Configuration _config;
|
||||
|
||||
private StringU8 _header = new("No Selection"u8);
|
||||
private StringU8 _incognito = new("No Selection"u8);
|
||||
|
||||
public DesignHeader(DesignFileSystem fileSystem, IncognitoButton incognito, DesignChanged designChanged, Configuration config,
|
||||
DesignConverter converter, StateManager stateManager, EditorHistory history, DesignManager manager, ActorObjectManager objects)
|
||||
{
|
||||
_fileSystem = fileSystem;
|
||||
_designChanged = designChanged;
|
||||
_config = config;
|
||||
LeftButtons.AddButton(new SetFromClipboardButton(fileSystem, converter, manager), 100);
|
||||
LeftButtons.AddButton(new DesignUndoButton(fileSystem, manager), 90);
|
||||
LeftButtons.AddButton(new ExportToClipboardButton(fileSystem, converter), 80);
|
||||
LeftButtons.AddButton(new ApplyCharacterButton(fileSystem, manager, objects, stateManager, converter), 70);
|
||||
LeftButtons.AddButton(new UndoButton(fileSystem, history), 60);
|
||||
|
||||
RightButtons.AddButton(incognito, 50);
|
||||
RightButtons.AddButton(new LockedButton(fileSystem, manager), 100);
|
||||
_fileSystem.Selection.Changed += OnSelectionChanged;
|
||||
OnSelectionChanged();
|
||||
designChanged.Subscribe(OnDesignChanged, DesignChanged.Priority.DesignHeader);
|
||||
}
|
||||
|
||||
private void OnDesignChanged(in DesignChanged.Arguments arguments)
|
||||
{
|
||||
if (arguments.Type is not DesignChanged.Type.Renamed)
|
||||
return;
|
||||
|
||||
if (arguments.Design != _fileSystem.Selection.Selection?.Value)
|
||||
return;
|
||||
|
||||
_header = new StringU8(arguments.Design.Name);
|
||||
}
|
||||
|
||||
private void OnSelectionChanged()
|
||||
{
|
||||
if (_fileSystem.Selection.Selection?.GetValue<Design>() is { } selection)
|
||||
{
|
||||
_header = new StringU8(selection.Name);
|
||||
_incognito = new StringU8(selection.Incognito);
|
||||
}
|
||||
else if (_fileSystem.Selection.OrderedNodes.Count > 0)
|
||||
{
|
||||
_header = new StringU8($"{_fileSystem.Selection.OrderedNodes.Count} Objects Selected");
|
||||
_incognito = _header;
|
||||
}
|
||||
else
|
||||
{
|
||||
_header = new StringU8("No Selection"u8);
|
||||
_incognito = _header;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Draw(Vector2 size)
|
||||
{
|
||||
var color = ColorId.HeaderButtons.Value();
|
||||
using var _ = ImGuiColor.Text.Push(color).Push(ImGuiColor.Border, color);
|
||||
base.Draw(size with { Y = Im.Style.FrameHeight });
|
||||
}
|
||||
|
||||
public override ReadOnlySpan<byte> Text
|
||||
=> _config.Ephemeral.IncognitoMode ? _incognito : _header;
|
||||
|
||||
private sealed class LockedButton(DesignFileSystem fileSystem, DesignManager manager) : BaseIconButton<AwesomeIcon>
|
||||
{
|
||||
public override bool IsVisible
|
||||
=> fileSystem.Selection.Selection is not null;
|
||||
|
||||
public override AwesomeIcon Icon
|
||||
=> ((Design)fileSystem.Selection.Selection!.Value).WriteProtected() ? LunaStyle.LockedIcon : LunaStyle.UnlockedIcon;
|
||||
|
||||
public override bool HasTooltip
|
||||
=> true;
|
||||
|
||||
public override void DrawTooltip()
|
||||
=> Im.Text(((Design)fileSystem.Selection.Selection!.Value).WriteProtected()
|
||||
? "Make this design editable."u8
|
||||
: "Write-protect this design."u8);
|
||||
|
||||
public override void OnClick()
|
||||
=> manager.SetWriteProtection((Design)fileSystem.Selection.Selection!.Value,
|
||||
!((Design)fileSystem.Selection.Selection!.Value).WriteProtected());
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_fileSystem.Selection.Changed -= OnSelectionChanged;
|
||||
_designChanged.Unsubscribe(OnDesignChanged);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
using Dalamud.Interface;
|
||||
using Glamourer.Automation;
|
||||
using Glamourer.Config;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Designs.Links;
|
||||
using ImSharp;
|
||||
|
|
@ -9,7 +10,7 @@ namespace Glamourer.Gui.Tabs.DesignTab;
|
|||
|
||||
public class DesignLinkDrawer(
|
||||
DesignLinkManager linkManager,
|
||||
DesignFileSystemSelector selector,
|
||||
DesignFileSystem fileSystem,
|
||||
LinkDesignCombo combo,
|
||||
DesignColors colorManager,
|
||||
Configuration config) : IUiService
|
||||
|
|
@ -19,6 +20,9 @@ public class DesignLinkDrawer(
|
|||
private int _dragDropTargetIndex = -1;
|
||||
private LinkOrder _dragDropTargetOrder = LinkOrder.None;
|
||||
|
||||
private Design Selected
|
||||
=> (Design)fileSystem.Selection.Selection!.Value;
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
using var h = DesignPanelFlag.DesignLinks.Header(config);
|
||||
|
|
@ -44,23 +48,23 @@ public class DesignLinkDrawer(
|
|||
switch (_dragDropTargetOrder)
|
||||
{
|
||||
case LinkOrder.Before:
|
||||
for (var i = selector.Selected!.Links.Before.Count - 1; i >= _dragDropTargetIndex; --i)
|
||||
linkManager.MoveDesignLink(selector.Selected!, i, LinkOrder.Before, 0, LinkOrder.After);
|
||||
for (var i = Selected.Links.Before.Count - 1; i >= _dragDropTargetIndex; --i)
|
||||
linkManager.MoveDesignLink(Selected, i, LinkOrder.Before, 0, LinkOrder.After);
|
||||
break;
|
||||
case LinkOrder.After:
|
||||
for (var i = 0; i <= _dragDropTargetIndex; ++i)
|
||||
{
|
||||
linkManager.MoveDesignLink(selector.Selected!, 0, LinkOrder.After, selector.Selected!.Links.Before.Count,
|
||||
linkManager.MoveDesignLink(Selected, 0, LinkOrder.After, Selected.Links.Before.Count,
|
||||
LinkOrder.Before);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
else if (_dragDropTargetOrder is LinkOrder.Self)
|
||||
linkManager.MoveDesignLink(selector.Selected!, _dragDropIndex, _dragDropOrder, selector.Selected!.Links.Before.Count,
|
||||
linkManager.MoveDesignLink(Selected, _dragDropIndex, _dragDropOrder, Selected.Links.Before.Count,
|
||||
LinkOrder.Before);
|
||||
else
|
||||
linkManager.MoveDesignLink(selector.Selected!, _dragDropIndex, _dragDropOrder, _dragDropTargetIndex, _dragDropTargetOrder);
|
||||
linkManager.MoveDesignLink(Selected, _dragDropIndex, _dragDropOrder, _dragDropTargetIndex, _dragDropTargetOrder);
|
||||
|
||||
_dragDropIndex = -1;
|
||||
_dragDropTargetIndex = -1;
|
||||
|
|
@ -80,9 +84,9 @@ public class DesignLinkDrawer(
|
|||
6 * Im.Style.FrameHeight + 5 * Im.Style.ItemInnerSpacing.X);
|
||||
|
||||
using var style = ImStyleDouble.ItemSpacing.Push(Im.Style.ItemInnerSpacing);
|
||||
DrawSubList(table, selector.Selected!.Links.Before, LinkOrder.Before);
|
||||
DrawSubList(table, Selected.Links.Before, LinkOrder.Before);
|
||||
DrawSelf(table);
|
||||
DrawSubList(table, selector.Selected!.Links.After, LinkOrder.After);
|
||||
DrawSubList(table, Selected.Links.After, LinkOrder.After);
|
||||
DrawNew(table);
|
||||
MoveLink();
|
||||
}
|
||||
|
|
@ -91,7 +95,7 @@ public class DesignLinkDrawer(
|
|||
{
|
||||
using var id = Im.Id.Push((int)LinkOrder.Self);
|
||||
table.NextColumn();
|
||||
var color = colorManager.GetColor(selector.Selected!);
|
||||
var color = colorManager.GetColor(Selected);
|
||||
using (AwesomeIcon.Font.Push())
|
||||
{
|
||||
using var c = ImGuiColor.Text.Push(color);
|
||||
|
|
@ -103,11 +107,11 @@ public class DesignLinkDrawer(
|
|||
using (ImGuiColor.Text.Push(color))
|
||||
{
|
||||
Im.Cursor.FrameAlign();
|
||||
Im.Selectable(config.Ephemeral.IncognitoMode ? selector.Selected!.Incognito : selector.Selected!.Name.Text);
|
||||
Im.Selectable(config.Ephemeral.IncognitoMode ? Selected.Incognito : Selected.Name);
|
||||
}
|
||||
|
||||
Im.Tooltip.OnHover("Current Design"u8);
|
||||
DrawDragDrop(selector.Selected!, LinkOrder.Self, 0);
|
||||
DrawDragDrop(Selected, LinkOrder.Self, 0);
|
||||
table.NextColumn();
|
||||
using (AwesomeIcon.Font.Push())
|
||||
{
|
||||
|
|
@ -133,7 +137,7 @@ public class DesignLinkDrawer(
|
|||
using (ImGuiColor.Text.Push(colorManager.GetColor(design)))
|
||||
{
|
||||
Im.Cursor.FrameAlign();
|
||||
Im.Selectable(config.Ephemeral.IncognitoMode ? design.Incognito : design.Name.Text);
|
||||
Im.Selectable(config.Ephemeral.IncognitoMode ? design.Incognito : design.Name);
|
||||
}
|
||||
|
||||
DrawDragDrop(design, order, i);
|
||||
|
|
@ -143,7 +147,7 @@ public class DesignLinkDrawer(
|
|||
DrawApplicationBoxes(i, order, flags);
|
||||
|
||||
if (delete)
|
||||
linkManager.RemoveDesignLink(selector.Selected!, i--, order);
|
||||
linkManager.RemoveDesignLink(Selected, i--, order);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -163,11 +167,11 @@ public class DesignLinkDrawer(
|
|||
}
|
||||
else
|
||||
{
|
||||
canAddBefore = LinkContainer.CanAddLink(selector.Selected!, design, LinkOrder.Before, out var error);
|
||||
canAddBefore = LinkContainer.CanAddLink(Selected, design, LinkOrder.Before, out var error);
|
||||
ttBefore = canAddBefore
|
||||
? $"Add a link at the top of the list to {design.Name}."
|
||||
: $"Can not add a link to {design.Name}:\n{error}";
|
||||
canAddAfter = LinkContainer.CanAddLink(selector.Selected!, design, LinkOrder.After, out error);
|
||||
canAddAfter = LinkContainer.CanAddLink(Selected, design, LinkOrder.After, out error);
|
||||
ttAfter = canAddAfter
|
||||
? $"Add a link at the bottom of the list to {design.Name}."
|
||||
: $"Can not add a link to {design.Name}:\n{error}";
|
||||
|
|
@ -175,13 +179,13 @@ public class DesignLinkDrawer(
|
|||
|
||||
if (ImEx.Icon.Button(FontAwesomeIcon.ArrowCircleUp.Icon(), ttBefore, !canAddBefore))
|
||||
{
|
||||
linkManager.AddDesignLink(selector.Selected!, design!, LinkOrder.Before);
|
||||
linkManager.MoveDesignLink(selector.Selected!, selector.Selected!.Links.Before.Count - 1, LinkOrder.Before, 0, LinkOrder.Before);
|
||||
linkManager.AddDesignLink(Selected, design!, LinkOrder.Before);
|
||||
linkManager.MoveDesignLink(Selected, Selected.Links.Before.Count - 1, LinkOrder.Before, 0, LinkOrder.Before);
|
||||
}
|
||||
|
||||
Im.Line.Same();
|
||||
if (ImEx.Icon.Button(FontAwesomeIcon.ArrowCircleDown.Icon(), ttAfter, !canAddAfter))
|
||||
linkManager.AddDesignLink(selector.Selected!, design!, LinkOrder.After);
|
||||
linkManager.AddDesignLink(Selected, design!, LinkOrder.After);
|
||||
}
|
||||
|
||||
private void DrawDragDrop(Design design, LinkOrder order, int index)
|
||||
|
|
@ -227,7 +231,7 @@ public class DesignLinkDrawer(
|
|||
Im.Line.Same();
|
||||
Box(4);
|
||||
if (newType != current)
|
||||
linkManager.ChangeApplicationType(selector.Selected!, idx, order, newType);
|
||||
linkManager.ChangeApplicationType(Selected, idx, order, newType);
|
||||
return;
|
||||
|
||||
void Box(int i)
|
||||
|
|
|
|||
|
|
@ -1,34 +1,28 @@
|
|||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.ImGuiFileDialog;
|
||||
using Dalamud.Interface.ImGuiFileDialog;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Framework;
|
||||
using Glamourer.Api.Enums;
|
||||
using Glamourer.Automation;
|
||||
using Glamourer.Config;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Designs.History;
|
||||
using Glamourer.GameData;
|
||||
using Glamourer.Gui.Customization;
|
||||
using Glamourer.Gui.Equipment;
|
||||
using Glamourer.Gui.Materials;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.State;
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using ImSharp;
|
||||
using Luna;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Text;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Interop;
|
||||
using static Glamourer.Gui.Tabs.HeaderDrawer;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.DesignTab;
|
||||
|
||||
public class DesignPanel
|
||||
public class DesignPanel : IPanel
|
||||
{
|
||||
private readonly FileDialogManager _fileDialog = new();
|
||||
private readonly DesignFileSystemSelector _selector;
|
||||
private readonly CustomizationDrawer _customizationDrawer;
|
||||
private readonly DesignFileSystem _fileSystem;
|
||||
private readonly DesignManager _manager;
|
||||
private readonly ActorObjectManager _objects;
|
||||
private readonly StateManager _state;
|
||||
|
|
@ -37,18 +31,13 @@ public class DesignPanel
|
|||
private readonly Configuration _config;
|
||||
private readonly DesignDetailTab _designDetails;
|
||||
private readonly ImportService _importService;
|
||||
private readonly DesignConverter _converter;
|
||||
private readonly MultiDesignPanel _multiDesignPanel;
|
||||
private readonly CustomizeParameterDrawer _parameterDrawer;
|
||||
private readonly DesignLinkDrawer _designLinkDrawer;
|
||||
private readonly MaterialDrawer _materials;
|
||||
private readonly EditorHistory _history;
|
||||
private readonly Button[] _leftButtons;
|
||||
private readonly Button[] _rightButtons;
|
||||
|
||||
|
||||
public DesignPanel(DesignFileSystemSelector selector,
|
||||
CustomizationDrawer customizationDrawer,
|
||||
public DesignPanel(CustomizationDrawer customizationDrawer,
|
||||
DesignManager manager,
|
||||
ActorObjectManager objects,
|
||||
StateManager state,
|
||||
|
|
@ -62,9 +51,8 @@ public class DesignPanel
|
|||
CustomizeParameterDrawer parameterDrawer,
|
||||
DesignLinkDrawer designLinkDrawer,
|
||||
MaterialDrawer materials,
|
||||
EditorHistory history)
|
||||
DesignFileSystem fileSystem)
|
||||
{
|
||||
_selector = selector;
|
||||
_customizationDrawer = customizationDrawer;
|
||||
_manager = manager;
|
||||
_objects = objects;
|
||||
|
|
@ -74,32 +62,16 @@ public class DesignPanel
|
|||
_config = config;
|
||||
_designDetails = designDetails;
|
||||
_importService = importService;
|
||||
_converter = converter;
|
||||
_multiDesignPanel = multiDesignPanel;
|
||||
_parameterDrawer = parameterDrawer;
|
||||
_designLinkDrawer = designLinkDrawer;
|
||||
_materials = materials;
|
||||
_history = history;
|
||||
_leftButtons =
|
||||
[
|
||||
new SetFromClipboardButton(this),
|
||||
new DesignUndoButton(this),
|
||||
new ExportToClipboardButton(this),
|
||||
new ApplyCharacterButton(this),
|
||||
new UndoButton(this),
|
||||
];
|
||||
_rightButtons =
|
||||
[
|
||||
new LockButton(this),
|
||||
//new IncognitoButton(_config),
|
||||
];
|
||||
_fileSystem = fileSystem;
|
||||
}
|
||||
|
||||
private void DrawHeader()
|
||||
=> HeaderDrawer.Draw(SelectionName, 0, ImGuiColor.FrameBackground.Get().Color, _leftButtons, _rightButtons);
|
||||
|
||||
private string SelectionName
|
||||
=> _selector.Selected == null ? "No Selection" : _config.Ephemeral.IncognitoMode ? _selector.Selected.Incognito : _selector.Selected.Name.Text;
|
||||
private Design Selection
|
||||
=> (Design)_fileSystem.Selection.Selection!.Value;
|
||||
|
||||
private void DrawEquipment()
|
||||
{
|
||||
|
|
@ -109,22 +81,22 @@ public class DesignPanel
|
|||
|
||||
_equipmentDrawer.Prepare();
|
||||
|
||||
var usedAllStain = _equipmentDrawer.DrawAllStain(out var newAllStain, _selector.Selected!.WriteProtected());
|
||||
var usedAllStain = _equipmentDrawer.DrawAllStain(out var newAllStain, Selection.WriteProtected());
|
||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||
{
|
||||
var data = EquipDrawData.FromDesign(_manager, _selector.Selected!, slot);
|
||||
var data = EquipDrawData.FromDesign(_manager, Selection, slot);
|
||||
_equipmentDrawer.DrawEquip(data);
|
||||
if (usedAllStain)
|
||||
_manager.ChangeStains(_selector.Selected, slot, newAllStain);
|
||||
_manager.ChangeStains(Selection, slot, newAllStain);
|
||||
}
|
||||
|
||||
var mainhand = EquipDrawData.FromDesign(_manager, _selector.Selected!, EquipSlot.MainHand);
|
||||
var offhand = EquipDrawData.FromDesign(_manager, _selector.Selected!, EquipSlot.OffHand);
|
||||
var mainhand = EquipDrawData.FromDesign(_manager, Selection, EquipSlot.MainHand);
|
||||
var offhand = EquipDrawData.FromDesign(_manager, Selection, EquipSlot.OffHand);
|
||||
_equipmentDrawer.DrawWeapons(mainhand, offhand, true);
|
||||
|
||||
foreach (var slot in BonusExtensions.AllFlags)
|
||||
{
|
||||
var data = BonusDrawData.FromDesign(_manager, _selector.Selected!, slot);
|
||||
var data = BonusDrawData.FromDesign(_manager, Selection, slot);
|
||||
_equipmentDrawer.DrawBonusItem(data);
|
||||
}
|
||||
|
||||
|
|
@ -136,30 +108,30 @@ public class DesignPanel
|
|||
|
||||
private void DrawEquipmentMetaToggles()
|
||||
{
|
||||
using (var _ = ImRaii.Group())
|
||||
using (Im.Group())
|
||||
{
|
||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(MetaIndex.HatState, _manager, _selector.Selected!));
|
||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromDesign(CrestFlag.Head, _manager, _selector.Selected!));
|
||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(MetaIndex.HatState, _manager, Selection));
|
||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromDesign(CrestFlag.Head, _manager, Selection));
|
||||
}
|
||||
|
||||
Im.Line.Same();
|
||||
using (var _ = ImRaii.Group())
|
||||
using (Im.Group())
|
||||
{
|
||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(MetaIndex.VisorState, _manager, _selector.Selected!));
|
||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromDesign(CrestFlag.Body, _manager, _selector.Selected!));
|
||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(MetaIndex.VisorState, _manager, Selection));
|
||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromDesign(CrestFlag.Body, _manager, Selection));
|
||||
}
|
||||
|
||||
Im.Line.Same();
|
||||
using (var _ = ImRaii.Group())
|
||||
using (Im.Group())
|
||||
{
|
||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(MetaIndex.WeaponState, _manager, _selector.Selected!));
|
||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromDesign(CrestFlag.OffHand, _manager, _selector.Selected!));
|
||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(MetaIndex.WeaponState, _manager, Selection));
|
||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromDesign(CrestFlag.OffHand, _manager, Selection));
|
||||
}
|
||||
|
||||
Im.Line.Same();
|
||||
using (var _ = ImRaii.Group())
|
||||
using (Im.Group())
|
||||
{
|
||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(MetaIndex.EarState, _manager, _selector.Selected!));
|
||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(MetaIndex.EarState, _manager, Selection));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -169,25 +141,25 @@ public class DesignPanel
|
|||
return;
|
||||
|
||||
var expand = _config.AutoExpandDesignPanel.HasFlag(DesignPanelFlag.Customization);
|
||||
using var h = Im.Tree.HeaderId(_selector.Selected!.DesignData.ModelId is 0
|
||||
? "Customization"
|
||||
: $"Customization (Model Id #{_selector.Selected!.DesignData.ModelId})###Customization",
|
||||
using var h = Im.Tree.HeaderId(Selection.DesignData.ModelId is 0
|
||||
? "Customization"u8
|
||||
: $"Customization (Model Id #{Selection.DesignData.ModelId})###Customization",
|
||||
expand ? TreeNodeFlags.DefaultOpen : TreeNodeFlags.None);
|
||||
if (!h)
|
||||
return;
|
||||
|
||||
if (_customizationDrawer.Draw(_selector.Selected!.DesignData.Customize, _selector.Selected.Application.Customize,
|
||||
_selector.Selected!.WriteProtected(), false))
|
||||
if (_customizationDrawer.Draw(Selection.DesignData.Customize, Selection.Application.Customize,
|
||||
Selection.WriteProtected(), false))
|
||||
foreach (var idx in CustomizeIndex.Values)
|
||||
{
|
||||
var flag = idx.ToFlag();
|
||||
var newValue = _customizationDrawer.ChangeApply.HasFlag(flag);
|
||||
_manager.ChangeApplyCustomize(_selector.Selected, idx, newValue);
|
||||
_manager.ChangeApplyCustomize(Selection, idx, newValue);
|
||||
if (_customizationDrawer.Changed.HasFlag(flag))
|
||||
_manager.ChangeCustomize(_selector.Selected, idx, _customizationDrawer.Customize[idx]);
|
||||
_manager.ChangeCustomize(Selection, idx, _customizationDrawer.Customize[idx]);
|
||||
}
|
||||
|
||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(MetaIndex.Wetness, _manager, _selector.Selected!));
|
||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(MetaIndex.Wetness, _manager, Selection));
|
||||
Im.Dummy(new Vector2(Im.Style.TextHeight / 2));
|
||||
}
|
||||
|
||||
|
|
@ -197,7 +169,7 @@ public class DesignPanel
|
|||
if (!h)
|
||||
return;
|
||||
|
||||
_parameterDrawer.Draw(_manager, _selector.Selected!);
|
||||
_parameterDrawer.Draw(_manager, Selection);
|
||||
}
|
||||
|
||||
private void DrawMaterialValues()
|
||||
|
|
@ -206,52 +178,52 @@ public class DesignPanel
|
|||
if (!h)
|
||||
return;
|
||||
|
||||
_materials.Draw(_selector.Selected!);
|
||||
_materials.Draw(Selection);
|
||||
}
|
||||
|
||||
private void DrawCustomizeApplication()
|
||||
{
|
||||
using var id = ImUtf8.PushId("Customizations"u8);
|
||||
var set = _selector.Selected!.CustomizeSet;
|
||||
using var id = Im.Id.Push("Customizations"u8);
|
||||
var set = Selection.CustomizeSet;
|
||||
var available = set.SettingAvailable | CustomizeFlag.Clan | CustomizeFlag.Gender | CustomizeFlag.BodyType;
|
||||
var flags = _selector.Selected!.ApplyCustomizeExcludingBodyType == 0 ? 0 :
|
||||
(_selector.Selected!.ApplyCustomize & available) == available ? 3 : 1;
|
||||
if (ImGui.CheckboxFlags("Apply All Customizations", ref flags, 3))
|
||||
var flags = Selection.ApplyCustomizeExcludingBodyType is 0 ? 0ul :
|
||||
(Selection.ApplyCustomize & available) == available ? 3ul : 1ul;
|
||||
if (Im.Checkbox("Apply All Customizations"u8, ref flags, 3ul))
|
||||
{
|
||||
var newFlags = flags == 3;
|
||||
_manager.ChangeApplyCustomize(_selector.Selected!, CustomizeIndex.Clan, newFlags);
|
||||
_manager.ChangeApplyCustomize(_selector.Selected!, CustomizeIndex.Gender, newFlags);
|
||||
var newFlags = flags is 3;
|
||||
_manager.ChangeApplyCustomize(Selection, CustomizeIndex.Clan, newFlags);
|
||||
_manager.ChangeApplyCustomize(Selection, CustomizeIndex.Gender, newFlags);
|
||||
foreach (var index in CustomizationExtensions.AllBasic)
|
||||
_manager.ChangeApplyCustomize(_selector.Selected!, index, newFlags);
|
||||
_manager.ChangeApplyCustomize(Selection, index, newFlags);
|
||||
}
|
||||
|
||||
var applyClan = _selector.Selected!.DoApplyCustomize(CustomizeIndex.Clan);
|
||||
if (ImUtf8.Checkbox($"Apply {CustomizeIndex.Clan.ToNameU8()}", ref applyClan))
|
||||
_manager.ChangeApplyCustomize(_selector.Selected!, CustomizeIndex.Clan, applyClan);
|
||||
var applyClan = Selection.DoApplyCustomize(CustomizeIndex.Clan);
|
||||
if (Im.Checkbox($"Apply {CustomizeIndex.Clan.ToNameU8()}", ref applyClan))
|
||||
_manager.ChangeApplyCustomize(Selection, CustomizeIndex.Clan, applyClan);
|
||||
|
||||
var applyGender = _selector.Selected!.DoApplyCustomize(CustomizeIndex.Gender);
|
||||
if (ImUtf8.Checkbox($"Apply {CustomizeIndex.Gender.ToNameU8()}", ref applyGender))
|
||||
_manager.ChangeApplyCustomize(_selector.Selected!, CustomizeIndex.Gender, applyGender);
|
||||
var applyGender = Selection.DoApplyCustomize(CustomizeIndex.Gender);
|
||||
if (Im.Checkbox($"Apply {CustomizeIndex.Gender.ToNameU8()}", ref applyGender))
|
||||
_manager.ChangeApplyCustomize(Selection, CustomizeIndex.Gender, applyGender);
|
||||
|
||||
|
||||
foreach (var index in CustomizationExtensions.All.Where(set.IsAvailable))
|
||||
{
|
||||
var apply = _selector.Selected!.DoApplyCustomize(index);
|
||||
if (ImUtf8.Checkbox($"Apply {set.Option(index)}", ref apply))
|
||||
_manager.ChangeApplyCustomize(_selector.Selected!, index, apply);
|
||||
var apply = Selection.DoApplyCustomize(index);
|
||||
if (Im.Checkbox($"Apply {set.Option(index)}", ref apply))
|
||||
_manager.ChangeApplyCustomize(Selection, index, apply);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawCrestApplication()
|
||||
{
|
||||
using var id = ImUtf8.PushId("Crests"u8);
|
||||
var flags = (uint)_selector.Selected!.Application.Crest;
|
||||
var bigChange = ImGui.CheckboxFlags("Apply All Crests", ref flags, (uint)CrestExtensions.AllRelevant);
|
||||
using var id = Im.Id.Push("Crests"u8);
|
||||
var flags = (ulong)Selection.Application.Crest;
|
||||
var bigChange = Im.Checkbox("Apply All Crests"u8, ref flags, (ulong)CrestExtensions.AllRelevant);
|
||||
foreach (var flag in CrestExtensions.AllRelevantSet)
|
||||
{
|
||||
var apply = bigChange ? ((CrestFlag)flags & flag) == flag : _selector.Selected!.DoApplyCrest(flag);
|
||||
if (ImUtf8.Checkbox($"Apply {flag.ToLabel()} Crest", ref apply) || bigChange)
|
||||
_manager.ChangeApplyCrest(_selector.Selected!, flag, apply);
|
||||
var apply = bigChange ? ((CrestFlag)flags & flag) == flag : Selection.DoApplyCrest(flag);
|
||||
if (Im.Checkbox($"Apply {flag.ToLabel()} Crest", ref apply) || bigChange)
|
||||
_manager.ChangeApplyCrest(Selection, flag, apply);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -261,63 +233,59 @@ public class DesignPanel
|
|||
if (!h)
|
||||
return;
|
||||
|
||||
using var disabled = Im.Disabled(_selector.Selected!.WriteProtected());
|
||||
using var disabled = Im.Disabled(Selection.WriteProtected());
|
||||
|
||||
DrawAllButtons();
|
||||
|
||||
using (var _ = ImUtf8.Group())
|
||||
using (Im.Group())
|
||||
{
|
||||
DrawCustomizeApplication();
|
||||
ImUtf8.IconDummy();
|
||||
Im.FrameDummy();
|
||||
DrawCrestApplication();
|
||||
ImUtf8.IconDummy();
|
||||
Im.FrameDummy();
|
||||
DrawMetaApplication();
|
||||
}
|
||||
|
||||
ImGui.SameLine(210 * Im.Style.GlobalScale + Im.Style.ItemSpacing.X);
|
||||
using (var _ = ImRaii.Group())
|
||||
Im.Line.Same(210 * Im.Style.GlobalScale + Im.Style.ItemSpacing.X);
|
||||
using (Im.Group())
|
||||
{
|
||||
void ApplyEquip(string label, EquipFlag allFlags, bool stain, IEnumerable<EquipSlot> slots)
|
||||
{
|
||||
var flags = (uint)(allFlags & _selector.Selected!.Application.Equip);
|
||||
using var id = ImUtf8.PushId(label);
|
||||
var bigChange = ImGui.CheckboxFlags($"Apply All {label}", ref flags, (uint)allFlags);
|
||||
var flags = (ulong)(allFlags & Selection.Application.Equip);
|
||||
using var id = Im.Id.Push(label);
|
||||
var bigChange = Im.Checkbox($"Apply All {label}", ref flags, (ulong)allFlags);
|
||||
if (stain)
|
||||
foreach (var slot in slots)
|
||||
{
|
||||
var apply = bigChange ? ((EquipFlag)flags).HasFlag(slot.ToStainFlag()) : _selector.Selected!.DoApplyStain(slot);
|
||||
if (ImUtf8.Checkbox($"Apply {slot.ToName()} Dye", ref apply) || bigChange)
|
||||
_manager.ChangeApplyStains(_selector.Selected!, slot, apply);
|
||||
var apply = bigChange ? ((EquipFlag)flags).HasFlag(slot.ToStainFlag()) : Selection.DoApplyStain(slot);
|
||||
if (Im.Checkbox($"Apply {slot.ToName()} Dye", ref apply) || bigChange)
|
||||
_manager.ChangeApplyStains(Selection, slot, apply);
|
||||
}
|
||||
else
|
||||
foreach (var slot in slots)
|
||||
{
|
||||
var apply = bigChange ? ((EquipFlag)flags).HasFlag(slot.ToFlag()) : _selector.Selected!.DoApplyEquip(slot);
|
||||
if (ImUtf8.Checkbox($"Apply {slot.ToName()}", ref apply) || bigChange)
|
||||
_manager.ChangeApplyItem(_selector.Selected!, slot, apply);
|
||||
var apply = bigChange ? ((EquipFlag)flags).HasFlag(slot.ToFlag()) : Selection.DoApplyEquip(slot);
|
||||
if (Im.Checkbox($"Apply {slot.ToName()}", ref apply) || bigChange)
|
||||
_manager.ChangeApplyItem(Selection, slot, apply);
|
||||
}
|
||||
}
|
||||
|
||||
ApplyEquip("Weapons", ApplicationTypeExtensions.WeaponFlags, false, new[]
|
||||
{
|
||||
EquipSlot.MainHand,
|
||||
EquipSlot.OffHand,
|
||||
});
|
||||
ApplyEquip("Weapons", ApplicationTypeExtensions.WeaponFlags, false, [EquipSlot.MainHand, EquipSlot.OffHand]);
|
||||
|
||||
ImUtf8.IconDummy();
|
||||
Im.FrameDummy();
|
||||
ApplyEquip("Armor", ApplicationTypeExtensions.ArmorFlags, false, EquipSlotExtensions.EquipmentSlots);
|
||||
|
||||
ImUtf8.IconDummy();
|
||||
Im.FrameDummy();
|
||||
ApplyEquip("Accessories", ApplicationTypeExtensions.AccessoryFlags, false, EquipSlotExtensions.AccessorySlots);
|
||||
|
||||
ImUtf8.IconDummy();
|
||||
Im.FrameDummy();
|
||||
ApplyEquip("Dyes", ApplicationTypeExtensions.StainFlags, true,
|
||||
EquipSlotExtensions.FullSlots);
|
||||
|
||||
ImUtf8.IconDummy();
|
||||
Im.FrameDummy();
|
||||
DrawParameterApplication();
|
||||
|
||||
ImUtf8.IconDummy();
|
||||
Im.FrameDummy();
|
||||
DrawBonusSlotApplication();
|
||||
}
|
||||
}
|
||||
|
|
@ -327,9 +295,9 @@ public class DesignPanel
|
|||
var enabled = _config.DeleteDesignModifier.IsActive();
|
||||
bool? equip = null;
|
||||
bool? customize = null;
|
||||
var size = new Vector2(210 * Im.Style.GlobalScale, 0);
|
||||
if (ImUtf8.ButtonEx("Disable Everything"u8,
|
||||
"Disable application of everything, including any existing advanced dyes, advanced customizations, crests and wetness."u8, size,
|
||||
var size = ImEx.ScaledVectorX(210);
|
||||
if (ImEx.Button("Disable Everything"u8, size,
|
||||
"Disable application of everything, including any existing advanced dyes, advanced customizations, crests and wetness."u8,
|
||||
!enabled))
|
||||
{
|
||||
equip = false;
|
||||
|
|
@ -337,11 +305,11 @@ public class DesignPanel
|
|||
}
|
||||
|
||||
if (!enabled)
|
||||
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking.");
|
||||
Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking.");
|
||||
|
||||
Im.Line.Same();
|
||||
if (ImUtf8.ButtonEx("Enable Everything"u8,
|
||||
"Enable application of everything, including any existing advanced dyes, advanced customizations, crests and wetness."u8, size,
|
||||
if (ImEx.Button("Enable Everything"u8, size,
|
||||
"Enable application of everything, including any existing advanced dyes, advanced customizations, crests and wetness."u8,
|
||||
!enabled))
|
||||
{
|
||||
equip = true;
|
||||
|
|
@ -349,10 +317,10 @@ public class DesignPanel
|
|||
}
|
||||
|
||||
if (!enabled)
|
||||
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking.");
|
||||
Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking.");
|
||||
|
||||
if (ImUtf8.ButtonEx("Equipment Only"u8,
|
||||
"Enable application of anything related to gear, disable anything that is not related to gear."u8, size,
|
||||
if (ImEx.Button("Equipment Only"u8, size,
|
||||
"Enable application of anything related to gear, disable anything that is not related to gear."u8,
|
||||
!enabled))
|
||||
{
|
||||
equip = true;
|
||||
|
|
@ -360,11 +328,11 @@ public class DesignPanel
|
|||
}
|
||||
|
||||
if (!enabled)
|
||||
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking.");
|
||||
Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking.");
|
||||
|
||||
Im.Line.Same();
|
||||
if (ImUtf8.ButtonEx("Customization Only"u8,
|
||||
"Enable application of anything related to customization, disable anything that is not related to customization."u8, size,
|
||||
if (ImEx.Button("Customization Only"u8, size,
|
||||
"Enable application of anything related to customization, disable anything that is not related to customization."u8,
|
||||
!enabled))
|
||||
{
|
||||
equip = false;
|
||||
|
|
@ -372,85 +340,82 @@ public class DesignPanel
|
|||
}
|
||||
|
||||
if (!enabled)
|
||||
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking.");
|
||||
Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking.");
|
||||
|
||||
if (ImUtf8.ButtonEx("Default Application"u8,
|
||||
if (ImEx.Button("Default Application"u8, size,
|
||||
"Set the application rules to the default values as if the design was newly created, without any advanced features or wetness."u8,
|
||||
size,
|
||||
!enabled))
|
||||
{
|
||||
_manager.ChangeApplyMulti(_selector.Selected!, true, true, true, false, true, true, false, true);
|
||||
_manager.ChangeApplyMeta(_selector.Selected!, MetaIndex.Wetness, false);
|
||||
_manager.ChangeApplyMulti(Selection, true, true, true, false, true, true, false, true);
|
||||
_manager.ChangeApplyMeta(Selection, MetaIndex.Wetness, false);
|
||||
}
|
||||
|
||||
if (!enabled)
|
||||
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking.");
|
||||
Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking.");
|
||||
|
||||
Im.Line.Same();
|
||||
if (ImUtf8.ButtonEx("Disable Advanced"u8, "Disable all advanced dyes and customizations but keep everything else as is."u8,
|
||||
size,
|
||||
!enabled))
|
||||
_manager.ChangeApplyMulti(_selector.Selected!, null, null, null, false, null, null, false, null);
|
||||
if (ImEx.Button("Disable Advanced"u8, size, "Disable all advanced dyes and customizations but keep everything else as is."u8, !enabled))
|
||||
_manager.ChangeApplyMulti(Selection, null, null, null, false, null, null, false, null);
|
||||
|
||||
if (!enabled)
|
||||
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking.");
|
||||
Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking.");
|
||||
|
||||
if (equip is null && customize is null)
|
||||
return;
|
||||
|
||||
_manager.ChangeApplyMulti(_selector.Selected!, equip, customize, equip, customize.HasValue && !customize.Value ? false : null, null,
|
||||
_manager.ChangeApplyMulti(Selection, equip, customize, equip, customize.HasValue && !customize.Value ? false : null, null,
|
||||
equip, equip, equip);
|
||||
if (equip.HasValue)
|
||||
{
|
||||
_manager.ChangeApplyMeta(_selector.Selected!, MetaIndex.HatState, equip.Value);
|
||||
_manager.ChangeApplyMeta(_selector.Selected!, MetaIndex.VisorState, equip.Value);
|
||||
_manager.ChangeApplyMeta(_selector.Selected!, MetaIndex.WeaponState, equip.Value);
|
||||
_manager.ChangeApplyMeta(_selector.Selected!, MetaIndex.EarState, equip.Value);
|
||||
_manager.ChangeApplyMeta(Selection, MetaIndex.HatState, equip.Value);
|
||||
_manager.ChangeApplyMeta(Selection, MetaIndex.VisorState, equip.Value);
|
||||
_manager.ChangeApplyMeta(Selection, MetaIndex.WeaponState, equip.Value);
|
||||
_manager.ChangeApplyMeta(Selection, MetaIndex.EarState, equip.Value);
|
||||
}
|
||||
|
||||
if (customize.HasValue)
|
||||
_manager.ChangeApplyMeta(_selector.Selected!, MetaIndex.Wetness, customize.Value);
|
||||
_manager.ChangeApplyMeta(Selection, MetaIndex.Wetness, customize.Value);
|
||||
}
|
||||
|
||||
private static readonly IReadOnlyList<string> MetaLabels =
|
||||
private static readonly IReadOnlyList<StringU8> MetaLabels =
|
||||
[
|
||||
"Apply Wetness",
|
||||
"Apply Hat Visibility",
|
||||
"Apply Visor State",
|
||||
"Apply Weapon Visibility",
|
||||
"Apply Viera Ear Visibility",
|
||||
new("Apply Wetness"u8),
|
||||
new("Apply Hat Visibility"u8),
|
||||
new("Apply Visor State"u8),
|
||||
new("Apply Weapon Visibility"u8),
|
||||
new("Apply Viera Ear Visibility"u8),
|
||||
];
|
||||
|
||||
private void DrawMetaApplication()
|
||||
{
|
||||
using var id = ImUtf8.PushId("Meta");
|
||||
const uint all = (uint)MetaExtensions.All;
|
||||
var flags = (uint)_selector.Selected!.Application.Meta;
|
||||
var bigChange = ImGui.CheckboxFlags("Apply All Meta Changes", ref flags, all);
|
||||
using var id = Im.Id.Push("Meta"u8);
|
||||
const ulong all = (ulong)MetaExtensions.All;
|
||||
var flags = (ulong)Selection.Application.Meta;
|
||||
var bigChange = Im.Checkbox("Apply All Meta Changes"u8, ref flags, all);
|
||||
|
||||
foreach (var (index, label) in MetaExtensions.AllRelevant.Zip(MetaLabels))
|
||||
{
|
||||
var apply = bigChange ? ((MetaFlag)flags).HasFlag(index.ToFlag()) : _selector.Selected!.DoApplyMeta(index);
|
||||
if (ImUtf8.Checkbox(label, ref apply) || bigChange)
|
||||
_manager.ChangeApplyMeta(_selector.Selected!, index, apply);
|
||||
var apply = bigChange ? ((MetaFlag)flags).HasFlag(index.ToFlag()) : Selection.DoApplyMeta(index);
|
||||
if (Im.Checkbox(label, ref apply) || bigChange)
|
||||
_manager.ChangeApplyMeta(Selection, index, apply);
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly IReadOnlyList<string> BonusSlotLabels =
|
||||
private static readonly IReadOnlyList<StringU8> BonusSlotLabels =
|
||||
[
|
||||
"Apply Facewear",
|
||||
new("Apply Facewear"u8),
|
||||
];
|
||||
|
||||
private void DrawBonusSlotApplication()
|
||||
{
|
||||
using var id = ImUtf8.PushId("Bonus"u8);
|
||||
var flags = _selector.Selected!.Application.BonusItem;
|
||||
var bigChange = BonusExtensions.AllFlags.Count > 1 && ImUtf8.Checkbox("Apply All Bonus Slots"u8, ref flags, BonusExtensions.All);
|
||||
using var id = Im.Id.Push("Bonus"u8);
|
||||
var flags = Selection.Application.BonusItem;
|
||||
var bigChange = BonusExtensions.AllFlags.Count > 1 && Im.Checkbox("Apply All Bonus Slots"u8, ref flags, BonusExtensions.All);
|
||||
foreach (var (index, label) in BonusExtensions.AllFlags.Zip(BonusSlotLabels))
|
||||
{
|
||||
var apply = bigChange ? flags.HasFlag(index) : _selector.Selected!.DoApplyBonusItem(index);
|
||||
if (ImUtf8.Checkbox(label, ref apply) || bigChange)
|
||||
_manager.ChangeApplyBonusItem(_selector.Selected!, index, apply);
|
||||
var apply = bigChange ? flags.HasFlag(index) : Selection.DoApplyBonusItem(index);
|
||||
if (Im.Checkbox(label, ref apply) || bigChange)
|
||||
_manager.ChangeApplyBonusItem(Selection, index, apply);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -458,65 +423,62 @@ public class DesignPanel
|
|||
private void DrawParameterApplication()
|
||||
{
|
||||
using var id = Im.Id.Push("Parameter"u8);
|
||||
var flags = (ulong)_selector.Selected!.Application.Parameters;
|
||||
var flags = (ulong)Selection.Application.Parameters;
|
||||
var bigChange = Im.Checkbox("Apply All Customize Parameters"u8, ref flags, (ulong)CustomizeParameterExtensions.All);
|
||||
foreach (var flag in CustomizeParameterExtensions.AllFlags)
|
||||
{
|
||||
var apply = bigChange ? ((CustomizeParameterFlag)flags).HasFlag(flag) : _selector.Selected!.DoApplyParameter(flag);
|
||||
var apply = bigChange ? ((CustomizeParameterFlag)flags).HasFlag(flag) : Selection.DoApplyParameter(flag);
|
||||
if (Im.Checkbox($"Apply {flag.ToNameU8()}", ref apply) || bigChange)
|
||||
_manager.ChangeApplyParameter(_selector.Selected!, flag, apply);
|
||||
_manager.ChangeApplyParameter(Selection, flag, apply);
|
||||
}
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> Id
|
||||
=> "DesignPanel"u8;
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
using var group = ImUtf8.Group();
|
||||
if (_selector.SelectedPaths.Count > 1)
|
||||
_importService.CreateDatSource();
|
||||
if (_fileSystem.Selection.OrderedNodes.Count > 1)
|
||||
{
|
||||
_multiDesignPanel.Draw();
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
DrawPanel();
|
||||
|
||||
if (_fileSystem.Selection.Selection is null || Selection.WriteProtected())
|
||||
return;
|
||||
|
||||
if (_importService.CreateDatTarget(out var dat))
|
||||
{
|
||||
DrawHeader();
|
||||
DrawPanel();
|
||||
|
||||
if (_selector.Selected == null || _selector.Selected.WriteProtected())
|
||||
return;
|
||||
|
||||
if (_importService.CreateDatTarget(out var dat))
|
||||
{
|
||||
_manager.ChangeCustomize(_selector.Selected!, CustomizeIndex.Clan, dat.Customize[CustomizeIndex.Clan]);
|
||||
_manager.ChangeCustomize(_selector.Selected!, CustomizeIndex.Gender, dat.Customize[CustomizeIndex.Gender]);
|
||||
foreach (var idx in CustomizationExtensions.AllBasic)
|
||||
_manager.ChangeCustomize(_selector.Selected!, idx, dat.Customize[idx]);
|
||||
Glamourer.Messager.NotificationMessage(
|
||||
$"Applied games .dat file {dat.Description} customizations to {_selector.Selected.Name}.", NotificationType.Success, false);
|
||||
}
|
||||
else if (_importService.CreateCharaTarget(out var designBase, out var name))
|
||||
{
|
||||
_manager.ApplyDesign(_selector.Selected!, designBase);
|
||||
Glamourer.Messager.NotificationMessage($"Applied Anamnesis .chara file {name} to {_selector.Selected.Name}.",
|
||||
NotificationType.Success, false);
|
||||
}
|
||||
_manager.ChangeCustomize(Selection, CustomizeIndex.Clan, dat.Customize[CustomizeIndex.Clan]);
|
||||
_manager.ChangeCustomize(Selection, CustomizeIndex.Gender, dat.Customize[CustomizeIndex.Gender]);
|
||||
foreach (var idx in CustomizationExtensions.AllBasic)
|
||||
_manager.ChangeCustomize(Selection, idx, dat.Customize[idx]);
|
||||
Glamourer.Messager.NotificationMessage(
|
||||
$"Applied games .dat file {dat.Description} customizations to {Selection.Name}.", NotificationType.Success, false);
|
||||
}
|
||||
else if (_importService.CreateCharaTarget(out var designBase, out var name))
|
||||
{
|
||||
_manager.ApplyDesign(Selection, designBase);
|
||||
Glamourer.Messager.NotificationMessage($"Applied Anamnesis .chara file {name} to {Selection.Name}.",
|
||||
NotificationType.Success, false);
|
||||
}
|
||||
|
||||
_importService.CreateDatSource();
|
||||
}
|
||||
|
||||
private void DrawPanel()
|
||||
{
|
||||
using var table = Im.Table.Begin("##Panel"u8, 1, TableFlags.BordersOuter | TableFlags.ScrollY, Im.ContentRegion.Available);
|
||||
if (!table || _selector.Selected is null)
|
||||
using var table = Im.Table.Begin("##Panel"u8, 1, TableFlags.ScrollY, Im.ContentRegion.Available);
|
||||
if (!table || _fileSystem.Selection.Selection is null)
|
||||
return;
|
||||
|
||||
ImGui.TableSetupScrollFreeze(0, 1);
|
||||
ImGui.TableNextColumn();
|
||||
if (_selector.Selected is null)
|
||||
return;
|
||||
table.SetupScrollFreeze(0, 1);
|
||||
table.NextColumn();
|
||||
|
||||
Im.Dummy(Vector2.Zero);
|
||||
DrawButtonRow();
|
||||
ImGui.TableNextColumn();
|
||||
table.NextColumn();
|
||||
|
||||
DrawCustomize();
|
||||
DrawEquipment();
|
||||
|
|
@ -543,15 +505,15 @@ public class DesignPanel
|
|||
private void DrawApplyToSelf()
|
||||
{
|
||||
var (id, data) = _objects.PlayerData;
|
||||
if (!ImGuiUtil.DrawDisabledButton("Apply to Yourself", Vector2.Zero,
|
||||
"Apply the current design with its settings to your character.\nHold Control to only apply gear.\nHold Shift to only apply customizations.",
|
||||
if (!ImEx.Button("Apply to Yourself"u8, Vector2.Zero,
|
||||
"Apply the current design with its settings to your character.\nHold Control to only apply gear.\nHold Shift to only apply customizations."u8,
|
||||
!data.Valid))
|
||||
return;
|
||||
|
||||
if (_state.GetOrCreate(id, data.Objects[0], out var state))
|
||||
{
|
||||
using var _ = _selector.Selected!.TemporarilyRestrictApplication(ApplicationCollection.FromKeys());
|
||||
_state.ApplyDesign(state, _selector.Selected!, ApplySettings.ManualWithLinks with { IsFinal = true });
|
||||
using var _ = Selection.TemporarilyRestrictApplication(ApplicationCollection.FromKeys());
|
||||
_state.ApplyDesign(state, Selection, ApplySettings.ManualWithLinks with { IsFinal = true });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -560,33 +522,33 @@ public class DesignPanel
|
|||
var (id, data) = _objects.TargetData;
|
||||
var tt = id.IsValid
|
||||
? data.Valid
|
||||
? "Apply the current design with its settings to your current target.\nHold Control to only apply gear.\nHold Shift to only apply customizations."
|
||||
: "The current target can not be manipulated."
|
||||
: "No valid target selected.";
|
||||
if (!ImGuiUtil.DrawDisabledButton("Apply to Target", Vector2.Zero, tt, !data.Valid))
|
||||
? "Apply the current design with its settings to your current target.\nHold Control to only apply gear.\nHold Shift to only apply customizations."u8
|
||||
: "The current target can not be manipulated."u8
|
||||
: "No valid target selected."u8;
|
||||
if (!ImEx.Button("Apply to Target"u8, Vector2.Zero, tt, !data.Valid))
|
||||
return;
|
||||
|
||||
if (_state.GetOrCreate(id, data.Objects[0], out var state))
|
||||
{
|
||||
using var _ = _selector.Selected!.TemporarilyRestrictApplication(ApplicationCollection.FromKeys());
|
||||
_state.ApplyDesign(state, _selector.Selected!, ApplySettings.ManualWithLinks with { IsFinal = true });
|
||||
using var _ = Selection.TemporarilyRestrictApplication(ApplicationCollection.FromKeys());
|
||||
_state.ApplyDesign(state, Selection, ApplySettings.ManualWithLinks with { IsFinal = true });
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawSaveToDat()
|
||||
{
|
||||
var verified = _importService.Verify(_selector.Selected!.DesignData.Customize, out _);
|
||||
var verified = _importService.Verify(Selection.DesignData.Customize, out _);
|
||||
var tt = verified
|
||||
? "Export the currently configured customizations of this design to a character creation data file."
|
||||
: "The current design contains customizations that can not be applied during character creation.";
|
||||
? "Export the currently configured customizations of this design to a character creation data file."u8
|
||||
: "The current design contains customizations that can not be applied during character creation."u8;
|
||||
var startPath = GetUserPath();
|
||||
if (startPath.Length == 0)
|
||||
if (startPath.Length is 0)
|
||||
startPath = null;
|
||||
if (ImGuiUtil.DrawDisabledButton("Export to Dat", Vector2.Zero, tt, !verified))
|
||||
if (ImEx.Button("Export to Dat"u8, Vector2.Zero, tt, !verified))
|
||||
_fileDialog.SaveFileDialog("Save File...", ".dat", "FFXIV_CHARA_01.dat", ".dat", (v, path) =>
|
||||
{
|
||||
if (v && _selector.Selected != null)
|
||||
_importService.SaveDesignAsDat(path, _selector.Selected!.DesignData.Customize, _selector.Selected!.Name);
|
||||
if (v && _fileSystem.Selection.Selection?.GetValue<Design>() is not null)
|
||||
_importService.SaveDesignAsDat(path, Selection.DesignData.Customize, Selection.Name);
|
||||
}, startPath);
|
||||
|
||||
_fileDialog.Draw();
|
||||
|
|
@ -594,164 +556,4 @@ public class DesignPanel
|
|||
|
||||
private static unsafe string GetUserPath()
|
||||
=> Framework.Instance()->UserPathString;
|
||||
|
||||
|
||||
private sealed class LockButton(DesignPanel panel) : Button
|
||||
{
|
||||
public override bool Visible
|
||||
=> panel._selector.Selected != null;
|
||||
|
||||
protected override string Description
|
||||
=> panel._selector.Selected!.WriteProtected()
|
||||
? "Make this design editable."
|
||||
: "Write-protect this design.";
|
||||
|
||||
protected override FontAwesomeIcon Icon
|
||||
=> panel._selector.Selected!.WriteProtected()
|
||||
? FontAwesomeIcon.Lock
|
||||
: FontAwesomeIcon.LockOpen;
|
||||
|
||||
protected override void OnClick()
|
||||
=> panel._manager.SetWriteProtection(panel._selector.Selected!, !panel._selector.Selected!.WriteProtected());
|
||||
}
|
||||
|
||||
private sealed class SetFromClipboardButton(DesignPanel panel) : Button
|
||||
{
|
||||
public override bool Visible
|
||||
=> panel._selector.Selected != null;
|
||||
|
||||
protected override bool Disabled
|
||||
=> panel._selector.Selected?.WriteProtected() ?? true;
|
||||
|
||||
protected override string Description
|
||||
=> "Try to apply a design from your clipboard over this design.\nHold Control to only apply gear.\nHold Shift to only apply customizations.";
|
||||
|
||||
protected override FontAwesomeIcon Icon
|
||||
=> FontAwesomeIcon.Clipboard;
|
||||
|
||||
protected override void OnClick()
|
||||
{
|
||||
try
|
||||
{
|
||||
var text = ImGui.GetClipboardText();
|
||||
var (applyEquip, applyCustomize) = UiHelpers.ConvertKeysToBool();
|
||||
var design = panel._converter.FromBase64(text, applyCustomize, applyEquip, out _)
|
||||
?? throw new Exception("The clipboard did not contain valid data.");
|
||||
panel._manager.ApplyDesign(panel._selector.Selected!, design);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Messager.NotificationMessage(ex, $"Could not apply clipboard to {panel._selector.Selected!.Name}.",
|
||||
$"Could not apply clipboard to design {panel._selector.Selected!.Identifier}", NotificationType.Error, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class DesignUndoButton(DesignPanel panel) : Button
|
||||
{
|
||||
public override bool Visible
|
||||
=> panel._selector.Selected != null;
|
||||
|
||||
protected override bool Disabled
|
||||
=> !panel._manager.CanUndo(panel._selector.Selected) || (panel._selector.Selected?.WriteProtected() ?? true);
|
||||
|
||||
protected override string Description
|
||||
=> "Undo the last time you applied an entire design onto this design, if you accidentally overwrote your design with a different one.";
|
||||
|
||||
protected override FontAwesomeIcon Icon
|
||||
=> FontAwesomeIcon.SyncAlt;
|
||||
|
||||
protected override void OnClick()
|
||||
{
|
||||
try
|
||||
{
|
||||
panel._manager.UndoDesignChange(panel._selector.Selected!);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Messager.NotificationMessage(ex, $"Could not undo last changes to {panel._selector.Selected!.Name}.",
|
||||
NotificationType.Error,
|
||||
false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class ExportToClipboardButton(DesignPanel panel) : Button
|
||||
{
|
||||
public override bool Visible
|
||||
=> panel._selector.Selected != null;
|
||||
|
||||
protected override string Description
|
||||
=> "Copy the current design to your clipboard.";
|
||||
|
||||
protected override FontAwesomeIcon Icon
|
||||
=> FontAwesomeIcon.Copy;
|
||||
|
||||
protected override void OnClick()
|
||||
{
|
||||
try
|
||||
{
|
||||
var text = panel._converter.ShareBase64(panel._selector.Selected!);
|
||||
ImGui.SetClipboardText(text);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Messager.NotificationMessage(ex, $"Could not copy {panel._selector.Selected!.Name} data to clipboard.",
|
||||
$"Could not copy data from design {panel._selector.Selected!.Identifier} to clipboard", NotificationType.Error, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class ApplyCharacterButton(DesignPanel panel) : Button
|
||||
{
|
||||
public override bool Visible
|
||||
=> panel._selector.Selected != null && panel._objects.Player.Valid;
|
||||
|
||||
protected override string Description
|
||||
=> "Overwrite this design with your character's current state.";
|
||||
|
||||
protected override bool Disabled
|
||||
=> panel._selector.Selected?.WriteProtected() ?? true;
|
||||
|
||||
protected override FontAwesomeIcon Icon
|
||||
=> FontAwesomeIcon.UserEdit;
|
||||
|
||||
protected override void OnClick()
|
||||
{
|
||||
try
|
||||
{
|
||||
var (player, actor) = panel._objects.PlayerData;
|
||||
if (!player.IsValid || !actor.Valid || !panel._state.GetOrCreate(player, actor.Objects[0], out var state))
|
||||
throw new Exception("No player state available.");
|
||||
|
||||
var design = panel._converter.Convert(state, ApplicationRules.FromModifiers(state))
|
||||
?? throw new Exception("The clipboard did not contain valid data.");
|
||||
panel._selector.Selected!.GetMaterialDataRef().Clear();
|
||||
panel._manager.ApplyDesign(panel._selector.Selected!, design);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Messager.NotificationMessage(ex, $"Could not apply player state to {panel._selector.Selected!.Name}.",
|
||||
$"Could not apply player state to design {panel._selector.Selected!.Identifier}", NotificationType.Error, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class UndoButton(DesignPanel panel) : Button
|
||||
{
|
||||
protected override string Description
|
||||
=> "Undo the last change.";
|
||||
|
||||
protected override FontAwesomeIcon Icon
|
||||
=> FontAwesomeIcon.Undo;
|
||||
|
||||
public override bool Visible
|
||||
=> panel._selector.Selected != null;
|
||||
|
||||
protected override bool Disabled
|
||||
=> (panel._selector.Selected?.WriteProtected() ?? true) || !panel._history.CanUndo(panel._selector.Selected);
|
||||
|
||||
protected override void OnClick()
|
||||
=> panel._history.Undo(panel._selector.Selected!);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Glamourer.Config;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Interop;
|
||||
using ImSharp;
|
||||
|
|
@ -6,27 +7,55 @@ using Luna;
|
|||
|
||||
namespace Glamourer.Gui.Tabs.DesignTab;
|
||||
|
||||
public sealed class DesignTab(DesignFileSystemSelector selector, DesignPanel panel, ImportService importService, DesignManager manager)
|
||||
: ITab<MainTabType>
|
||||
public sealed class DesignTab : TwoPanelLayout, ITab<MainTabType>
|
||||
{
|
||||
public ReadOnlySpan<byte> Label
|
||||
private readonly ImportService _importService;
|
||||
private readonly DesignManager _manager;
|
||||
private readonly UiConfig _uiConfig;
|
||||
|
||||
public DesignTab(DesignFileSystemDrawer drawer, DesignPanel panel, ImportService importService, DesignManager manager, DesignFilter filter,
|
||||
DesignHeader header, UiConfig uiConfig)
|
||||
{
|
||||
LeftHeader = drawer.Header;
|
||||
LeftPanel = drawer;
|
||||
LeftFooter = drawer.Footer;
|
||||
|
||||
RightHeader = header;
|
||||
RightPanel = panel;
|
||||
RightFooter = NopHeaderFooter.Instance;
|
||||
_importService = importService;
|
||||
_manager = manager;
|
||||
_uiConfig = uiConfig;
|
||||
}
|
||||
|
||||
public override ReadOnlySpan<byte> Label
|
||||
=> "Designs"u8;
|
||||
|
||||
public MainTabType Identifier
|
||||
=> MainTabType.Designs;
|
||||
|
||||
public void DrawContent()
|
||||
protected override void DrawLeftGroup(in TwoPanelWidth width)
|
||||
{
|
||||
selector.Draw();
|
||||
if (importService.CreateCharaTarget(out var designBase, out var name))
|
||||
base.DrawLeftGroup(in width);
|
||||
if (_importService.CreateCharaTarget(out var designBase, out var name))
|
||||
{
|
||||
var newDesign = manager.CreateClone(designBase, name, true);
|
||||
var newDesign = _manager.CreateClone(designBase, name, true);
|
||||
Glamourer.Messager.NotificationMessage($"Imported Anamnesis .chara file {name} as new design {newDesign.Name}",
|
||||
NotificationType.Success, false);
|
||||
}
|
||||
|
||||
Im.Line.Same();
|
||||
panel.Draw();
|
||||
importService.CreateCharaSource();
|
||||
_importService.CreateCharaSource();
|
||||
}
|
||||
|
||||
protected override float MinimumWidth
|
||||
=> LeftFooter.MinimumWidth;
|
||||
|
||||
protected override float MaximumWidth
|
||||
=> Im.Window.Width - 500 * Im.Style.GlobalScale;
|
||||
|
||||
protected override void SetWidth(float width, ScalingMode mode)
|
||||
=> _uiConfig.DesignsTabScale = new TwoPanelWidth(width, mode);
|
||||
|
||||
|
||||
public void DrawContent()
|
||||
=> Draw(_uiConfig.DesignsTabScale);
|
||||
}
|
||||
|
|
|
|||
39
Glamourer/Gui/Tabs/DesignTab/DesignUndoButton.cs
Normal file
39
Glamourer/Gui/Tabs/DesignTab/DesignUndoButton.cs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Glamourer.Designs;
|
||||
using ImSharp;
|
||||
using Luna;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.DesignTab;
|
||||
|
||||
public sealed class DesignUndoButton(DesignFileSystem fileSystem, DesignManager manager) : BaseIconButton<AwesomeIcon>
|
||||
{
|
||||
public override bool IsVisible
|
||||
=> fileSystem.Selection.Selection is not null;
|
||||
|
||||
public override AwesomeIcon Icon
|
||||
=> LunaStyle.ResetIcon;
|
||||
|
||||
public override bool Enabled
|
||||
=> !((Design)fileSystem.Selection.Selection!.Value).WriteProtected() && manager.CanUndo((Design)fileSystem.Selection.Selection!.Value);
|
||||
|
||||
public override bool HasTooltip
|
||||
=> true;
|
||||
|
||||
public override void DrawTooltip()
|
||||
=> Im.Text(
|
||||
"Undo the last time you applied an entire design onto this design, if you accidentally overwrote your design with a different one."u8);
|
||||
|
||||
public override void OnClick()
|
||||
{
|
||||
try
|
||||
{
|
||||
manager.UndoDesignChange((Design)fileSystem.Selection.Selection!.Value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Messager.NotificationMessage(ex,
|
||||
$"Could not undo last changes to {((Design)fileSystem.Selection.Selection!.Value).Name}.",
|
||||
NotificationType.Error, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
36
Glamourer/Gui/Tabs/DesignTab/ExportToClipboardButton.cs
Normal file
36
Glamourer/Gui/Tabs/DesignTab/ExportToClipboardButton.cs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Glamourer.Designs;
|
||||
using ImSharp;
|
||||
using Luna;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.DesignTab;
|
||||
|
||||
public sealed class ExportToClipboardButton(DesignFileSystem fileSystem, DesignConverter converter) : BaseIconButton<AwesomeIcon>
|
||||
{
|
||||
public override bool IsVisible
|
||||
=> fileSystem.Selection.Selection is not null;
|
||||
|
||||
public override AwesomeIcon Icon
|
||||
=> LunaStyle.ToClipboardIcon;
|
||||
|
||||
public override bool HasTooltip
|
||||
=> true;
|
||||
|
||||
public override void DrawTooltip()
|
||||
=> Im.Text("Copy the current design to your clipboard."u8);
|
||||
|
||||
public override void OnClick()
|
||||
{
|
||||
var design = (Design)fileSystem.Selection.Selection!.Value;
|
||||
try
|
||||
{
|
||||
var text = converter.ShareBase64(design);
|
||||
Im.Clipboard.Set(text);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Messager.NotificationMessage(ex, $"Could not copy {design.Name} data to clipboard.",
|
||||
$"Could not copy data from design {design.Identifier} to clipboard", NotificationType.Error, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
28
Glamourer/Gui/Tabs/DesignTab/LockButton.cs
Normal file
28
Glamourer/Gui/Tabs/DesignTab/LockButton.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
using Glamourer.Designs;
|
||||
using ImSharp;
|
||||
using Luna;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.DesignTab;
|
||||
|
||||
public sealed class LockButton(DesignFileSystem fileSystem, DesignManager manager) : BaseIconButton<AwesomeIcon>
|
||||
{
|
||||
public override bool IsVisible
|
||||
=> fileSystem.Selection.Selection is not null;
|
||||
|
||||
public override AwesomeIcon Icon
|
||||
=> ((Design)fileSystem.Selection.Selection!.Value).WriteProtected()
|
||||
? LunaStyle.LockedIcon
|
||||
: LunaStyle.UnlockedIcon;
|
||||
|
||||
public override bool HasTooltip
|
||||
=> true;
|
||||
|
||||
public override void DrawTooltip()
|
||||
=> Im.Text(((Design)fileSystem.Selection.Selection!.Value).WriteProtected()
|
||||
? "Make this design editable."u8
|
||||
: "Write-protect this design."u8);
|
||||
|
||||
public override void OnClick()
|
||||
=> manager.SetWriteProtection((Design)fileSystem.Selection.Selection!.Value,
|
||||
!((Design)fileSystem.Selection.Selection!.Value).WriteProtected());
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Glamourer.Config;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Interop.Penumbra;
|
||||
using Glamourer.State;
|
||||
|
|
@ -7,11 +8,14 @@ using Luna;
|
|||
|
||||
namespace Glamourer.Gui.Tabs.DesignTab;
|
||||
|
||||
public class ModAssociationsTab(PenumbraService penumbra, DesignFileSystemSelector selector, DesignManager manager, Configuration config)
|
||||
public sealed class ModAssociationsTab(PenumbraService penumbra, DesignFileSystem fileSystem, DesignManager manager, Configuration config) : IUiService
|
||||
{
|
||||
private readonly ModCombo _modCombo = new(penumbra, Glamourer.Log, selector);
|
||||
private readonly ModCombo _modCombo = new(penumbra, fileSystem);
|
||||
private (Mod, ModSettings)[]? _copy;
|
||||
|
||||
private Design Selection
|
||||
=> (Design)fileSystem.Selection.Selection!.Value;
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
using var h = DesignPanelFlag.ModAssociations.Header(config);
|
||||
|
|
@ -35,7 +39,7 @@ public class ModAssociationsTab(PenumbraService penumbra, DesignFileSystemSelect
|
|||
{
|
||||
var size = new Vector2((Im.ContentRegion.Available.X - 2 * Im.Style.ItemSpacing.X) / 3, 0);
|
||||
if (Im.Button("Copy All to Clipboard"u8, size))
|
||||
_copy = selector.Selected!.AssociatedMods.Select(kvp => (kvp.Key, kvp.Value)).ToArray();
|
||||
_copy = Selection.AssociatedMods.Select(kvp => (kvp.Key, kvp.Value)).ToArray();
|
||||
|
||||
Im.Line.Same();
|
||||
|
||||
|
|
@ -44,7 +48,7 @@ public class ModAssociationsTab(PenumbraService penumbra, DesignFileSystemSelect
|
|||
? $"Add {_copy.Length} mod association(s) from clipboard."
|
||||
: "Copy some mod associations to the clipboard, first."u8, _copy is null))
|
||||
foreach (var (mod, setting) in _copy!)
|
||||
manager.UpdateMod(selector.Selected!, mod, setting);
|
||||
manager.UpdateMod(Selection, mod, setting);
|
||||
|
||||
Im.Line.Same();
|
||||
|
||||
|
|
@ -53,10 +57,10 @@ public class ModAssociationsTab(PenumbraService penumbra, DesignFileSystemSelect
|
|||
? $"Set {_copy.Length} mod association(s) from clipboard and discard existing."
|
||||
: "Copy some mod associations to the clipboard, first."u8, _copy is null))
|
||||
{
|
||||
while (selector.Selected!.AssociatedMods.Count > 0)
|
||||
manager.RemoveMod(selector.Selected!, selector.Selected!.AssociatedMods.Keys[0]);
|
||||
while (Selection.AssociatedMods.Count > 0)
|
||||
manager.RemoveMod(Selection, Selection.AssociatedMods.Keys[0]);
|
||||
foreach (var (mod, setting) in _copy!)
|
||||
manager.AddMod(selector.Selected!, mod, setting);
|
||||
manager.AddMod(Selection, mod, setting);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -75,13 +79,13 @@ public class ModAssociationsTab(PenumbraService penumbra, DesignFileSystemSelect
|
|||
var (id, name) = penumbra.CurrentCollection;
|
||||
if (ImEx.Button("Apply Mod Associations"u8, Vector2.Zero,
|
||||
$"Try to apply all associated mod settings to Penumbras current collection {name}",
|
||||
selector.Selected!.AssociatedMods.Count is 0 || id == Guid.Empty))
|
||||
Selection.AssociatedMods.Count is 0 || id == Guid.Empty))
|
||||
ApplyAll();
|
||||
}
|
||||
|
||||
public void ApplyAll()
|
||||
{
|
||||
foreach (var (mod, settings) in selector.Selected!.AssociatedMods)
|
||||
foreach (var (mod, settings) in Selection.AssociatedMods)
|
||||
penumbra.SetMod(mod, settings, StateSource.Manual, false);
|
||||
}
|
||||
|
||||
|
|
@ -103,7 +107,7 @@ public class ModAssociationsTab(PenumbraService penumbra, DesignFileSystemSelect
|
|||
|
||||
Mod? removedMod = null;
|
||||
(Mod mod, ModSettings settings)? updatedMod = null;
|
||||
foreach (var (idx, (mod, settings)) in selector.Selected!.AssociatedMods.Index())
|
||||
foreach (var (idx, (mod, settings)) in Selection.AssociatedMods.Index())
|
||||
{
|
||||
using var id = Im.Id.Push(idx);
|
||||
DrawAssociatedModRow(table, mod, settings, out var removedModTmp, out var updatedModTmp);
|
||||
|
|
@ -116,10 +120,10 @@ public class ModAssociationsTab(PenumbraService penumbra, DesignFileSystemSelect
|
|||
DrawNewModRow(table);
|
||||
|
||||
if (removedMod.HasValue)
|
||||
manager.RemoveMod(selector.Selected!, removedMod.Value);
|
||||
manager.RemoveMod(Selection, removedMod.Value);
|
||||
|
||||
if (updatedMod.HasValue)
|
||||
manager.UpdateMod(selector.Selected!, updatedMod.Value.mod, updatedMod.Value.settings);
|
||||
manager.UpdateMod(Selection, updatedMod.Value.mod, updatedMod.Value.settings);
|
||||
}
|
||||
|
||||
private void DrawAssociatedModRow(in Im.TableDisposable table, Mod mod, ModSettings settings, out Mod? removedMod,
|
||||
|
|
@ -240,18 +244,17 @@ public class ModAssociationsTab(PenumbraService penumbra, DesignFileSystemSelect
|
|||
|
||||
private void DrawNewModRow(in Im.TableDisposable table)
|
||||
{
|
||||
var currentName = _modCombo.CurrentSelection.Mod.Name;
|
||||
var currentDir = _modCombo.Selection;
|
||||
table.NextColumn();
|
||||
var tt = string.IsNullOrEmpty(currentName)
|
||||
var tt = currentDir.Length is 0
|
||||
? "Please select a mod first."u8
|
||||
: selector.Selected!.AssociatedMods.ContainsKey(_modCombo.CurrentSelection.Mod)
|
||||
: Selection.AssociatedMods.ContainsKey(new Mod(_modCombo.SelectionName, currentDir))
|
||||
? "The design already contains an association with the selected mod."u8
|
||||
: StringU8.Empty;
|
||||
|
||||
if (ImEx.Icon.Button(LunaStyle.AddObjectIcon, tt, tt.Length > 0))
|
||||
manager.AddMod(selector.Selected!, _modCombo.CurrentSelection.Mod, _modCombo.CurrentSelection.Settings);
|
||||
manager.AddMod(Selection, new Mod(_modCombo.SelectionName, _modCombo.Selection), _modCombo.Settings);
|
||||
table.NextColumn();
|
||||
_modCombo.Draw("##new", string.IsNullOrEmpty(currentName) ? "Select new Mod..." : currentName, string.Empty,
|
||||
Im.ContentRegion.Available.X, Im.Style.TextHeight);
|
||||
_modCombo.Draw("##new"u8, Im.ContentRegion.Available.X);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,95 +1,123 @@
|
|||
using Dalamud.Interface.Utility;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Interop.Penumbra;
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using ImSharp;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Log;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Text;
|
||||
using OtterGui.Widgets;
|
||||
using MouseWheelType = OtterGui.Widgets.MouseWheelType;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.DesignTab;
|
||||
|
||||
public sealed class ModCombo : FilterComboCache<(Mod Mod, ModSettings Settings, int Count)>
|
||||
public sealed class ModCombo(PenumbraService penumbra, DesignFileSystem fileSystem) : FilterComboBase<ModCombo.CacheItem>(new ModFilter())
|
||||
{
|
||||
public ModCombo(PenumbraService penumbra, Logger log, DesignFileSystemSelector selector)
|
||||
: base(() => penumbra.GetMods(selector.Selected?.FilteredItemNames.ToArray() ?? []), MouseWheelType.None, log)
|
||||
=> SearchByParts = false;
|
||||
|
||||
protected override string ToString((Mod Mod, ModSettings Settings, int Count) obj)
|
||||
=> obj.Mod.Name;
|
||||
|
||||
protected override bool IsVisible(int globalIndex, LowerString filter)
|
||||
=> filter.IsContained(Items[globalIndex].Mod.Name) || filter.IsContained(Items[globalIndex].Mod.DirectoryName);
|
||||
|
||||
protected override bool DrawSelectable(int globalIdx, bool selected)
|
||||
public readonly struct CacheItem(in Mod mod, in ModSettings settings, int count)
|
||||
{
|
||||
using var id = ImUtf8.PushId(globalIdx);
|
||||
var (mod, settings, count) = Items[globalIdx];
|
||||
bool ret;
|
||||
var color = settings.Enabled
|
||||
public readonly StringPair Name = new(mod.Name);
|
||||
public readonly StringPair Directory = new(mod.DirectoryName);
|
||||
public readonly ModSettings Settings = settings;
|
||||
public readonly int Count = count;
|
||||
|
||||
public readonly Vector4 Color = settings.Enabled
|
||||
? count > 0
|
||||
? ColorId.ContainsItemsEnabled.Value()
|
||||
: ImGuiColor.Text.Get()
|
||||
? ColorId.ContainsItemsEnabled.Value().ToVector()
|
||||
: Im.Style[ImGuiColor.Text]
|
||||
: count > 0
|
||||
? ColorId.ContainsItemsDisabled.Value()
|
||||
: ImGuiColor.TextDisabled.Get();
|
||||
using (ImGuiColor.Text.Push(color))
|
||||
? ColorId.ContainsItemsDisabled.Value().ToVector()
|
||||
: Im.Style[ImGuiColor.TextDisabled];
|
||||
|
||||
public readonly bool DifferingNames = string.Equals(mod.Name, mod.DirectoryName, StringComparison.CurrentCultureIgnoreCase);
|
||||
}
|
||||
|
||||
public StringPair SelectionName { get; private set; } = new("Select new Mod...", new StringU8("Select new Mod..."u8));
|
||||
public string Selection { get; private set; } = string.Empty;
|
||||
public ModSettings Settings { get; private set; } = ModSettings.Empty;
|
||||
|
||||
public bool Draw(Utf8StringHandler<LabelStringHandlerBuffer> label, float previewWidth)
|
||||
{
|
||||
if (!Draw(label, SelectionName.Utf8, StringU8.Empty, previewWidth, out var newItem))
|
||||
return false;
|
||||
|
||||
SelectionName = newItem.Name;
|
||||
Selection = newItem.Directory.Utf16;
|
||||
Settings = newItem.Settings;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override float ItemHeight
|
||||
=> Im.Style.TextHeightWithSpacing;
|
||||
|
||||
protected override IEnumerable<CacheItem> GetItems()
|
||||
=> penumbra.GetMods(fileSystem.Selection.Selection?.GetValue<Design>()?.FilteredItemNames.ToArray() ?? []).Select(t => new CacheItem(t.Mod, t.Settings, t.Count));
|
||||
|
||||
protected override bool DrawItem(in CacheItem item, int globalIndex, bool selected)
|
||||
{
|
||||
bool ret;
|
||||
using (ImGuiColor.Text.Push(item.Color))
|
||||
{
|
||||
ret = ImUtf8.Selectable(mod.Name, selected);
|
||||
ret = Im.Selectable(item.Name.Utf8, selected);
|
||||
}
|
||||
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.PopupBorderSize, 2 * Im.Style.GlobalScale);
|
||||
using var tt = ImUtf8.Tooltip();
|
||||
var namesDifferent = mod.Name != mod.DirectoryName;
|
||||
Im.Dummy(new Vector2(300 * Im.Style.GlobalScale, 0));
|
||||
using (ImUtf8.Group())
|
||||
{
|
||||
if (namesDifferent)
|
||||
ImUtf8.Text("Directory Name"u8);
|
||||
ImUtf8.Text("Enabled"u8);
|
||||
ImUtf8.Text("Priority"u8);
|
||||
ImUtf8.Text("Affected Design Items"u8);
|
||||
DrawSettingsLeft(settings);
|
||||
}
|
||||
|
||||
ImGui.SameLine(Math.Max(ImGui.GetItemRectSize().X + 3 * Im.Style.ItemSpacing.X, 150 * Im.Style.GlobalScale));
|
||||
using (ImUtf8.Group())
|
||||
{
|
||||
if (namesDifferent)
|
||||
ImUtf8.Text(mod.DirectoryName);
|
||||
ImUtf8.Text($"{settings.Enabled}");
|
||||
ImUtf8.Text($"{settings.Priority}");
|
||||
ImUtf8.Text($"{count}");
|
||||
DrawSettingsRight(settings);
|
||||
}
|
||||
}
|
||||
if (Im.Item.Hovered())
|
||||
DrawTooltip(item);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static void DrawSettingsLeft(ModSettings settings)
|
||||
private static void DrawTooltip(in CacheItem item)
|
||||
{
|
||||
using var style = ImStyleSingle.PopupBorderThickness.Push(2 * Im.Style.GlobalScale);
|
||||
using var tt = Im.Tooltip.Begin();
|
||||
|
||||
Im.Dummy(ImEx.ScaledVectorX(300));
|
||||
using (Im.Group())
|
||||
{
|
||||
if (item.DifferingNames)
|
||||
Im.Text("Directory Name"u8);
|
||||
Im.Text("Enabled"u8);
|
||||
Im.Text("Priority"u8);
|
||||
Im.Text("Affected Design Items"u8);
|
||||
DrawSettingsLeft(item.Settings);
|
||||
}
|
||||
|
||||
Im.Line.Same(Math.Max(Im.Item.Size.X + 3 * Im.Style.ItemSpacing.X, 150 * Im.Style.GlobalScale));
|
||||
using (Im.Group())
|
||||
{
|
||||
if (item.DifferingNames)
|
||||
Im.Text(item.Directory.Utf8);
|
||||
Im.Text($"{item.Settings.Enabled}");
|
||||
Im.Text($"{item.Settings.Priority}");
|
||||
Im.Text($"{item.Count}");
|
||||
DrawSettingsRight(item.Settings);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawSettingsLeft(in ModSettings settings)
|
||||
{
|
||||
foreach (var setting in settings.Settings)
|
||||
{
|
||||
ImUtf8.Text(setting.Key);
|
||||
Im.Text(setting.Key);
|
||||
for (var i = 1; i < setting.Value.Count; ++i)
|
||||
Im.Line.New();
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawSettingsRight(ModSettings settings)
|
||||
public static void DrawSettingsRight(in ModSettings settings)
|
||||
{
|
||||
foreach (var setting in settings.Settings)
|
||||
{
|
||||
if (setting.Value.Count == 0)
|
||||
ImUtf8.Text("<None Enabled>"u8);
|
||||
Im.Text("<None Enabled>"u8);
|
||||
else
|
||||
foreach (var option in setting.Value)
|
||||
ImUtf8.Text(option);
|
||||
Im.Text(option);
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool IsSelected(CacheItem item, int globalIndex)
|
||||
=> Selection.Equals(item.Directory.Utf16, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
private sealed class ModFilter : TextFilterBase<CacheItem>
|
||||
{
|
||||
public override bool WouldBeVisible(in CacheItem item, int globalIndex)
|
||||
=> base.WouldBeVisible(in item, globalIndex) || WouldBeVisible(item.Directory.Utf16);
|
||||
|
||||
protected override string ToFilterString(in CacheItem item, int globalIndex)
|
||||
=> item.Name.Utf16;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue