Add a quick design bar.

This commit is contained in:
Ottermandias 2023-10-11 17:22:46 +02:00
parent 56303be6ae
commit 277b26cc92
11 changed files with 533 additions and 223 deletions

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Dalamud.Configuration; using Dalamud.Configuration;
using Dalamud.Game.ClientState.Keys;
using Dalamud.Interface.Internal.Notifications; using Dalamud.Interface.Internal.Notifications;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.Gui; using Glamourer.Gui;
@ -18,25 +19,30 @@ namespace Glamourer;
public class Configuration : IPluginConfiguration, ISavable public class Configuration : IPluginConfiguration, ISavable
{ {
public bool Enabled { get; set; } = true; public bool Enabled { get; set; } = true;
public bool UseRestrictedGearProtection { get; set; } = false; public bool UseRestrictedGearProtection { get; set; } = false;
public bool OpenFoldersByDefault { get; set; } = false; public bool OpenFoldersByDefault { get; set; } = false;
public bool AutoRedrawEquipOnChanges { get; set; } = false; public bool AutoRedrawEquipOnChanges { get; set; } = false;
public bool EnableAutoDesigns { get; set; } = true; public bool EnableAutoDesigns { get; set; } = true;
public bool IncognitoMode { get; set; } = false; public bool IncognitoMode { get; set; } = false;
public bool UnlockDetailMode { get; set; } = true; public bool UnlockDetailMode { get; set; } = true;
public bool HideApplyCheckmarks { get; set; } = false; public bool HideApplyCheckmarks { get; set; } = false;
public bool SmallEquip { get; set; } = false; public bool SmallEquip { get; set; } = false;
public bool UnlockedItemMode { get; set; } = false; public bool UnlockedItemMode { get; set; } = false;
public byte DisableFestivals { get; set; } = 1; public byte DisableFestivals { get; set; } = 1;
public bool EnableGameContextMenu { get; set; } = true; public bool EnableGameContextMenu { get; set; } = true;
public bool HideWindowInCutscene { get; set; } = false; public bool HideWindowInCutscene { get; set; } = false;
public bool ShowAutomationSetEditing { get; set; } = true; public bool ShowAutomationSetEditing { get; set; } = true;
public bool ShowAllAutomatedApplicationRules { get; set; } = true; public bool ShowAllAutomatedApplicationRules { get; set; } = true;
public bool ShowUnlockedItemWarnings { get; set; } = true; public bool ShowUnlockedItemWarnings { get; set; } = true;
public bool RevertManualChangesOnZoneChange { get; set; } = false; public bool RevertManualChangesOnZoneChange { get; set; } = false;
public MainWindow.TabType SelectedTab { get; set; } = MainWindow.TabType.Settings; public bool ShowDesignQuickBar { get; set; } = false;
public DoubleModifier DeleteDesignModifier { get; set; } = new(ModifierHotkey.Control, ModifierHotkey.Shift); public bool LockDesignQuickBar { get; set; } = false;
public bool ShowQuickBarInTabs { get; set; } = true;
public ModifiableHotkey ToggleQuickDesignBar { get; set; } = new(VirtualKey.D, ModifierHotkey.Control, ModifierHotkey.Shift);
public MainWindow.TabType SelectedTab { get; set; } = MainWindow.TabType.Settings;
public DoubleModifier DeleteDesignModifier { get; set; } = new(ModifierHotkey.Control, ModifierHotkey.Shift);
public int LastSeenVersion { get; set; } = GlamourerChangelog.LastChangelogVersion; public int LastSeenVersion { get; set; } = GlamourerChangelog.LastChangelogVersion;
public ChangeLogDisplayType ChangeLogDisplayType { get; set; } = ChangeLogDisplayType.New; public ChangeLogDisplayType ChangeLogDisplayType { get; set; } = ChangeLogDisplayType.New;

View file

@ -1,5 +1,6 @@
using System; using System;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.Gui;
using OtterGui.Classes; using OtterGui.Classes;
namespace Glamourer.Events; namespace Glamourer.Events;
@ -76,14 +77,17 @@ public sealed class DesignChanged : EventWrapper<Action<DesignChanged.Type, Desi
public enum Priority public enum Priority
{ {
/// <seealso cref="Automation.AutoDesignManager.OnDesignChange"/>
AutoDesignManager = 1,
/// <seealso cref="DesignFileSystem.OnDesignChange"/> /// <seealso cref="DesignFileSystem.OnDesignChange"/>
DesignFileSystem = 0, DesignFileSystem = 0,
/// <seealso cref="Gui.Tabs.DesignTab.DesignFileSystemSelector.OnDesignChange"/> /// <seealso cref="Gui.Tabs.DesignTab.DesignFileSystemSelector.OnDesignChange"/>
DesignFileSystemSelector = -1, DesignFileSystemSelector = -1,
/// <seealso cref="Automation.AutoDesignManager.OnDesignChange"/> /// <seealso cref="RevertDesignCombo.OnDesignChange"/>
AutoDesignManager = 1, DesignCombo = -2,
} }
public DesignChanged() public DesignChanged()

View file

@ -1,62 +0,0 @@
using System.Numerics;
using Dalamud.Interface;
using Glamourer.Automation;
using Glamourer.Events;
using Glamourer.Interop;
using Glamourer.State;
using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
namespace Glamourer.Gui;
public class ConvenienceRevertButtons
{
private readonly StateManager _stateManager;
private readonly AutoDesignApplier _autoDesignApplier;
private readonly ObjectManager _objects;
private readonly Configuration _config;
public ConvenienceRevertButtons(StateManager stateManager, AutoDesignApplier autoDesignApplier, ObjectManager objects,
Configuration config)
{
_stateManager = stateManager;
_autoDesignApplier = autoDesignApplier;
_objects = objects;
_config = config;
}
public void DrawButtons(float yPos)
{
_objects.Update();
var (playerIdentifier, playerData) = _objects.PlayerData;
string? error = null;
if (!playerIdentifier.IsValid || !playerData.Valid)
error = "No player character available.";
if (!_stateManager.TryGetValue(playerIdentifier, out var state))
error = "The player character was not modified by Glamourer yet.";
else if (state.IsLocked)
error = "The state of the player character is currently locked.";
var buttonSize = new Vector2(ImGui.GetFrameHeight());
var spacing = ImGui.GetStyle().ItemInnerSpacing;
ImGui.SetCursorPos(new Vector2(ImGui.GetWindowContentRegionMax().X - 2 * buttonSize.X - spacing.X, yPos - 1));
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing);
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.RedoAlt.ToIconString(), buttonSize,
error ?? "Revert the player character to its game state.", error != null, true))
_stateManager.ResetState(state, StateChanged.Source.Manual);
ImGui.SameLine();
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.SyncAlt.ToIconString(), buttonSize,
error ?? "Revert the player character to its automation state.", error != null && _config.EnableAutoDesigns, true))
foreach (var actor in playerData.Objects)
{
_autoDesignApplier.ReapplyAutomation(actor, playerIdentifier, state);
_stateManager.ReapplyState(actor);
}
}
}

