Current state.

This commit is contained in:
Ottermandias 2025-09-27 12:43:48 +02:00
parent 924c9b9f7e
commit a805e9c56d
15 changed files with 345 additions and 314 deletions

2
Luna

@ -1 +1 @@
Subproject commit 15636cda90725e6af7071512cf9873dd273570fc Subproject commit 0e9fdb2bbfb02d76468a3f968b07289664d90808

@ -1 +1 @@
Subproject commit dd14131793e5ae47cc8e9232f46469216017b5aa Subproject commit 648b6fc2ce600a95ab2b2ced27e1639af2b04502

View file

@ -1,6 +1,7 @@
using Luna; using Luna;
using Penumbra.Collections; using Penumbra.Collections;
using Penumbra.Collections.Manager; using Penumbra.Collections.Manager;
using Penumbra.UI.CollectionTab;
namespace Penumbra.Communication; namespace Penumbra.Communication;
@ -41,7 +42,10 @@ public sealed class CollectionChange(Logger log)
ModFileSystemSelector = 0, ModFileSystemSelector = 0,
/// <seealso cref="Mods.ModSelection.OnCollectionChange"/> /// <seealso cref="Mods.ModSelection.OnCollectionChange"/>
ModSelection = 10, ModSelection = 10,
/// <seealso cref="CollectionCombo.OnCollectionChanged"/>
CollectionCombo = 15,
} }
/// <summary> The arguments for a collection change event. </summary> /// <summary> The arguments for a collection change event. </summary>

View file

@ -4,7 +4,6 @@ using Newtonsoft.Json;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
using Penumbra.Communication; using Penumbra.Communication;
using Penumbra.Enums; using Penumbra.Enums;
using Penumbra.Mods;
using Penumbra.Mods.Manager; using Penumbra.Mods.Manager;
using Penumbra.Services; using Penumbra.Services;
using Penumbra.UI; using Penumbra.UI;

View file

