Current State.

This commit is contained in:
Ottermandias 2024-12-27 17:51:17 +01:00
parent 98a89bb2b4
commit 282189ef6d
9 changed files with 115 additions and 56 deletions

@ -1 +1 @@
Subproject commit 97e9f427406f82a59ddef764b44ecea654a51623
Subproject commit fdda2054c26a30111ac55984ed6efde7f7214b68

View file

@ -333,6 +333,9 @@ public class CollectionCacheManager : IDisposable, IService
cache.ReloadMod(mod, true);
break;
case ModSettingChange.TemporarySetting:
cache.ReloadMod(mod!, true);
break;
case ModSettingChange.MultiInheritance:
case ModSettingChange.MultiEnableState:
FullRecalculation(collection);

View file

@ -88,7 +88,7 @@ public class CollectionEditor(SaveService saveService, CommunicatorService commu
/// <summary>
/// Set a given setting group settingName of mod idx to newValue if it differs from the current value and fix it if necessary.
/// /// If the mod is currently inherited, stop the inheritance.
/// If the mod is currently inherited, stop the inheritance.
/// </summary>
public bool SetModSetting(ModCollection collection, Mod mod, int groupIdx, Setting newValue)
{
@ -103,6 +103,18 @@ public class CollectionEditor(SaveService saveService, CommunicatorService commu
return true;
}
public bool SetTemporarySettings(ModCollection collection, Mod mod, TemporaryModSettings? settings, int key = 0)
{
key = settings?.Lock ?? key;
var old = collection.GetTempSettings(mod.Index);
if (old != null && old.Lock != 0 && old.Lock != key)
return false;
collection.Settings.SetTemporary(mod.Index, settings);
InvokeChange(collection, ModSettingChange.TemporarySetting, mod, Setting.Indefinite, 0);
return true;
}
/// <summary> Copy the settings of an existing (sourceMod != null) or stored (sourceName) mod to another mod, if they exist. </summary>
public bool CopyModSettings(ModCollection collection, Mod? sourceMod, string sourceName, Mod? targetMod, string targetName)
{
@ -168,7 +180,7 @@ public class CollectionEditor(SaveService saveService, CommunicatorService commu
if (inherit == (settings == null))
return false;
ModSettings? settings1 = inherit ? null : collection.GetInheritedSettings(mod.Index).Settings?.DeepCopy() ?? ModSettings.DefaultSettings(mod);
var settings1 = inherit ? null : collection.GetInheritedSettings(mod.Index).Settings?.DeepCopy() ?? ModSettings.DefaultSettings(mod);
collection.Settings.Set(mod.Index, settings1);
return true;
}
@ -179,7 +191,8 @@ public class CollectionEditor(SaveService saveService, CommunicatorService commu
{
saveService.QueueSave(new ModCollectionSave(modStorage, changedCollection));
communicator.ModSettingChanged.Invoke(changedCollection, type, mod, oldValue, groupIdx, false);
RecurseInheritors(changedCollection, type, mod, oldValue, groupIdx);
if (type is not ModSettingChange.TemporarySetting)
RecurseInheritors(changedCollection, type, mod, oldValue, groupIdx);
}
/// <summary> Trigger changes in all inherited collections. </summary>

View file

@ -36,17 +36,11 @@ public class ModSelection : EventWrapper<Mod?, Mod?, ModSelection.Priority>
_communicator.ModSettingChanged.Subscribe(OnSettingChange, ModSettingChanged.Priority.ModSelection);
}
public ModSettings Settings { get; private set; } = ModSettings.Empty;
public ModCollection Collection { get; private set; } = ModCollection.Empty;
public Mod? Mod { get; private set; }
public ModSettings? OwnSettings { get; private set; }
public bool IsTemporary
=> OwnSettings != Settings;
public TemporaryModSettings? AsTemporarySettings
=> Settings as TemporaryModSettings;
public ModSettings Settings { get; private set; } = ModSettings.Empty;
public ModCollection Collection { get; private set; } = ModCollection.Empty;
public Mod? Mod { get; private set; }
public ModSettings? OwnSettings { get; private set; }
public TemporaryModSettings? TemporarySettings { get; private set; }
public void SelectMod(Mod? mod)
{
@ -98,6 +92,7 @@ public class ModSelection : EventWrapper<Mod?, Mod?, ModSelection.Priority>
{
(var settings, Collection) = _collections.Current.GetActualSettings(Mod.Index);
OwnSettings = _collections.Current.GetOwnSettings(Mod.Index);
TemporarySettings = _collections.Current.GetTempSettings(Mod.Index);
Settings = settings ?? ModSettings.Empty;
}
}

View file

@ -12,6 +12,7 @@ namespace Penumbra.Mods.Settings;
public class ModSettings
{
public static readonly ModSettings Empty = new();
public SettingList Settings { get; internal init; } = [];
public ModPriority Priority { get; set; }
public bool Enabled { get; set; }

View file

@ -5,4 +5,21 @@ public sealed class TemporaryModSettings : ModSettings
public string Source = string.Empty;
public int Lock = 0;
public bool ForceInherit;
// Create default settings for a given mod.
public static TemporaryModSettings DefaultSettings(Mod mod, string source, int key = 0)
=> new()
{
Enabled = false,
Source = source,
Lock = key,
Priority = ModPriority.Default,
Settings = SettingList.Default(mod),
};
}
public static class ModSettingsExtensions
{
public static bool IsTemporary(this ModSettings? settings)
=> settings is TemporaryModSettings;
}

View file

@ -742,7 +742,7 @@ public class ItemSwapTab : IDisposable, ITab, IUiService
private void OnSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, Setting oldValue, int groupIdx, bool inherited)
{
if (collection != _collectionManager.Active.Current || mod != _mod)
if (collection != _collectionManager.Active.Current || mod != _mod || type is ModSettingChange.TemporarySetting)
return;
_swapData.LoadMod(_mod, _modSettings);

View file

@ -1,8 +1,9 @@
using ImGuiNET;
using OtterGui.Custom;
namespace Penumbra.UI.Classes;
public enum ColorId
public enum ColorId : short
{
EnabledMod,
DisabledMod,
@ -10,6 +11,7 @@ public enum ColorId
InheritedMod,
InheritedDisabledMod,
NewMod,
NewModTint,
ConflictingMod,
HandledConflictMod,
FolderExpanded,
@ -31,10 +33,8 @@ public enum ColorId
ResTreeNonNetworked,
PredefinedTagAdd,
PredefinedTagRemove,
TemporaryEnabledMod,
TemporaryDisabledMod,
TemporaryInheritedMod,
TemporaryInheritedDisabledMod,
TemporaryModSettingsTint,
NoTint,
}
public static class Colors
@ -52,6 +52,18 @@ public static class Colors
public const uint ReniColorHovered = CustomGui.ReniColorHovered;
public const uint ReniColorActive = CustomGui.ReniColorActive;
public static uint Tinted(this ColorId color, ColorId tint)
{
var tintValue = ImGui.ColorConvertU32ToFloat4(tint.Value());
var value = ImGui.ColorConvertU32ToFloat4(color.Value());
var negAlpha = 1 - tintValue.W;
var newAlpha = negAlpha * value.W + tintValue.W;
var newR = (negAlpha * value.W * value.X + tintValue.W * tintValue.X) / newAlpha;
var newG = (negAlpha * value.W * value.Y + tintValue.W * tintValue.Y) / newAlpha;
var newB = (negAlpha * value.W * value.Z + tintValue.W * tintValue.Z) / newAlpha;
return ImGui.ColorConvertFloat4ToU32(new Vector4(newR, newG, newB, newAlpha));
}
public static (uint DefaultColor, string Name, string Description) Data(this ColorId color)
=> color switch
{
@ -83,10 +95,9 @@ public static class Colors
ColorId.ResTreeNonNetworked => ( 0xFFC0C0FF, "On-Screen: Non-Players (Local)", "Non-player entities handled locally, in the On-Screen tab." ),
ColorId.PredefinedTagAdd => ( 0xFF44AA44, "Predefined Tags: Add Tag", "A predefined tag that is not present on the current mod and can be added." ),
ColorId.PredefinedTagRemove => ( 0xFF2222AA, "Predefined Tags: Remove Tag", "A predefined tag that is already present on the current mod and can be removed." ),
ColorId.TemporaryEnabledMod => ( 0xFFFFC0A0, "Mod Enabled By Temporary Settings", "A mod that is enabled by temporary settings in the currently selected collection." ),
ColorId.TemporaryDisabledMod => ( 0xFFB08070, "Mod Disabled By Temporary Settings", "A mod that is disabled by temporary settings in the currently selected collection." ),
ColorId.TemporaryInheritedMod => ( 0xFFE8FFB0, "Mod Enabled By Temporary Inheritance", "A mod that is forced to inherit by temporary settings in the currently selected collection." ),
ColorId.TemporaryInheritedDisabledMod => ( 0xFF90A080, "Mod Disabled By Temporary Inheritance", "A mod that is forced to inherit by temporary settings in the currently selected collection." ),
ColorId.TemporaryModSettingsTint => ( 0x30FF0000, "Mod with Temporary Settings", "A mod that has temporary settings. This color is used as a tint for the regular state colors." ),
ColorId.NewModTint => ( 0x8000FF00, "New Mod Tint", "A mod that was newly imported or created during this session and has not been enabled yet. This color is used as a tint for the regular state colors."),
ColorId.NoTint => ( 0x00000000, "No Tint", "The default tint for all mods."),
_ => throw new ArgumentOutOfRangeException( nameof( color ), color, null ),
// @formatter:on
};

View file

@ -62,6 +62,8 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
SubscribeRightClickFolder(f => SetQuickMove(f, 1, _config.QuickMoveFolder2, s => { _config.QuickMoveFolder2 = s; _config.Save(); }), 120);
SubscribeRightClickFolder(f => SetQuickMove(f, 2, _config.QuickMoveFolder3, s => { _config.QuickMoveFolder3 = s; _config.Save(); }), 130);
SubscribeRightClickLeaf(ToggleLeafFavorite);
SubscribeRightClickLeaf(RemoveTemporarySettings);
SubscribeRightClickLeaf(DisableTemporarily);
SubscribeRightClickLeaf(l => QuickMove(l, _config.QuickMoveFolder1, _config.QuickMoveFolder2, _config.QuickMoveFolder3));
SubscribeRightClickMain(ClearDefaultImportFolder, 100);
SubscribeRightClickMain(() => ClearQuickMove(0, _config.QuickMoveFolder1, () => {_config.QuickMoveFolder1 = string.Empty; _config.Save();}), 110);
@ -194,7 +196,7 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
protected override void DrawLeafName(FileSystem<Mod>.Leaf leaf, in ModState state, bool selected)
{
var flags = selected ? ImGuiTreeNodeFlags.Selected | LeafFlags : LeafFlags;
using var c = ImRaii.PushColor(ImGuiCol.Text, state.Color.Value())
using var c = ImRaii.PushColor(ImGuiCol.Text, state.Color.Tinted(state.Tint))
.Push(ImGuiCol.HeaderHovered, 0x4000FFFF, leaf.Value.Favorite);
using var id = ImRaii.PushId(leaf.Value.Index);
ImRaii.TreeNode(leaf.Value.Name, flags).Dispose();
@ -264,6 +266,23 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
_modManager.DataEditor.ChangeModFavorite(mod.Value, !mod.Value.Favorite);
}
private void RemoveTemporarySettings(FileSystem<Mod>.Leaf mod)
{
var tempSettings = _collectionManager.Active.Current.GetTempSettings(mod.Value.Index);
if (tempSettings is { Lock: 0 })
if (ImUtf8.MenuItem("Remove Temporary Settings"))
_collectionManager.Editor.SetTemporarySettings(_collectionManager.Active.Current, mod.Value, null);
}
private void DisableTemporarily(FileSystem<Mod>.Leaf mod)
{
var tempSettings = _collectionManager.Active.Current.GetTempSettings(mod.Value.Index);
if (tempSettings == null || tempSettings.Lock == 0)
if (ImUtf8.MenuItem("Disable Temporarily"))
_collectionManager.Editor.SetTemporarySettings(_collectionManager.Active.Current, mod.Value,
TemporaryModSettings.DefaultSettings(mod.Value, "User Context-Menu"));
}
private void SetDefaultImportFolder(ModFileSystem.Folder folder)
{
if (!ImGui.MenuItem("Set As Default Import Folder"))
@ -392,8 +411,6 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
ImGuiUtil.BulletTextColored(ColorId.InheritedMod.Value(), "enabled due to inheritance from another collection.");
ImGuiUtil.BulletTextColored(ColorId.InheritedDisabledMod.Value(), "disabled due to inheritance from another collection.");
ImGuiUtil.BulletTextColored(ColorId.UndefinedMod.Value(), "unconfigured in all inherited collections.");
ImGuiUtil.BulletTextColored(ColorId.NewMod.Value(),
"newly imported during this session. Will go away when first enabling a mod or when Penumbra is reloaded.");
ImGuiUtil.BulletTextColored(ColorId.HandledConflictMod.Value(),
"enabled and conflicting with another enabled Mod, but on different priorities (i.e. the conflict is solved).");
ImGuiUtil.BulletTextColored(ColorId.ConflictingMod.Value(),
@ -501,6 +518,7 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
public struct ModState
{
public ColorId Color;
public ColorId Tint;
public ModPriority Priority;
}
@ -571,31 +589,31 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
=> !_filter.IsVisible(leaf);
/// <summary> Only get the text color for a mod if no filters are set. </summary>
private ColorId GetTextColor(Mod mod, ModSettings? settings, ModCollection collection)
private (ColorId Color, ColorId Tint) GetTextColor(Mod mod, ModSettings? settings, ModCollection collection)
{
if (_modManager.IsNew(mod))
return ColorId.NewMod;
var tint = settings.IsTemporary()
? ColorId.TemporaryModSettingsTint
: _modManager.IsNew(mod)
? ColorId.NewModTint
: ColorId.NoTint;
if (settings.IsTemporary())
tint = ColorId.TemporaryModSettingsTint;
if (settings == null)
return ColorId.UndefinedMod;
return (ColorId.UndefinedMod, tint);
if (!settings.Enabled)
return collection != _collectionManager.Active.Current
? ColorId.InheritedDisabledMod
: settings is TemporaryModSettings
? ColorId.TemporaryDisabledMod
: ColorId.DisabledMod;
if (settings is TemporaryModSettings)
return ColorId.TemporaryEnabledMod;
return (collection != _collectionManager.Active.Current
? ColorId.InheritedDisabledMod
: ColorId.DisabledMod, tint);
var conflicts = _collectionManager.Active.Current.Conflicts(mod);
if (conflicts.Count == 0)
return collection != _collectionManager.Active.Current ? ColorId.InheritedMod : ColorId.EnabledMod;
return (collection != _collectionManager.Active.Current ? ColorId.InheritedMod : ColorId.EnabledMod, tint);
return conflicts.Any(c => !c.Solved)
return (conflicts.Any(c => !c.Solved)
? ColorId.ConflictingMod
: ColorId.HandledConflictMod;
: ColorId.HandledConflictMod, tint);
}
private bool CheckStateFilters(Mod mod, ModSettings? settings, ModCollection collection, ref ModState state)
@ -627,6 +645,15 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
return true;
}
// isNew color takes precedence before other colors.
if (settings.IsTemporary())
state.Tint = ColorId.TemporaryModSettingsTint;
else if (isNew)
state.Tint = ColorId.NewModTint;
else
state.Tint = ColorId.NoTint;
// Handle settings.
if (settings == null)
{
@ -640,9 +667,7 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
{
state.Color = collection != _collectionManager.Active.Current
? ColorId.InheritedDisabledMod
: settings is TemporaryModSettings
? ColorId.TemporaryDisabledMod
: ColorId.DisabledMod;
: ColorId.DisabledMod;
if (!_stateFilter.HasFlag(ModFilter.Disabled)
|| !_stateFilter.HasFlag(ModFilter.NoConflict))
return true;
@ -652,10 +677,7 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
if (!_stateFilter.HasFlag(ModFilter.Enabled))
return true;
if (settings is TemporaryModSettings)
state.Color = ColorId.TemporaryEnabledMod;
// Conflicts can only be relevant if the mod is enabled.
// Conflicts can only be relevant if the mod is enabled.
var conflicts = _collectionManager.Active.Current.Conflicts(mod);
if (conflicts.Count > 0)
{
@ -664,14 +686,14 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
if (!_stateFilter.HasFlag(ModFilter.UnsolvedConflict))
return true;
state.Color = settings is TemporaryModSettings ? ColorId.TemporaryEnabledMod : ColorId.ConflictingMod;
state.Color = ColorId.ConflictingMod;
}
else
{
if (!_stateFilter.HasFlag(ModFilter.SolvedConflict))
return true;
state.Color = settings is TemporaryModSettings ? ColorId.TemporaryEnabledMod : ColorId.HandledConflictMod;
state.Color = ColorId.HandledConflictMod;
}
}
else if (!_stateFilter.HasFlag(ModFilter.NoConflict))
@ -680,9 +702,6 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
}
}
// isNew color takes precedence before other colors.
if (isNew)
state.Color = ColorId.NewMod;
return false;
}
@ -704,7 +723,7 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
if (_stateFilter != ModFilterExtensions.UnfilteredStateMods)
return CheckStateFilters(mod, settings, collection, ref state);
state.Color = GetTextColor(mod, settings, collection);
(state.Color, state.Tint) = GetTextColor(mod, settings, collection);
return false;
}