View file

@ -0,0 +1,179 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Dalamud.Interface.Utility;
using Glamourer.Automation;
using Glamourer.Customization;
using Glamourer.Designs;
using Glamourer.Events;
using Glamourer.Services;
using ImGuiNET;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Log;
using OtterGui.Widgets;
namespace Glamourer.Gui;
public abstract class DesignComboBase : FilterComboCache<Tuple<Design, string>>, IDisposable
{
private readonly Configuration _config;
private readonly DesignChanged _designChanged;
protected readonly TabSelected TabSelected;
protected float InnerWidth;
protected DesignComboBase(Func<IReadOnlyList<Tuple<Design, string>>> generator, Logger log, DesignChanged designChanged,
TabSelected tabSelected, Configuration config)
: base(generator, log)
{
_designChanged = designChanged;
TabSelected = tabSelected;
_config = config;
_designChanged.Subscribe(OnDesignChange, DesignChanged.Priority.DesignCombo);
}
public bool Incognito
=> _config.IncognitoMode;
void IDisposable.Dispose()
=> _designChanged.Unsubscribe(OnDesignChange);
protected override bool DrawSelectable(int globalIdx, bool selected)
{
var ret = base.DrawSelectable(globalIdx, selected);
var (design, path) = Items[globalIdx];
if (path.Length > 0 && design.Name != path)
{
var start = ImGui.GetItemRectMin();
var pos = start.X + ImGui.CalcTextSize(design.Name).X;
var maxSize = ImGui.GetWindowPos().X + ImGui.GetWindowContentRegionMax().X;
var remainingSpace = maxSize - pos;
var requiredSize = ImGui.CalcTextSize(path).X + ImGui.GetStyle().ItemInnerSpacing.X;
var offset = remainingSpace - requiredSize;
if (ImGui.GetScrollMaxY() == 0)
offset -= ImGui.GetStyle().ItemInnerSpacing.X;
if (offset < ImGui.GetStyle().ItemSpacing.X)
ImGuiUtil.HoverTooltip(path);
else
ImGui.GetWindowDrawList().AddText(start with { X = pos + offset },
ImGui.GetColorU32(ImGuiCol.TextDisabled), path);
}
return ret;
}
protected bool Draw(Design? currentDesign, string? label, float width)
{
InnerWidth = 400 * ImGuiHelpers.GlobalScale;
CurrentSelectionIdx = Math.Max(Items.IndexOf(p => currentDesign == p.Item1), 0);
CurrentSelection = Items[CurrentSelectionIdx];
var name = label ?? "Select Design Here...";
var ret = Draw("##design", name, string.Empty, width, ImGui.GetTextLineHeightWithSpacing())
&& CurrentSelection != null;
if (currentDesign != null)
{
if (ImGui.IsItemClicked(ImGuiMouseButton.Right) && ImGui.GetIO().KeyCtrl)
TabSelected.Invoke(MainWindow.TabType.Designs, currentDesign);
ImGuiUtil.HoverTooltip("Control + Right-Click to move to design.");
}
return ret;
}
protected override string ToString(Tuple<Design, string> obj)
=> obj.Item1.Name.Text;
protected override float GetFilterWidth()
=> InnerWidth - 2 * ImGui.GetStyle().FramePadding.X;
protected override bool IsVisible(int globalIndex, LowerString filter)
{
var (design, path) = Items[globalIndex];
return filter.IsContained(path) || design.Name.Lower.Contains(filter.Lower);
}
private void OnDesignChange(DesignChanged.Type type, Design design, object? data = null)
{
switch (type)
{
case DesignChanged.Type.Created:
case DesignChanged.Type.Renamed:
Cleanup();
break;
case DesignChanged.Type.Deleted:
Cleanup();
if (CurrentSelection?.Item1 == design)
{
CurrentSelectionIdx = -1;
CurrentSelection = null;
}
break;
}
}
}
public sealed class DesignCombo : DesignComboBase
{
public DesignCombo(DesignManager designs, DesignFileSystem fileSystem, Logger log, DesignChanged designChanged, TabSelected tabSelected,
Configuration config)
: base(
() => designs.Designs
.Select(d => new Tuple<Design, string>(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty))
.OrderBy(d => d.Item2)
.ToList(), log, designChanged, tabSelected, config)
{ }
public Design? Design
=> CurrentSelection?.Item1;
public void Draw(float width)
=> Draw(Design, (Incognito ? Design?.Incognito : Design?.Name.Text) ?? string.Empty, width);
}
public sealed class RevertDesignCombo : DesignComboBase, IDisposable
{
public const int RevertDesignIndex = -1228;
public readonly Design RevertDesign;
private readonly AutoDesignManager _autoDesignManager;
public RevertDesignCombo(DesignManager designs, DesignFileSystem fileSystem, TabSelected tabSelected,
ItemManager items, CustomizationService customize, Logger log, DesignChanged designChanged, AutoDesignManager autoDesignManager,
Configuration config)
: this(designs, fileSystem, tabSelected, CreateRevertDesign(customize, items), log, designChanged, autoDesignManager, config)
{ }
private RevertDesignCombo(DesignManager designs, DesignFileSystem fileSystem, TabSelected tabSelected,
Design revertDesign, Logger log, DesignChanged designChanged, AutoDesignManager autoDesignManager, Configuration config)
: base(() => designs.Designs
.Select(d => new Tuple<Design, string>(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty))
.OrderBy(d => d.Item2)
.Prepend(new Tuple<Design, string>(revertDesign, string.Empty))
.ToList(), log, designChanged, tabSelected, config)
{
RevertDesign = revertDesign;
_autoDesignManager = autoDesignManager;
}
public void Draw(AutoDesignSet set, AutoDesign? design, int autoDesignIndex)
{
if (!Draw(design?.Design, design?.Name(Incognito), ImGui.GetContentRegionAvail().X))
return;
if (autoDesignIndex >= 0)
_autoDesignManager.ChangeDesign(set, autoDesignIndex, CurrentSelection!.Item1 == RevertDesign ? null : CurrentSelection!.Item1);
else
_autoDesignManager.AddDesign(set, CurrentSelection!.Item1 == RevertDesign ? null : CurrentSelection!.Item1);
}
private static Design CreateRevertDesign(CustomizationService customize, ItemManager items)
=> new(customize, items)
{
Index = RevertDesignIndex,
Name = AutoDesign.RevertName,
ApplyCustomize = CustomizeFlagExtensions.AllRelevant,
};
}