@ -6,7 +6,7 @@ using Penumbra.Util;
namespace Penumbra.Mods.Manager; namespace Penumbra.Mods.Manager;
public class ModCacheManager : IDisposable, Luna.IService public class ModCacheManager : IDisposable, Luna.IRequiredService
{ {
private readonly Configuration _config; private readonly Configuration _config;
private readonly CommunicatorService _communicator; private readonly CommunicatorService _communicator;

View file

@ -1,5 +1,4 @@
using Dalamud.Plugin; using Dalamud.Plugin;
using Dalamud.Bindings.ImGui;
using OtterGui; using OtterGui;
using Penumbra.Api; using Penumbra.Api;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
@ -17,6 +16,7 @@ using OtterGui.Tasks;
using Penumbra.UI; using Penumbra.UI;
using ResidentResourceManager = Penumbra.Interop.Services.ResidentResourceManager; using ResidentResourceManager = Penumbra.Interop.Services.ResidentResourceManager;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using ImSharp;
using Lumina.Excel.Sheets; using Lumina.Excel.Sheets;
using Luna; using Luna;
using Penumbra.Communication; using Penumbra.Communication;
@ -24,9 +24,9 @@ using Penumbra.GameData.Data;
using Penumbra.Interop; using Penumbra.Interop;
using Penumbra.Interop.Hooks; using Penumbra.Interop.Hooks;
using Penumbra.Interop.Hooks.PostProcessing; using Penumbra.Interop.Hooks.PostProcessing;
using Penumbra.Interop.Hooks.ResourceLoading;
using DynamisIpc = OtterGui.Services.DynamisIpc; using DynamisIpc = OtterGui.Services.DynamisIpc;
using MessageService = Penumbra.Services.MessageService; using MessageService = Penumbra.Services.MessageService;
using MouseButton = Penumbra.Api.Enums.MouseButton;
namespace Penumbra; namespace Penumbra;
@ -72,20 +72,17 @@ public class Penumbra : IDalamudPlugin
: "Unknown"; : "Unknown";
Log.Information( Log.Information(
$"Loading Penumbra Version {_validityChecker.Version}, Commit #{_validityChecker.CommitHash} with Waiting For Plugins: {startup}..."); $"Loading Penumbra Version {_validityChecker.Version}, Commit #{_validityChecker.CommitHash} with Waiting For Plugins: {startup}...");
_services.GetService<BackupService>(); // Initialize because not required anywhere else. _services.GetService<BackupService>(); // Initialize early to create backups.
_config = _services.GetService<Configuration>(); _config = _services.GetService<Configuration>();
_characterUtility = _services.GetService<CharacterUtility>(); _characterUtility = _services.GetService<CharacterUtility>();
_tempMods = _services.GetService<TempModManager>(); _tempMods = _services.GetService<TempModManager>();
_residentResources = _services.GetService<ResidentResourceManager>(); _residentResources = _services.GetService<ResidentResourceManager>();
_services.GetService<ResourceManagerService>(); // Initialize because not required anywhere else.
_modManager = _services.GetService<ModManager>(); _modManager = _services.GetService<ModManager>();
_collectionManager = _services.GetService<CollectionManager>(); _collectionManager = _services.GetService<CollectionManager>();
_tempCollections = _services.GetService<TempCollectionManager>(); _tempCollections = _services.GetService<TempCollectionManager>();
_redrawService = _services.GetService<RedrawService>(); _redrawService = _services.GetService<RedrawService>();
_communicatorService = _services.GetService<CommunicatorService>(); _communicatorService = _services.GetService<CommunicatorService>();
_gameData = _services.GetService<IDataManager>(); _gameData = _services.GetService<IDataManager>();
_services.GetService<ResourceService>(); // Initialize because not required anywhere else.
_services.GetService<ModCacheManager>(); // Initialize because not required anywhere else.
_collectionManager.Caches.CreateNecessaryCaches(); _collectionManager.Caches.CreateNecessaryCaches();
_services.GetService<PathResolver>(); _services.GetService<PathResolver>();
@ -118,13 +115,13 @@ public class Penumbra : IDalamudPlugin
{ {
_services.GetService<IpcProviders>(); _services.GetService<IpcProviders>();
var itemSheet = _services.GetService<IDataManager>().GetExcelSheet<Item>(); var itemSheet = _services.GetService<IDataManager>().GetExcelSheet<Item>();
_communicatorService.ChangedItemHover.Subscribe((in ChangedItemHover.Arguments args) => _communicatorService.ChangedItemHover.Subscribe((in args) =>
{ {
if (args.Data is IdentifiedItem { Item.Id.IsItem: true }) if (args.Data is IdentifiedItem { Item.Id.IsItem: true })
ImGui.TextUnformatted("Left Click to create an item link in chat."); Im.Text("Left Click to create an item link in chat."u8);
}, ChangedItemHover.Priority.Link); }, ChangedItemHover.Priority.Link);
_communicatorService.ChangedItemClick.Subscribe((in ChangedItemClick.Arguments args) => _communicatorService.ChangedItemClick.Subscribe((in args) =>
{ {
if (args is { Button: MouseButton.Left, Data: IdentifiedItem item } && itemSheet.GetRow(item.Item.ItemId.Id) is { } i) if (args is { Button: MouseButton.Left, Data: IdentifiedItem item } && itemSheet.GetRow(item.Item.ItemId.Id) is { } i)
Messager.LinkItem(i); Messager.LinkItem(i);
@ -255,10 +252,6 @@ public class Penumbra : IDalamudPlugin
sb.Append( sb.Append(
$"> **`#Temp Mods: `** {_tempMods.Mods.Sum(kvp => kvp.Value.Count) + _tempMods.ModsForAllCollections.Count}\n"); $"> **`#Temp Mods: `** {_tempMods.Mods.Sum(kvp => kvp.Value.Count) + _tempMods.ModsForAllCollections.Count}\n");
void PrintCollection(ModCollection c, CollectionCache _)
=> sb.Append(
$"> **`Collection {c.Identity.AnonymizedName + ':',-18}`** Inheritances: `{c.Inheritance.DirectlyInheritsFrom.Count,3}`, Enabled Mods: `{c.ActualSettings.Count(s => s is { Enabled: true }),4}`, Conflicts: `{c.AllConflicts.SelectMany(x => x).Sum(x => x is { HasPriority: true, Solved: true } ? x.Conflicts.Count : 0),5}/{c.AllConflicts.SelectMany(x => x).Sum(x => x.HasPriority ? x.Conflicts.Count : 0),5}`\n");
sb.AppendLine("**Collections**"); sb.AppendLine("**Collections**");
sb.Append($"> **`#Collections: `** {_collectionManager.Storage.Count - 1}\n"); sb.Append($"> **`#Collections: `** {_collectionManager.Storage.Count - 1}\n");
sb.Append($"> **`#Temp Collections: `** {_tempCollections.Count}\n"); sb.Append($"> **`#Temp Collections: `** {_tempCollections.Count}\n");
@ -280,6 +273,10 @@ public class Penumbra : IDalamudPlugin
PrintCollection(collection, collection._cache!); PrintCollection(collection, collection._cache!);
return sb.ToString(); return sb.ToString();
void PrintCollection(ModCollection c, CollectionCache _)
=> sb.Append(
$"> **`Collection {c.Identity.AnonymizedName + ':',-18}`** Inheritances: `{c.Inheritance.DirectlyInheritsFrom.Count,3}`, Enabled Mods: `{c.ActualSettings.Count(s => s is { Enabled: true }),4}`, Conflicts: `{c.AllConflicts.SelectMany(x => x).Sum(x => x is { HasPriority: true, Solved: true } ? x.Conflicts.Count : 0),5}/{c.AllConflicts.SelectMany(x => x).Sum(x => x.HasPriority ? x.Conflicts.Count : 0),5}`\n");
} }
private static string CollectLocaleEnvironmentVariables() private static string CollectLocaleEnvironmentVariables()

View file

@ -184,7 +184,7 @@ public partial class ModEditWindow
registry.CurrentUsage == registry.SubModUsage.Count ? ColorId.NewMod : ColorId.InheritedMod; registry.CurrentUsage == registry.SubModUsage.Count ? ColorId.NewMod : ColorId.InheritedMod;
using (ImRaii.PushColor(ImGuiCol.Text, color.Value())) using (ImRaii.PushColor(ImGuiCol.Text, color.Value()))
{ {
if (UiHelpers.Selectable(registry.RelPath.Path, selected)) if (Im.Selectable(registry.RelPath.Path.Span, selected))
{ {
if (selected) if (selected)
_selectedFiles.Remove(registry); _selectedFiles.Remove(registry);

View file

@ -1,5 +1,6 @@
using Dalamud.Interface; using Dalamud.Interface;
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using ImSharp;
using OtterGui; using OtterGui;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Text; using OtterGui.Text;
@ -11,25 +12,16 @@ using Penumbra.UI.CollectionTab;
namespace Penumbra.UI.Classes; namespace Penumbra.UI.Classes;
public class CollectionSelectHeader : Luna.IUiService public class CollectionSelectHeader(
CollectionManager collectionManager,
TutorialService tutorial,
ModSelection selection,
CollectionResolver resolver,
Configuration config,
CollectionCombo combo)
: Luna.IUiService
{ {
private readonly CollectionCombo _collectionCombo; private readonly ActiveCollections _activeCollections = collectionManager.Active;
private readonly ActiveCollections _activeCollections;
private readonly TutorialService _tutorial;
private readonly ModSelection _selection;
private readonly CollectionResolver _resolver;
private readonly Configuration _config;
public CollectionSelectHeader(CollectionManager collectionManager, TutorialService tutorial, ModSelection selection,
CollectionResolver resolver, Configuration config)
{
_tutorial = tutorial;
_selection = selection;
_resolver = resolver;
_config = config;
_activeCollections = collectionManager.Active;
_collectionCombo = new CollectionCombo(collectionManager, () => collectionManager.Storage.OrderBy(c => c.Identity.Name).ToList());
}
/// <summary> Draw the header line that can quick switch between collections. </summary> /// <summary> Draw the header line that can quick switch between collections. </summary>
public void Draw(bool spacing) public void Draw(bool spacing)
@ -47,10 +39,10 @@ public class CollectionSelectHeader : Luna.IUiService
DrawCollectionButton(buttonSize, GetPlayerCollectionInfo(), 3); DrawCollectionButton(buttonSize, GetPlayerCollectionInfo(), 3);
DrawCollectionButton(buttonSize, GetInheritedCollectionInfo(), 4); DrawCollectionButton(buttonSize, GetInheritedCollectionInfo(), 4);
_collectionCombo.Draw("##collectionSelector", comboWidth, ColorId.SelectedCollection.Value()); combo.Draw("##collectionSelector"u8, comboWidth, ColorId.SelectedCollection.Value());
} }
_tutorial.OpenTutorial(BasicTutorialSteps.CollectionSelectors); tutorial.OpenTutorial(BasicTutorialSteps.CollectionSelectors);
if (!_activeCollections.CurrentCollectionInUse) if (!_activeCollections.CurrentCollectionInUse)
ImGuiUtil.DrawTextButton("The currently selected collection is not used in any way.", -Vector2.UnitX, Colors.PressEnterWarningBg); ImGuiUtil.DrawTextButton("The currently selected collection is not used in any way.", -Vector2.UnitX, Colors.PressEnterWarningBg);
@ -58,26 +50,26 @@ public class CollectionSelectHeader : Luna.IUiService
private void DrawTemporaryCheckbox() private void DrawTemporaryCheckbox()
{ {
var hold = _config.IncognitoModifier.IsActive(); var hold = config.IncognitoModifier.IsActive();
using (ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, ImUtf8.GlobalScale)) using (ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, ImUtf8.GlobalScale))
{ {
var tint = _config.DefaultTemporaryMode var tint = config.DefaultTemporaryMode
? ImGuiCol.Text.Tinted(ColorId.TemporaryModSettingsTint) ? ImGuiCol.Text.Tinted(ColorId.TemporaryModSettingsTint)
: ImGui.GetColorU32(ImGuiCol.TextDisabled); : ImGui.GetColorU32(ImGuiCol.TextDisabled);
using var color = ImRaii.PushColor(ImGuiCol.ButtonHovered, ImGui.GetColorU32(ImGuiCol.FrameBg), !hold) using var color = ImRaii.PushColor(ImGuiCol.ButtonHovered, ImGui.GetColorU32(ImGuiCol.FrameBg), !hold)
.Push(ImGuiCol.ButtonActive, ImGui.GetColorU32(ImGuiCol.FrameBg), !hold) .Push(ImGuiCol.ButtonActive, ImGui.GetColorU32(ImGuiCol.FrameBg), !hold)
.Push(ImGuiCol.Border, tint, _config.DefaultTemporaryMode); .Push(ImGuiCol.Border, tint, config.DefaultTemporaryMode);
if (ImUtf8.IconButton(FontAwesomeIcon.Stopwatch, ""u8, default, false, tint, ImGui.GetColorU32(ImGuiCol.FrameBg)) && hold) if (ImUtf8.IconButton(FontAwesomeIcon.Stopwatch, ""u8, default, false, tint, ImGui.GetColorU32(ImGuiCol.FrameBg)) && hold)
{ {
_config.DefaultTemporaryMode = !_config.DefaultTemporaryMode; config.DefaultTemporaryMode = !config.DefaultTemporaryMode;
_config.Save(); config.Save();
} }
} }
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled,
"Toggle the temporary settings mode, where all changes you do create temporary settings first and need to be made permanent if desired."u8); "Toggle the temporary settings mode, where all changes you do create temporary settings first and need to be made permanent if desired."u8);
if (!hold) if (!hold)
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"\nHold {_config.IncognitoModifier} while clicking to toggle."); ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"\nHold {config.IncognitoModifier} while clicking to toggle.");
} }
private enum CollectionState private enum CollectionState
@ -116,7 +108,7 @@ public class CollectionSelectHeader : Luna.IUiService
private (ModCollection?, string, string, bool) GetPlayerCollectionInfo() private (ModCollection?, string, string, bool) GetPlayerCollectionInfo()
{ {
var collection = _resolver.PlayerCollection(); var collection = resolver.PlayerCollection();
return CheckCollection(collection) switch return CheckCollection(collection) switch
{ {
CollectionState.Empty => (collection, "None", "The loaded player character is configured to use no mods.", true), CollectionState.Empty => (collection, "None", "The loaded player character is configured to use no mods.", true),
@ -145,7 +137,7 @@ public class CollectionSelectHeader : Luna.IUiService
private (ModCollection?, string, string, bool) GetInheritedCollectionInfo() private (ModCollection?, string, string, bool) GetInheritedCollectionInfo()
{ {
var collection = _selection.Mod == null ? null : _selection.Collection; var collection = selection.Mod == null ? null : selection.Collection;
return CheckCollection(collection, true) switch return CheckCollection(collection, true) switch
{ {
CollectionState.Unavailable => (null, "Not Inherited", CollectionState.Unavailable => (null, "Not Inherited",

View file

@ -1,46 +1,64 @@
using Dalamud.Bindings.ImGui; using ImSharp;
using Luna; using Luna;
using OtterGui.Raii; using Penumbra.Collections;
using OtterGui.Text; using Penumbra.Collections.Manager;
using OtterGui.Widgets; using Penumbra.Communication;
using Penumbra.Collections;
using Penumbra.Collections.Manager; namespace Penumbra.UI.CollectionTab;
namespace Penumbra.UI.CollectionTab; public sealed class CollectionCombo : SimpleFilterCombo<ModCollection>, IDisposable, IUiService
{
public sealed class CollectionCombo(CollectionManager manager, Func<IReadOnlyList<ModCollection>> items) private readonly Im.ColorDisposable _color = new();
: FilterComboCache<ModCollection>(items, MouseWheelType.Control, Penumbra.Log) private readonly Im.StyleDisposable _style = new();
{ private readonly CollectionManager _manager;
private readonly ImRaii.Color _color = new(); private readonly CollectionChange _event;
protected override void DrawFilter(int currentSelected, float width) public CollectionCombo(CollectionManager manager, CollectionChange @event)
{ : base(SimpleFilterType.Text)
_color.Dispose(); {
base.DrawFilter(currentSelected, width); _manager = manager;
} _event = @event;
Current = _manager.Active.Current;
public void Draw(string label, float width, uint color) _event.Subscribe(OnCollectionChanged, CollectionChange.Priority.CollectionCombo);
{ ClearFilterOnSelection = true;
var current = manager.Active.Current; ClearFilterOnCacheDisposal = true;
if (current != CurrentSelection) }
{
CurrentSelectionIdx = Items.IndexOf(current); private void OnCollectionChanged(in CollectionChange.Arguments arguments)
UpdateSelection(current); {
} if (arguments.Type is not CollectionType.Current)
return;
_color.Push(ImGuiCol.FrameBg, color).Push(ImGuiCol.FrameBgHovered, color);
if (Draw(label, current.Identity.Name, string.Empty, width, ImGui.GetTextLineHeightWithSpacing()) && CurrentSelection != null) Current = _manager.Active.Current;
manager.Active.SetCollection(CurrentSelection, CollectionType.Current); }
_color.Dispose();
} public override StringU8 DisplayString(in ModCollection value)
=> new(value.Identity.Name);
protected override string ToString(ModCollection obj)
=> obj.Identity.Name; public override string FilterString(in ModCollection value)
=> value.Identity.Name;
protected override void DrawCombo(string label, string preview, string tooltip, int currentSelected, float previewWidth, float itemHeight,
ImGuiComboFlags flags) public override IEnumerable<ModCollection> GetBaseItems()
{ => _manager.Storage.OrderBy(c => c.Identity.Name);
base.DrawCombo(label, preview, tooltip, currentSelected, previewWidth, itemHeight, flags);
ImUtf8.HoverTooltip("Control and mouse wheel to scroll."u8); public void Draw(Utf8StringHandler<LabelStringHandlerBuffer> label, float width, Rgba32 color)
} {
} _color.Push(ImGuiColor.FrameBackground, color)
.Push(ImGuiColor.FrameBackgroundHovered, color);
if (Draw(label, Current!, "Control and mouse wheel to scroll."u8, width, out var collection))
_manager.Active.SetCollection(collection, CollectionType.Current);
_color.Dispose();
_style.Dispose();
}
public void Dispose()
=> _event.Unsubscribe(OnCollectionChanged);
protected override void PreDrawFilter()
{
_color.Dispose();
_style.PushDefault(ImStyleDouble.ItemSpacing);
base.PreDrawFilter();
}
}

View file

@ -1,34 +1,32 @@
using Dalamud.Interface; using ImSharp;
using Dalamud.Bindings.ImGui; using Luna;
using Penumbra.UI.Classes; using Penumbra.UI.Classes;
using OtterGui.Raii;
using OtterGui.Text; namespace Penumbra.UI;
namespace Penumbra.UI; public class IncognitoService(TutorialService tutorial, Configuration config) : IUiService
{
public class IncognitoService(TutorialService tutorial, Configuration config) : Luna.IService public bool IncognitoMode
{ => config.Ephemeral.IncognitoMode;
public bool IncognitoMode
=> config.Ephemeral.IncognitoMode; public void DrawToggle(float width)
{
public void DrawToggle(float width) var hold = config.IncognitoModifier.IsActive();
{ var color = ColorId.FolderExpanded.Value();
var hold = config.IncognitoModifier.IsActive(); using (new Im.ColorStyleDisposable().PushBorder(ImStyleBorder.Frame, color))
var color = ColorId.FolderExpanded.Value(); {
using (ImRaii.PushFrameBorder(ImUtf8.GlobalScale, color)) var tt = IncognitoMode ? "Toggle incognito mode off."u8 : "Toggle incognito mode on."u8;
{ var icon = IncognitoMode ? LunaStyle.IncognitoOn : LunaStyle.IncognitoOff;
var tt = IncognitoMode ? "Toggle incognito mode off."u8 : "Toggle incognito mode on."u8; if (ImEx.Icon.Button(icon, tt, size: new Vector2(width, Im.Style.FrameHeight), textColor: color) && hold)
var icon = IncognitoMode ? FontAwesomeIcon.EyeSlash : FontAwesomeIcon.Eye; {
if (ImUtf8.IconButton(icon, tt, new Vector2(width, ImUtf8.FrameHeight), false, color) && hold) config.Ephemeral.IncognitoMode = !IncognitoMode;
{ config.Ephemeral.Save();
config.Ephemeral.IncognitoMode = !IncognitoMode; }
config.Ephemeral.Save();
} if (!hold)
Im.Tooltip.OnHover(HoveredFlags.AllowWhenDisabled, $"\nHold {config.IncognitoModifier} while clicking to toggle.");
if (!hold) }
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"\nHold {config.IncognitoModifier} while clicking to toggle.");
} tutorial.OpenTutorial(BasicTutorialSteps.Incognito);
}
tutorial.OpenTutorial(BasicTutorialSteps.Incognito); }
}
}

View file

@ -1,6 +1,7 @@
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using FFXIVClientStructs.FFXIV.Client.Game.Object; using FFXIVClientStructs.FFXIV.Client.Game.Object;
using FFXIVClientStructs.FFXIV.Client.System.Resource; using FFXIVClientStructs.FFXIV.Client.System.Resource;
using ImSharp;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Widgets; using OtterGui.Widgets;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
@ -102,50 +103,50 @@ public sealed class ResourceWatcher : IDisposable, ITab, Luna.IUiService
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + ImGui.GetTextLineHeightWithSpacing() / 2); ImGui.SetCursorPosY(ImGui.GetCursorPosY() + ImGui.GetTextLineHeightWithSpacing() / 2);
var isEnabled = _ephemeral.EnableResourceWatcher; var isEnabled = _ephemeral.EnableResourceWatcher;
if (ImGui.Checkbox("Enable", ref isEnabled)) if (Im.Checkbox("Enable"u8, ref isEnabled))
{ {
_ephemeral.EnableResourceWatcher = isEnabled; _ephemeral.EnableResourceWatcher = isEnabled;
_ephemeral.Save(); _ephemeral.Save();
} }
ImGui.SameLine(); Im.Line.Same();
DrawMaxEntries(); DrawMaxEntries();
ImGui.SameLine(); Im.Line.Same();
if (ImGui.Button("Clear")) if (Im.Button("Clear"u8))
Clear(); Clear();
ImGui.SameLine(); ImGui.SameLine();
var onlyMatching = _ephemeral.OnlyAddMatchingResources; var onlyMatching = _ephemeral.OnlyAddMatchingResources;
if (ImGui.Checkbox("Store Only Matching", ref onlyMatching)) if (Im.Checkbox("Store Only Matching"u8, ref onlyMatching))
{ {
_ephemeral.OnlyAddMatchingResources = onlyMatching; _ephemeral.OnlyAddMatchingResources = onlyMatching;
_ephemeral.Save(); _ephemeral.Save();
} }
ImGui.SameLine(); Im.Line.Same();
var writeToLog = _ephemeral.EnableResourceLogging; var writeToLog = _ephemeral.EnableResourceLogging;
if (ImGui.Checkbox("Write to Log", ref writeToLog)) if (Im.Checkbox("Write to Log"u8, ref writeToLog))
{ {
_ephemeral.EnableResourceLogging = writeToLog; _ephemeral.EnableResourceLogging = writeToLog;
_ephemeral.Save(); _ephemeral.Save();
} }
ImGui.SameLine(); Im.Line.Same();
DrawFilterInput(); DrawFilterInput();
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + ImGui.GetTextLineHeightWithSpacing() / 2); Im.Cursor.Y += Im.Style.TextHeightWithSpacing / 2;
_table.Draw(ImGui.GetTextLineHeightWithSpacing()); _table.Draw(Im.Style.TextHeightWithSpacing);
} }
private void DrawFilterInput() private void DrawFilterInput()
{ {
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); Im.Item.SetNextWidth(Im.ContentRegion.Available.X);
var tmp = _logFilter; var tmp = _logFilter;
var invalidRegex = _logRegex == null && _logFilter.Length > 0; var invalidRegex = _logRegex is null && _logFilter.Length > 0;
using var color = ImRaii.PushColor(ImGuiCol.Border, Colors.RegexWarningBorder, invalidRegex); using var color =
using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, UiHelpers.Scale, invalidRegex); new Im.ColorStyleDisposable().PushBorder(ImStyleBorder.Frame, Colors.RegexWarningBorder, Im.Style.GlobalScale, invalidRegex);
if (ImGui.InputTextWithHint("##logFilter", "If path matches this Regex...", ref tmp, 256)) if (Im.Input.Text("##logFilter"u8, ref tmp, "If path matches this Regex..."u8))
UpdateFilter(tmp, true); UpdateFilter(tmp, true);
} }
@ -180,8 +181,8 @@ public sealed class ResourceWatcher : IDisposable, ITab, Luna.IUiService
private void DrawMaxEntries() private void DrawMaxEntries()
{ {
ImGui.SetNextItemWidth(80 * UiHelpers.Scale); Im.Item.SetNextWidth(80 * Im.Style.GlobalScale);
ImGui.InputInt("Max. Entries", ref _newMaxEntries, 0, 0); Im.Input.Scalar("Max. Entries"u8, ref _newMaxEntries);
var change = ImGui.IsItemDeactivatedAfterEdit(); var change = ImGui.IsItemDeactivatedAfterEdit();
if (ImGui.IsItemClicked(ImGuiMouseButton.Right) && ImGui.GetIO().KeyCtrl) if (ImGui.IsItemClicked(ImGuiMouseButton.Right) && ImGui.GetIO().KeyCtrl)
{ {

View file

@ -789,6 +789,7 @@ public class DebugTab : Window, ITab, IUiService
Im.Text("Exists"u8); Im.Text("Exists"u8);
Im.Text("File Size"u8); Im.Text("File Size"u8);
} }
Im.Line.SameInner(); Im.Line.SameInner();
using (Im.Group()) using (Im.Group())
{ {
@ -1102,8 +1103,8 @@ public class DebugTab : Window, ITab, IUiService
ImGui.TableNextColumn(); ImGui.TableNextColumn();
Penumbra.Dynamis.DrawPointer((nint)imc); Penumbra.Dynamis.DrawPointer((nint)imc);
ImGui.TableNextColumn(); ImGui.TableNextColumn();
if (imc != null) if (imc is not null)
UiHelpers.Text(imc); Im.Text(imc->FileName().Span);
var mdl = (RenderModel*)model->Models[i]; var mdl = (RenderModel*)model->Models[i];
ImGui.TableNextColumn(); ImGui.TableNextColumn();
@ -1112,9 +1113,7 @@ public class DebugTab : Window, ITab, IUiService
continue; continue;
ImGui.TableNextColumn(); ImGui.TableNextColumn();
{ Im.Text(mdl->ResourceHandle->FileName().Span);
UiHelpers.Text(mdl->ResourceHandle);
}
} }
} }
@ -1209,14 +1208,7 @@ public class DebugTab : Window, ITab, IUiService
ImGui.TableNextColumn(); ImGui.TableNextColumn();
ImGui.TextUnformatted(r->RefCount.ToString()); ImGui.TextUnformatted(r->RefCount.ToString());
ImGui.TableNextColumn(); ImGui.TableNextColumn();
ref var name = ref r->FileName; Im.Text(r->FileName.AsSpan());
if (name.Capacity > 15)
UiHelpers.Text(name.BufferPtr, (int)name.Length);
else
fixed (byte* ptr = name.Buffer)
{
UiHelpers.Text(ptr, (int)name.Length);
}
}); });
} }

View file

@ -2,8 +2,8 @@ using Dalamud.Bindings.ImGui;
using Dalamud.Game; using Dalamud.Game;
using FFXIVClientStructs.FFXIV.Client.System.Resource; using FFXIVClientStructs.FFXIV.Client.System.Resource;
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle; using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
using FFXIVClientStructs.Interop;
using FFXIVClientStructs.STD; using FFXIVClientStructs.STD;
using ImSharp;
using OtterGui; using OtterGui;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Widgets; using OtterGui.Widgets;
@ -52,7 +52,7 @@ public class ResourceTab(Configuration config, ResourceManagerService resourceMa
private string _resourceManagerFilter = string.Empty; private string _resourceManagerFilter = string.Empty;
/// <summary> Draw a single resource map. </summary> /// <summary> Draw a single resource map. </summary>
private unsafe void DrawResourceMap(ResourceCategory category, uint ext, StdMap<uint, Pointer<ResourceHandle>>* map) private unsafe void DrawResourceMap(ResourceCategory category, uint ext, StdMap<uint, FFXIVClientStructs.Interop.Pointer<ResourceHandle>>* map)
{ {
if (map == null) if (map == null)
return; return;
@ -86,7 +86,7 @@ public class ResourceTab(Configuration config, ResourceManagerService resourceMa
var resource = (Interop.Structs.ResourceHandle*)r; var resource = (Interop.Structs.ResourceHandle*)r;
ImGui.TableNextColumn(); ImGui.TableNextColumn();
UiHelpers.Text(resource); Im.Text(resource->FileName().Span);
if (ImGui.IsItemClicked()) if (ImGui.IsItemClicked())
{ {
var data = resource->CsHandle.GetData(); var data = resource->CsHandle.GetData();
@ -106,7 +106,7 @@ public class ResourceTab(Configuration config, ResourceManagerService resourceMa
/// <summary> Draw a full category for the resource manager. </summary> /// <summary> Draw a full category for the resource manager. </summary>
private unsafe void DrawCategoryContainer(ResourceCategory category, private unsafe void DrawCategoryContainer(ResourceCategory category,
StdMap<uint, Pointer<StdMap<uint, Pointer<ResourceHandle>>>>* map, int idx) StdMap<uint, FFXIVClientStructs.Interop.Pointer<StdMap<uint, FFXIVClientStructs.Interop.Pointer<ResourceHandle>>>>* map, int idx)
{ {
if (map == null) if (map == null)
return; return;

View file

@ -104,30 +104,104 @@ public class SettingsTab : ITab, IUiService
_tutorial.OpenTutorial(BasicTutorialSteps.Faq2); _tutorial.OpenTutorial(BasicTutorialSteps.Faq2);
} }
private readonly TwoPanelLayout _test = new(); public sealed class TestFlattened : IFlattenedTreeNode
{
public int ParentIndex { get; set; }
public int StartsLineTo { get; set; }
public int IndentationDepth { get; set; }
public void Draw()
{
Im.Tree.Node("",
TreeNodeFlags.DefaultOpen
| TreeNodeFlags.NoTreePushOnOpen); //Im.Selectable($"{ParentIndex} {StartsLineTo} {IndentationDepth}"));
}
}
private static readonly List<TestFlattened> List =
[
new()
{
IndentationDepth = 0,
ParentIndex = -1,
StartsLineTo = 2,
},
new()
{
IndentationDepth = 1,
ParentIndex = 0,
StartsLineTo = -1,
},
new()
{
IndentationDepth = 1,
ParentIndex = 0,
StartsLineTo = -1,
},
new()
{
IndentationDepth = 0,
ParentIndex = -1,
StartsLineTo = 8,
},
new()
{
IndentationDepth = 1,
ParentIndex = 3,
StartsLineTo = 6,
},
new()
{
IndentationDepth = 2,
ParentIndex = 4,
StartsLineTo = -1,
},
new()
{
IndentationDepth = 2,
ParentIndex = 4,
StartsLineTo = 7,
},
new()
{
IndentationDepth = 3,
ParentIndex = 6,
StartsLineTo = -1,
},
new()
{
IndentationDepth = 1,
ParentIndex = 3,
StartsLineTo = -1,
},
];
public void DrawContent() public void DrawContent()
{ {
using var child = ImRaii.Child("##SettingsTab", -Vector2.One, false); using var child = ImRaii.Child("##SettingsTab", -Vector2.One, false);
if (!child) if (!child)
return; return;
DrawEnabledBox(); using var c2 = ImRaii.Child("a", new Vector2(300, 5 * ImGui.GetTextLineHeightWithSpacing()), true,
EphemeralCheckbox("Lock Main Window", "Prevent the main window from being resized or moved.", _config.Ephemeral.FixMainWindow, ImGuiWindowFlags.AlwaysVerticalScrollbar);
v => _config.Ephemeral.FixMainWindow = v); TreeLine.Draw(List, 0xFFFFFFFF);
ImGui.NewLine(); //DrawEnabledBox();
DrawRootFolder(); //EphemeralCheckbox("Lock Main Window", "Prevent the main window from being resized or moved.", _config.Ephemeral.FixMainWindow,
DrawDirectoryButtons(); // v => _config.Ephemeral.FixMainWindow = v);
ImGui.NewLine(); //
ImGui.NewLine(); //ImGui.NewLine();
//DrawRootFolder();
DrawGeneralSettings(); //DrawDirectoryButtons();
_migrationDrawer.Draw(); //ImGui.NewLine();
DrawColorSettings(); //ImGui.NewLine();
DrawPredefinedTagsSection(); //
DrawAdvancedSettings(); //DrawGeneralSettings();
DrawSupportButtons(); //_migrationDrawer.Draw();
//DrawColorSettings();
//DrawPredefinedTagsSection();
//DrawAdvancedSettings();
//DrawSupportButtons();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]

View file

@ -1,131 +1,87 @@
using Dalamud.Interface.ImGuiNotification; using Dalamud.Interface.ImGuiNotification;
using Dalamud.Interface.Utility; using ImSharp;
using Dalamud.Bindings.ImGui; using Luna;
using Luna; using ImGuiId = ImSharp.ImGuiId;
using OtterGui;
using OtterGui.Raii; namespace Penumbra.UI;
using Penumbra.Interop.Structs;
using Penumbra.String; public static class UiHelpers
{
namespace Penumbra.UI; /// <summary> The longest support button text. </summary>
public static ReadOnlySpan<byte> SupportInfoButtonText
public static class UiHelpers => "Copy Support Info to Clipboard"u8;
{
/// <summary> Draw text given by a ByteString. </summary> /// <summary>
public static unsafe void Text(ByteString s) /// Draw a button that copies the support info to clipboards.
=> ImGuiNative.TextUnformatted(s.Path, s.Path + s.Length); /// </summary>
/// <param name="penumbra"></param>
/// <summary> Draw text given by a byte pointer and length. </summary> public static void DrawSupportButton(Penumbra penumbra)
public static unsafe void Text(byte* s, int length) {
=> ImGuiNative.TextUnformatted(s, s + length); if (!Im.Button(SupportInfoButtonText))
return;
/// <summary> Draw text given by a byte span. </summary>
public static unsafe void Text(ReadOnlySpan<byte> s) var text = penumbra.GatherSupportInformation();
{ Im.Clipboard.Set(text);
fixed (byte* pS = s) Penumbra.Messager.NotificationMessage("Copied Support Info to Clipboard.", NotificationType.Success, false);
{ }
Text(pS, s.Length);
} /// <summary> Draw a button to open a specific directory in a file explorer.</summary>
} /// <param name="id">Specific ID for the given type of directory.</param>
/// <param name="directory">The directory to open.</param>
/// <summary> Draw the name of a resource file. </summary> /// <param name="condition">Whether the button is available. </param>
public static unsafe void Text(ResourceHandle* resource) public static void DrawOpenDirectoryButton(ImGuiId id, DirectoryInfo directory, bool condition)
=> Text(resource->CsHandle.FileName.AsSpan()); {
using var _ = Im.Id.Push(id);
/// <summary> Draw a ByteString as a selectable. </summary> if (ImEx.Button("Open Directory"u8, Vector2.Zero, "Open this directory in your configured file explorer."u8,
public static unsafe bool Selectable(ByteString s, bool selected) !condition || !Directory.Exists(directory.FullName)))
{ Process.Start(new ProcessStartInfo(directory.FullName)
var tmp = (byte)(selected ? 1 : 0); {
return ImGuiNative.Selectable(s.Path, tmp, ImGuiSelectableFlags.None, Vector2.Zero) != 0; UseShellExecute = true,
} });
}
/// <summary>
/// A selectable that copies its text to clipboard on selection and provides a on-hover tooltip about that, /// <summary> Draw default vertical space. </summary>
/// using an ByteString. public static void DefaultLineSpace()
/// </summary> => Im.Dummy(DefaultSpace);
public static unsafe void CopyOnClickSelectable(ByteString text)
{ /// <summary> Vertical spacing between groups. </summary>
if (ImGuiNative.Selectable(text.Path, 0, ImGuiSelectableFlags.None, Vector2.Zero) != 0) public static Vector2 DefaultSpace;
ImGuiNative.SetClipboardText(text.Path);
/// <summary> Width of most input fields. </summary>
if (ImGui.IsItemHovered()) public static Vector2 InputTextWidth;
ImGui.SetTooltip("Click to copy to clipboard.");
} /// <summary> Frame Height for square icon buttons. </summary>
public static Vector2 IconButtonSize;
/// <summary> The longest support button text. </summary>
public const string SupportInfoButtonText = "Copy Support Info to Clipboard"; /// <summary> Input Text Width with space for an additional button with spacing of 3 between them. </summary>
public static float InputTextMinusButton3;
/// <summary>
/// Draw a button that copies the support info to clipboards. /// <summary> Input Text Width with space for an additional button with spacing of default item spacing between them. </summary>
/// </summary> public static float InputTextMinusButton;
/// <param name="penumbra"></param>
public static void DrawSupportButton(Penumbra penumbra) /// <summary> Multiples of the current Global Scale </summary>
{ public static float Scale;
if (!ImGui.Button(SupportInfoButtonText))
return; public static float ScaleX2;
public static float ScaleX3;
var text = penumbra.GatherSupportInformation(); public static float ScaleX4;
ImGui.SetClipboardText(text); public static float ScaleX5;
Penumbra.Messager.NotificationMessage($"Copied Support Info to Clipboard.", NotificationType.Success, false);
} public static void SetupCommonSizes()
{
/// <summary> Draw a button to open a specific directory in a file explorer.</summary> if (Im.Style.GlobalScale != Scale)
/// <param name="id">Specific ID for the given type of directory.</param> {
/// <param name="directory">The directory to open.</param> Scale = Im.Style.GlobalScale;
/// <param name="condition">Whether the button is available. </param> DefaultSpace = new Vector2(0, 10 * Scale);
public static void DrawOpenDirectoryButton(int id, DirectoryInfo directory, bool condition) InputTextWidth = new Vector2(350f * Scale, 0);
{ ScaleX2 = Scale * 2;
using var _ = ImRaii.PushId(id); ScaleX3 = Scale * 3;
if (ImGuiUtil.DrawDisabledButton("Open Directory", Vector2.Zero, "Open this directory in your configured file explorer.", ScaleX4 = Scale * 4;
!condition || !Directory.Exists(directory.FullName))) ScaleX5 = Scale * 5;
Process.Start(new ProcessStartInfo(directory.FullName) }
{
UseShellExecute = true, IconButtonSize = new Vector2(Im.Style.FrameHeight);
}); InputTextMinusButton3 = InputTextWidth.X - IconButtonSize.X - ScaleX3;
} InputTextMinusButton = InputTextWidth.X - IconButtonSize.X - Im.Style.ItemSpacing.X;
}
/// <summary> Draw default vertical space. </summary> }
public static void DefaultLineSpace()
=> ImGui.Dummy(DefaultSpace);
/// <summary> Vertical spacing between groups. </summary>
public static Vector2 DefaultSpace;
/// <summary> Width of most input fields. </summary>
public static Vector2 InputTextWidth;
/// <summary> Frame Height for square icon buttons. </summary>
public static Vector2 IconButtonSize;
/// <summary> Input Text Width with space for an additional button with spacing of 3 between them. </summary>
public static float InputTextMinusButton3;
/// <summary> Input Text Width with space for an additional button with spacing of default item spacing between them. </summary>
public static float InputTextMinusButton;
/// <summary> Multiples of the current Global Scale </summary>
public static float Scale;
public static float ScaleX2;
public static float ScaleX3;
public static float ScaleX4;
public static float ScaleX5;
public static void SetupCommonSizes()
{
if (ImGuiHelpers.GlobalScale != Scale)
{
Scale = ImGuiHelpers.GlobalScale;
DefaultSpace = new Vector2(0, 10 * Scale);
InputTextWidth = new Vector2(350f * Scale, 0);
ScaleX2 = Scale * 2;
ScaleX3 = Scale * 3;
ScaleX4 = Scale * 4;
ScaleX5 = Scale * 5;
}
IconButtonSize = new Vector2(ImGui.GetFrameHeight());
InputTextMinusButton3 = InputTextWidth.X - IconButtonSize.X - ScaleX3;
InputTextMinusButton = InputTextWidth.X - IconButtonSize.X - ImGui.GetStyle().ItemSpacing.X;
}
}