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

View file

@ -1,12 +1,10 @@
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) : Luna.IService public class IncognitoService(TutorialService tutorial, Configuration config) : IUiService
{ {
public bool IncognitoMode public bool IncognitoMode
=> config.Ephemeral.IncognitoMode; => config.Ephemeral.IncognitoMode;
@ -15,18 +13,18 @@ public class IncognitoService(TutorialService tutorial, Configuration config) :
{ {
var hold = config.IncognitoModifier.IsActive(); var hold = config.IncognitoModifier.IsActive();
var color = ColorId.FolderExpanded.Value(); var color = ColorId.FolderExpanded.Value();
using (ImRaii.PushFrameBorder(ImUtf8.GlobalScale, color)) using (new Im.ColorStyleDisposable().PushBorder(ImStyleBorder.Frame, color))
{ {
var tt = IncognitoMode ? "Toggle incognito mode off."u8 : "Toggle incognito mode on."u8; var tt = IncognitoMode ? "Toggle incognito mode off."u8 : "Toggle incognito mode on."u8;
var icon = IncognitoMode ? FontAwesomeIcon.EyeSlash : FontAwesomeIcon.Eye; var icon = IncognitoMode ? LunaStyle.IncognitoOn : LunaStyle.IncognitoOff;
if (ImUtf8.IconButton(icon, tt, new Vector2(width, ImUtf8.FrameHeight), false, color) && hold) if (ImEx.Icon.Button(icon, tt, size: new Vector2(width, Im.Style.FrameHeight), textColor: color) && hold)
{ {
config.Ephemeral.IncognitoMode = !IncognitoMode; config.Ephemeral.IncognitoMode = !IncognitoMode;
config.Ephemeral.Save(); config.Ephemeral.Save();
} }
if (!hold) if (!hold)
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"\nHold {config.IncognitoModifier} while clicking to toggle."); Im.Tooltip.OnHover(HoveredFlags.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,7 +104,77 @@ 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()
{ {
@ -112,22 +182,26 @@ public class SettingsTab : ITab, IUiService
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,59 +1,15 @@
using Dalamud.Interface.ImGuiNotification; using Dalamud.Interface.ImGuiNotification;
using Dalamud.Interface.Utility; using ImSharp;
using Dalamud.Bindings.ImGui;
using Luna; using Luna;
using OtterGui; using ImGuiId = ImSharp.ImGuiId;
using OtterGui.Raii;
using Penumbra.Interop.Structs;
using Penumbra.String;
namespace Penumbra.UI; namespace Penumbra.UI;
public static class UiHelpers public static class UiHelpers
{ {
/// <summary> Draw text given by a ByteString. </summary>
public static unsafe void Text(ByteString s)
=> ImGuiNative.TextUnformatted(s.Path, s.Path + s.Length);
/// <summary> Draw text given by a byte pointer and length. </summary>
public static unsafe void Text(byte* s, int length)
=> ImGuiNative.TextUnformatted(s, s + length);
/// <summary> Draw text given by a byte span. </summary>
public static unsafe void Text(ReadOnlySpan<byte> s)
{
fixed (byte* pS = s)
{
Text(pS, s.Length);
}
}
/// <summary> Draw the name of a resource file. </summary>
public static unsafe void Text(ResourceHandle* resource)
=> Text(resource->CsHandle.FileName.AsSpan());
/// <summary> Draw a ByteString as a selectable. </summary>
public static unsafe bool Selectable(ByteString s, bool selected)
{
var tmp = (byte)(selected ? 1 : 0);
return ImGuiNative.Selectable(s.Path, tmp, ImGuiSelectableFlags.None, Vector2.Zero) != 0;
}
/// <summary>
/// A selectable that copies its text to clipboard on selection and provides a on-hover tooltip about that,
/// using an ByteString.
/// </summary>
public static unsafe void CopyOnClickSelectable(ByteString text)
{
if (ImGuiNative.Selectable(text.Path, 0, ImGuiSelectableFlags.None, Vector2.Zero) != 0)
ImGuiNative.SetClipboardText(text.Path);
if (ImGui.IsItemHovered())
ImGui.SetTooltip("Click to copy to clipboard.");
}
/// <summary> The longest support button text. </summary> /// <summary> The longest support button text. </summary>
public const string SupportInfoButtonText = "Copy Support Info to Clipboard"; public static ReadOnlySpan<byte> SupportInfoButtonText
=> "Copy Support Info to Clipboard"u8;
/// <summary> /// <summary>
/// Draw a button that copies the support info to clipboards. /// Draw a button that copies the support info to clipboards.
@ -61,22 +17,22 @@ public static class UiHelpers
/// <param name="penumbra"></param> /// <param name="penumbra"></param>
public static void DrawSupportButton(Penumbra penumbra) public static void DrawSupportButton(Penumbra penumbra)
{ {
if (!ImGui.Button(SupportInfoButtonText)) if (!Im.Button(SupportInfoButtonText))
return; return;
var text = penumbra.GatherSupportInformation(); var text = penumbra.GatherSupportInformation();
ImGui.SetClipboardText(text); Im.Clipboard.Set(text);
Penumbra.Messager.NotificationMessage($"Copied Support Info to Clipboard.", NotificationType.Success, false); Penumbra.Messager.NotificationMessage("Copied Support Info to Clipboard.", NotificationType.Success, false);
} }
/// <summary> Draw a button to open a specific directory in a file explorer.</summary> /// <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="id">Specific ID for the given type of directory.</param>
/// <param name="directory">The directory to open.</param> /// <param name="directory">The directory to open.</param>
/// <param name="condition">Whether the button is available. </param> /// <param name="condition">Whether the button is available. </param>
public static void DrawOpenDirectoryButton(int id, DirectoryInfo directory, bool condition) public static void DrawOpenDirectoryButton(ImGuiId id, DirectoryInfo directory, bool condition)
{ {
using var _ = ImRaii.PushId(id); using var _ = Im.Id.Push(id);
if (ImGuiUtil.DrawDisabledButton("Open Directory", Vector2.Zero, "Open this directory in your configured file explorer.", if (ImEx.Button("Open Directory"u8, Vector2.Zero, "Open this directory in your configured file explorer."u8,
!condition || !Directory.Exists(directory.FullName))) !condition || !Directory.Exists(directory.FullName)))
Process.Start(new ProcessStartInfo(directory.FullName) Process.Start(new ProcessStartInfo(directory.FullName)
{ {
@ -86,7 +42,7 @@ public static class UiHelpers
/// <summary> Draw default vertical space. </summary> /// <summary> Draw default vertical space. </summary>
public static void DefaultLineSpace() public static void DefaultLineSpace()
=> ImGui.Dummy(DefaultSpace); => Im.Dummy(DefaultSpace);
/// <summary> Vertical spacing between groups. </summary> /// <summary> Vertical spacing between groups. </summary>
public static Vector2 DefaultSpace; public static Vector2 DefaultSpace;
@ -113,9 +69,9 @@ public static class UiHelpers
public static void SetupCommonSizes() public static void SetupCommonSizes()
{ {
if (ImGuiHelpers.GlobalScale != Scale) if (Im.Style.GlobalScale != Scale)
{ {
Scale = ImGuiHelpers.GlobalScale; Scale = Im.Style.GlobalScale;
DefaultSpace = new Vector2(0, 10 * Scale); DefaultSpace = new Vector2(0, 10 * Scale);
InputTextWidth = new Vector2(350f * Scale, 0); InputTextWidth = new Vector2(350f * Scale, 0);
ScaleX2 = Scale * 2; ScaleX2 = Scale * 2;
@ -124,8 +80,8 @@ public static class UiHelpers
ScaleX5 = Scale * 5; ScaleX5 = Scale * 5;
} }
IconButtonSize = new Vector2(ImGui.GetFrameHeight()); IconButtonSize = new Vector2(Im.Style.FrameHeight);
InputTextMinusButton3 = InputTextWidth.X - IconButtonSize.X - ScaleX3; InputTextMinusButton3 = InputTextWidth.X - IconButtonSize.X - ScaleX3;
InputTextMinusButton = InputTextWidth.X - IconButtonSize.X - ImGui.GetStyle().ItemSpacing.X; InputTextMinusButton = InputTextWidth.X - IconButtonSize.X - Im.Style.ItemSpacing.X;
} }
} }