View file

@ -0,0 +1,255 @@
using System;
using System.Numerics;
using Dalamud.Game.ClientState.Keys;
using Dalamud.Interface;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin.Services;
using Glamourer.Automation;
using Glamourer.Events;
using Glamourer.Interop;
using Glamourer.Interop.Structs;
using Glamourer.State;
using ImGuiNET;
using OtterGui;
using OtterGui.Classes;
using Penumbra.GameData.Actors;
namespace Glamourer.Gui;
public class DesignQuickBar : Window, IDisposable
{
private ImGuiWindowFlags GetFlags
=> _config.LockDesignQuickBar
? ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoBackground
: ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoFocusOnAppearing;
private readonly Configuration _config;
private readonly DesignCombo _designCombo;
private readonly StateManager _stateManager;
private readonly AutoDesignApplier _autoDesignApplier;
private readonly ObjectManager _objects;
private readonly IKeyState _keyState;
private readonly ImRaii.Style _windowPadding = new();
private DateTime _keyboardToggle = DateTime.UnixEpoch;
public DesignQuickBar(Configuration config, DesignCombo designCombo, StateManager stateManager, IKeyState keyState,
ObjectManager objects, AutoDesignApplier autoDesignApplier)
: base("Glamourer Quick Bar", ImGuiWindowFlags.NoDecoration)
{
_config = config;
_designCombo = designCombo;
_stateManager = stateManager;
_keyState = keyState;
_objects = objects;
_autoDesignApplier = autoDesignApplier;
IsOpen = _config.ShowDesignQuickBar;
DisableWindowSounds = true;
Size = Vector2.Zero;
}
public void Dispose()
=> _windowPadding.Dispose();
public override void PreOpenCheck()
{
CheckHotkeys();
IsOpen = _config.ShowDesignQuickBar;
}
public override void PreDraw()
{
Flags = GetFlags;
Size = new Vector2(12 * ImGui.GetFrameHeight(), ImGui.GetFrameHeight());
_windowPadding.Push(ImGuiStyleVar.WindowPadding, new Vector2(ImGuiHelpers.GlobalScale * 4));
}
public override void PostDraw()
=> _windowPadding.Dispose();
public override void Draw()
=> Draw(ImGui.GetContentRegionAvail().X);
private void Draw(float width)
{
_objects.Update();
using var group = ImRaii.Group();
var spacing = ImGui.GetStyle().ItemInnerSpacing;
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing);
var contentRegion = width;
var buttonSize = new Vector2(ImGui.GetFrameHeight());
var comboSize = contentRegion - 3 * buttonSize.X - 3 * spacing.X;
_designCombo.Draw(comboSize);
PrepareButtons();
ImGui.SameLine();
DrawApplyButton(buttonSize);
ImGui.SameLine();
DrawRevertButton(buttonSize);
ImGui.SameLine();
DrawRevertAutomationButton(buttonSize);
}
private ActorIdentifier _playerIdentifier;
private ActorData _playerData;
private ActorState? _playerState;
private ActorData _targetData;
private ActorIdentifier _targetIdentifier;
private ActorState? _targetState;
private void PrepareButtons()
{
_objects.Update();
(_playerIdentifier, _playerData) = _objects.PlayerData;
(_targetIdentifier, _targetData) = _objects.TargetData;
if (!_stateManager.TryGetValue(_playerIdentifier, out _playerState))
_playerState = null;
if (!_stateManager.TryGetValue(_targetIdentifier, out _targetState))
_targetState = null;
}
private void DrawApplyButton(Vector2 size)
{
var design = _designCombo.Design;
var available = 0;
var tooltip = string.Empty;
if (design == null)
{
tooltip = "No design selected.";
}
else
{
if (_playerIdentifier.IsValid && _playerData.Valid)
{
available |= 1;
tooltip = $"Left-Click: Apply {(_config.IncognitoMode ? design.Incognito : design.Name)} to yourself.";
}
if (_targetIdentifier.IsValid && _targetData.Valid)
{
if (available != 0)
tooltip += '\n';
available |= 2;
tooltip += $"Right-Click: Apply {(_config.IncognitoMode ? design.Incognito : design.Name)} to {_targetIdentifier}.";
}
if (available == 0)
tooltip = "Neither player character nor target available.";
}
var (clicked, id, data, state) = ResolveTarget(FontAwesomeIcon.PlayCircle, size, tooltip, available);
if (!clicked)
return;
if (state == null && !_stateManager.GetOrCreate(id, data.Objects[0], out state))
{
Glamourer.Messager.NotificationMessage($"Could not apply {design!.Incognito} to {id.Incognito(null)}: Failed to create state.");
return;
}
var (applyGear, applyCustomize) = UiHelpers.ConvertKeysToFlags();
using var _ = design!.TemporarilyRestrictApplication(applyGear, applyCustomize);
_stateManager.ApplyDesign(design, state, StateChanged.Source.Manual);
}
public void DrawRevertButton(Vector2 buttonSize)
{
var available = 0;
var tooltip = string.Empty;
if (_playerIdentifier.IsValid && _playerState is { IsLocked: false })
{
available |= 1;
tooltip = "Left-Click: Revert the player character to their game state.";
}
if (_targetIdentifier.IsValid && _targetState is { IsLocked: false })
{
if (available != 0)
tooltip += '\n';
available |= 2;
tooltip += $"Right-Click: Revert {_targetIdentifier} to their game state.";
}
if (available == 0)
tooltip = "Neither player character nor target are available, have state modified by Glamourer, or their state is locked.";
var (clicked, id, data, state) = ResolveTarget(FontAwesomeIcon.RedoAlt, buttonSize, tooltip, available);
if (clicked)
_stateManager.ResetState(state!, StateChanged.Source.Manual);
}
public void DrawRevertAutomationButton(Vector2 buttonSize)
{
var available = 0;
var tooltip = string.Empty;
if (!_config.EnableAutoDesigns)
{
tooltip = "Automation is not enabled, you can not reset to automation state.";
}
else
{
if (_playerIdentifier.IsValid && _playerState is { IsLocked: false } && _playerData.Valid)
{
available |= 1;
tooltip = "Left-Click: Revert the player character to their automation state.";
}
if (_targetIdentifier.IsValid && _targetState is { IsLocked: false } && _targetData.Valid)
{
if (available != 0)
tooltip += '\n';
available |= 2;
tooltip += $"Right-Click: Revert {_targetIdentifier} to their automation state.";
}
if (available == 0)
tooltip = "Neither player character nor target are available, have state modified by Glamourer, or their state is locked.";
}
var (clicked, id, data, state) = ResolveTarget(FontAwesomeIcon.SyncAlt, buttonSize, tooltip, available);
if (!clicked)
{ }
else
{
foreach (var actor in data.Objects)
{
_autoDesignApplier.ReapplyAutomation(actor, id, state!);
_stateManager.ReapplyState(actor);
}
}
}
private (bool, ActorIdentifier, ActorData, ActorState?) ResolveTarget(FontAwesomeIcon icon, Vector2 buttonSize, string tooltip,
int available)
{
ImGuiUtil.DrawDisabledButton(icon.ToIconString(), buttonSize, tooltip, available == 0, true);
if ((available & 1) == 1 && ImGui.IsItemClicked(ImGuiMouseButton.Left))
return (true, _playerIdentifier, _playerData, _playerState);
if ((available & 2) == 2 && ImGui.IsItemClicked(ImGuiMouseButton.Right))
return (true, _targetIdentifier, _targetData, _targetState);
return (false, ActorIdentifier.Invalid, ActorData.Invalid, null);
}
private void CheckHotkeys()
{
if (_keyboardToggle > DateTime.UtcNow || !CheckKeyState(_config.ToggleQuickDesignBar, false))
return;
_keyboardToggle = DateTime.UtcNow.AddMilliseconds(500);
_config.ShowDesignQuickBar = !_config.ShowDesignQuickBar;
_config.Save();
}
public bool CheckKeyState(ModifiableHotkey key, bool noKey)
{
if (key.Hotkey == VirtualKey.NO_KEY)
return noKey;
return _keyState[key.Hotkey] && key.Modifier1.IsActive() && key.Modifier2.IsActive();
}
}

View file

@ -13,7 +13,7 @@ public class GlamourerWindowSystem : IDisposable
private readonly PenumbraChangedItemTooltip _penumbraTooltip; private readonly PenumbraChangedItemTooltip _penumbraTooltip;
public GlamourerWindowSystem(UiBuilder uiBuilder, MainWindow ui, GenericPopupWindow popups, PenumbraChangedItemTooltip penumbraTooltip, public GlamourerWindowSystem(UiBuilder uiBuilder, MainWindow ui, GenericPopupWindow popups, PenumbraChangedItemTooltip penumbraTooltip,
Configuration config, UnlocksTab unlocksTab, GlamourerChangelog changelog) Configuration config, UnlocksTab unlocksTab, GlamourerChangelog changelog, DesignQuickBar quick)
{ {
_uiBuilder = uiBuilder; _uiBuilder = uiBuilder;
_ui = ui; _ui = ui;
@ -22,6 +22,7 @@ public class GlamourerWindowSystem : IDisposable
_windowSystem.AddWindow(popups); _windowSystem.AddWindow(popups);
_windowSystem.AddWindow(unlocksTab); _windowSystem.AddWindow(unlocksTab);
_windowSystem.AddWindow(changelog.Changelog); _windowSystem.AddWindow(changelog.Changelog);
_windowSystem.AddWindow(quick);
_uiBuilder.Draw += _windowSystem.Draw; _uiBuilder.Draw += _windowSystem.Draw;
_uiBuilder.OpenConfigUi += _ui.Toggle; _uiBuilder.OpenConfigUi += _ui.Toggle;
_uiBuilder.DisableCutsceneUiHide = !config.HideWindowInCutscene; _uiBuilder.DisableCutsceneUiHide = !config.HideWindowInCutscene;

View file

@ -1,6 +1,6 @@
using System; using System;
using System.Numerics; using System.Numerics;
using Dalamud.Interface; using Dalamud.Interface.Utility;
using Dalamud.Interface.Windowing; using Dalamud.Interface.Windowing;
using Dalamud.Plugin; using Dalamud.Plugin;
using Glamourer.Designs; using Glamourer.Designs;
@ -30,10 +30,10 @@ public class MainWindow : Window, IDisposable
Messages = 6, Messages = 6,
} }
private readonly Configuration _config; private readonly Configuration _config;
private readonly TabSelected _event; private readonly DesignQuickBar _quickBar;
private readonly ConvenienceRevertButtons _convenienceButtons; private readonly TabSelected _event;
private readonly ITab[] _tabs; private readonly ITab[] _tabs;
public readonly SettingsTab Settings; public readonly SettingsTab Settings;
public readonly ActorTab Actors; public readonly ActorTab Actors;
@ -46,8 +46,7 @@ public class MainWindow : Window, IDisposable
public TabType SelectTab = TabType.None; public TabType SelectTab = TabType.None;
public MainWindow(DalamudPluginInterface pi, Configuration config, SettingsTab settings, ActorTab actors, DesignTab designs, public MainWindow(DalamudPluginInterface pi, Configuration config, SettingsTab settings, ActorTab actors, DesignTab designs,
DebugTab debugTab, AutomationTab automation, UnlocksTab unlocks, TabSelected @event, ConvenienceRevertButtons convenienceButtons, DebugTab debugTab, AutomationTab automation, UnlocksTab unlocks, TabSelected @event, MessagesTab messages, DesignQuickBar quickBar)
MessagesTab messages)
: base(GetLabel()) : base(GetLabel())
{ {
pi.UiBuilder.DisableGposeUiHide = true; pi.UiBuilder.DisableGposeUiHide = true;
@ -56,16 +55,16 @@ public class MainWindow : Window, IDisposable
MinimumSize = new Vector2(700, 675), MinimumSize = new Vector2(700, 675),
MaximumSize = ImGui.GetIO().DisplaySize, MaximumSize = ImGui.GetIO().DisplaySize,
}; };
Settings = settings; Settings = settings;
Actors = actors; Actors = actors;
Designs = designs; Designs = designs;
Automation = automation; Automation = automation;
Debug = debugTab; Debug = debugTab;
Unlocks = unlocks; Unlocks = unlocks;
_event = @event; _event = @event;
_convenienceButtons = convenienceButtons; Messages = messages;
Messages = messages; _quickBar = quickBar;
_config = config; _config = config;
_tabs = new ITab[] _tabs = new ITab[]
{ {
settings, settings,
@ -93,7 +92,11 @@ public class MainWindow : Window, IDisposable
_config.Save(); _config.Save();
} }
_convenienceButtons.DrawButtons(yPos); if (_config.ShowQuickBarInTabs)
{
ImGui.SetCursorPos(new Vector2(ImGui.GetWindowContentRegionMax().X - 10 * ImGui.GetFrameHeight(), yPos - ImGuiHelpers.GlobalScale));
_quickBar.Draw();
}
} }
private ReadOnlySpan<byte> ToLabel(TabType type) private ReadOnlySpan<byte> ToLabel(TabType type)

View file

@ -1,109 +0,0 @@
using System;
using System.Linq;
using Dalamud.Interface.Utility;
using Glamourer.Automation;
using Glamourer.Customization;
using Glamourer.Designs;
using Glamourer.Events;
using Glamourer.Services;
using ImGuiNET;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Log;
using OtterGui.Widgets;
namespace Glamourer.Gui.Tabs.AutomationTab;
public sealed class DesignCombo : FilterComboCache<(Design, string)>
{
public const int RevertDesignIndex = -1228;
public readonly Design RevertDesign;
private readonly AutoDesignManager _manager;
private readonly TabSelected _tabSelected;
private float _innerWidth;
public DesignCombo(AutoDesignManager manager, DesignManager designs, DesignFileSystem fileSystem, TabSelected tabSelected,
ItemManager items, CustomizationService customize, Logger log)
: this(manager, designs, fileSystem, tabSelected, CreateRevertDesign(customize, items), log)
{ }
private DesignCombo(AutoDesignManager manager, DesignManager designs, DesignFileSystem fileSystem, TabSelected tabSelected,
Design revertDesign, Logger log)
: base(() => designs.Designs.Select(d => (d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty)).OrderBy(d => d.Item2)
.Prepend((revertDesign, string.Empty)).ToList(), log)
{
_manager = manager;
_tabSelected = tabSelected;
RevertDesign = revertDesign;
}
protected override bool DrawSelectable(int globalIdx, bool selected)
{
var ret = base.DrawSelectable(globalIdx, selected);
var (design, path) = Items[globalIdx];
if (path.Length > 0 && design.Name != path)
{
var start = ImGui.GetItemRectMin();
var pos = start.X + ImGui.CalcTextSize(design.Name).X;
var maxSize = ImGui.GetWindowPos().X + ImGui.GetWindowContentRegionMax().X;
var remainingSpace = maxSize - pos;
var requiredSize = ImGui.CalcTextSize(path).X + ImGui.GetStyle().ItemInnerSpacing.X;
var offset = remainingSpace - requiredSize;
if (ImGui.GetScrollMaxY() == 0)
offset -= ImGui.GetStyle().ItemInnerSpacing.X;
if (offset < ImGui.GetStyle().ItemSpacing.X)
ImGuiUtil.HoverTooltip(path);
else
ImGui.GetWindowDrawList().AddText(start with { X = pos + offset },
ImGui.GetColorU32(ImGuiCol.TextDisabled), path);
}
return ret;
}
protected override float GetFilterWidth()
=> _innerWidth - 2 * ImGui.GetStyle().FramePadding.X;
public void Draw(AutoDesignSet set, AutoDesign? design, int autoDesignIndex, bool incognito)
{
_innerWidth = 400 * ImGuiHelpers.GlobalScale;
CurrentSelectionIdx = Math.Max(Items.IndexOf(p => design?.Design == p.Item1), 0);
CurrentSelection = Items[CurrentSelectionIdx];
var name = design?.Name(incognito) ?? "Select Design Here...";
if (Draw("##design", name, string.Empty, ImGui.GetContentRegionAvail().X,
ImGui.GetTextLineHeightWithSpacing())
&& CurrentSelection.Item1 != null)
{
if (autoDesignIndex >= 0)
_manager.ChangeDesign(set, autoDesignIndex, CurrentSelection.Item1 == RevertDesign ? null : CurrentSelection.Item1);
else
_manager.AddDesign(set, CurrentSelection.Item1 == RevertDesign ? null : CurrentSelection.Item1);
}
if (design?.Design != null)
{
if (ImGui.IsItemClicked(ImGuiMouseButton.Right) && ImGui.GetIO().KeyCtrl)
_tabSelected.Invoke(MainWindow.TabType.Designs, design.Design);
ImGuiUtil.HoverTooltip("Control + Right-Click to move to design.");
}
}
protected override string ToString((Design, string) obj)
=> obj.Item1.Name.Text;
protected override bool IsVisible(int globalIndex, LowerString filter)
{
var (design, path) = Items[globalIndex];
return filter.IsContained(path) || design.Name.Lower.Contains(filter.Lower);
}
private static Design CreateRevertDesign(CustomizationService customize, ItemManager items)
=> new(customize, items)
{
Index = RevertDesignIndex,
Name = AutoDesign.RevertName,
ApplyCustomize = CustomizeFlagExtensions.AllRelevant,
};
}

View file

@ -30,7 +30,7 @@ public class SetPanel
private readonly CustomizationService _customizations; private readonly CustomizationService _customizations;
private readonly Configuration _config; private readonly Configuration _config;
private readonly DesignCombo _designCombo; private readonly RevertDesignCombo _designCombo;
private readonly JobGroupCombo _jobGroupCombo; private readonly JobGroupCombo _jobGroupCombo;
private readonly IdentifierDrawer _identifierDrawer; private readonly IdentifierDrawer _identifierDrawer;
@ -39,7 +39,7 @@ public class SetPanel
private Action? _endAction; private Action? _endAction;
public SetPanel(SetSelector selector, AutoDesignManager manager, JobService jobs, ItemUnlockManager itemUnlocks, DesignCombo designCombo, public SetPanel(SetSelector selector, AutoDesignManager manager, JobService jobs, ItemUnlockManager itemUnlocks, RevertDesignCombo designCombo,
CustomizeUnlockManager customizeUnlocks, CustomizationService customizations, IdentifierDrawer identifierDrawer, Configuration config) CustomizeUnlockManager customizeUnlocks, CustomizationService customizations, IdentifierDrawer identifierDrawer, Configuration config)
{ {
_selector = selector; _selector = selector;
@ -209,7 +209,7 @@ public class SetPanel
ImGui.Selectable($"#{idx + 1:D2}"); ImGui.Selectable($"#{idx + 1:D2}");
DrawDragDrop(Selection, idx); DrawDragDrop(Selection, idx);
ImGui.TableNextColumn(); ImGui.TableNextColumn();
_designCombo.Draw(Selection, design, idx, _selector.IncognitoMode); _designCombo.Draw(Selection, design, idx);
DrawDragDrop(Selection, idx); DrawDragDrop(Selection, idx);
if (singleRow) if (singleRow)
{ {
@ -237,7 +237,7 @@ public class SetPanel
ImGui.AlignTextToFramePadding(); ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("New"); ImGui.TextUnformatted("New");
ImGui.TableNextColumn(); ImGui.TableNextColumn();
_designCombo.Draw(Selection, null, -1, _selector.IncognitoMode); _designCombo.Draw(Selection, null, -1);
ImGui.TableNextRow(); ImGui.TableNextRow();
_endAction?.Invoke(); _endAction?.Invoke();

View file

@ -1,9 +1,12 @@
using System; using System;
using System.Linq;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Dalamud.Game.ClientState.Keys;
using Dalamud.Interface; using Dalamud.Interface;
using Dalamud.Interface.Components; using Dalamud.Interface.Components;
using Dalamud.Interface.Utility; using Dalamud.Interface.Utility;
using Dalamud.Plugin.Services;
using Glamourer.Gui.Tabs.DesignTab; using Glamourer.Gui.Tabs.DesignTab;
using Glamourer.Interop; using Glamourer.Interop;
using Glamourer.Interop.Penumbra; using Glamourer.Interop.Penumbra;
@ -18,6 +21,7 @@ namespace Glamourer.Gui.Tabs;
public class SettingsTab : ITab public class SettingsTab : ITab
{ {
private readonly VirtualKey[] _validKeys;
private readonly Configuration _config; private readonly Configuration _config;
private readonly DesignFileSystemSelector _selector; private readonly DesignFileSystemSelector _selector;
private readonly StateListener _stateListener; private readonly StateListener _stateListener;
@ -30,7 +34,7 @@ public class SettingsTab : ITab
public SettingsTab(Configuration config, DesignFileSystemSelector selector, StateListener stateListener, public SettingsTab(Configuration config, DesignFileSystemSelector selector, StateListener stateListener,
CodeService codeService, PenumbraAutoRedraw autoRedraw, ContextMenuService contextMenuService, UiBuilder uiBuilder, CodeService codeService, PenumbraAutoRedraw autoRedraw, ContextMenuService contextMenuService, UiBuilder uiBuilder,
GlamourerChangelog changelog, FunModule funModule) GlamourerChangelog changelog, FunModule funModule, IKeyState keys)
{ {
_config = config; _config = config;
_selector = selector; _selector = selector;
@ -41,6 +45,7 @@ public class SettingsTab : ITab
_uiBuilder = uiBuilder; _uiBuilder = uiBuilder;
_changelog = changelog; _changelog = changelog;
_funModule = funModule; _funModule = funModule;
_validKeys = keys.GetValidVirtualKeys().Prepend(VirtualKey.NO_KEY).ToArray();
} }
public ReadOnlySpan<byte> Label public ReadOnlySpan<byte> Label
@ -99,11 +104,23 @@ public class SettingsTab : ITab
if (!ImGui.CollapsingHeader("Interface")) if (!ImGui.CollapsingHeader("Interface"))
return; return;
Checkbox("Smaller Equip Display", "Use single-line display without icons and small dye buttons instead of double-line display.", Checkbox("Show Quick Design Bar",
_config.SmallEquip, v => _config.SmallEquip = v); "Show a bar separate from the main window that allows you to quickly apply designs or revert your character and target.",
Checkbox("Show Application Checkboxes", _config.ShowDesignQuickBar, v => _config.ShowDesignQuickBar = v);
"Show the application checkboxes in the Customization and Equipment panels of the design tab, instead of only showing them under Application Rules.", Checkbox("Lock Quick Design Bar", "Prevent the quick design bar from being moved and lock it in place.", _config.LockDesignQuickBar,
!_config.HideApplyCheckmarks, v => _config.HideApplyCheckmarks = !v); v => _config.LockDesignQuickBar = v);
if (Widget.ModifiableKeySelector("Hotkey to Toggle Quick Design Bar", "Set a hotkey that opens or closes the quick design bar.",
100 * ImGuiHelpers.GlobalScale,
_config.ToggleQuickDesignBar, v => _config.ToggleQuickDesignBar = v, _validKeys))
_config.Save();
Checkbox("Show Quick Design Bar in Main Window",
"Show the quick design bar in the tab selection part of the main window, too.",
_config.ShowQuickBarInTabs, v => _config.ShowQuickBarInTabs = v);
ImGui.Dummy(Vector2.Zero);
ImGui.Separator();
ImGui.Dummy(Vector2.Zero);
Checkbox("Enable Game Context Menus", "Whether to show a Try On via Glamourer button on context menus for equippable items.", Checkbox("Enable Game Context Menus", "Whether to show a Try On via Glamourer button on context menus for equippable items.",
_config.EnableGameContextMenu, v => _config.EnableGameContextMenu, v =>
{ {
@ -120,14 +137,29 @@ public class SettingsTab : ITab
_config.HideWindowInCutscene = v; _config.HideWindowInCutscene = v;
_uiBuilder.DisableCutsceneUiHide = !v; _uiBuilder.DisableCutsceneUiHide = !v;
}); });
ImGui.Dummy(Vector2.Zero);
ImGui.Separator();
ImGui.Dummy(Vector2.Zero);
Checkbox("Smaller Equip Display", "Use single-line display without icons and small dye buttons instead of double-line display.",
_config.SmallEquip, v => _config.SmallEquip = v);
Checkbox("Show Application Checkboxes",
"Show the application checkboxes in the Customization and Equipment panels of the design tab, instead of only showing them under Application Rules.",
!_config.HideApplyCheckmarks, v => _config.HideApplyCheckmarks = !v);
if (Widget.DoubleModifierSelector("Design Deletion Modifier", if (Widget.DoubleModifierSelector("Design Deletion Modifier",
"A modifier you need to hold while clicking the Delete Design button for it to take effect.", 100 * ImGuiHelpers.GlobalScale, "A modifier you need to hold while clicking the Delete Design button for it to take effect.", 100 * ImGuiHelpers.GlobalScale,
_config.DeleteDesignModifier, v => _config.DeleteDesignModifier = v)) _config.DeleteDesignModifier, v => _config.DeleteDesignModifier = v))
_config.Save(); _config.Save();
DrawFolderSortType();
Checkbox("Auto-Open Design Folders", Checkbox("Auto-Open Design Folders",
"Have design folders open or closed as their default state after launching.", _config.OpenFoldersByDefault, "Have design folders open or closed as their default state after launching.", _config.OpenFoldersByDefault,
v => _config.OpenFoldersByDefault = v); v => _config.OpenFoldersByDefault = v);
DrawFolderSortType();
ImGui.Dummy(Vector2.Zero);
ImGui.Separator();
ImGui.Dummy(Vector2.Zero);
Checkbox("Show all Application Rule Checkboxes for Automation", Checkbox("Show all Application Rule Checkboxes for Automation",
"Show multiple separate application rule checkboxes for automated designs, instead of a single box for enabling or disabling.", "Show multiple separate application rule checkboxes for automated designs, instead of a single box for enabling or disabling.",
_config.ShowAllAutomatedApplicationRules, v => _config.ShowAllAutomatedApplicationRules = v); _config.ShowAllAutomatedApplicationRules, v => _config.ShowAllAutomatedApplicationRules = v);

View file

@ -131,6 +131,7 @@ public static class ServiceManager
.AddSingleton<DesignPanel>() .AddSingleton<DesignPanel>()
.AddSingleton<DesignTab>() .AddSingleton<DesignTab>()
.AddSingleton<DesignCombo>() .AddSingleton<DesignCombo>()
.AddSingleton<RevertDesignCombo>()
.AddSingleton<ModAssociationsTab>() .AddSingleton<ModAssociationsTab>()
.AddSingleton<DesignDetailTab>() .AddSingleton<DesignDetailTab>()
.AddSingleton<UnlockTable>() .AddSingleton<UnlockTable>()
@ -142,7 +143,7 @@ public static class ServiceManager
.AddSingleton<SetPanel>() .AddSingleton<SetPanel>()
.AddSingleton<IdentifierDrawer>() .AddSingleton<IdentifierDrawer>()
.AddSingleton<GlamourerChangelog>() .AddSingleton<GlamourerChangelog>()
.AddSingleton<ConvenienceRevertButtons>(); .AddSingleton<DesignQuickBar>();
private static IServiceCollection AddApi(this IServiceCollection services) private static IServiceCollection AddApi(this IServiceCollection services)
=> services.AddSingleton<CommandService>() => services.AddSingleton<CommandService>()